Appearance
Agent 设计基础
本节介绍构建 Agent 的三大核心组件:模型(Model)、工具(Tools)、指令(Instructions)。
核心组件
在最基本的形式中,Agent 由三个核心组件构成:
| 组件 | 说明 |
|---|---|
| 模型(Model) | 为 Agent 推理和决策提供支持的 LLM |
| 工具(Tools) | Agent 可以用来执行操作的外部函数或 API |
| 指令(Instructions) | 定义 Agent 行为的明确指南和护栏 |
python
weather_agent = Agent(
name="Weather agent",
instructions="You are a helpful agent who can talk to users about the weather",
tools=[get_weather],
)关于代码示例
本系列的代码示例基于 OpenAI Agents SDK。其他框架(如 LangChain、CrewAI、AutoGen)在概念层面相通,API 和配置方式有所不同。
选择模型
基本原则
不同的模型在任务复杂度、延迟和成本方面有不同的优势和权衡。
| 任务类型 | 推荐模型 | 原因 |
|---|---|---|
| 简单检索、意图分类 | 较小、快速的模型(如 Haiku) | 任务简单,不需要最智能的模型 |
| 复杂推理、多步决策 | 能力更强的模型(如 Sonnet、GPT-4) | 需要更强的推理能力 |
模型选择策略
推荐方法:
- 建立基准:先用最胜任的模型构建原型,建立性能基准
- 逐步替换:尝试换用较小的模型,看是否仍能达到可接受的结果
- 确保质量:不要过早限制 Agent 的能力,确保准确率目标能达成
选择模型的原则:
- 设置评估以建立性能基准
- 使用可用的最佳模型专注于满足准确率目标
- 在可能的情况下,通过用较小的模型替换较大的模型来优化成本和延迟
定义工具
工具的作用
工具通过使用底层应用程序或系统的 API 来扩展 Agent 的能力。对于没有 API 的遗留系统,Agent 可以依赖计算机使用模型通过 Web 和应用程序 UI 直接与这些系统交互——就像人类一样。
工具设计原则
每个工具都应该有一个标准化定义,使工具和 Agent 之间能够灵活地建立多对多关系。
良好工具的特征:
- 良好文档化
- 经过充分测试
- 可重用
- 清晰的名称和参数
- 详细的描述
三种工具类型
| 类型 | 描述 | 示例 |
|---|---|---|
| 数据 | 检索上下文和信息,管理短期/长期记忆 | 查询数据库、读取 PDF、搜索网络、读取向量库 |
| 操作 | 与系统交互执行操作 | 发送邮件、更新 CRM、添加记录 |
| 编排 | Agent 作为其他 Agent 的工具(详见第 5 篇:Agent 编排模式) | 退款 Agent、研究 Agent |
工具格式建议
有几种方法可以指定同一个操作。例如,你可以通过编写 diff 或重写整个文件来指定文件编辑。在 JSON 或 Markdown 中返回代码。
工具格式最佳实践:
| 建议 | 说明 |
|---|---|
| 给模型足够的 token 来"思考" | 避免模型陷入困境 |
| 保持格式接近模型自然看到的文本形式 | 减少理解成本 |
| 确保没有格式化"开销" | 不需要精确计数、不需要字符串转义 |
经验法则:思考在人机交互(HCI)上投入了多少精力,并计划在创建良好的 Agent-计算机接口(ACI)上投入同样多的精力。
工具定义示例
python
@function_tool
def save_results(output):
db.insert({
"output": output,
"timestamp": datetime.datetime.now(),
})
return "File saved"
search_agent = Agent(
name="Search agent",
instructions="Help the user search the internet and save results if asked.",
tools=[WebSearchTool(), save_results],
)工具工程最佳实践
| 实践 | 说明 |
|---|---|
| 设身处地为模型着想 | 根据描述和参数,使用这个工具是否显而易见? |
| 清晰的参数名称 | 想象为团队中的初级开发人员编写文档字符串 |
| 测试模型如何使用工具 | 运行许多示例输入,查看模型犯的错误,并迭代改进 |
| 防错设计 | 更改参数使其更难犯错 |
示例:防错设计
在为 SWE-bench 构建 Agent 时,发现 Agent 在移出根目录后使用相对文件路径会犯错。解决方案:更改工具,始终要求使用绝对文件路径——模型可以完美地使用这个方法。
工具集成标准
模型上下文协议(MCP) 是连接 Agent 与外部工具的开放标准。你只需实现一个 MCP 客户端即可接入不断增长的第三方工具生态(如数据库、云服务、代码仓库等),无需为每个工具编写自定义集成。参见 modelcontextprotocol.io。
错误处理模式
工具调用必然会遇到失败。Agent 需要能够从错误中恢复:
| 模式 | 说明 | 示例 |
|---|---|---|
| 重试 | 网络错误时自动重试(带退避) | get_order_status 超时 → 等待 1 秒重试 |
| 降级 | 主工具失败时用替代方案 | 数据库查询失败 → 改用缓存数据 |
| 上报 | Agent 无法自行解决时请求人工 | "系统暂时无法访问,我帮你转接人工客服" |
| 验证 | 检查工具输出合理性 | API 返回空列表 → 确认查询条件后再决定 |
重试示例
python
@function_tool
async def get_order_status(order_id: str) -> str:
"""查询订单状态,失败时会自动重试"""
max_retries = 3
for attempt in range(max_retries):
try:
result = await api.get_order(order_id)
return f"订单状态: {result.status}"
except ConnectionError:
if attempt < max_retries - 1:
await asyncio.sleep(1 * (attempt + 1)) # 退避等待
continue
return "系统暂时无法查询,请稍后再试或联系人工客服。"配置指令
高质量的指令对于任何 LLM 驱动的应用都是必不可少的,但对于 Agent 尤为关键。清晰的指令可以减少歧义并改善 Agent 决策,从而实现更顺畅的工作流执行和更少的错误。
指令最佳实践
| 实践 | 说明 |
|---|---|
| 使用现有文档 | 使用现有的操作程序、支持脚本或策略文档来创建 LLM 友好的例程 |
| 分解任务 | 从密集资源中提供更小、更清晰的步骤,有助于减少歧义 |
| 定义明确的动作 | 确保每个步骤对应一个特定的动作或输出 |
| 处理边缘情况 | 预见常见变化,包含条件步骤或分支处理 |
指令结构建议
- 使用现有文档:在客户服务中,例程大致可以映射到知识库中的各个文章
- 分解任务:提供更小、更清晰的步骤,帮助模型更好地遵循指令
- 明确动作:例如,一个步骤可能指示 Agent 询问用户的订单号或调用 API 检索账户详情
- 捕获边缘情况:例如当用户提供不完整信息或提出意外问题时如何处理
自动生成指令
可以使用高级模型(如 o1 或 o3‑mini)从现有文档自动生成指令:
"You are an expert in writing instructions for an LLM agent.
Convert the following help center document into a clear set of instructions,
written in a numbered list.
Ensure that there is no ambiguity, and that the instructions are written as
directions for an agent."记忆架构
Agent 在循环运行中需要"记住"信息,这涉及两个层面的记忆:
| 层面 | 机制 | 说明 |
|---|---|---|
| 短期记忆 | LLM 上下文窗口 | 会话历史、当前轮次的 Observe/Think/Act 记录,受 token 限制 |
| 长期记忆 | 外部存储(向量数据库/文件/数据库) | 跨会话持久信息,如用户偏好、历史订单、知识库 |
短期记忆管理
上下文窗口是有限的。常见策略:
- 滑动窗口:只保留最近的 N 轮对话,丢弃较早的历史
- 摘要压缩:当上下文接近上限时,用 LLM 将历史摘要成一段话
- 结构化剪枝:丢弃低价值信息(如确认消息、中间思考步骤),保留关键决策
长期记忆的实现
python
async def retrieve_user_context(user_id: str) -> str:
"""从向量数据库检索用户历史信息"""
query = f"用户 {user_id} 的历史偏好和订单信息"
results = await vector_db.search(query, top_k=5)
return "\n".join(r.content for r in results)
async def remember(agent, user_id: str):
"""Agent 工具:检索用户长期记忆并注入上下文"""
context = await retrieve_user_context(user_id)
return f"用户的已知信息:\n{context}"总结
构建可靠 Agent 的核心原则:
- 从坚实的基础开始:将有能力模型与定义明确的工具和清晰、结构化的指令配对
- 谨慎增加复杂性:只有当简单方案不足时才添加多步 Agent
- 注重工具设计:好的工具设计可以显著提升 Agent 的可靠性
- 清晰的结构化指令:指令应该明确、无歧义、可操作
一句话总结:Agent 的质量取决于其组件的质量——选择合适的模型、设计良好的工具、编写清晰的指令。
