Harness 怎样让 LLM 每次都看到“足够、正确、可信,而且不浪费”的信息?
1. Agent 每轮到底怎么运行
可以把 Agent 看成:
Agent = LLM + 上下文 + 工具
但真正驱动循环的是 Harness:
Harness 组装上下文
↓
LLM 根据上下文决定下一步动作
↓
Harness 执行工具
↓
工具产生结果
↓
Harness 把结果追加到轨迹
↓
再次调用 LLM
所以:
- LLM 负责思考和选择动作。
- 工具负责产生真实结果。
- Harness 负责把整个循环运转起来。
2. 上下文不是模型自己找的
每次调用 LLM 前,上下文都是 Harness 组装的,通常包括:
系统指令
项目规则
相关 ToolSpec
Skill 目录或已加载的 Skill
用户任务
历史对话
模型之前的 tool call
工具结果
当前运行状态
压缩后的历史摘要
LLM 看不到上下文之外的任何东西。
比如 cwd、sandbox、approval、当前时间,只有 Harness:
- 写进上下文;
- 或者提供相应工具;
模型才知道。
这就是“注入”:把程序运行时的信息转换成模型能够看到的消息。
3. 上下文分为稳定部分和动态部分
可以简单分成:
稳定前缀
├── System Prompt
├── 项目规则
├── ToolSpec
└── Skill 目录
动态轨迹
├── 用户任务
├── assistant 回复
├── tool call
├── tool result
└── 状态更新
稳定前缀尽量不要频繁变化,因为这关系到 Prompt Cache。
动态信息则尽量追加在末尾。
4. KV Cache 和 Prompt Cache
KV Cache:
- 位于模型推理内部;
- 保存已经计算过的注意力中间结果;
- Agent 通常不能直接控制。
Prompt Cache:
- 位于模型 API 服务层;
- 相同的 Prompt 前缀可以复用;
- Harness 可以通过上下文布局提高命中率。
因此上下文工程要做到:
固定内容放前面
动态内容放后面
不要无意义地改写历史
不要每轮重排 ToolSpec
如果前面的内容发生变化,后面的缓存通常也无法继续复用。
5. System Prompt 负责教模型怎么工作
System Prompt 类似员工手册,应该包含:
- 身份和职责;
- 工作流程;
- 工具使用原则;
- 验证要求;
- 安全边界;
- 输出要求。
但它只能影响模型行为,不能作为最终安全保障。
例如:
“退款超过 500 元必须审批”
可以写进 Prompt,帮助模型判断。
但退款工具内部仍然必须写代码校验,因为模型可能误解、忘记或者不遵守。
6. Few-shot 是给模型看案例
Few-shot 主要有两类:
- 固定示例:长期稳定、频繁使用的标准案例。
- 临时检索示例:根据当前任务动态找来的相关案例。
固定示例适合稳定规则,但会一直占用上下文。
动态示例更相关、更节省空间,但需要检索系统支持。
它们都是帮助模型理解,不应该代替代码中的业务校验。
7. ToolSpec 是工具的说明书
ToolSpec 告诉模型:
- 工具叫什么;
- 能做什么;
- 什么场景应该使用;
- 参数有哪些;
- 参数格式是什么。
例如:
模型看到 ToolSpec
↓
模型产生 tool call
↓
Harness 校验并执行工具
↓
工具返回 result
ToolSpec 不是工具本身,也不是安全边界。
真正的权限、参数校验、金额限制和副作用控制,仍然必须由 Harness 和工具代码保证。
8. 工具也需要渐进式披露
如果 Agent 有 100 个工具,不应该一开始就把全部完整 ToolSpec 给模型。
可以采用:
先提供相关工具目录
↓
模型发现需要某个工具
↓
Harness 再加载完整 ToolSpec
这样可以:
- 减少上下文占用;
- 降低模型选错工具的概率;
- 提高缓存命中;
- 降低调用成本。
9. Skill 也是渐进式加载
Skill 通常分为三层:
第一层:名称和简介
第二层:完整 SKILL.md
第三层:引用资料、模板、脚本、资源
初始上下文只需要放 Skill 的名称和简介。
模型选择 Skill 后,Harness 才读取完整 SKILL.md。模板、脚本和参考资料仍然放在上下文外,需要时再读取或执行。
所以不是“Skill 全部等于上下文”,而是:
Harness 管理 Skill;Skill 中被读取的内容才进入上下文。
10. 状态栏让模型知道自己现在在哪
长任务中,模型容易忘记当前运行状态,因此 Harness 可以在轨迹末尾注入状态栏:
当前目录
当前阶段
剩余预算
已修改文件
连续失败次数
后台任务状态
待办事项
权限和沙箱状态
状态栏应该由 Harness 根据真实程序状态生成,而不是让模型自己猜。
但是状态栏仍然只是让模型“知道限制”,真正的限制仍要由代码执行。
11. 护栏和 ToolSpec 的区别
输入侧护栏:
- 检查用户输入;
- 防止明显恶意指令;
- 标记外部不可信内容;
- 避免 Prompt Injection 混入高优先级规则。
执行侧护栏:
- 权限检查;
- 参数校验;
- 沙箱隔离;
- 审批;
- 金额和调用次数限制;
- 阻止危险操作。
输出侧护栏:
- 隐私脱敏;
- 敏感信息检查;
- 输出格式校验;
- 防止把内部 Prompt、密钥或不可信内容直接返回。
而 ToolSpec 主要是给模型看的说明书,不是强制安全机制。
12. 上下文太长时要压缩
上下文越长,不仅成本越高,模型也可能更难抓住重点,这就是 context rot。
但压缩不能简单地“把前面全部总结一下”。
更合理的是分层处理:
1. 限制工具输出体积
2. 删除重复和无关噪声
3. 压缩局部旧轨迹
4. 把阶段结果归档
5. 最后才做全量压缩
完整日志应该放到磁盘或 artifact 中。
上下文只保留:
- 与问题相关的日志;
- 关键报错;
- 调用栈;
- 文件引用;
- 原文重新读取方式。
压缩时不能丢掉:
- 用户目标;
- 操作范围;
- 用户约束;
- 架构决策;
- 已修改文件;
- 失败原因;
- 验证结果;
- 待办事项和回滚方式。
13. 有时隔离比压缩更好
如果主 Agent 需要从 100 个文件中找到 3 个相关文件,可以启动子 Agent。
子 Agent 自己读取大量文件,但最后只返回:
3 个相关文件
判断依据
关键发现
这样大量探索轨迹不会污染主 Agent 的上下文。
所以:
能通过子 Agent 隔离的信息,就不一定要塞进主上下文后再压缩。
最后串成一句话
第二章完整链路就是:
Harness 选择信息
→ 组装稳定前缀和动态轨迹
→ 按需加载 ToolSpec、Skill 和资料
→ 注入运行状态
→ 调用 LLM
→ 执行模型选择的工具
→ 把结果写回轨迹
→ 控制输出体积
→ 必要时分层压缩
→ 大规模探索交给隔离的子 Agent
因此
上下文工程主要是 Harness 的职责。Harness 管理上下文的组装、Skill 加载、工具披露、状态注入、压缩策略、安全边界和缓存命中。
634



被折叠的 条评论
为什么被折叠?



