Agent loop
上一篇停在点名:finish_reason 为 tool_calls,get_weekday 还没跑。循环是运行时补上的那一圈。它读时钟,把看见的写回 messages,再请求一次。
先执行
运行时按 function.name 找到 get_weekday,把 arguments 从字符串解析成对象,用里面的 timezone 去看钟。
这一步不经过模型。观察是运行时自己写的,例如:
2026-10-08 星期四
写回去,再请求
用户那句后面接两条:上一轮的 assistant 原样贴回,再接一条 role: tool。tools 仍带上。
curl https://api.deepseek.com/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $DEEPSEEK_API_KEY" \
-d '{
"model": "deepseek-chat",
"messages": [
{"role": "user", "content": "今天星期几"},
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_8f2a",
"type": "function",
"function": {
"name": "get_weekday",
"arguments": "{\"timezone\":\"Asia/Shanghai\"}"
}
}
]
},
{
"role": "tool",
"tool_call_id": "call_8f2a",
"content": "2026-10-08 星期四"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weekday",
"description": "按给定时区返回当前日期和星期几",
"parameters": {
"type": "object",
"properties": {
"timezone": {
"type": "string",
"description": "IANA 时区,例如 Asia/Shanghai"
}
},
"required": ["timezone"]
}
}
}
],
"stream": false
}'
回来才是给人看的话:
{
"choices": [
{
"message": {
"role": "assistant",
"content": "今天是 2026-10-08,星期四。"
},
"finish_reason": "stop"
}
]
}
翻译成人话:
tool_call_id对上call_8f2a。对不上,这条观察就挂空content里的「星期四」是给模型看的观察,还不是最终回答- 这轮模型才把星期几说出来,
finish_reason为stop
少贴 assistant 那条,或少贴 role: tool,模型就看不见这次读钟。写回去仍是把材料塞进同一次 messages,服务端并不忽然记住了。
还能再转
问星期几,通常一圈就停。循环之所以要转,是因为下一轮响应里仍可能有 tool_calls。有,就再执行、再写回、再请求。
停有两个口子:不再伸手,或步数到顶。到顶只是防止它一直点名。点哪个就执行哪个,这圈还没有权限和审批。报错、空转、一轮里同时点多条,后面再写。
总结
- 循环 = 执行 → 写成
role: tool→ 再请求 assistant原样贴回,tool_call_id对上那次idfinish_reason为stop时,content才是给用户的话- 步数到顶就停。门禁仍是后面的 Harness
参见
- 上一篇:Tool
- 底板:Messages · 写回仍是多轮
- 另一套写回:Responses API 用
function_call_output - Agent 百科里尚未成文的相邻词条:Tool result、Dead loop、Harness
- 动手:从 0 实现一个 Agent · 阶段三(循环与工具)