文章https://bojieli.github.io/ai-agent-book/
第一章 Agent基础知识
Agent = LLM + 上下文 +工具
上下文 = 系统提示词(System Prompt) + 工具定义 (Tool Definitions)+ 用户消息(User Messages)+ 模型回复(Assistant Messages)+ 工具执行结果(Tool Results)
系统提示词 + 工具定义 = 静态前缀
用户消息 + 模型回复 + 工具执行结果 = 动态历史消息 = 轨迹(trajectory)
ReAct = Reasoning + Acting 实际上是推理-动作-评估-推理-动作-评估
Harness的核心就是“上下文+工具”,再加上三层保障机制: 约束 + 验证 +纠正
Agent = LLM + [ 上下文 + 工具 + 约束 + 验证 + 纠正 ] = Model + Harness
Harness Engineering 就是设计和优化模型基础设施之外的工程实践
Harness的机制包括:
- 流程状态管理
- 多层上下文压缩
- 权限分类
- 熔断器:当错误连续发生时自动“断电”停止重试
- 错误恢复机制:捕获异常、回滚到上一稳定状态、重试或交还给人类
演进路线
提示词工程(Prompt Engineering):优化输入给模型的自然语言来提升输出质量
↓
上下文工程(Context Engineering):系统性管理模型能看到的所有信息(系统指令、工具定义、对话历史、外部知识)
↓
Harness工程(Harness Engineering):进一步扩展到“模型在什么样的系统中运行”,涵盖了约束机制、验证手段、反馈循环和错误恢复等模型之外的全部基础设施
↓
Loop工程(Loop Engineering):从单词运行扩展到跨轮次的持续自助运转:谁来发现下一件该做的事、何时验证、何时才算真正完成
Harness的五个核心准则
- 上下文——信息充分性,让Agent在每个决策点都基于足够的信息判断
- 工具——接口清晰:工具命名直观、参数有例子、边界有说明
- 约束——故障安全默认值:所有能力默认关闭,必须显示开放
- 验证——输入隔离:安全检查只看结构化数据(如模型返回的JSON字段),而不是模型自由生成的文本(因为攻击者可能通过提示注入操作模型输出)
- 纠正——再确认无法恢复之前,不暴露中间态(例如工具调用失败时先静默重试,不将半成品结果展示给用户)
思考题:
- ★★ 如果你只能给一个 Agent 系统增加一项能力——更强的模型、更丰富的上下文、还是更多的工具——你会选哪个?在什么条件下你的选择会改变?
回答: 我会选择更丰富的上下文,更丰富的上下文意味着掌握的信息越多、判断越准、并且不会因为轻易丢失上下文而在循环推理、执行、约束、验证、纠正中出现错误,能完成更多轮地思考,大大提升准确性和可靠性。但是如果模型的能力成为了瓶颈,给再多的上下文也无法推理出结果,这个时候我选择更强的模型。
GPT:7/10 更丰富的上下文可能带来更多有效的信息、更完整的任务状态、更少的信息丢。但也有可能带来噪声、冲突信息、注意力稀释和更高成本。真正重要的不是“上下文更多”,而是上下文质量更高、组织得更好、能按需检索。 另外,选择发生变化的条件不只有模型成为瓶颈:
- Agent不知道外界实时状态时,应该选择更多工具
- Agent已有信息和工具,但推理、规划能力不足时,需要更强的模型
- 模型和工具都足够,但任务状态经常丢失时,需要更好的上下文管理
更完整的结论:
默认选择上下文管理能力,而不是单纯扩大上下文窗口;但当任务瓶颈来自于外界信息获取时选择工具,来自推理能力时选择模型。
- ★★★ ReAct 循环中,Agent 的每一次 LLM 调用都会看到完整的历史轨迹。随着轨迹增长,这种设计的成本是二次方增长的。有没有办法在不丢失关键信息的前提下打破这个二次方?
回答: 压缩精简多层次轨迹
GPT:4/10 回答方向正确,但是太抽象。 二次方的成本来源于:第t次调用需要重新读取前面全部t-1轮内容,总读近似量为: 1+2+⋯+n=O(n^2)
可以通过以下方式打破:
- 将旧轨迹总结为结构化轨迹
- 只保留最近若干轮原始轨迹
- 将历史观察结果保存为外部记忆,按需检索
- 对重复工具输出去重
- 讲过程历史和任务状态分离
例如上下文可以变成:
固定任务目标
+ 当前结构化状态
+ 最近3轮原始轨迹
+ 与当前步骤相关的历史记忆
这样每次读取的内容可以保持相对稳定,使总成本更接近O(n)
- ★★ “模型即 Agent” 范式意味着模型在工具调用决策上越来越自主。但本章论证了 Harness 工程的重要性反而在增加。这两个趋势如何共存?Agent 框架未来的核心价值体现在哪些方面? 回答: 模型的自主意味着模型的临场判断越来越准确,可以适应更复杂多变的场景,在这种情况下,Harness更加重要,因为模型越强,没有约束,可能破坏和后果更加严重,所以更需要Harness的约束、验证、纠正,来让模型在正确的轨迹推理、执行。
GPT:8/10 核心理解正确:
模型越自主,潜在影响范围越大,因此越需要外部约束。
不过Harness的价值不只是防止模型“破坏”,还包括为模型提供稳定的运行环境。未来Agent的框架的核心价值主要体现在:
- 工具权限和风险控制
- 状态管理与上下文管理
- 结果验证
- 重试、超时和降级
- 可观测性和审计
- 人工介入
- 成本和资源预算
更准确的结论:
模型负责在局部环境中灵活决策,Harness负责定义边界、维护状态、验证结果并控制系统级风险。
这两个趋势不是冲突,而是分工越来越清晰。
- ★★ 消融实验中 “工具结果反馈” 的缺失导致 Agent 陷入无限循环。在生产环境中,除了工具结果缺失,还有哪些情况可能导致 Agent 无限循环?你会设计怎样的检测和终止机制?
回答: 当模型能获取的上下文太少的情况下,第一轮导致了错误,第二轮纠错,第三轮又重新出发导致了相同的错误,对于这个相同的错误又执行了相同的纠错,导致循环。对于这种情况,我会对执行多轮后对前几轮整体进行评判,是否一直在重复。 还有网络一直请求失败,但是没有对请求失败次数进行限制,也会导致无限循环。对于这种情况,需要对请求失败次数进行限制。
GPT:8/10 给出的情况和对应机制都正确。 补充常见循环原因:
- 工具返回状态成功,但实际状态没有变化
- 多个工具互相撤销结果
- 目标本身不可完成
- 验证标准过于严苛或互相矛盾
- Agent不断搜索,但没有获得新信息
- 规划期反复生成语义相同的计划
检测机制不能只看“轮数”,还应检测:
- 相同工具和参数重复调用
- 状态连续多轮没有变化
- 新增信息量接近零
- 相同错误反复出现
- 剩余预算不足
终止后也不应只报失败,而应输出:
已尝试什么
为什么无法继续
当前完成到哪里
需要用户提供什么
- ★ 本章用感知、行动、策略三个维度分析了五个 Agent 产品。请选择一个你日常使用的 AI 产品,用这三个维度进行分析,并思考它的架构设计是否合理。如果由你来设计这个 AI 产品,有哪些改进空间?
回答: 我平时用cursor和codex,我觉得在感知方面,cursor相较于codex更差一些,获取的上下文更少,对相同的coding问题,codex能获取到更多有用的信息,在代码库中获取更完整的上下文。行动方面,codex会在做的过程中继续围绕问题搜索代码,还会反复确认修改的内容是否正确,cursor这方面没有做得很好。策略方面,我更喜欢codex多轮思考、行动、确认。 如果让我来设计cursor,我也倾向于多轮思考、行动、确认,而不是上来就总体看一下问题,然后一股脑全部实现,实现后也没有仔细确认。
GPT:7/10 你的观察比较具体,尤其是注意到了Codex会:
- 主动继续搜索代码
- 修改后进行确认
- 形成多轮行动闭环 但是这里把“感知能力”和“策略差异”混在了一起 例如Codex 获取更多代码信息,可能不是底层感知接口更强,也可能是:
它的策略更愿意主动搜索,因此最终获得了更多上下文。
可以这样区分:
- 感知:能够读取哪些文件、日志、Git diff、测试结果
- 行动:能够搜索、编辑、运行命令、执行测试
- 策略:什么时候搜索、什么时候修改、什么时候验证、什么时候停止
改进建议可以更具体一些:
- 修改前先构建影响范围
- 修改后自动检查diff
- 根据代码选择相关测试
- 失败时重新定位原因,而不是重复修改
- 在高风险改动前请求确认
- ★★ 如果你要设计一个专门处理航班订票的客服系统,你会选择工作流模式还是自主 Agent 模式?有没有可能在同一个系统中混合使用两种模式?
回答: 如果让我来设计一个专门处理航班订票的客服系统,我会倾向于两者结合,当只是处理简单的订票流程的时候,可以按照“搜索—订票—让用户确认位置—订位置—返回订票是否成功”d的工作流模式来执行,但是如果是退票、更改位置、修改中转等复杂场景,可以由自主Agent介入。
GPT:8/10 混合模式的方向正确,但复杂场景不一定意味着应该完全交给自主Agent。 实际上,退票、改签、中转修改虽然复杂,但涉及:
- 价格变化
- 退改规则
- 身份信息
- 支付
- 法律和责任
因此它们更需要确定性的工作流和人工确认。 更合理的架构是:
Agent负责理解用户意图、收集条件、比较方案和解释规则;工作流负责查询库存、锁定价格、校验身份、支付和最终出票。
也就是说,不是:
简单任务 → 工作流
复杂任务 → 自主 Agent
而是:
开放式理解和方案生成 → Agent
确定性交易和高风险执行 → 工作流
- ★★★ 护栏部分提到了工具风险评级。如果一个工具在大多数情况下是低风险的,但在特定参数组合下变为高风险(如
delete_file删除普通文件 vs 删除系统文件),你会如何设计动态风险评估?
回答: 对于一个工具,一般都是行为+目标的结合,行为上“增删改查”中,风险从高到低依次是“删—改—增—查”,但是匹配执行目标,普通文件的风险肯定比系统文件的风险低,我更倾向于权重分配,从风险高到低对行为分配权重,将文件类别也从风险高到低行为分配权重,然后行为+目标算出最终的风险分值,来评估风险
GPT:8/10 提出的“行为风险 + 目标风险”的加权评分,已经接近可实现的工程方案。
不过单纯做线程加权还不够,因为某些风险具有硬触发条件。例如:
delete_file("/tmp/test.txt")
和:
delete_file("/System/Library/...")
后者不应该只是比分更高,而应直接进入禁止或人工确认状态。
更合理的动态风险评估包括:
其中:
- Action:查、增、改、删
- Target:临时文件、项目文件、系统文件
- Scope:单个文件还是整个目录
- Reversibility:是否可恢复
- Context:是否符合当前任务目标
然后增加硬规则:
系统目录删除 → 禁止
递归删除 → 强制确认
项目文件删除 → 创建备份后确认
临时文件删除 → 可自动执行
你的答案方向很好,再增加“规则优先于评分”会更成熟
- ★★ 本章的 Agent 产品表格中,所有 Agent 的动作空间都是 “开放式” 的。一个受限的动作空间(比如只能从预定义选项中选择)在什么场景下反而优于开放式?
回答: 我觉得涉及到用户的金钱这种场景,比如订飞机票,最好动作是受限的,只能在用户确认信息后确认执行购买才能执行购买,而不是全部自动执行,Agent负责收集信息,将决定权交给用户,而不是开放式,想自己买就自己买,自己觉得哪一趟更快就买,也许用户想要更便宜或者更安全的行程组合。
GPT:6/10 你正确识别了高风险场景,但回答主要讨论的“用户确认”,还没有完全回答“动作空间受限”
受限动作空间指的是Agent只能从预定义的动作中选择,例如:
SEARCH_FLIGHT
SELECT_FLIGHT
REQUEST_CONFIRMATION
BOOK_FLIGHT
CANCEL
而不是能自由生成任意操作或参数。
它适合:
- 金融交易
- 医疗流程
- 权限管理
- 航班和酒店预订
- 工业控制
- 合规审批
- 状态机明确的业务流程
它的优势是:
- 更容易验证
- 更容易审计
- 更容易测试
- 不容易产生非法动作
- 可以保证流程状态一致
你提到的“只有用户确认后才能购买”,属于动作空间约束中的一种,但需要把概念再提升一层。
- ★★ 人工干预机制要求 Agent 能 “优雅地移交控制”。但在实践中,用户可能不在线、响应很慢、或者给出模糊的指令。此时 Agent 应该怎么办?
回答: 用户不在线:对于需要用户审核的内容,用户不在线,优先不执行,告知Agent需要执行什么操作,当前还没有修改,如果用户同意,可以发送允许命令 响应很慢:响应很慢的时候Agent可以多执行一些获取信息的工具,然后举一反三地提供给用户只需要回答是或者否的选项,让用户确认,减少交流信息的次数 模糊的指令:针对模型的指令,也是需要Agent对用户的指令进行举一反三地猜测,然后询问用户是否是这样,减少需要用户输入信息的情况,而是让用户确认是否是这样。
GPT:8/10 用户不在线: 评价:正确。 你提出“优先不执行、说明准备执行什么、等待明确授权”,符合安全默认原则,尤其适用于删除、付款、发送等不可逆操作。 还可以补充一点:Agent不只是“不执行”,还应当保存当前任务状态,包括:
- 已经完成了什么
- 正在等待什么确认
- 计划执行什么操作
- 用户回来后如何继续
“发送允许命令”略偏具体实现,更通用的说法是“等待用户明确确认”。
响应很慢: 评价:方向很好,但需要增加边界。 你提出Agent可以先做更多信息收集,再把问题整理成“是/否”或有限选项,这能减少多轮沟通,是很好的产品设计思路。
但需要强调:Agent只能继续执行低风险、可逆、不会改变外部状态的操作,例如搜索、读取、分析、比较。 不能因为用户响应慢,就继续付款、删除或提交。
模式指令: 评价:基本正确。
你说 Agent 应先推测用户意图,再让用户确认,而不是要求用户重新完整描述,这体现了“降低交互成本”。
不过“举一反三地猜测”最好改成:
根据上下文生成一个或几个最可能的解释,并明确标注这些是推测,请用户选择或确认。
因为 Agent 不应把推测当成事实。对于低风险任务,可以在合理假设下继续;对于高风险任务,必须确认。
更完整的参考答案:
当用户暂时无法完成接管时,Agent 应将任务保存为可恢复的等待状态,而不是无限等待或自行做出高风险决定。用户不在线时,对于需要确认的不可逆操作应默认暂停,并清楚说明待执行操作及其影响。用户响应较慢时,Agent 可以继续完成搜索、分析和方案整理等低风险工作,再把问题压缩为少量明确选项,降低用户的回复成本。对于模糊指令,Agent 应结合上下文提出最可能的解释,并让用户确认;如果涉及高风险或不可逆操作,则不能基于猜测直接执行。
- ★★★ 引言指出 “好的设计原则应该穿越模型的迭代周期”。试举一个你认为可能会随模型进步而过时的当前 Agent 设计原则,并说明理由。
回答: 我觉得目前网上推的各种Skill和Rules,以后都会过时,用户通过自然语言约束的Agent行为,在LLM性能越来越好后可能反而会影响Agent的能力,用户写的Skill 和 Rules通常只是在特定场景或者出现问题的那个时刻好用,但是一般沉淀加载到Agent后也不会清理,对之后Agent处理任务反而是一种不必要的约束。随着LLM的性能增强,LLM的决策会比人更全面。
GPT:8/10 观点很有价值,而且比简单说“Prompt Engineering会过时”更深入。
指出了真实问题:
Rules和Skills会不断沉淀,最终形成历史包袱,过度约束模型。
这一判断成立,但“Rules和Skills全部会过时”略显绝对。即使模型更强,以下内容仍然需要保留:
- 企业安全规则
- 权限边界
- 合规要求
- 业务事实
- 工具协议
- 输出格式
- 不可逆的操作的确认机制
更可能过时的是:
- 为弥补模型弱点而写的大量微观提示
- 针对特定模型行为的复杂Prompt技巧
- 过度细化的步骤规定
- 大量重复、冲突和低质量规则。
结论可以修改为:
随着模型能力提升,面向模型缺陷的微观行为规则会逐渐过时,但面向系统边界、安全、业务约束和责任控制的规则仍会长期存在。
第二章 上下文工程

模型本身的智力只是基础,上下文的质量才是Agent能力真正上限。
大多数团队的关键知识只是隐形的:架构决策只有老员工记得,业务规则靠口口相传,重要的背景信息所在聊天记录里。如果团队本身就是一个信息黑洞,再好的AI Agent也无计可施。对于远程工作友好的团队往往也对AI Agent友好。
人和模型一样,最重要的是Context。
2.1 消息的四种角色
大模型API的核心是一个消息列表Messages,列表中的每天消息都有一个角色(role)标识,模型根据角色来理解每条消息的含义和来源
- system:系统提示词。开发者编写,定义Agent的身份、行为规则、约束条件。最高优先级。整个对话通常只有一条,放在消息列表最前面。
- user:用户消息。来自终端用户的输入,是Agent需要响应的请求。
- assistant:助手消息。模型之前的回复,包括文本回复和工具调用请求。在多轮对话中,之前的assistant消息会被放回消息列表,让模型“记住”之前说过什么。
- tool:工具结果。模型框架执行了工具之后,将结果以tool角色的消息送回模型。每条tool消息通过
tool_call_id与对应的工具调用请求关联。
工具定义(tools)作为请求的独立字段,而非消息,告诉模型有哪些工具可以用、每个工具接受什么参数。
2.2 单论对话:最简单的API调用
“无状态”指的是模型API默认不会记住上一次请求发送了什么,也不会自动记住上一轮回答。
每次调用都是无状态的,所以模型需要的信息必须在请求的消息列表中完整提供。
//═══ Request constructed by the Agent framework ═══
{
"models": "Qwen3-0.6B",
"messages":[
{
"roles": "system",
"content": "You are a helpful coding assistant. Follow user instructions."
},
{
"role": "user",
"content": "Hello, who are you?"
}
]
}//═══ Reponse return by the API ═══
{
"choices": [{
"message":{
"role":"assistant",
"content":"Hi! I'm a coding assistant. I can help you write code, debug issues, and explain technical concepts. How can I help?"
}
}
]
}
choices:模型为这次请求生成的候选回答列表,某些模型API可以一次生成多个候选答案,choices[0]是第一个候选答案。
2.3 带工具调用的多轮交互:Agent的核心循环
第一次 API 调用——Agent 框架发送初始请求:
//═══ Request constructed by the Agent framework ═══
{
"models": "Qwen3-0.6B",
"messages":[
{
"roles": "system",
"content": "You are a helpful coding assistant. Follow user instructions."
},
{
"role": "user",
"content": "What's the current time and weather in Vancouver?"
}
],
"tools":[
{
"type": "function",
"function": {
"name": "get_current_time",
"description": "Get the current date and time in a specific timezone.",
"parameters": {
"type": "object",
"properties": {
"timezone": {
"type": "string",
"description": "Timezone name, e.g. America/Vancouver"
}
}
}
}
},
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a specific city",
"parameters": {
"type": "object",
"propertoes": {
"city": {
"type": "string",
"description": "City name"
},
"unit": {
"type": "string",
"enum": ["celsius","fahrenheit"]
}
}
}
}
}
]
}模型返回工具调用请求(不是最终回复)
// ═══ Response returned by the API (model decides to call tools) ═══
{
"choices": [{
"message": {
"role": "assistant",
"content": null, // No text response
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_current_time",
"arguments": "{\"timezone\": \"America/Vancouver\"}"
}
},
{
"id": "call_def456",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\": \"Vancouver\", \"unit\": \"celsius\"}"
}
}
]
}
}
]
}模型并没有直接回答用户的问题,而是返回两个工具调用请求。模型只是发出了调用请求,真正执行工具的是Agent框架。Agent框架的关键是:模型负责决策(调用什么工具、传什么参数),Agent框架负责执行(实际调用API、运行代码)。
Agent框架执行工具,然后发起第二次API调用: Agent框架拿到模型的工具调用请求后,实际执行这两个工具,然后将完整的对话历史加上工具执行结果一起发给模型:
// ═══ Request constructed by the Agent framework (2nd call) ═══
{
"model": "Qwen3-0.6B",
"messages": [
{
"role": "system", // ← Same as 1st call
"content": "You are a helpful assistant. Use the provided tools to get real-time information when needed."
},
{
"role": "user", // ← Same as 1st call
"content": "What's the current time and weather in Vancouver?"
},
{
"role": "assistant", // ← Model output from 1st call, included verbatim
"content": null,
"tool_calls": [
{ "id": "call_abc123", "function": { "name": "get_current_time", "arguments": "{\"timezone\": \"America/Vancouver\"}" } },
{ "id": "call_def456", "function": { "name": "get_weather", "arguments": "{\"city\": \"Vancouver\", \"unit\": \"celsius\"}" } }
]
},
{
"role": "tool", // ← Generated by Agent framework (tool execution result)
"tool_call_id": "call_abc123",
"content": "{\"timezone\": \"America/Vancouver\", \"datetime\": \"2025-09-13T05:18:47\", \"day_of_week\": \"Saturday\"}"
},
{
"role": "tool", // ← Generated by Agent framework (tool execution result)
"tool_call_id": "call_def456",
"content": "{\"city\": \"Vancouver\", \"temperature\": 13.2, \"unit\": \"celsius\", \"conditions\": \"clear\", \"humidity\": 93}"
}
],
"tools": [ ... ] // ← Same tool definitions as above, omitted
}关键细节:
- 第二次请求包含了第一次请求的全部对话历史——system消息、user消息、第一次assistant回复(包含工具调用),以及新增的tool结果。因为“每次调用都是无状态”。
- 第一次的assistant消息被原样放回消息列表——模型看到自己之前的决策。
- tool消息通过
tool_call_id与对应的工具调用关联。
模型根据工具结果生成最终回复:
// ═══ Response returned by the API (final reply) ═══
{
"choices": [{
"message": {
"role": "assistant", // ← Generated by model
"content": "It's currently 5:18 AM on Saturday, September 13, 2025 in Vancouver.\n\nWeather: 13.2°C with clear skies and 93% humidity. It's quite cool this morning - you might want to grab a jacket."
}
}]
}Agent框架的核心工作就是管理messages列表。
从API视角看上下文的构成

静态前缀(System Prompt + Tool Definitions)不变 对话历史/轨迹(user → assistant → tool result → user →…)不断增长,可以压缩