news 2026/10/26 8:23:09

AI Agent越聊越笨?上下文工程5个关键取舍与实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AI Agent越聊越笨?上下文工程5个关键取舍与实践

先把话说在前面:我自己踩过一个特别典型的坑。半年前我做了一个带读网页、查资料、自动写总结这类工具的 Agent,丢进长时间会话里跑,前 10 轮表现惊为天人,说什么都能接住。可跑到第 25 轮左右,它开始记错用户偏好,甚至把一个已经完成的任务又原封不动执行了一遍。当时我第一反应是“模型智力不行”,后来把对话日志一帧一帧拉出来看,才意识到问题根本不在模型,而是上下文工程没做对。

AI Agent 的“越聊越笨”,绝大多数时候不是模型变蠢了,而是喂给模型的上下文出了问题。这个话题我观察了很久,发现凡是做 Agent 落地的人,最容易忽略、也最喜欢甩锅给模型的,恰恰就是这一层。我不打算讲模型选型,也不聊训练,只聊上下文工程里最关键的 5 个取舍。每个取舍背后都有代价,没有标准答案,只有适合当前场景的答案。适合正在做 AI Agent 搭建、天天纠结 token 预算、想让 Agent 稳定跑完长任务的开发者参考,看完可以直接拿这些取舍逻辑回自己项目里做决策。

1. 先搞清楚:Agent 是怎么“越聊越笨”的

1.1 上下文窗口不是硬盘,是白板

很多人把上下文窗口理解成“记忆硬盘”,以为窗口越大,记住的东西就越多。这个理解是错的。更准确的类比是一块白板:刚开始是干净的,你往上写流程、写需求,模型看得清清楚楚;写到后面,白板不够用了,要么新的内容把旧的盖上,要么你手动擦掉一部分。模型每次推理时,能看到的只有当前白板上的东西,而不是整个对话历史。

这解释了一个怪现象:同一个模型,你在对话框里问它简单问题,它聪明得很;丢进 Agent 里跑长任务,它却像个鱼一样只有 7 秒记忆。不是模型变了,是它眼前的白板被写满了,而且写上去的内容里,大部分是没有用的工具输出、中间思考过程、重复日志。

更麻烦的是,白板上的字迹不是越新越重要。用户的原始约束可能写在第 3 轮,到第 40 轮时已经被挤到边角甚至擦掉了,模型自然只能靠猜。猜的结果就是“一本正经地胡说八道”。

1.2 三种常见的衰减机制

我把长期跑 Agent 观察到的“变笨”过程归纳成三种机制,理解了它们,再看后面的 5 个取舍就顺了。

第一种是截断挤压。上下文窗口有硬上限,一旦达到上限,最常见的处理方式是“先进先出”,最早的内容被踢出去。早期用户说过的偏好、业务规则、禁止事项,往往就这么被悄悄挤掉了。等后面某个步骤需要用到这些信息时,模型拿不到,只能自己脑补,一补就偏。

第二种是信噪比劣化。token 总量有限,塞进来的内容里 80% 都是工具返回结果、日志、重复的系统提示,真正关键的指令只占很小一块。大家知道 Transformer 的注意力机制是“一视同仁”地扫描所有 token 的,信息密度低了,关键细节被淹没在噪声里,模型抓错重点的概率大幅上升。

第三种是错误级联。模型基于残缺上下文做出一步错误推测,这个推测会被当成新的“事实”写进后续上下文,后面的步骤全部建立在这个错误前提上。你以为它只是犯了一个错,实际上它是沿着错误一路狂奔,越聊越理直气壮。

2. 取舍一:上下文窗口留多长,才算“够用又不浪费”

2.1 窗口越大越聪明?别被参数带偏

市面上常见模型的上下文窗口从 8K、32K、128K 到 200K+ 都有。不少团队选模型时追着大窗口跑,心想“一次塞进去不会错”。但大窗口是有代价的,而且代价还不小:

首先是 token 费用。调用模型是按 token 计费的,上下文越长,每次调用的成本越高。一个 Agent 完成任务通常要调用十几二十次模型,上下文从 4K 涨到 8K,总成本直接翻倍。我见过一个自动化运营的 Agent 项目,上线一周成本爆掉,原因就是每次调用都把全部历史带上了。

其次是延迟。上下文长度直接影响推理速度,输入 token 翻倍,单次响应时间可能从 1 秒涨到 3 秒以上。用户感受是明显的“变卡了”。

再一个是注意力稀释。实验做多了你会发现,同样一个复杂指令,放在 4K 上下文里执行正确率明显高于塞在 20K 上下文中。上下文越长,模型越容易漏掉埋在中段的关键信息。所以“大窗口”只是给了你更多操作空间,不等于你应该每次都用满。

2.2 用 token 预算倒推窗口大小

我的习惯是:每个任务先算 token 预算,再定窗口策略,而不是反过来。

举个例子。假设你的 Agent 要做多轮信息收集和写作,单轮消息大约是:系统指令 1,000 token,用户输入 200 token,工具返回结果平均 600 token,模型回复 400 token。一轮完整交互大概 2,200 token。如果在一次会话里要跑 50 轮,全量保留就是 110K token,已经顶到主流窗口的上限,还没给你留任何缓冲。

这时候我会把预算拆成几块:

内容区块预算占比说明
系统级指令10%角色、输出格式、工具使用规则
长期事实区15%用户偏好、业务约束、项目背景
近期对话缓存40%最近 5~10 轮原文,滑动窗口
工具结果区25%最近任务的必要返回,其余摘要
检索增强区10%按需检索到的历史片段

如果上下文窗口是 128K,我通常会把硬上限设成 96K,留 25% 余量给突发情况。达到 80% 用量时就触发压缩或归档,而不是等窗口塞满了才处理。这个比例没有标准答案,但先算清楚每一轮大概消耗多少 token,再决定“最多保留几轮原文”,会让你对窗口使用有掌控感,而不是两眼一抹黑。

3. 取舍二:原文一字不差,还是压缩后保留

3.1 原文保留的最大价值是“不失真”

要我说,原文是金,摘要只是铜。原文不会编造细节,用户第 12 轮说过“报告里不要出现百分比”,原文里就是这句话,模型读了几十轮之后依然能看到原话,准确率高得吓人。

但原文的代价也很明显:太占地方,而且充满噪声。工具返回个 JSON,一次可能就 2000 token,里面真正有用的可能就一个字段。这两千 token 既挤占了窗口,又稀释了注意力。所以一味留原文,长任务一定是死路一条,除非你的任务只有三五轮。

3.2 摘要压缩,为什么常常压不准

既然原文太贵,很多人自然想到“用模型把历史对话总结一下”。这个方向没错,但直接做会踩三个坑。

第一个坑是细节丢失。你把 10 轮对话压成 200 字,模型很可能只提取了“主要意图”,但把用户提过的精确数字、明确禁止项、指定格式全压没了。摘要适合保留“发生过什么”,但不擅长保留“必须怎么做”。

第二个坑是编造。让模型总结时,如果原文里信息不足或表述模糊,模型会自动补全合理但从未出现的细节。这个后果比截断还可怕——截断只是丢失信息,摘要可能产生虚假信息。虚假信息一旦进入上下文,后面所有推理都建立在沙地上。

第三个坑是级联失真。为了控制长度,你可能会对摘要再做摘要。摘要的摘要,误差会逐级放大,最后得到的内容可能只剩一句“用户要求写个报告”,所有细节全没了。

3.3 三级混合策略:原文和摘要各司其职

我现在的做法是分层,而不是二选一。

第一层是硬性约束区。用户偏好、业务红线、输出格式、关键参数,这些不可压缩、不可改写,永远以原文形式常驻上下文。你可以把这些内容当作 Agent 的“宪法”,摘要可以不提,但宪法必须原样保留。

第二层是近期对话区。最近 5~10 轮的对话保留原文,窗口滑动,保证模型对当前正在做的事有完整、准确的理解。这个区域直接对接“当前任务”,失真成本最高,不值得省。

第三层是历史记忆区。更早的内容做结构性摘要,而且摘要必须带模板。我做总结时会强制要求输出四个字段:已完成动作、还未完成的事、不可改变的约束、不确定/存疑的地方。有了这四个字段,即便历史再长,模型也能快速知道“哪些事不能动、哪些事还没做”,而不是只记得一句笼统的概括。

4. 取舍三:短期会话记忆,还是长期外部记忆

4.1 上下文窗口再大,也装不下 Agent 的“一生”

如果 Agent 只在一个会话里工作,前面说的窗口策略够用了。但很多 Agent 是要长期跑的:今天你跟它说“以后所有报告都用简体字”,明天它换个会话帮你干活,这句约束如果只存在于上一个会话的上下文里,它就等于从没存在过。

所以必须把上下文划分为两层:短期会话记忆和长期外部记忆。短期记忆是当前会话中的上下文窗口,相当于工作台;长期记忆是存放在外部存储的事实,相当于你的档案柜。工作台随时可以清空,档案柜要一直保留。这个区分是 Agent 能“越来越懂你”而不是“越来越笨”的前提。

4.2 第二大脑的存储选型:向量库、KV 存储、还是文件

长期外部记忆不是摆个数据库就行,要根据数据类型选存储,我的经验分三种:

  • 语义检索型(向量数据库):适合用户没给明确关键词、需要“相关内容联想”的场景。比如 Agent 写周报时,需要从过去 30 天的对话里找出和“项目风险”相关的段落,这时候向量检索最合适。但要注意,向量相似度不等于事实正确,检索结果必须经过一道验证,不能让模型直接采信。

  • 精确检索型(KV 数据库或关系型数据库):适合用户 ID、偏好开关、业务参数这类结构化事实。比如“用户喜欢繁体/简体”“输出格式必须为 Markdown”“禁止触达某个频道”。这些数据用结构化字段存,读取快、不漂移、不会产生歧义,可靠性远超向量方案。

  • 原始日志型(文件或消息队列):适合保存全部原始对话和工具结果。不直接注入上下文,主要用来事后回放、排查问题。我排查 Agent 变笨时,90% 的线索都来自这里的原始记录。

顺便说一句,现在“基于 Rust 写 AI Agent”讨论度很高,有些团队甚至用 Rust 重写 Agent 引擎,就是为了让记忆检索、调度链路快一个数量级。这其实说明一个问题:决定 Agent 长期能力的,恰恰是这层外部记忆和调度架构,而不是模型窗口本身。

4.3 记忆同步的正确时机

外部记忆不是每个回合都写。高频写入会导致两个问题:一是调用成本爆增,每个回合你都要多调一次模型做“记忆抽取”;二是写入噪声太多,把有价值的事实淹没在碎片里。

我一般用两个触发条件。第一个是时间条件:每完成一个独立子任务或每 5 轮,做一次记忆抽取。第二个是异常条件:检测到用户表达了新偏好、修改了旧要求、或者给出了明确的禁止/允许指令,立刻写入外部存储。抽取的内容也不是“总结全部”,而是抽“用户画像的变化、业务约束的增减、未完成任务的更新”。这样档案柜里存的东西大部分是有用的,不是流水账。

会话启动时,再从外部存储按需加载本会话需要的长期事实。加载多少可以自己控制:全量加载一定会爆窗口,只加载与当前任务语义相关的部分即可。一句话总结:写入要克制,读取要精准。

5. 取舍四:按时间顺序全给,还是按相关性捡重点

5.1 顺序保留是“流水账”,相关性检索是“索引卡片”

默认情况下,把历史对话按时间排列喂给模型是最省事的。但这在整个上下文比较长的时候很有问题:流水账里什么都有,有用信息被自身淹没。模型需要自己从一堆无关内容里“捞针”,捞不到就是变笨。

另一种思路是:不关心顺序,只关心相关性。把上下文当作一个检索系统,每次请求时,根据当前用户问题去外部记忆里捞出最相关的 3~5 个片段,再组装进上下文。这个思路接近 RAG,也是很多 Agent 框架的默认选项。

但这里有个很容易翻车的地方:相关性排序和“任务重要性”不是一回事。用户第 3 轮说过“这份合同必须在一小时内发出”,这一条在语义上和 40 轮后的“把合同发给我”高度相关,但按时间线它早被冲掉了,如果你只按“最近消息”截取,这条关键约束就没了。所以我建议不要把顺序和检索对立起来,而是混合使用:近期对话用顺序窗口保真,历史信息用相关性检索补全。

5.2 调整 prompt 段落位置,通常可以显著提高召回

除了用什么,还要注意放在哪。我观察到一个反复出现的现象:把关键信息放在上下文的开头和结尾,模型响应相关指令的准确率明显高于放中间。这也符合对注意力机制的理解:开头有“首位效应”,结尾有“近因效应”,中间的内容最容易互相覆盖、被忽略。

实操时,我会把最终注入的上下文排成这样的顺序:固定身份角色在最前,本次任务的直接指令和约束靠后紧挨着用户消息,历史和检索片段夹在中间。这样模型每次开口前最先看到身份、最后看到当前任务,中间是参考资料。如果你有“用户明确要求 XYZ”,一定要把它放在靠近末尾的位置,别让它躺在历史摘要的中段。

5.3 检索粒度与触发时机,怎么才算“刚刚好”

检索粒度太粗,等于没检索,所有内容都进来,窗口照样爆;粒度太细,每句话都要检索一次,模型疯狂重复引用同一批内容,也是浪费。

我的经验是把检索的触发时机定在三个节点:用户新消息进入时、工具返回结果后、子任务状态切换时。每次检索的目标不是“覆盖全部历史”,而是“回答当前这一步最需要知道的信息”。

额外给一个避坑建议:检索结果不要“提头去尾”直接塞进上下文,最好在每段前后加一个明确的开始和结束标记,比如<memory>…</memory>或者简单的分隔线。这样模型能识别出“这部分是外部引用”,而不是自己的推理结论,减少把检索内容误当事实的机率。这个细节非常小,但排查时你会发现,很多“胡说八道”的根源就是模型把检索到的片段和真实结论混在一起了。

6. 取舍五:系统提示词,往重了写还是往轻了写

6.1 系统提示词绝不是越长越好

很多团队都有这个执念:把所有规则、行业术语、工具说明全塞进 system prompt,恨不得写五千字。结果就是 token 预算被吃掉一大块,同时模型要同时“遵守”几十条规则,规则一多,互相冲突或者被忽略的概率急剧上升。你把 30 条规则塞给它,它真正内化的可能只有前几条和最后几条,中间的规则全成了摆设。

我并不是说 system prompt 应该越短越好,太短也不行。角色信息缺失、工具调用格式不定义清楚,模型会自行发挥,Agent 的稳定性立马被击穿。正确的方向不是“重”或“轻”,而是“分层动态组装”。

6.2 动态组装:让系统指令在正确的时间出现在正确的位置

我把系统提示拆成三层:

  • 静态层:真正不变的内容,比如“你是文档助手”“输出用 Markdown”“调用工具时必须填写必填参数”。这些内容全项目共享,写一次,数量控制在几百 token 以内。
  • 动态层:每次请求时根据当前任务临时拼装,包括本次任务目标、可用的工具列表、用户当前身份与画像、本次需要遵守的特别约束。比如用户本次指定“只要结论不要分析”,那就把这条临时约束放在最靠后的位置,紧贴用户消息。
  • 上下文层:历史摘要和检索片段,按前文说的排列规则注入。

一个可参考的伪代码结构长这样:

def build_prompt(session, user_message, memories): # 1) 固定身份与全局规则,放在开头 blocks = [session.global_rules] # 2) 本次任务动态指令 blocks.append(session.get_current_task_instruction()) # 3) 外部记忆与历史摘要,放在中间 blocks.append(memories.retrieve(user_message)) # 4) 近期对话原文 blocks.append(session.recent_messages(6)) # 5) 用户当前消息与“最终指令”,放最后 blocks.append(user_message) return join(blocks)

这个结构本身不复杂,但它保证了每个环节各司其职:身份信息恒定,任务信息明确,历史信息按需加载,当前指令永远最后出现。比起把所有规则堆在前面让模型自己权衡,这种组装方式稳定得多。

7. 落地实操:让它成为一套可维护的框架,而不是某次灵光一现

7.1 按“热、温、冷”三层管理上下文生命周期

只懂取舍还不够,要有一个能长期跑不崩的框架。我习惯把上下文划分为热、温、冷三层:

  • 热数据:当前任务正在使用的信息。比如正在写的文档内容、最近 5 轮对话、本次调用的工具结果。这些随每次请求完整保留,窗口预算给得最足。
  • 温数据:上一个子任务的摘要、仍可能被引用的前置结果。以结构化摘要形式保留,量不大,按需展开。
  • 冷数据:更早的全部原始记录、已完成任务的细节。放入外部记忆,只通过检索进入上下文。

这个分层也可以直接对应你选的存储结构:热数据在内存里,温数据在 KV/缓存里,冷数据在向量库或文件里。这样一套分层的生命周期,既能控制 token 预算,又能兼顾对话的连贯性,而且排查问题时有据可查。

7.2 量化“变笨”:给你的 Agent 建立一套体检指标

判断 Agent 有没有变笨,不能靠感觉。我给自己项目定的指标有四个:

  • 关键信息召回率:准备一组测试问题,把早期对话中的约束作为“标准答案”,跑一轮长会话后问模型这些约束,看能答对多少。比如第 3 轮用户说了“全程不要提竞品”,到第 20 轮问一句“本项目有什么禁忌”,答对就是召回成功。
  • 偏好遵从率:跑一系列生成任务,检查输出是否符合用户既定偏好,比如格式、语气、简单句。
  • 重复执行率:在长任务末尾检查工具调用序列,统计有没有出现“已经完成又做一遍”的步骤。
  • 中断恢复率:在第 20 轮强行打断,让它描述“当前任务进行到哪一步”,看回答和实际状态的匹配度。

出现异常时,除了看最终结果,还要保留“压缩前的原文”和“注入后的最终上下文”。很多问题的定位都靠对比这两份内容:是原始信息丢了,还是信息在压缩时被写歪了,一目了然。

8. 现场问题排查实录:三个让我头疼的场景

8.1 任务执行到一半,Agent 把用户上限参数改掉了

这个场景发生在一次自动文案生成项目上。用户最初设定“每篇文章上限 800 字”,跑到第 15 轮,Agent 突然生成了一篇 2000 字的长文。查日志发现,用户那句“上限 800 字”早被滑出窗口,Agent 只能从历史里含糊记得“好像有字数要求”,于是自己脑补了一个“越详细越好”。修法就是把它放进“硬性约束区”,任何截断都不允许碰它。这类问题的核心教训是:凡是和“业务红线”相关的参数,永远不能只靠窗口滑动来保留。

8.2 工具结果把当前用户消息冲出了窗口

另一个常见场景是:Agent 调了三次搜索工具,每次返回上千 token 的结果,结果第三次工具输出还没处理完,用户当前消息就被挤出了有效注意力范围。模型看着一堆工具 JSON 回答用户,自然答非所问。我的调整是:滑动窗口不是简单的先进先出,要优先保住“最近用户消息”和“当前任务指令”,工具输出反而可以放进一个可折叠区域,必要时先做个几十 token 的浓缩再进窗口。

8.3 摘要越抽越薄,把业务约束抽没了

还有一次是摘要链出了问题。原始摘要 1200 字,到第二轮压缩变成 300 字,用户“不要用感叹号”的要求直接蒸发。后来我改成强制模板摘要,每轮总结必须保留“不可违反约束”字段,并且这个字段只允许原文摘录、不允许改写。从那以后,无论压缩多少层,业务约束基本不会丢。这里顺便建议:所有摘要工作,最好用一个单独的模型调用来生成,不要在主对话里顺手总结,避免互相干扰。

8.4 给上下文加“标记线”的隐蔽收益

最后分享一个小技巧:在注入检索片段和外部记忆前后加上明确的标记。习惯用<memory>和</memory>标签来包裹外部信息。这个做法的两个好处,一是模型能更清楚地区分“外部引用”和“自身推论”,减少事实混淆;二是排查 prompt 时,你可以一眼看到哪些内容来自检索、哪些来自原文。别小看这个细节,它能让很多问题从“玄学”变成“可定位”。

9. 写到最后,再分享一点个人习惯

上下文工程这件事,说到底就是做权衡:窗口长度换成本,原文保真换摘要压缩,短期记忆换长期外部记忆,顺序上下文换相关性检索,系统指令的重量换动态组装。每一边都有收益,也都有不可忽视的代价。没有哪个方案是永远最优的,但一个好的 Agent 项目,一定是把每个取舍都明确写进代码、写进监控指标的。

我自己现在的习惯是:每个 Agent 项目上线第一天,就把 token 预算表、上下文分层结构、变笨体检指标写进 README。后面不管谁来维护、改了多少版,只要这套上下文策略还在,Agent 就不会跑着跑着突然“智商归零”。如果你正在被“越聊越笨”折磨,不妨先别急着换更大的模型,回去把上下文日志拉出来,对照上面这 5 个取舍逐一检查。多数时候,问题不在模型,而在你喂给它的那一堆字里。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/26 8:23:08

t3code全栈脚手架:端到端类型安全的Next.js + tRPC实践指南

前两天把一个攒了很久的 side project 用 t3code 重新搭了一版&#xff0c;整个过程比我预想中顺不少。顺手把 t3code 的使用心得和实现细节整理成文&#xff0c;给打算从零开始做全栈应用、又不想在脚手架配置上浪费太多时间的同学做个参考。t3code 是一个基于 T3 技术栈的全栈…

作者头像 李华
网站建设 2026/10/26 8:23:07

智能体系统SkillMastery架构设计与产业化实践

1. SkillMastery系统架构解析SkillMastery作为一套面向全领域的智能体系统&#xff0c;其核心架构采用了分层模块化设计。我在实际部署中发现&#xff0c;这种架构能够有效平衡灵活性与性能需求。系统主要分为以下四个层级&#xff1a;1.1 基础能力层基础能力层负责处理原始数据…

作者头像 李华
网站建设 2026/10/26 8:22:26

DeepSeek Harness插件开发:本地AI工作台的技能协议与实战

1. DeepSeek Harness 是什么&#xff0c;它和你熟悉的 IDE 插件根本不是一回事很多人第一次看到“DeepSeek Harness 插件开发”这个标题&#xff0c;下意识就联想到 IntelliJ IDEA 里点几下就能装上的代码补全插件&#xff0c;或者 Chrome 浏览器右上角那个带小图标、点开就能翻…

作者头像 李华
网站建设 2026/10/26 8:20:39

MySQL启动失败分析与innodb_buffer_pool_size配置优化

1. MySQL服务启动失败的常见场景分析作为一名长期与MySQL打交道的DBA&#xff0c;我见过太多因为配置不当导致服务无法启动的案例。当你在终端输入systemctl start mysql后看到那个刺眼的红色"failed"时&#xff0c;内心的崩溃感我深有体会。根据我的经验统计&#x…

作者头像 李华
网站建设 2026/10/26 8:20:23

AI漫剧六道工序:从黑箱生成到毫秒级声学控制

1. 为什么“一场戏走完6道工序”是漫剧制作的分水岭去年冬天&#xff0c;我在一个小型内容工作室带新人做AI漫剧试水项目。当时团队里有个刚毕业的编导&#xff0c;信心满满地用主流AI工具一口气生成了30分钟的完整音频——人物对白、环境音效、BGM全齐&#xff0c;连情绪起伏都…

作者头像 李华
网站建设 2026/10/26 8:19:55

C++实现麻将胡牌与听牌核心逻辑:数据结构、递归优化与并行计算

简介&#xff1a;这是一份面向C初学者与游戏开发爱好者的麻将游戏完整源码工程&#xff0c;基于Visual C与MFC构建&#xff0c;适合用来学习Windows桌面游戏开发流程与面向对象编程思想。压缩包共135个文件&#xff0c;约3.86MB&#xff0c;包含9个cpp源文件与10个头文件承载核…

作者头像 李华