原文: You Don’t Know AI Agents - Tw93 发布日期: 2026年3月22日
前言
作者在写了 “The Claude Code You Don’t Know” 后,发现自己对 Agent 底层基础的理解还不够深入。结合团队在生产环境中部署 Agent 的经验,他决定重新梳理这些知识,写成这篇文章。
文章核心观点:
- 用更贵的模型不总是带来突破性提升
- Harness 的质量对成功率影响比模型选择更大
- 调试 Agent 时,首先检查工具定义——大多数错误来自描述不准确
- 评估系统本身的 bug 比 Agent 的 bug 更难发现
一、Agent 循环是怎么工作的
1.1 核心就四步
Agent 循环的本质就是一个不断重复的循环:
感知 → 决策 → 行动 → 反馈 → 回到感知
你输入一句话,Agent 思考该用什么工具,调用工具执行,获取结果,再思考下一步。一直循环,直到任务完成。
1.2 核心代码很短
说出来你可能不信,Agent 循环的核心代码不到 20 行。剩下的代码都是在扩展工具、优化上下文、管理记忆这些事情上。
1.3 Workflow 和 Agent 的区别
| 类型 | 谁决定下一步 |
|---|---|
| Workflow | 人预先写好的流程,Agent 按步执行 |
| Agent | Agent 自己看情况决定下一步做什么 |
现实中的产品经常混用这两种模式,关键是搞清楚谁在掌控节奏。
1.4 五种常见模式
| 模式 | 什么时候用 |
|---|---|
| Prompt Chaining | 一步一步做,后一步依赖前一步(例:先写大纲再写内容) |
| Routing | 分类后走不同流程(例:技术支持 vs 账单问题走不同路径) |
| Parallelization | 同时做多件事或多个方案投票(例:代码多版本生成让用户选) |
| Orchestrator-Workers | 一个中央 Agent 分解任务、分派给子 Agent、汇总结果 |
| Evaluator-Optimizer | 生成→评估→改进→再评估,直到达标(例:翻译到满意为止) |
二、为什么 Harness 比模型本身更重要
2.1 什么是 Harness
Harness 的意思是”脚手架”——不是说 Agent 本身,而是围绕 Agent 构建的一整套基础设施。
它包括四个部分:
| 部分 | 什么意思 |
|---|---|
| 验收基线 | 怎么算”完成”?达到什么标准算成功? |
| 执行边界 | Agent 能做什么、不能做什么 |
| 反馈信号 | Agent 怎么知道自己做对了还是做错了 |
| 回退机制 | 出问题了怎么办 |
2.2 一个反直觉的事实
很多人觉得 Agent 效果不好是因为模型不够强。但实际上,花时间优化 Harness 比换模型更值。
这就像做菜:
- 好的厨师(模型)用土灶(烂 harness)做不出好菜
- 普通厨师(一般模型)用现代厨房(好 harness)能做出还不错的菜
2.3 OpenAI 怎么用 Agent 做到 10 倍效率
三个工程师,5 个月,100 万行代码,1500 个 PR。
他们总结了四个关键做法:
1. “Agent 看不到的等于不存在”
很多团队把文档放在 Notion、Confluence,但 Agent 运行时根本读不到这些。所以知识必须放在 Agent 能读到的地方,比如代码库里的 AGENTS.md。
2. “约束要编码进去,不能只写文档”
你写一百遍”不要直接 push 到 main”,Agent 大概率还是会 push。把这条规则编码成 CI 检查,比文档有效一百倍。
3. “端到端自主完成”
从验证当前状态 → 复现 bug → 修复 → 测试 → 提交 PR → 处理 review → 合并,全程 Agent 自己搞定,不需要人盯着。
4. “减少合并摩擦”
在高吞吐量场景下,等人工 review 的成本往往高于接受偶尔的小错误。写好一次自动检查,到处生效。
2.4 Harness 的目标:让任务进入”理想区”
任务可以按两个维度分类:
| 目标清晰 | 目标模糊 | |
|---|---|---|
| 结果可自动验证 | ✅ 理想区:Agent 非常适合 | ⚠️ 容易走偏:自动反馈但不知道往哪走 |
| 结果需人工验证 | ⚠️ 受制于人工速度 | ❌ 双重困境:Agent 基本无用 |
Harness 的目标是把任务推进理想区——让”对错”由机器标准判断,而不是人盯着看。
三、上下文工程:怎么让 Agent 长时间保持专注
3.1 最大的敌人:Context Rot
Transformer 的注意力是 O(n²),上下文越长,有效信号越容易被噪声淹没。
典型症状:对话到第 20 轮时,Agent 开始犯迷糊,说的话跟前面脱节。这就是 Context Rot——上下文退化。
3.2 解决方案:分层管理
不要把所有东西都塞进上下文,而是分层管理:
| 层级 | 放什么 | 怎么用 |
|---|---|---|
| 永久层 | 身份、核心规则、严格禁止 | 每次都加载,保持精简 |
| 按需层 | 技能文档、领域知识 | 需要时再加载 |
| 运行时层 | 当前时间、用户ID、会话状态 | 每轮动态追加 |
| 记忆层 | 跨会话的重要事实 | 存在文件里,需要时读取 |
核心原则:确定性逻辑不要放上下文,让代码处理。
3.3 三种压缩策略
当上下文快满时,需要压缩:
| 策略 | 怎么压缩 | 丢什么 | 适合场景 |
|---|---|---|---|
| 滑动窗口 | 直接扔掉最早的对话 | 早期上下文 | 短对话 |
| LLM 摘要 | 让模型总结 | 细节,保留决策 | 长任务、有关键决策 |
| 工具结果替换 | 只保留”工具调用成功/失败” | 工具原始输出 | 工具密集型任务 |
3.4 Prompt Caching 的秘密
大模型推理时会缓存 Key-Value 对。如果当前请求的前缀和之前某个请求完全一致,KV 不需要重新计算。
关键点:缓存要求精确匹配,一个 token 的差别都不行。
所以要把静态内容(系统提示、工具定义)放在一起,动态内容(当前输入、工具结果)放最后。这样缓存命中率更高。
3.5 Skills:被低估的利器
Skills 的核心思想是:系统提示只保留索引,完整知识需要时再加载。
// 系统提示只写索引
const systemPrompt = `Available Skills:
// - deploy: 部署到生产环境
// - code-review: 代码审查清单
// `;
// 实际内容按需加载
async function loadSkill(name) {
return fs.readFile(`./skills/${name}.md`);
}Skills 描述写好很难。关键是:
- 说什么时候用
- 说什么时候不用(反例很重要!)
- 说输出是什么
有反例 vs 没反例,准确率从 53% 提升到 85%,还快 18%。
四、工具设计:决定 Agent 能做什么
4.1 工具不是越多越好
5 个 MCP 服务器可以带来 55000 tokens 的工具定义开销,占 200K 上下文窗口的 30%。
问题不是缺工具,是工具选不对、描述不清楚、返回没用的结果。
4.2 好工具 vs 差工具
| 维度 | 好工具 | 差工具 |
|---|---|---|
| 粒度 | 一个工具完成一个目标 | 一个工具对应一个 API 调用 |
| 返回 | 返回下一步需要的字段 | 返回完整原始数据 |
| 错误 | 告诉 Agent 怎么修复 | 只说”出错了” |
| 描述 | 说清楚什么时候用、什么时候不用 | 只说工具能做什么 |
4.3 工具设计进化三代
| 阶段 | 问题 | 改进 |
|---|---|---|
| 第一代 | 每个 API 端面对应一个工具,粒度太细 | 太碎了 |
| 第二代 | 按 Agent 目标设计工具 | 一次调用完成一个目标 |
| 第三代 | 优化发现、调用、描述 | Tool Search、代码化调用、示例 |
4.4 一个好工具的例子
// 差工具:参数模糊,错误信息无用
{
name: "update_yuque_post",
input_schema: {
properties: {
post_id: { type: "string" },
content: { type: "string" },
}
}
}
// 错误时返回: "Error: update failed"
// 好工具:清晰描述、结构化错误
{
name: "update_yuque_post",
description: "更新语雀文章;不适用于创建新文章",
inputSchema: z.object({
post_id: z.string().describe("语雀文章ID,仅数字字符串,如 '12345678'"),
content_markdown: z.string().describe("Markdown 格式的主要内容"),
}),
run: async (input) => {
if (!post) throw new ToolError("文章不存在", {
error_code: "POST_NOT_FOUND",
suggestion: "请先调用 list_yuque_posts 获取有效ID"
});
}
}4.5 调试 Agent 的第一反应:检查工具定义
大多数工具选择错误是描述不准确,而不是模型能力不行。先把工具描述写清楚,往往能解决大部分问题。
五、记忆系统:让 Agent 有”记忆”
5.1 Agent 缺乏时间感
人关了电脑第二天还能记得昨天的任务,但 Agent 每次新建会话都是从零开始。
为了让 Agent 跨会话保持一致,必须单独设计记忆层。
5.2 四种记忆
| 类型 | 相当于人的什么 | 存在哪 |
|---|---|---|
| 工作记忆 | 短期记忆,正在想的 | 上下文窗口 |
| 程序记忆 | 技能记忆,会做但不用刻意想 | Skills 文件 |
| 情景记忆 | 发生了什么 | JSONL 日志文件 |
| 语义记忆 | 重要事实 | MEMORY.md |
5.3 ChatGPT 怎么设计记忆
| 层级 | 内容 | 持久化 |
|---|---|---|
| 会话元数据 | 设备、位置、模式 | 不持久化 |
| 用户记忆 | ~33 个关键偏好 | 每次注入 |
| 对话摘要 | ~15 个近期聊天摘要 | 预生成 |
| 当前会话 | 滑动窗口 | 不持久化 |
注意:ChatGPT 没用向量数据库,就是简单的文本文件 + 关键词搜索。复杂不一定好。
5.4 记忆整合要可逆
上下文快满时要把旧内容移到记忆文件里。关键是这个过程必须可逆——原始消息不要删,万一整合失败了还能找回。
六、怎么让 Agent 自主工作更长时间
6.1 长任务的常见死法
长任务最常见的失败不是某一步做错了,而是会话结束时任务没完成,但下一会话无法准确恢复到之前状态。
要么上下文耗尽了,要么恢复时丢掉了关键信息。
6.2 跨会话恢复方案
把任务状态外部化到文件系统:
Initializer Agent(只跑一次)
↓ 生成
feature-list.json(功能清单)
init.sh(初始化脚本)
claude-progress.txt(进度跟踪)
Coding Agent(循环跑)
↓ 每次启动时
读取 claude-progress.txt
继续上一个功能
完成后更新进度
提交 git commit
退出
6.3 任务状态要显式化
不要让 Agent 在”脑子里”记住做到哪了,要写出来:
{
"tasks": [
{"id": "1", "desc": "读取现有配置", "status": "completed"},
{"id": "2", "desc": "修改数据库schema", "status": "in_progress"},
{"id": "3", "desc": "更新API端点", "status": "pending"}
]
}七、多 Agent 怎么配合工作
7.1 不是只有”一起做”这一种
多 Agent 有两种基本模式:
| 模式 | 怎么工作 | 什么时候用 |
|---|---|---|
| 导演模式 | 人盯着一个 Agent,逐轮调整 | 需要精细控制 |
| 编排模式 | 人开头、中间 Agent 并行、最后审查结果 | 规模化、自动化 |
编排模式的价值:人只出现在开头和结尾,中间变成对产物(分支、PR)的审查,而不是持续的介入。
7.2 子 Agent 要隔离
子 Agent 探索、试错、调试的过程不应该污染主 Agent 的上下文。
主 Agent 只需要结论,探索细节留在子 Agent 自己的消息历史里。
7.3 协调需要协议
当多个 Agent 要配合时,自然语言不够用——模型记不住谁答应什么、谁在等谁。
必须有结构化的协议:定义消息格式、状态机、依赖关系。
7.4 错误会级联放大
A 错了 → B 以为 A 对 → C 基于 B 的错误继续 → 全错了
这就是为什么需要交叉验证:让 Agent 独立判断,而不是盲目跟随前面的结论。
八、评估:怎么衡量 Agent 做得好不好
8.1 Agent 评估比普通评估复杂得多
| 类型 | 怎么做 |
|---|---|
| 传统评估 | 输入 → 看输出对不对 |
| Agent 评估 | 准备环境 → Agent 执行多步 → 验证环境最终状态 |
Agent 评估不仅要听 Agent 说什么,更要看环境中实际发生了什么。
8.2 两个指标不能混
| 指标 | 什么意思 | 什么时候用 |
|---|---|---|
| Pass@k | k 次中至少一次成功 | 探索能力边界 |
| Pass^k | k 次全部成功 | 发布前回归测试 |
8.3 评估系统本身也可能有 bug
如果评估系统有 bug,调 Agent 只会越调越偏。
常见评估 bug:环境资源不足导致进程被 kill、评分器本身有 bug、测试用例脱离实际。
分数下降时,先检查基础设施,再调 Agent。
九 Tracing:怎么调试 Agent
9.1 出了问题是需要完整记录
APM 只能看延迟和错误率,但对 Agent 内部的决策过程一无所知。
Agent 出问题时,你需要看完整的执行 trace,才能定位到是模型在哪一步决策失误。
9.2 记录什么
每次 Agent 执行要记录:
├── 完整 Prompt(含系统提示)
├── 所有消息轮次
├── 每次工具调用 + 参数 + 返回值
├── 推理链(如果有)
├── 最终输出
└── Token 消耗 + 延迟
9.3 两层可观测性
| 层级 | 做什么 |
|---|---|
| 人工层 | 采样错误案例、长对话、用户负面反馈 |
| 自动层 | LLM 评分全量 trace,用人工标注校准 |
两层都要有:单独靠人工无法规模化,单独靠自动会漂移。
十、安全:Agent 可能带来哪些风险
10.1 先想安全,再想功能
Agent 能执行 shell 命令,这意味着 git push、rm、数据库写入都在射程内。
三件必须先做:
| 防护 | 什么意思 |
|---|---|
| 白名单 | 只有授权用户能调用这个 Agent |
| 隔离 | Agent 只能操作指定目录,出去就报错 |
| 审计 | 记录所有操作,方便回溯 |
10.2 提示注入防护
Agent 会读取网页、邮件、文档,这些内容可能包含恶意指令。
防护原则:
- 外部内容进入上下文时明确标记来源
- 敏感操作要求用户显式确认
- 不给 Agent 超出需要的权限
十一、一句话总结
Agent 的可靠性来自工程,而不是模型本身。
模型很重要,但Harness、上下文管理、工具设计、评估系统这些工程实践往往更能决定成败。
附:常见反模式
- 把确定性逻辑塞进上下文(应该让代码处理)
- 工具数量多但描述模糊(应该精简 + 描述清晰)
- Skills 塞太多(应该按需加载)
- 没有评估就上线(应该从第一个失败 case 开始建测试)
- 不记录 trace(出了故障无法复现)
整理自 Tw93 的文章,2026-03-22