框架实战递进练习
这些练习默认你已经完成 裸 API 手写 Agent、LangGraph 和 Agent SDK 的最小示例。每题都从上一题的代码继续改,不要另起一个完全不同的天气或聊天 demo。
练习的目标是让你亲手做出取舍。代码可以很短,但每次改动都要记录:改了哪个边界、运行现象有什么变化、出现问题时你先查哪里。
练习一:给裸 API Agent 加知识检索
起点
从 Raw API 版本开始。保留已有的模型调用循环和一个只读工具,把天气工具替换为本地 search_knowledge。知识库可以先用列表或 JSON 文件模拟,返回标题、片段、来源和更新时间。
要改哪段
- 修改工具 schema,让
query成为必填字符串,并限制返回条数。 - 修改工具执行函数,只返回模型回答所需的字段,不要把整个文档文件塞进消息。
- 在 system prompt 中写明:没有检索证据时要说明资料不足,不能编造公司制度。
- 在日志中增加检索耗时、命中文档 ID 和返回片段数量。
预期现象
询问“VPN 怎么申请”时,模型会先调用检索工具,再根据片段回答并带出来源。询问“讲个笑话”时,不应因为工具描述太宽泛而盲目检索。
排查提示
- 工具始终不被调用:先检查
tools是否真正传入模型,以及 description 是否说明了适用场景。 - 模型回答没有使用片段:打印回传的
role: tool消息,确认 JSON 可解析且没有超出上下文。 - 所有问题都触发检索:收窄工具描述,并增加一个“不需要知识库”的反例测试。
思考方向
为什么“没有证据时拒答”应该同时写在提示词和代码日志里?如果检索结果带有恶意指令,下一步你会在哪一层做过滤?
练习二:把同一分支搬进 LangGraph
起点
使用练习一的两个工具:search_knowledge 和 get_ticket。先在 Raw API 中确认两种问题都能跑,再打开 LangGraph 版本。
要改哪段
- 在 state 中增加一个表示请求类型的字段,例如
intent,并规定只有路由节点写它。 - 新增一个路由节点,根据用户问题把流程送往知识检索或工单查询。
- 为“无法判断”设置明确出口:要求用户补充信息,不能让图随机选择工具。
- 让两个分支最后汇合到同一个回答节点,避免复制两份生成逻辑。
预期现象
“VPN 怎么申请”与“INC-1042 到哪一步”会走不同节点,但最终由同一个回答节点组织结果。模糊问题会走澄清出口,图不会无限绕圈。
排查提示
- 节点拿不到用户问题:检查 state 初始化时是否把原始消息放进
messages。 - 图直接结束:打印条件边返回值,并核对映射表中的节点名称。
- 两个分支的结果互相覆盖:检查 state 字段的单一写入者,必要时把证据拆成不同字段。
思考方向
路由由模型分类还是由规则分类更合适?如果分类模型本身失败,默认走哪个出口才不会误调用有副作用工具?
练习三:加入可控的超时与重试
起点
继续使用 LangGraph 版本,让 get_ticket("INC-9999") 模拟超时,让 search_knowledge("unknown") 模拟空结果。
要改哪段
- 在工具层给异常统一分类,至少区分超时、参数错误、权限错误和资料为空。
- 在 state 中增加重试计数,明确最大值,不要用“只要失败就重试”。
- 增加错误路由:超时可重试一次,权限错误转人工或结束,空结果回到澄清节点。
- 为每次重试记录原因、间隔和最终去向。
预期现象
超时任务最多重试一次后结束或改走备用路径;参数错误不会重复调用;资料为空会要求用户换一个关键词。运行轨迹里能看出失败节点和重试次数。
排查提示
- 图不断循环:检查重试计数是否写回 state,以及条件边是否真的读取了最新值。
- 重试导致重复创建工单:确认副作用工具尚未接入自动重试路径,并为工具增加幂等键设计。
- 错误信息泄露内部堆栈:在回传模型前做错误消息裁剪,完整堆栈只写受控日志。
思考方向
哪些错误适合交给模型“换个参数再试”,哪些错误必须由程序直接终止?如果备用路径需要另一个模型,预算和超时如何传递?
练习四:在副作用工具前加入人工确认
起点
从 LangGraph 版本复制出一个“创建工单”分支。用户提出“帮我创建 VPN 权限申请工单”时,先生成草稿,不要立即执行 create_ticket。
要改哪段
- 新增草稿节点,产出标题、描述、目标资源和风险提示。
- 在真正的工具节点前增加暂停点或人工确认节点,保存稳定的任务 ID。
- 审批恢复时重新校验操作者、草稿版本和参数摘要,确认一致后才调用工具。
- 为批准、拒绝、过期和重复批准分别设计结束状态。
预期现象
首次运行会停在“等待确认”,不会出现工单编号。批准后只创建一次并返回编号;拒绝或过期会给出清晰原因,模型不能绕过暂停点直接调用工具。
排查提示
- 重启后找不到待审批任务:检查 checkpoint 或持久化存储是否使用稳定任务 ID。
- 重复批准创建两张工单:检查幂等键和已处理状态是否在执行前后都被记录。
- 用户修改草稿后仍执行旧参数:比较审批时的版本号或参数哈希,不一致就要求重新确认。
思考方向
人工确认是 UI 交互、工作流状态还是模型对话的一部分?为什么不能只在 prompt 中写“执行前请询问用户”?
练习五:把最小流程迁移到 Agent SDK 并做选择
起点
选择你正在使用的官方 Agent SDK。用它重写练习一的“知识检索 + 最终回答”路径,保留同样的工具 schema、固定问题和 mock 返回值。
要改哪段
- 把 Raw API 的 system prompt 和工具列表映射到 SDK 的 Agent 定义。
- 把手写循环替换为 SDK runner,打开事件或 tracing 输出。
- 在工具函数内部保留参数、权限和结果裁剪,不要因为 SDK 自动注册工具就删除校验。
- 用同一组十条问题对比两版的成功率、调用次数、延迟、token 和失败轨迹。
预期现象
SDK 版通常少了消息拼接和循环样板,但工具边界和业务错误处理仍然存在。某些默认行为(例如重试、session 保存或事件粒度)可能与 Raw API 不同,必须记录实际版本和配置。
排查提示
- SDK 找不到工具:检查注册对象、函数签名和异步/同步模式是否符合当前版本文档。
- 结果无法和 Raw API 对齐:先锁定模型、提示词、工具返回值和温度,再比较框架行为。
- tracing 看不到敏感字段脱敏:检查 SDK 的采样和导出配置,必要时在进入 SDK 前先裁剪输入。
思考方向
你的项目真正缺的是哪一部分:少写循环、统一 tracing、handoff,还是可暂停的复杂流程?如果半年后要支持跨模型供应商,当前 SDK 的绑定会带来什么迁移成本?
练习提交前的自检
完成五题后,回到 同一个任务,三种 Agent 写法 的对照表,补齐每一格,并写下最终选择的依据。至少保留一条成功轨迹、一条工具失败轨迹和一条审批拒绝轨迹。
不要用“代码更少”“框架更流行”作为唯一结论。能否定位失败、控制副作用、恢复中断和解释成本,才是这组练习要验证的能力。