文章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字段),而不是模型自由生成的文本(因为攻击者可能通过提示注入操作模型输出)
  • 纠正——再确认无法恢复之前,不暴露中间态(例如工具调用失败时先静默重试,不将半成品结果展示给用户)

思考题:

  1. ★★ 如果你只能给一个 Agent 系统增加一项能力——更强的模型、更丰富的上下文、还是更多的工具——你会选哪个?在什么条件下你的选择会改变?

回答: 我会选择更丰富的上下文,更丰富的上下文意味着掌握的信息越多、判断越准、并且不会因为轻易丢失上下文而在循环推理、执行、约束、验证、纠正中出现错误,能完成更多轮地思考,大大提升准确性和可靠性。但是如果模型的能力成为了瓶颈,给再多的上下文也无法推理出结果,这个时候我选择更强的模型。

GPT:7/10 更丰富的上下文可能带来更多有效的信息、更完整的任务状态、更少的信息丢。但也有可能带来噪声、冲突信息、注意力稀释和更高成本。真正重要的不是“上下文更多”,而是上下文质量更高、组织得更好、能按需检索。 另外,选择发生变化的条件不只有模型成为瓶颈:

  • Agent不知道外界实时状态时,应该选择更多工具
  • Agent已有信息和工具,但推理、规划能力不足时,需要更强的模型
  • 模型和工具都足够,但任务状态经常丢失时,需要更好的上下文管理

更完整的结论:

默认选择上下文管理能力,而不是单纯扩大上下文窗口;但当任务瓶颈来自于外界信息获取时选择工具,来自推理能力时选择模型。


  1. ★★★ ReAct 循环中,Agent 的每一次 LLM 调用都会看到完整的历史轨迹。随着轨迹增长,这种设计的成本是二次方增长的。有没有办法在不丢失关键信息的前提下打破这个二次方?

回答: 压缩精简多层次轨迹

GPT:4/10 回答方向正确,但是太抽象。 二次方的成本来源于:第t次调用需要重新读取前面全部t-1轮内容,总读近似量为: 1+2+⋯+n=O(n^2)

可以通过以下方式打破:

  • 将旧轨迹总结为结构化轨迹
  • 只保留最近若干轮原始轨迹
  • 将历史观察结果保存为外部记忆,按需检索
  • 对重复工具输出去重
  • 讲过程历史和任务状态分离

例如上下文可以变成:

固定任务目标
+ 当前结构化状态
+ 最近3轮原始轨迹
+ 与当前步骤相关的历史记忆

这样每次读取的内容可以保持相对稳定,使总成本更接近O(n)


  1. ★★ “模型即 Agent” 范式意味着模型在工具调用决策上越来越自主。但本章论证了 Harness 工程的重要性反而在增加。这两个趋势如何共存?Agent 框架未来的核心价值体现在哪些方面? 回答: 模型的自主意味着模型的临场判断越来越准确,可以适应更复杂多变的场景,在这种情况下,Harness更加重要,因为模型越强,没有约束,可能破坏和后果更加严重,所以更需要Harness的约束、验证、纠正,来让模型在正确的轨迹推理、执行。

GPT:8/10 核心理解正确:

模型越自主,潜在影响范围越大,因此越需要外部约束。

不过Harness的价值不只是防止模型“破坏”,还包括为模型提供稳定的运行环境。未来Agent的框架的核心价值主要体现在:

  • 工具权限和风险控制
  • 状态管理与上下文管理
  • 结果验证
  • 重试、超时和降级
  • 可观测性和审计
  • 人工介入
  • 成本和资源预算

更准确的结论:

模型负责在局部环境中灵活决策,Harness负责定义边界、维护状态、验证结果并控制系统级风险。

这两个趋势不是冲突,而是分工越来越清晰。


  1. ★★ 消融实验中 “工具结果反馈” 的缺失导致 Agent 陷入无限循环。在生产环境中,除了工具结果缺失,还有哪些情况可能导致 Agent 无限循环?你会设计怎样的检测和终止机制?

回答: 当模型能获取的上下文太少的情况下,第一轮导致了错误,第二轮纠错,第三轮又重新出发导致了相同的错误,对于这个相同的错误又执行了相同的纠错,导致循环。对于这种情况,我会对执行多轮后对前几轮整体进行评判,是否一直在重复。 还有网络一直请求失败,但是没有对请求失败次数进行限制,也会导致无限循环。对于这种情况,需要对请求失败次数进行限制。

GPT:8/10 给出的情况和对应机制都正确。 补充常见循环原因:

  • 工具返回状态成功,但实际状态没有变化
  • 多个工具互相撤销结果
  • 目标本身不可完成
  • 验证标准过于严苛或互相矛盾
  • Agent不断搜索,但没有获得新信息
  • 规划期反复生成语义相同的计划

检测机制不能只看“轮数”,还应检测:

  • 相同工具和参数重复调用
  • 状态连续多轮没有变化
  • 新增信息量接近零
  • 相同错误反复出现
  • 剩余预算不足

终止后也不应只报失败,而应输出:

已尝试什么
为什么无法继续
当前完成到哪里
需要用户提供什么

  1. ★ 本章用感知、行动、策略三个维度分析了五个 Agent 产品。请选择一个你日常使用的 AI 产品,用这三个维度进行分析,并思考它的架构设计是否合理。如果由你来设计这个 AI 产品,有哪些改进空间?

回答: 我平时用cursor和codex,我觉得在感知方面,cursor相较于codex更差一些,获取的上下文更少,对相同的coding问题,codex能获取到更多有用的信息,在代码库中获取更完整的上下文。行动方面,codex会在做的过程中继续围绕问题搜索代码,还会反复确认修改的内容是否正确,cursor这方面没有做得很好。策略方面,我更喜欢codex多轮思考、行动、确认。 如果让我来设计cursor,我也倾向于多轮思考、行动、确认,而不是上来就总体看一下问题,然后一股脑全部实现,实现后也没有仔细确认。

GPT:7/10 你的观察比较具体,尤其是注意到了Codex会:

  • 主动继续搜索代码
  • 修改后进行确认
  • 形成多轮行动闭环 但是这里把“感知能力”和“策略差异”混在了一起 例如Codex 获取更多代码信息,可能不是底层感知接口更强,也可能是:

它的策略更愿意主动搜索,因此最终获得了更多上下文。

可以这样区分:

  • 感知:能够读取哪些文件、日志、Git diff、测试结果
  • 行动:能够搜索、编辑、运行命令、执行测试
  • 策略:什么时候搜索、什么时候修改、什么时候验证、什么时候停止

改进建议可以更具体一些:

  • 修改前先构建影响范围
  • 修改后自动检查diff
  • 根据代码选择相关测试
  • 失败时重新定位原因,而不是重复修改
  • 在高风险改动前请求确认

  1. ★★ 如果你要设计一个专门处理航班订票的客服系统,你会选择工作流模式还是自主 Agent 模式?有没有可能在同一个系统中混合使用两种模式?

回答: 如果让我来设计一个专门处理航班订票的客服系统,我会倾向于两者结合,当只是处理简单的订票流程的时候,可以按照“搜索—订票—让用户确认位置—订位置—返回订票是否成功”d的工作流模式来执行,但是如果是退票、更改位置、修改中转等复杂场景,可以由自主Agent介入。

GPT:8/10 混合模式的方向正确,但复杂场景不一定意味着应该完全交给自主Agent。 实际上,退票、改签、中转修改虽然复杂,但涉及:

  • 价格变化
  • 退改规则
  • 身份信息
  • 支付
  • 法律和责任

因此它们更需要确定性的工作流和人工确认。 更合理的架构是:

Agent负责理解用户意图、收集条件、比较方案和解释规则;工作流负责查询库存、锁定价格、校验身份、支付和最终出票。

也就是说,不是:

简单任务 → 工作流
复杂任务 → 自主 Agent

而是:

开放式理解和方案生成 → Agent
确定性交易和高风险执行 → 工作流

  1. ★★★ 护栏部分提到了工具风险评级。如果一个工具在大多数情况下是低风险的,但在特定参数组合下变为高风险(如 delete_file 删除普通文件 vs 删除系统文件),你会如何设计动态风险评估?

回答: 对于一个工具,一般都是行为+目标的结合,行为上“增删改查”中,风险从高到低依次是“删—改—增—查”,但是匹配执行目标,普通文件的风险肯定比系统文件的风险低,我更倾向于权重分配,从风险高到低对行为分配权重,将文件类别也从风险高到低行为分配权重,然后行为+目标算出最终的风险分值,来评估风险

GPT:8/10 提出的“行为风险 + 目标风险”的加权评分,已经接近可实现的工程方案。

不过单纯做线程加权还不够,因为某些风险具有硬触发条件。例如:

delete_file("/tmp/test.txt")

和:

delete_file("/System/Library/...")

后者不应该只是比分更高,而应直接进入禁止或人工确认状态。

更合理的动态风险评估包括:

其中:

  • Action:查、增、改、删
  • Target:临时文件、项目文件、系统文件
  • Scope:单个文件还是整个目录
  • Reversibility:是否可恢复
  • Context:是否符合当前任务目标

然后增加硬规则:

系统目录删除 → 禁止
递归删除 → 强制确认
项目文件删除 → 创建备份后确认
临时文件删除 → 可自动执行

你的答案方向很好,再增加“规则优先于评分”会更成熟


  1. ★★ 本章的 Agent 产品表格中,所有 Agent 的动作空间都是 “开放式” 的。一个受限的动作空间(比如只能从预定义选项中选择)在什么场景下反而优于开放式?

回答: 我觉得涉及到用户的金钱这种场景,比如订飞机票,最好动作是受限的,只能在用户确认信息后确认执行购买才能执行购买,而不是全部自动执行,Agent负责收集信息,将决定权交给用户,而不是开放式,想自己买就自己买,自己觉得哪一趟更快就买,也许用户想要更便宜或者更安全的行程组合。

GPT:6/10 你正确识别了高风险场景,但回答主要讨论的“用户确认”,还没有完全回答“动作空间受限”

受限动作空间指的是Agent只能从预定义的动作中选择,例如:

SEARCH_FLIGHT
SELECT_FLIGHT
REQUEST_CONFIRMATION
BOOK_FLIGHT
CANCEL

而不是能自由生成任意操作或参数。

它适合:

  • 金融交易
  • 医疗流程
  • 权限管理
  • 航班和酒店预订
  • 工业控制
  • 合规审批
  • 状态机明确的业务流程

它的优势是:

  • 更容易验证
  • 更容易审计
  • 更容易测试
  • 不容易产生非法动作
  • 可以保证流程状态一致

你提到的“只有用户确认后才能购买”,属于动作空间约束中的一种,但需要把概念再提升一层。


  1. ★★ 人工干预机制要求 Agent 能 “优雅地移交控制”。但在实践中,用户可能不在线、响应很慢、或者给出模糊的指令。此时 Agent 应该怎么办?

回答: 用户不在线:对于需要用户审核的内容,用户不在线,优先不执行,告知Agent需要执行什么操作,当前还没有修改,如果用户同意,可以发送允许命令 响应很慢:响应很慢的时候Agent可以多执行一些获取信息的工具,然后举一反三地提供给用户只需要回答是或者否的选项,让用户确认,减少交流信息的次数 模糊的指令:针对模型的指令,也是需要Agent对用户的指令进行举一反三地猜测,然后询问用户是否是这样,减少需要用户输入信息的情况,而是让用户确认是否是这样。

GPT:8/10 用户不在线: 评价:正确。 你提出“优先不执行、说明准备执行什么、等待明确授权”,符合安全默认原则,尤其适用于删除、付款、发送等不可逆操作。 还可以补充一点:Agent不只是“不执行”,还应当保存当前任务状态,包括:

  • 已经完成了什么
  • 正在等待什么确认
  • 计划执行什么操作
  • 用户回来后如何继续

“发送允许命令”略偏具体实现,更通用的说法是“等待用户明确确认”。

响应很慢: 评价:方向很好,但需要增加边界。 你提出Agent可以先做更多信息收集,再把问题整理成“是/否”或有限选项,这能减少多轮沟通,是很好的产品设计思路。

但需要强调:Agent只能继续执行低风险、可逆、不会改变外部状态的操作,例如搜索、读取、分析、比较。 不能因为用户响应慢,就继续付款、删除或提交。

模式指令: 评价:基本正确。

你说 Agent 应先推测用户意图,再让用户确认,而不是要求用户重新完整描述,这体现了“降低交互成本”。

不过“举一反三地猜测”最好改成:

根据上下文生成一个或几个最可能的解释,并明确标注这些是推测,请用户选择或确认。

因为 Agent 不应把推测当成事实。对于低风险任务,可以在合理假设下继续;对于高风险任务,必须确认。

更完整的参考答案:

当用户暂时无法完成接管时,Agent 应将任务保存为可恢复的等待状态,而不是无限等待或自行做出高风险决定。用户不在线时,对于需要确认的不可逆操作应默认暂停,并清楚说明待执行操作及其影响。用户响应较慢时,Agent 可以继续完成搜索、分析和方案整理等低风险工作,再把问题压缩为少量明确选项,降低用户的回复成本。对于模糊指令,Agent 应结合上下文提出最可能的解释,并让用户确认;如果涉及高风险或不可逆操作,则不能基于猜测直接执行。


  1. ★★★ 引言指出 “好的设计原则应该穿越模型的迭代周期”。试举一个你认为可能会随模型进步而过时的当前 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
}

关键细节:

  1. 第二次请求包含了第一次请求的全部对话历史——system消息、user消息、第一次assistant回复(包含工具调用),以及新增的tool结果。因为“每次调用都是无状态”。
  2. 第一次的assistant消息被原样放回消息列表——模型看到自己之前的决策。
  3. 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 →…)不断增长,可以压缩