转载--Karpathy 怎么看 AI Agent(三):怎么给 Agent 搭一个真正能用的上下文
原文:https://mp.weixin.qq.com/s/qZRp0KI69PThh_YknMfVRw
一、从框架到操作之间的鸿沟
理解了"上下文是 Agent 的内存"之后,很多人面临的下一个问题是:
好,我知道上下文很重要。那我具体应该怎么做?
这个问题没有标准答案——不同的任务、不同的 Agent 架构、不同的模型,上下文设计的最优解是不同的。
但 Karpathy 的工程经验,以及 2024-2026 年大量生产级 Agent 系统的实践,给出了一套可以作为起点的设计原则。
这篇文章不讲理论,讲操作。从一个 Agent 上下文的四个组成部分开始,到怎么验证它有没有设计好,到怎么用失败来迭代它。
二、上下文的四个组成部分
一个运行中的 Agent,它的上下文窗口通常包含四类信息。理解这四类信息各自的作用和设计要点,是搭建有效上下文的第一步。
第一类:系统提示(System Prompt)
这是上下文的"宪法"——定义 Agent 是谁、能做什么、不能做什么、遇到各种情况应该怎么反应。
大多数人的系统提示写得像一段自我介绍:"你是一个专业的客服助手,负责回答用户关于产品的问题,语气友好,回答简洁。"
这样的系统提示能用,但不够用。在 Agent 场景里,系统提示更重要的部分是决策边界:
当用户要求你做系统提示没有明确授权的事情时,你怎么办?
当你不确定某个操作是否应该执行时,你是执行还是暂停等待确认?
当你遇到工具调用失败时,你是重试、跳过还是报告给用户?
当任务目标和某个中间步骤的结果产生矛盾时,你优先遵循哪个?
这些边界不写清楚,Agent 就会在这些情况下自行判断——有时候判断对,有时候判断错,而且不可预测。
设计要点:系统提示不是越长越好。把角色定义控制在 100 字以内,把决策边界写清楚,把最常见的异常情况的处理方式明确说明。长而模糊的系统提示,比短而精确的系统提示更差。
第二类:任务上下文(Task Context)
这是 Agent 执行当前任务需要知道的所有背景信息——不是永久性的角色定义,是这一次具体任务相关的信息。
包括:任务目标、相关数据、约束条件、历史记录、用户偏好、领域特定的术语和规则。
这一类是最容易出现"信息缺口"的地方。你知道这些信息,Agent 不知道,而且它不会主动来问你——它会用"听起来合理"的假设来填补。
设计要点:在开始任务之前,问自己一个问题:
如果我把这个任务交给一个聪明但对我们公司完全陌生的新员工,他最可能在哪里卡住或者做出错误假设?
把那些地方的信息,主动放进任务上下文。
不要期待 Agent 会问你——它通常不会,它会继续往前走。
第三类:工具结果(Tool Results)
Agent 调用工具——搜索、数据库查询、代码执行、API 调用——工具返回的结果会进入上下文,成为 Agent 下一步判断的依据。
这一类是最容易被忽视的上下文设计问题。大多数人在设计 Agent 系统时,把工具的输出直接传给 LLM,不做任何处理。
但工具的原始输出通常是为机器设计的,不是为 LLM 设计的:
数据库返回的 JSON,可能包含大量 Agent 不需要的字段
搜索结果,可能包含大量不相关的内容
代码执行结果,可能包含冗长的 stack trace,而 Agent 真正需要的只是错误类型和关键行号
API 返回,可能包含分页信息、元数据、状态码——Agent 只需要实际内容
把这些原始输出直接塞进上下文,是在用宝贵的 Token 容量装噪音。
设计要点:为每个工具设计一个"输出格式化层"——在工具结果进入上下文之前,把它压缩成 Agent 真正需要的格式。
一个好的工具输出格式化的标准:如果你把这个格式化后的输出给一个人看,他能在 10 秒内理解关键信息。如果不能,继续精简。
第四类:执行历史(Execution History)
在多步骤任务里,Agent 之前做了什么、得到了什么结果,需要在上下文里有某种形式的记录——否则 Agent 在后续步骤里会"忘记"前面发生了什么。
这是上下文窗口管理最复杂的部分,因为历史记录会随着任务推进不断增长,而上下文窗口是有限的。
处理这个问题有三种主要策略:
策略 A:保留原始历史把所有历史原封不动地保留在上下文里。简单,但随着任务推进,上下文会越来越长,最终超出窗口限制,或者因为过长而导致注意力稀释。适合步骤少、每步输出短的简单任务。
策略 B:滚动窗口只保留最近 N 步的历史,丢弃更早的。处理了长度问题,但 Agent 会"忘记"早期的重要决策。适合每一步都相对独立、不太依赖早期历史的任务。
策略 C:摘要压缩定期把历史压缩成摘要——用 LLM 把最近 10 步的历史总结成 3-5 句关键信息,然后用摘要替换原始历史。在保留关键信息的同时控制长度。适合长任务,但摘要质量会影响 Agent 后续判断。
设计要点:大多数生产级 Agent 系统,最终采用的是策略 B 和策略 C 的组合——对近期历史用策略 B 保留原文,对远期历史用策略 C 压缩成摘要。这个边界在哪里,需要根据具体任务调整。
三、一个可以直接用的上下文设计检查清单
在把 Agent 交给真实任务之前,过一遍这个清单:
【系统提示检查】□ 角色定义是否精确,有没有模糊的"专业"、"智能"之类的形容词?□ 决策边界是否写清楚——不确定时怎么办,遇到错误时怎么办?□ 最常见的异常情况(至少 3 种),是否有明确的处理指引?□ 系统提示是否有内部矛盾——两条规则在某种情况下会相互冲突?【任务上下文检查】□ 如果把这个任务交给完全不了解我方情况的新员工,他最可能在哪里卡住?□ 那些"我觉得不用说大家都知道"的信息,是否已经放进了上下文?□ 任务的成功标准,是否明确说明了?Agent 怎么知道它完成了?□ 有哪些约束条件(不能做什么、必须避免什么),是否明确说明?【工具结果检查】□ 每个工具的原始输出,是否经过了格式化处理再进入上下文?□ 格式化后的输出,是否去除了 Agent 不需要的字段和信息?□ 错误信息是否包含足够的信息让 Agent 判断下一步——只有错误码是不够的?【执行历史检查】□ 选择了哪种历史管理策略(原始/滚动/摘要)?这个选择适合这个任务吗?□ 有没有设计"强制保留"机制——某些早期决策,无论如何都不能被压缩掉?□ 上下文长度有没有监控——在接近窗口限制之前,有没有触发压缩的机制?
这个清单不是一次性的。每次 Agent 出现意外行为,回来对照清单,找到是哪一项没有处理好。
四、怎么知道你的上下文设计是否有效
上下文设计好不好,最终要看 Agent 的表现。但"Agent 表现不好"是一个太模糊的观察——你需要把它翻译成更具体的诊断。
诊断方法一:让 Agent 复述任务理解
在 Agent 开始执行之前,先让它用自己的话复述一遍它对任务的理解,包括:它认为的任务目标、它知道的约束条件、它打算怎么推进。
如果它的复述和你的预期有偏差,现在修正比任务跑完之后修正代价小得多。
这一步听起来多余,但在真实工程实践里,它能在任务开始阶段拦截大约一半的"方向性偏差"。
诊断方法二:在关键步骤检查 Agent 的推理
不是看最终输出,是看 Agent 在关键决策节点的推理过程。
大多数 Agent 框架支持让 Agent 输出它的思考过程(Chain of Thought)。在关键步骤上开启这个功能,看 Agent 的推理链:
它用了哪些信息做判断?
有没有你知道是错误的前提出现在推理链里?
有没有它本来应该知道但显然不知道的信息缺口?
推理链诊断比结果诊断更有效——因为它让你看到问题在哪里产生,而不是只看到问题的结果。
诊断方法三:故意制造边缘情况
在你把 Agent 部署到真实任务之前,用你能想到的边缘情况来测试它:
输入信息不完整时,它怎么处理?
工具调用失败时,它怎么处理?
任务目标和中间结果产生矛盾时,它怎么处理?
它接收到了相互矛盾的信息时,它怎么处理?
如果这些边缘情况在测试阶段没有被发现,它们会在生产环境里以更高的代价出现。
五、怎么用失败来迭代上下文
上下文设计不是一次性完成的,是在失败里迭代出来的。
Karpathy 有一个他自己实践的习惯:把 Agent 的每次失败分类,而不只是修复单次失败。
分类的框架很简单:
这次失败属于哪种类型?├── 信息缺口:Agent 需要某个信息,但上下文里没有├── 信息过载:上下文里信息太多太杂,Agent 忽略了关键部分├── 指令矛盾:系统提示或任务描述里有相互冲突的规则├── 工具输出噪音:工具返回的结果格式化不够,干扰了 Agent 判断├── 历史压缩损失:执行历史被压缩时丢失了关键信息└── 边界模糊:Agent 在决策边界上做了不符合预期的自主判断
知道了类型,修复方向就清楚了:
信息缺口 → 在对应位置补充任务上下文
信息过载 → 精简上下文,去掉不相关的信息
指令矛盾 → 回到系统提示,找到矛盾的地方明确优先级
工具输出噪音 → 改进工具的输出格式化层
历史压缩损失 → 调整历史管理策略,把关键决策标记为"强制保留"
边界模糊 → 在系统提示里补充对应边界情况的明确规则
每修复一类失败,同类任务的成功率就系统性地提升一点。这个迭代过程没有终点,但它有一个明显的规律:前十次失败能覆盖大约 80% 的常见问题类型。认真分析并修复这十次失败,Agent 的可靠性会有质的提升。
