KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA
05. 让模型第一次用上只读工具 — keel 龙骨
现在你已经知道模型的决定还不是动作。接下来给它一个只读工具 lookup_incident,让它查询事件资料,再根据结果回答用户。
现在你已经知道模型的决定还不是动作。接下来给它一个只读工具 lookup_incident,让它查询事件资料,再根据结果回答用户。
只读工具适合作为第一个工具,因为它不会修改外部世界。你可以先专注于消息往返和权限边界,不必同时处理重复创建、回滚和审批。
一次往返里发生了什么
sequenceDiagram
participant U as 用户
participant H as Harness
participant M as 模型
participant T as lookup_incident
U->>H: 查询 INC-001
H->>M: 用户消息 + 工具描述
M-->>H: assistant tool_call
H->>H: 注册、参数、权限检查
H->>T: 执行已验证参数
T-->>H: tool result
H->>M: assistant 请求 + tool result
M-->>H: 最终回答
H-->>U: 返回并记录
第二次模型调用不是重复。工具只返回结构化事实,模型需要再次看到事实,才能把它组织成面向用户的诊断。
消息的三次变化
messages = [
{"role": "user", "content": "查询 INC-001。"}
]
# 1. 保存模型完整的 assistant 消息,其中可能含 tool_calls。
messages.append(assistant_message)
# 2. 只把经过执行器检查的结果作为 tool 消息追加。
messages.append({
"role": "tool",
"tool_name": "lookup_incident",
"content": json.dumps(tool_result),
})
# 3. 再请求模型,根据工具事实生成最终回答。
不能只把工具结果拼成普通用户文本。role=tool 是协议语义的一部分,它告诉模型数据来自执行系统。
运行真实示例
python courses/foundation/agent-harness/course/project/examples/04_tool_calling.py
运行前预测:模型会不会一定请求工具?不会。概率模型可能直接回答,所以 Harness 必须处理“有工具决定”和“无工具决定”两条路径。
运行后检查:
- assistant 工具请求是否被完整保存;
- 工具结果是否带有结构化内容;
- 第二次模型调用是否同时看到用户消息、工具请求和工具结果;
- 工具名称是否通过允许列表,而不是直接交给
eval()或动态导入。
这个例子还没有解决什么
它仍然是一个最小闭环,可能缺少:
- 持久化运行状态;
- 多轮工具循环;
- 最大步数和 deadline;
- 工具输出校验;
- 详细错误分类;
- 写工具的审批。
下一章会把单次往返收进一个有状态、可停止、可恢复的 Harness.run()。