L01 从失败演示到可检查的Agent Loop
适用章节:M00–M01。本实验允许多种正确实现;验收不变量固定,不强制特定框架。
工程事故
用户询问A100物流。模型先查订单,再查物流,最后说“已经退款”。你的程序显示成功,但退款账本为空。另一个模型不断要求重复查询,程序永不退出。你需要修复的是执行与验收边界,而不只是改提示词。
最终交付
一个不依赖Agent框架的最小执行器;一份至少6条轨迹的诊断报告;一页“模型终止不等于任务成功”的设计说明。
已提供材料
data/retail/orders.json、specs/PROJECT_CONTRACT.md、reference/agentlab/runner.py(完成初稿后对照)
先独立写出预期行为再看参考解。所有商店、订单、试次与价格单位都是教学构造;不连接真实资金系统。
接口与不可破坏的约束
模型适配器返回四种互斥结果:final(text)、tool_calls(calls)、refusal(reason)、incomplete(reason)。工具调用至少包含id/name/arguments。执行器独立维护模型轮数、工具派发尝试数、事件序号和已见call_id。工具预算包括被拒绝的派发尝试,避免错误重试绕过预算。
不要解析模型的隐藏思维过程。模型最终解释是一种输出,不是业务账本。只读工具结果应携带可核对来源。执行器发生不可恢复协议错误时结束任务,不继续猜测。
实施步骤
- 先写A100只读查询契约:可以查订单/物流,不可以退款;终态文本必须与物流事实一致。
- 用手工列表实现ScriptedModel,不发真实模型请求。先运行正常的两次工具调用,再运行错误调用和拒绝。
- 写一个枚举驱动的循环,而不是把所有返回值当文本。每次派发前做预算检查。
- 关联call_id与结果;对同一轮多调用保持一一对应。拒绝缺失或重复ID。
- 记录结构化事件,保持序号单调;返回模型停止原因和业务验收状态两个字段。
- 为永不结束的工具脚本设置最大轮数。最后才接入可选真实模型适配器,并保留同一组契约测试。
验收矩阵
| 输入 | 必须观察到 |
|---|---|
| 查订单→查物流→最终回答 | 正确call_id,2次工具调用,账本不变化 |
| final文本“已退款”,没有退款工具 | MODEL_FINISHED,但business_success_verified=false |
| 同一call_id出现两次 | PROTOCOL_ERROR,第二次不派发 |
| 工具预算为0 | 首次工具请求即停止,不访问业务服务 |
| refusal或incomplete | 各自终止状态,不能混同成功 |
| 模型脚本耗尽 | 显式错误,不编造额外输出 |
如何运行已有参考测试
在包根目录运行:
python -m unittest discover -s reference/tests -v
本实验主要参考测试类为RunnerTests。已有测试默认验证reference/;测试通过不代表你的starter/实现通过。接入自己的实现时,将被测模块显式替换/注入,保持测试和领域契约不变,不要修改断言来迁就实现。
评分
协议与关联30;预算/终止25;独立验收20;测试15;设计解释10。
出现跨租户泄露、未经批准的退款或伪造结果时,不以其他分数抵消。允许记录未完成的选修功能,但必须说明范围。
进阶挑战
接入一个Provider,记录实际返回的usage与终止状态;若Provider不报告token用量,标记unknown,不能用字符数冒充token。比较“固定Workflow”与模型自由选工具的额外成本。
提交前自查
保存代码版本、运行命令、输入数据版本、输出日志、失败案例与设计决策。解释至少一个被你否决的方案。没有运行的测试标记“未运行”,不写“预计通过”。参考解见对应答案。