别再死磕提示词优化,真正该优化的是上下文
本文为 AgentScript 系列第二篇,承接上篇「模型到底看到了什么?」,提出一个核心认知:Agent 开发的核心优化方向,不是打磨提示词话术,而是管控模型可见的上下文信息。
提示词优化,是提示词工程之后最顺理成章的进阶方向。 既然手写提示词脆弱、不稳定,那就交给优化器自动迭代:调整指令措辞、挑选示例、基于数据与指标编译出更优质的提示词。
这个思路本身合理,也诞生了不少实用工具。最典型的代表就是 DSPy,它将自身定位为面向大模型的编程框架,而非简单的提示词工具;内置优化器可以根据自定义指标,自动调优程序内的提示词甚至模型权重。
但对绝大多数智能体(Agent)而言,提示词优化并不是优先级最高的优化目标。
问题不在于提示词不重要。 而是:提示词往往不该是最先优化的部分。
一个 Agent 的完整提示词,从来不是单纯的一句指令,而是多部分混合而成:
完整提示词 = 系统指令 + 角色设定 + 上下文数据 + 示例 + 用户指令
常规的提示词优化,大多只聚焦指令相关部分:措辞润色、示例选择、任务话术调整、Few‑Shot 样例设计。 可实际上,Agent 的行为表现,更多由上下文数据决定:检索文档、工具返回结果、记忆记录、中间状态、历史输出、摘要引用、其他智能体的返回内容。
只要模型能看到正确的证据,哪怕指令平平无奇,也能给出不错的结果; 反之,如果模型获取的信息本身有误,再精妙的指令,只会让错误答案看起来更通顺、更像真的。
本文要讲的,就是另一个更关键的优化目标:
不要只优化你对模型说了什么,更要优化模型能看到什么。

提示词,不是智能体的最小单元
单次大模型调用中,提示词似乎是天然的优化单元。 我们写指令、给示例、测试输出、微调话术,一套流程非常直观。
但这套思维,放到 Agent 多轮执行场景里就彻底失效了。 Agent 不止有一个固定提示词,它拥有不断变化的上下 文边界。
一个科研类智能体,上下文通常包含:
- 用户原始问题
- 检索关键词
- 搜索结果
- 召回文档
- 文档摘要
- 历史运行记忆
- 中间观测信息
- 失败尝试记录
- 工具调用异常
- 辅助智能体的输出
- 最终答案格式约束
这些信息里,一部分需要传入下一轮模型调用,另一部分则必须过滤。
Agent 出问题,很少是因为:
指令描述不够清晰。
更多是因为这三类问题:
模型看到了错误的上下文。
正确的上下文存在于程序中,却没有传入提示词。
上下文传入了,但格式、粒度不对。
工具返回内容过长、记忆查询到过时信息、摘要丢失关键引用、规划步骤需要精简摘要而回答步骤需要完整原文…… 这些本质都不是话术问题,而是上下文选择问题。
提示词文本:一个极难优化的软变量
提示词优化听起来很美好:用算法替代人工反复调试。 以 DSPy 的 MIPROv2 为例,它可以联合优化指令与 Few‑Shot 示例,基于下游任务指标,在无梯度、无模块标签的前提下,优化多阶段模型程序。 这是非常扎实且有价值的研究方向。
但它优化的对象,本质上是软性变量。 提示词文本存在这些天然缺陷:
- 维度极 高、自由文本无约束
- 高度依赖特定模型特性
- 约束困难、语义难以量化对比
- 极易过拟合
- 效果好时难以解释,效果差时难以排查
对比两次提示词的修改,只能看到文字变化,却很难解释系统为什么变得更稳定: 是指令本身真的更好? 还是某句话刚好契合当前模型的行为偏好? 还是示例刚好贴合验证集? 还是优化器找到了一个会随模型迭代失效的小技巧? 还是下游评估指标本身不完善?
这些问题不是否定提示词优化的价值,而是说明:自由文本形式的提示词,是一个非常难优化的目标。 它太贴近模型本身的黑盒行为,却远离了 Agent 内部真实的数据流问题。
上下文,才是更优质的优化目标
上下文和提示词完全不同。 上下文的核心不是一句话,而是一系列结构化决策:
模型该看原始工具结果,还是精简摘要? 该看前3条文档,还是前10条? 该读取近期记忆、长期经验,还是完全不使用记忆? 该保留带引用的原文片段,还是压缩后的观测总结? 最终回答步骤需要完整证据,规划步骤是否只需要极简摘要?
这些都是清晰、可枚举的结构化选择:
- 不使用上下文
- 精简摘要
- 常规总结
- 高相关文档
- 带引用的原文片段
- 记忆+文档组合
同时它也更容易排查、追踪:
- 本次运行 用了前5条文档,token上限4k
- 本次运行用了精简摘要,上限500token
- 本次运行完全禁用记忆
上下文优化的结果,跨模型兼容性更强。 不同模型对话术敏感度差异巨大,但“是否使用召回文档”这个决策,几乎不受模型版本影响。GPT、Claude、Gemini、Qwen 等,无论风格差异多大,都依赖正确的信息输入。
上下文优化同样可能过拟合,在一类场景有效、另一类场景失效。 但它的优势在于:出问题时可观测、可定位。 你能清晰看到选了哪些信息、过滤了哪些信息、模型是否获取了关键证据。你修改的是结构化决策,而非模糊的话术。
从提示词工程,走向上下文工程
提示词工程关心的是:
我该对模型说什么?
上下文工程关心的是:
模型该看到什么?
这个转变至关重要。
在常规 Python / TypeScript 编写的 Agent 中,上下文通常以字符串、消息数组、框架对象、模板的形式存在。本地数据与提示词的边界,全靠开发者的编码习惯维持。
而 AgentScript 从一开始就定下更严格的规则:
本地数据,默认不是提示词上下文。
工具返回、记忆查询结果、中间观测、执行日志、其他智能体输出,不会自动混入提示词。
如果需要让模型读取某份数据,必须通过 use 显式声明。
use input.question as "用户问题"
use docs.summary max 4k as "参考证据"
use past max 2k as "过往经验"
这是第一步:让上下文变得可见、可控。
一条 use 语句直接定义:数据来源、分类标签、token上限,它不再是一段模板字符串,而是一份上下文契约。
自然而然就走到了下一步: 既然上下文可以显式定义,那上下文的备选方案,同样可以显式定义。
use one of:把上下文搜索空间写进代码
AgentScript 用语法直接表达上下文的多种可选方案:
use one of {
none: empty
compact: scratch.digest max 500
verbose: scratch.summary max 4k
grounded: docs.top5 max 4k selected
} as "参考证据"
含义非常直白:
- 存在一个名为「参考证据」的上下文模块
- 可以选择完全不使用
- 可以使用极简摘要
- 可以使用常规总结
- 可以使用带原文的高相关文档
- 当前生效的方案是「带原文文档」
这里没有运行时魔法,没有隐藏的自动优化器,没有后台学习状态。
只是声明可选的上下文来源 + 标记当前最优选择。
use one of 定义搜索空间,selected 标记当前启用方案,empty 代表完全不启用。
模型不会看到备选列表,只会读取被选中的上下文内容。 如果选中原文文档,提示词就加载对应内容; 如果选中 none,该模块直接不出现。 全程可审计、运行时逻辑极简。
为什么“不启用”也要纳入统一机制
很多人会想:给可选上下文单独设计语法不就行了? 比如:
use? docs.top5 max 4k as "参考证据"
但这会让上下文可选性,变成另一套独立语法,增加语言复杂度。 更简洁的设计,是把「不启用」也作为一种备选方案:
use one of {
none: empty
docs: docs.top5 max 4k
} as "参考证据"
可读性更强,优化也更简单。 搜索空间一目了然:参考证据 =(不启用,文档)。 默认规则同样清晰:写在最前的方案为默认方案。 无需额外语法,上下文的有无,本身就是一种决策。