本文聚焦工程视角,给出 Agent 的“规划-执行-记忆”协作思路与可复用代码片段。
🎯 文章目标
- 把 Agent 拆成“规划(Planner)/工具(Tools)/记忆(Memory)/执行(Executor)”
- 给出函数调用与工具编排的工程化做法
- 明确观测、重试、幂等与安全的最小闭环
📚 背景/前置
- 典型链路:用户意图 → 规划 → 工具选择/调用 → 结果合并 → 反思/重试 → 输出
- 工程关注:标准化函数接口、可重试/幂等、权限隔离、耗时/失败可观测
🔧 核心内容
1) 设计要点
- 规划:用结构化计划(steps/goals/constraints),避免“纯自然语言计划”不可执行
- 工具:输入/输出 JSON Schema;错误语义化(错误码/重试建议)
- 记忆:短期(会话)+ 长期(外部存储);按“任务/用户/组织”隔离
- 执行:有状态(长任务)需引入队列与回放,保证幂等
2) 函数调用与工具编排
javascript
import OpenAI from 'openai'
const client = new OpenAI({ baseURL: process.env.OPENAI_API_BASE, apiKey: process.env.OPENAI_API_KEY })
const tools = [{
type: 'function',
function: {
name: 'searchTickets', description: '搜索工单',
parameters: { type:'object', properties:{ q:{type:'string'} }, required:['q'] }
}
},{
type: 'function',
function: {
name: 'closeTicket', description: '关闭工单',
parameters: { type:'object', properties:{ id:{type:'string'} }, required:['id'] }
}
}]
const plan = [
{ step: 1, action: 'searchTickets', input: { q: '退款' } },
{ step: 2, action: 'closeTicket', input: { id: '\{\{ticketId\}\}' } }
]
const msgs = [
{ role:'system', content:'严格按计划执行。无法执行要说明原因,不可编造。' },
{ role:'user', content:'帮我关闭最近的退款相关工单。' },
{ role:'system', content:`计划: \${JSON.stringify(plan)}`}
]
const rsp = await client.chat.completions.create({ model: process.env.CHAT_MODEL, messages: msgs, tools })
console.log(rsp.choices[0].message)
要点:
- 用计划结构显式传输“步骤/约束/变量”,避免全靠模型“猜”
- 工具的参数/返回体对齐 JSON Schema,异常携带“是否可重试/需人工介入”等字段
3) 记忆与状态
- 短期:会话级(上下文窗口内),用于近期意图与变量
- 长期:外部存储(如 Redis/DB/向量库),按 key 组织,注意权限与回收
- 长任务:任务 ID + 状态机(pending/running/succeeded/failed),幂等键与重试策略
4) 观测与评估
- 指标:成功率、平均步数、平均工具调用次数、平均延迟、失败分布(解析/权限/超时)
- 记录:请求上下文、计划、工具入参/结果、失败样本;支持回放
- 回归:固定计划与输入,评估不同模型/提示版本的稳定性
💡 实战示例:最小 Agent 执行器(Node.js)
javascript
async function exec(plan, call){
const ctx = {}
for (const p of plan){
const input = JSON.parse(JSON.stringify(p.input).replace('\{\{ticketId\}\}', ctx.ticketId||''))
const out = await call(p.action, input)
if (!out || out.error) throw new Error(out?.error || 'tool failed')
if (out.ticketId) ctx.ticketId = out.ticketId
}
return ctx
}
📊 对比/取舍(速查)
- 单工具 vs 工具链:工具链表达力更强,但需要状态与回退;简单场景优先单工具
- 模型驱动 vs 规则驱动规划:可解释性与稳定性优先时,规划要规则化/模板化
- 内存 vs 外部记忆:会话内方便,跨会话需外部存储与权限隔离
🧪 踩坑与经验
- 将“工具失败/权限不足/参数缺失”等错误语义化,便于重试与定位
- 任务超时需“补偿动作/回滚策略”;长任务务必幂等
- 观测缺失会导致 Agent 难以治理:务必保存计划与工具调用流水
📎 参考与延伸
- ReAct/Plan-and-Execute/LLM+工具链综述
- OpenAI 函数调用、JSON Schema、工具异常语义化设计
- 有状态任务编排与回放
💭 总结
- 用“规划结构化 + 工具 JSON Schema + 外部记忆 + 有状态执行 + 观测回放”组合,提升 Agent 的可控性与落地性