
前阵子有个做AI客服的朋友跟我抱怨说他们接的报销流程机器人用户聊到第十轮就开始“失忆”。明明前面已经确认了报销金额和发票类型后面让用户补一个银行卡号模型却突然反问“您是要报销哪一笔费用”气得用户当场转人工投诉。我一看日志典型的context-mode没设计好上下文被窗口机制硬生生截断了。这类问题在LLM应用开发里太常见了很多人根本没把“上下文”当成一个需要专门设计的模块而是完全依赖模型的上下文窗口硬扛扛不住就崩。context-mode这个话题说白了就是在不同的任务阶段用什么样的策略来组织、压缩、检索和传递上下文信息。它解决的是大模型在实际落地中最头疼的“记不住”“记不准”“记不全”三件事。这篇文章我打算把我在多个项目里实际用过的context-mode方案完整拆开讲包括五种主流模式的选型逻辑、一套可以直接抄作业的上下文管理器实现、以及踩坑经验。适合正在做AI应用开发、Prompt Engineering或者技术选型阶段被上下文预算折磨的同行参考。1. context-mode到底在解决什么问题1.1 从一次真实的翻车现场说起还是说回那个报销机器人。用户从“我要报销”开始经历了上传发票、识别金额、填写事由、确认收款账户四个阶段。前四轮一切正常问题出在第五轮之后。用户中途问了一句“交通费的报销上限是多少”然后又问“打车票可以报吗”问完才继续之前的流程。结果模型把“交通费”“打车票”这些新信息和之前的“报销住宿费”混在一起最后生成的报销单变成了“住宿费300元、交通费200元”两张单子而用户实际只提交了一张住宿发票。这个问题的根因不是模型笨而是当时整个对话的上下文模式是“全量堆叠先入先出”。新问题进来后旧的关键信息住宿费金额、发票编号没有被打上“高优先级”的标签随着token数逼近窗口上限最老的那些关键字段被最先挤出了上下文。模型在缺失关键信息的情况下只能根据最近几轮的碎片化内容“脑补”于是产生了幻觉。这种翻车在真实业务里每天都在发生。你以为你给模型配了128K的上下文窗口就万事大吉但实际上窗口只要还没被撑爆模型对信息的记忆强度也是不均匀的。OpenAI和Anthropic自己的技术文档里都承认过模型对中间位置的上下文关注度明显低于开头和结尾这就是“Lost in the Middle”现象。所以context-mode的第一个核心任务就是要主动管理上下文的“注意力分布”而不是把宝全押在窗口大小上。1.2 上下文失效的三个典型层次我做了几年LLM应用把上下文失效的问题归纳成了三个层次每个层次的解法完全不同。第一层是窗口溢出也就是token数超过模型上限系统被迫截断最老的内容。这是最显性、也最好排查的一层。你只需要数一数每次请求的prompt里塞了多少token再对比模型的上下文上限就能判断是不是这种问题。解法也是最成熟的包括滑动窗口、摘要压缩、向量检索后面我会展开讲。第二层是信息过载也就是上下文总长度没超限但相关度高的信息被大量无关信息稀释了。打个比方你让一个实习生去整理合同重点你把100页合同全丢给他他也会晕——哪怕这100页他都能看完但他不知道哪些是老板真正关心的。模型也一样当上下文中塞了10轮聊天记录其中只有2轮和当前任务强相关模型往往会被那8轮无关内容带偏。这一层的问题是“信息密度”不够光靠加大窗口没用反而会加剧。第三层是语义漂移这是最隐蔽也最致命的。每一轮对话单独看都对但前后拼接起来语义却悄悄发生了偏移。最常见的是用户说“那个”“这个”“跟刚才一样”这类指代模型需要结合多轮历史才能正确解析。而如果context-mode设计得不好比如把用户早期的“不要住宿发票”这个意图给压缩丢了后面模型就可能默认所有发票都可以报销。这种漂移不像截断那么明显但累积起来会让整个任务彻底偏航。我见过不少项目调了一两个月的prompt还是不稳定最后定位到是历史上下文在传递过程中被“传歪了”。1.3 为什么“无限上下文”不是银弹这两年模型厂商都在卷上下文长度从4K卷到128K再到1M甚至号称“无限上下文”。很多产品经理一听就说那不是context-mode不用做了反正都能记住。实际情况完全不是这样。我实测过一些长上下文模型把20万字的小说塞进去问它第三卷某个配角的第一次出场场景它能答对但你再追问这个配角和他表哥之间的关系变化轨迹它就开始东拉西扯。为什么因为关联推理需要的信息分散在超长上下文的各个角落模型在生成时需要“同时激活”这些分散的信息点而Transformer的自注意力机制在这种超长跨度下的推理能力是明显衰减的。另外还有一个更现实的问题——成本。自注意力机制的计算量随序列长度呈平方级增长也就是说上下文长度翻倍计算成本大约翻四倍。你把1M上下文全塞进去一次请求的账单够你心疼半天。所以就算模型支持超长上下文工程上也会用context-mode把真正喂给模型的内容控制在合理范围内。无限上下文解决不了注意力稀释和成本爆炸它只是给了你更大的缓冲区而context-mode才是真正决定这个缓冲区怎么用的调度策略。2. 五种主流context-mode拆解2.1 全量携带模式最稳但也最贵全量携带模式是最朴素的做法就是把所有历史记录原封不动地拼到当前请求里。早期做ChatBot基本都是这么干的prompt里一个messages数组越堆越长直到某一天收到一个“Context length exceeded”的报错。这种模式的优点在于零信息损失所有历史细节都保留着模型不会因为缺少某个字段而犯错。在任务步骤少、对话轮次短比如3到5轮、上下文总量远低于窗口上限的时候全量携带其实是最优解。你不需要做任何压缩和检索逻辑最简单调试也最直观。它的缺点同样明显。一是成本线性上涨每轮请求都要把之前的全部内容重新发送一遍token消耗随对话轮次增长用户聊得越久你越亏。二是干扰增强历史记录越多模型被无关信息带偏的概率越大。我做过的很多客服机器人项目里用户早期随口说的“我随便看看”这种话到后面会被模型当成购买意向反复追问。后来我把开场白阶段和咨询阶段的信息做了分离处理情况才好转。所以全量携带模式只适合短对话场景真要放开用建议在工程层面做一个对话轮次上限的硬限制超过N轮强制切换模式。2.2 滑动窗口模式简单粗暴的截断术滑动窗口模式稍微进阶一点只保留最近N轮对话更早的内容直接丢弃。就好比你手机聊天记录只显示最近100条更早的想翻也翻不到。这种模式的核心参数是窗口大小N。N设置得太大击穿上下文窗口上限的风险高N设置得太小很多需要远期信息的任务就直接废掉。一个比较实用的经验值对于大多数客服、问答场景N取8到15轮比较合适对于需要跨多步骤操作的流程型任务比如报销、售后处理建议至少保留到上个任务阶段完成的位置不要按轮次硬切而是按“任务阶段”切。不过滑动窗口有个隐藏风险就是“虚假安全感”。你看着每轮请求都在窗口内但实际上关键的远期信息已经被丢掉了而模型不会告诉你它忘了它只会基于不完整信息“自信地胡说”。我见过一个金融咨询机器人用户三天前问过“我不考虑高风险产品”第三天回来再问“帮我推荐几只基金”模型因为窗口滑掉了三天前的偏好设定推荐的全是高波动品类。这个锅不能全甩给模型窗口策略没考虑关键偏好的持久化才是根本原因。所以滑动窗口适合在成本敏感、且任务对远期信息依赖度低的场景用。如果你的业务里用户很可能在几天后回来继续一个长流程任务滑动窗口必须搭配一个关键信息抽取模块——把重要的偏好、承诺、状态字段单独存出来每次拼回窗口里。2.3 摘要压缩模式让模型替你做减法摘要压缩模式通俗说就是找个模型当“课代表”把一堆历史对话浓缩成一份精炼的笔记以后每轮都带着这份笔记。实际实现一般有两种思路。一种是每次对话结束后用LLM把新对话追加到旧摘要上生成一份全局摘要另一种是按固定步长做增量摘要每累计N轮或者每满一定token量就把新增部分摘要一次。两种方式各有优劣前者摘要质量更连贯但每次都要重写全部内容成本高后者效率高但多轮增量之后摘要内部可能出现“口径不一”的问题。摘要压缩模式最大的坑有两个。一个是大篇幅压缩必然导致细节丢失。用户报过一个发票编号“NO.20240218”摘要压着压着就变成了“用户有一张发票”再压几轮干脆变成“用户提交过发票”。这类关键字段在摘要里根本留不住。所以做摘要时不能只丢给模型一句“请总结”而是要给它一个带关键词提取的模板让它优先保留实体、数字、否定句和承诺类信息。另一个坑是摘要本身也是一个LLM调用的产物它也会出错、也会漂移一旦源头摘要错了后面所有对话都建立在错误之上。我的建议是摘要压缩模式适合用来做“中间层记忆”而不是“唯一记忆”。也就是摘要作为阅读材料核心事实字段仍然以key-value的形式单独存储跟摘要一起参与上下文组装。这样即使摘要发生漂移关键字段还能兜底校正。2.4 向量检索模式给上下文装上搜索引擎向量检索模式是RAG检索增强生成在对话管理上的延伸应用。核心思路是把所有历史消息切片、向量化存入向量数据库每次新对话进来用当前问题去检索最相关的历史片段把检索结果拼进上下文。这种模式最大的优势是突破了“时间顺序”的限制。滑动窗口和摘要压缩都是基于时间线削峰填谷而向量检索是基于“语义相关性”取数。用户三天前随口提到的一句话只要和当前问题语义相关照样能被捞回来。这对于那些跨度长、话题跳跃多的场景特别有用比如法律咨询、医疗问诊、复杂售后。但在实战中向量检索的坑也非常深。首先切片策略会影响检索质量。切得太碎比如一句一切语义不完整切得太粗比如一大段一切向量语义容易被稀释检索精度下降。我一般建议按“一个完整意图”为边界来切比如用户的一段连续输入或助手的一整段回答。其次向量相似度不等于“信息重要性”。检索回来的片段可能相关但未必是当前决策的关键依据需要再加一道过滤或重排序rerank逻辑。最后向量库的召回结果需要设置相似度阈值低于阈值宁可不放进去否则反而引入噪声。我不建议把向量检索当成唯一的context-mode而是把它和前面的模式组合使用。比如对用户历史记录里的“硬性偏好”单独建索引每次对话必须优先召回对普通的寒暄、闲聊、过程性内容则做摘要和淘汰。2.5 混合路由模式实用主义者的最终答案我做了这么多项目之后发现实际线上跑得最稳的方案几乎都是混合路由——根据当前对话的状态动态选择上下文组织方式而不是从头到尾死守一种策略。混合路由的核心是设置一个“路由判据”。最简单的判据是token阈值当前上下文预估token数不足窗口的60%时全量携带超过60%时先尝试压缩摘要压缩后仍不足再转向量检索。更精细的做法是按任务类型路由——如果当前对话属于“信息确认类”那就必须保证历史中所有确认过的结论都在上下文中如果属于“闲聊类”只保留最近几轮就够。但是混合路由也有它的代价就是复杂度显著上升。调试时要同时盯着规则逻辑、各模式的触发条件和切换时的信息损失任何一个环节出了bug都很难排查。我在生产环境里见过一些团队因为混合路由规则写得太复杂导致同一个问题每次答案都不一样。所以我的建议是混合路由要从简单规则起步跑通了再加判据不要一上来就搭一个全家桶。稳定优先花哨其次。3. 实操落地构建一个最小可用的context-mode管理器3.1 先定义上下文单元动手写代码之前我强烈建议先做一步你可能会觉得多余的“建模”工作把“上下文”这个概念拆成明确的单元结构。不要用整个对话作为一个单位来管理那样太粗糙后面做压缩和检索都无从下手。我用的是一个叫ContextUnit的结构每个单元代表一段完整的最小语义块。class ContextUnit: def __init__(self, uid, role, content, importance1.0, created_atNone, metadataNone): self.uid uid # 全局唯一ID self.role role # user / assistant / system self.content content # 文本内容 self.importance importance # 重要性权重0到1 self.created_at created_at or time.time() # 创建时间 self.metadata metadata or {} # 附加信息如任务阶段、主题分类这里面的importance字段是灵魂。它在运行时不是固定不变的而是根据后续对话动态调整的。比如用户明确说“这个很重要”“记住这一点”或者系统内部判定某条信息承载了当前任务的关键状态就把它的importance权重调高。有了这个字段后续的压缩和丢弃策略就有了排序的依据而不是盲目按时间顺序一刀切。3.2 权重衰减算法有了上下文单元之后下一步要解决的是“哪些信息该保留、哪些可以放弃”的排序问题。我用的是一个非常简单的加权打分不需要什么复杂公式。def compute_priority(unit, current_time, current_task_idNone): recency math.exp(-(current_time - unit.created_at) / config.half_life) importance unit.importance task_bonus 0.15 if (current_task_id and unit.metadata.get(task_id) current_task_id) else 0 return 0.5 * recency 0.4 * importance task_bonus这个公式的思路是一条上下文信息对当前决策的价值由三个因素共同决定。第一个因素是时效性刚聊过的内容理应先保留但引入指数衰减而不是线性衰减是因为对话热度的下降在现实中是“先快后慢”的——刚过5分钟的内容和过了5小时的内容重要性差距非常大但过了5天和过了8天的内容差距就没那么大了。第二个因素是重要性权重这个不随时间去只要系统标记过“重要”就一直在哪。第三个因素是任务归属如果这条信息和当前执行的任务阶段匹配额外加分。实际使用中我每轮对话结束后会更新所有在线单元的分数然后按分数排序。当上下文预算快用完时分数最低的单元会先被送去做摘要压缩而不是直接物理删除。这套逻辑跑起来之后之前那种“重要字段被挤出去”的翻车就很少再发生了。3.3 核心代码实现下面给出一套可以直接复制的代码骨架。这是一个简单的ContextModeManager实现了全量、滑动窗口、摘要、向量检索四种模式的选择和组装。import json import math import time from typing import List, Optional class ContextModeManager: def __init__(self, max_tokens6000, window_size10, half_life3000): self.units: List[ContextUnit] [] self.max_tokens max_tokens self.window_size window_size # 滑动窗口轮数 self.half_life half_life # 衰减半衰期单位秒 def add_unit(self, role, content, importance1.0, metadataNone): unit ContextUnit( uidfu_{int(time.time()*1000)}_{len(self.units)}, rolerole, contentcontent, importanceimportance, metadatametadata ) self.units.append(unit) def _estimate_tokens(self, text): # 中文场景下粗略按1字约1.5 token估算实际生产建议用tokenizer精确计算 return int(len(text) * 1.5) def _assemble_full(self, prompt): parts [] for unit in self.units: parts.append(f{unit.role}: {unit.content}) text \n.join(parts) f\nuser: {prompt} return text, self._estimate_tokens(text) def _assemble_sliding_window(self, prompt): recent_units self.units[-self.window_size*2:] # userassistant算一组 parts [f{u.role}: {u.content} for u in recent_units] text \n.join(parts) f\nuser: {prompt} return text, self._estimate_tokens(text) def _assemble_summary_and_core(self, prompt, summary): core_fields [u.content for u in self.units if u.importance 0.75] parts [f摘要: {summary}] if core_fields: parts.append(关键字段: ; .join(core_fields[-5:])) parts.append(fuser: {prompt}) text \n.join(parts) return text, self._estimate_tokens(text) def build_prompt(self, prompt, summary, modeauto): if mode full: return self._assemble_full(prompt) elif mode sliding: return self._assemble_sliding_window(prompt) elif mode summary: return self._assemble_summary_and_core(prompt, summary) # auto模式从full开始超限逐级降级 text, tokens self._assemble_full(prompt) if tokens self.max_tokens: return text, tokens text, tokens self._assemble_sliding_window(prompt) if tokens self.max_tokens: return text, tokens # 最后一层压缩摘要 高重要性字段 最近对话 recent_units self.units[-2:] recent_text \n.join([f{u.role}: {u.content} for u in recent_units]) text f摘要: {summary}\n最近对话:\n{recent_text}\nuser: {prompt} return text, self._estimate_tokens(text)这段代码的核心价值在于给你一个降级链一开始全量token超了就滑窗滑窗还超就用“摘要关键字段最近对话”兜底。每次降级都保证prompt里一定包含当前用户输入不会出现把用户问题都截掉的情况。实际生产环境里我通常还会在这个Manager外面套一层缓存层。同一个会话的摘要结果和核心字段缓存到Redis里过期时间设为2小时这样用户隔一会儿回来继续聊就不用重新算一遍摘要成本能省一半以上。3.4 关键参数调优经验关于max_tokens、window_size、half_life这三个核心参数我分享一下我踩过坑之后沉淀下来的调参方法。max_tokens别贪大建议设在模型上下文上限的60%到70%之间。为什么留这么多余量因为模型的输出也要占token而且每次调用系统prompt、few-shot示例、当前输入都额外占空间。你如果直接把上限拉到80%一旦单轮输出比较长就会直接触发超限报错。我习惯的做法是先压到50%跑通观察线上token分布再慢慢往上调。window_size这个参数很玄它不是越大越好。窗口越大信息越全但噪声也越多模型越容易被无关内容干扰。我做过一组对比实验同样一个7轮内能完成的报销流程任务window_size设成5轮时流程完成率91%设成10轮反而掉到84%。原因就是第1、2轮的无关闲聊比如用户说“今天下雨”被窗口带进了后续的每个请求干扰了模型对当前任务的判断。后来我改成了按任务阶段切窗口效果明显改善。half_life这个参数控制的是历史信息的“冷却速度”。如果是客服机器人用户一次会话时长普遍在10分钟内half_life可以设短一些比如10分钟让更旧的信息快速降权如果是有长期记忆需求的场景比如持续数周的理财顾问half_life我会拉到48小时以上。基本思路就是业务周期越长half_life越长。4. 高频故障与排查技巧实录4.1 token消耗突然暴涨这是context-mode上线之后最容易碰到的问题。第二天看账单发现成本翻了三四倍第一反应通常是“是不是有人恶意刷接口”查了半天发现没有其实问题出在摘要被反复重算。我踩过的一个具体案例是团队在摘要模块里用了“全量重写式”摘要也就是每次对话结束都把全部历史交给LLM重新生成一份完整摘要。这本来没什么问题但某一天线上流量突然翻倍每轮对话都触发全量重算token消耗直接爆炸。排查的方法是给所有LLM调用加日志按调用类型聚合token消耗一眼就能看出是“summary”类型占比异常。解决方式很简单增量摘要缓存。第一份摘要生成后之后只把新增对话追加给模型让它在旧摘要基础上做小修补而不是全部重写。每份摘要打上版本号和更新时间避免同一份摘要被反复生成。这个方法我实测下来token消耗能降40%到60%。4.2 摘要越压越“变味”摘要压缩模式有一个很常见的慢性病随着压缩轮次增加摘要内容开始偏离原始事实。最典型的是数字错误、否定句丢失、条件被省略。比如用户原话是“单价超过500元的部分我不管”经过两轮压缩可能变成“我对500元负责”意思完全反了。原因很好理解——LLM做摘要时倾向于提炼“主干信息”而“否定条件边界”恰恰是最容易被当作修饰成分丢掉的。解决思路有两个层面。第一是在摘要prompt里强制加入保留规则明确要求“必须保留所有数字、所有人名/公司名、所有否定词不、非、未、所有边界条件超过、少于、之外”。第二是在系统架构层面对关键字段做结构化存储不依赖摘要来保留。每轮对话跑一个轻量级的槽位抽取slot extraction把关键实体和状态值抽成key-value存到Redis里组装prompt时直接拼进去。这样即使摘要把500元丢成5000元结构化字段里的500元还能兜底纠偏。4.3 向量检索捞回来的内容反而害了模型用了向量检索模式之后经常会出现一个诡异的现象上下文里明明有正确的历史信息模型给出的答案却更差了。排查下来多半是检索回来的片段在误导模型。举个例子用户在咨询电脑配置时提到过“我平时只做文档办公”隔天又来咨询显示器检索模块根据“文档办公”这个强特征召回了一大段旧对话。模型看到这段历史把显示器推荐也拉回到了“办公够用就行”的水准完全没有考虑用户这次问的是4K设计屏的需求。问题就出在检索只看了语义相似性没有看“对话意图是否匹配”。我在项目里的回避方式是对检索结果加一道意图过滤先把当前用户输入做个意图分类只有历史片段属于同一意图类别时才允许被召回。另外向量召回只能作为候选集真正进入prompt前还要用一个轻量级rerank模型或规则重新打分排序。设定一个阈值低于阈值宁可不召回。宁可少给信息也别给错误信息带偏模型。4.4 混合路由的“切换振荡”混合路由模式上线后我遇到过一种特别恼火的情况同一个用户连续问两个相似的问题第一次命中全量模式第二次突然降级到滑窗模式导致第二次回答明显比第一次“缺上下文”。用户体验很割裂而且很难排查因为每次请求返回的元信息里模式字段值看起来都正常。后来加了详细日志才定位到问题出在“按token阈值路由”的规则上。第一个问题时正好有缓存命中token数偏低走了全量第二个问题因为某个背景任务往上下文里塞了一个大文档token瞬间超标直接跳到了滑窗模式。同一上下文在不同模式之间反复横跳模型每次都拿到不同格式的prompt效果自然不稳定。解决方法是给路由规则加“模式锁定”和“模式平滑”机制。一旦某个会话在第一轮确定了模式后面不要轻易切换除非token连续N次超限或者用户明确换了任务。切换时要打印一条日志标记“mode_changedtrue”方便出问题时回溯。加了这个机制之后线上“同一个问题答案差异大”的投诉量明显下降。4.5 多轮纠错中的“回滚困难”多轮对话里用户经常要改前面已经确认过的信息比如一开始说“住宿费300”隔了两轮又改成“住宿费500”。context-mode如果不做特殊处理会出现两种尴尬第一种是模型记住了新值但旧值还残留在历史里导致模型自言自语时把自己绕晕第二种是如果旧值已经被滑窗淘汰新值又因为某种原因没被正确入库模型两头都抓不住。排查这类问题时我一般会先看结构化字段的当前值再看摘要文本里是否有旧值残留。如果字段值已经是500但摘要里还写着“住宿费300元”那就是更新不同步了。解决方式是在槽位抽取模块里增加一条“字段变更”事件一旦检测到同一字段的新值和旧值冲突触发一次摘要校正把旧值从摘要里显式替换掉而不是靠模型自己“领悟”。这个操作在实践里的效果立竿见影纠错后任务成功率能回升到正常水平。5. 模式扩展context-mode的下一步玩法5.1 与Agent记忆分层结合如果你的项目已经进化到了Agent形态也就是模型不只是被动问答而是会主动调用工具、规划任务那么context-mode就要升级成“分层记忆”体系了。目前我用的结构是三层工作记忆对应当前任务内的高频上下文使用高权重高频召回策略情景记忆对应历史会话的关键事件使用“摘要向量检索”双通道组合访问语义记忆对应用户的长期偏好、知识图谱类信息基本是结构化存储。三个层级之间的数据流动靠一个事件总线来驱动——当某个上下文单元在短时间被连续访问多次系统就把它从情景记忆“晋升”到工作记忆当某个任务长期未激活工作记忆里的内容就逐渐降级归档到情景记忆。这套结构跑下来Agent在长周期任务里的表现稳定了很多。比如一个理财规划Agent用户这周聊基金、下周聊保险再下周回来问“我的整体资产配置怎么样”它能通过语义记忆调出用户的风险偏好和已有持仓然后结合当前对话做全局分析而不是“重新认识用户”。没有分层记忆的话这种跨周的连贯性是很难做到的。5.2 多模态上下文的趋势另一个明显趋势是多模态上下文的加入。现在的context-mode多数只处理文本但实际业务里用户上传图片、语音甚至录屏的情况越来越常见。之前做个售后服务Agent用户拍了一张产品故障图进来模型需要同时理解图片内容、对话历史和之前的维修记录才能给出准确判断。这就是一个典型的多模态上下文问题。目前的工程做法是把图片先做预处理提取出结构化描述文本比如“屏幕左下角有两厘米的裂纹显示区域正常”再放进统一的context-unit里参与权重排序。这样算是对多模态的一种“折中接入”。长期来看模型直接支持图文混合的注意力计算后会更好但当前阶段扎实的结构化提取反而更实用——它能让你用现有的文本context-mode体系无缝管理多模态信息。我在实际使用中最大的体会是context-mode没有一个“放之四海皆准”的标准答案它更像是一个权衡的艺术在信息完整度、成本、延迟、模型理解力之间找平衡点。与其纠结哪个模式最先进不如先把一套带降级链的机制跑起来把日志和指标建好然后根据线上数据持续调优。等你把上下文管理这件事从“靠直觉”变成“有数据支撑”LLM应用的稳定性和可维护性会上一个真实的台阶。