LLM 工具调用确认机制整理¶
文档目的¶
整理本次会话中关于“LLM 如何与 Agent/工具系统确认工具调用”的核心内容,形成一份可复用的知识文档。
一句话结论:
LLM 不会直接执行工具,它只会根据 Agent 提供的工具定义输出结构化调用请求;真正的确认发生在 Agent 对该请求进行校验、执行,并将结果回填给 LLM 的闭环中。
1. 参与者与职责¶
一个完整的工具调用链路里,通常有 4 个角色:
1.1 User(用户)¶
负责提出任务或问题,例如:
- 现在几点了?
- 读取某个文件内容
- 帮我检查目录下有哪些 Python 文件
1.2 LLM(大模型)¶
负责:
- 理解用户意图
- 判断是否需要工具
- 选择调用哪个工具
- 生成结构化 tool call
- 基于工具返回结果继续推理或输出最终答案
LLM 本身不直接操作系统、文件、浏览器或网络。
1.3 Agent(代理框架)¶
例如 Hermes 这类 Agent 框架,负责:
- 管理对话历史
- 将可用工具及其 schema 暴露给 LLM
- 接收并解析 LLM 的 tool call
- 校验工具调用是否合法
- 调度具体工具执行
- 将工具执行结果回填给 LLM
1.4 Tool(工具执行层)¶
负责真正执行动作,例如:
- terminal:执行 shell 命令
- read_file:读取文件
- search_files:搜索文件或内容
- browser:浏览器自动化
- web:联网检索
2. 工具调用确认的本质¶
“确认工具调用”不是一句自然语言上的“我来查一下”,而是一个结构化闭环:
- Agent 将工具定义(名称、描述、参数 schema)发给 LLM
- LLM 判断是否需要调用工具
- LLM 返回结构化 tool call
- Agent 校验这次调用是否合法
- Agent 真实执行工具
- Agent 将执行结果作为 tool message 回填给 LLM
- LLM 基于结果决定:继续调用、重试修正、还是输出最终答案
可以压缩成一句话:
LLM 负责“决定调用什么”,Agent 负责“确认能不能调、真的去执行、并把结果带回来”。
3. 工具调用前:Agent 如何把工具介绍给 LLM¶
在调用模型时,Agent 不只发送普通文本消息,还会附带工具定义。每个工具通常包含:
- 工具名,例如
terminal - 工具描述,例如“运行 shell 命令”
- 参数 schema,例如
command必须是字符串 - 调用约束,例如某些浏览器操作需要先 navigate 再 click
这一步的作用是告诉 LLM:
- 你现在有哪些外部能力可以用
- 每个能力应该以什么格式调用
- 哪些字段是必填
- 哪些值是合法的
这相当于把一份“可调用 API 文档”注入到模型上下文中。
4. LLM 如何决定要不要调用工具¶
当 LLM 同时看到:
- 系统规则
- 用户问题
- 工具定义
它会判断:
- 这个问题能否直接回答?
- 是否必须使用工具才能保证正确性?
- 当前是否存在合适的工具?
- 是否需要先获取外部信息再回答?
例如:
- “现在几点了?”属于实时信息,应该使用工具
- “Transformer 的 self-attention 是什么?”如果不要求查资料,通常可以直接回答
因此,是否调用工具,本质上是一次策略决策。
5. LLM 返回的不是自然语言,而是结构化 tool call¶
当 LLM 决定调用工具时,它不会只说“我去查一下”,而是返回一个结构化请求,典型形式如下:
{
"tool_calls": [
{
"id": "call_001",
"type": "function",
"function": {
"name": "terminal",
"arguments": "{\"command\": \"date\"}"
}
}
]
}
这表示:
- 选择了哪个工具:terminal
- 要传什么参数:command = date
- 用 id 追踪本次调用的结果
这一步并不是“工具已经执行成功”,而是“LLM 明确提交了一次调用请求”。
6. Agent 如何确认这次工具调用是否合法¶
LLM 发起调用后,Agent 不会无条件执行,通常还要经过校验。
6.1 工具存在性校验¶
检查 LLM 请求的工具是否真的已注册、已启用。
6.2 参数 schema 校验¶
检查:
- 参数类型是否正确
- 是否缺少必填字段
- 枚举值是否在允许范围内
6.3 上下文状态校验¶
例如浏览器工具可能要求:
- 先 navigate
- 再 snapshot
- 再 click
如果状态不满足,调用不能直接执行。
6.4 安全策略校验¶
例如:
- 高风险 shell 命令是否需要批准
- 是否访问了受限制资源
- 是否符合沙箱/权限策略
6.5 工具可用性校验¶
例如:
- API key 是否存在
- 浏览器会话是否可用
- 依赖命令是否安装
如果校验失败,Agent 可能:
- 直接拒绝本次调用
- 或将错误回填给 LLM,让其修正后重试
7. 工具真正执行时发生了什么¶
当校验通过,Agent 会把 tool call 路由到对应工具实现。
例如:
- terminal(command="date")
- read_file(path="/tmp/a.txt")
- search_files(pattern="*.py", path=".")
工具执行后,会返回一个结构化结果,例如:
{
"output": "Fri Apr 24 01:12:30 UTC 2026",
"exit_code": 0
}
或者失败时:
{
"error": "File not found: /tmp/a.txt",
"exit_code": 1
}
注意:
此时真正“拿到结果”的是 Agent,不是 LLM。
8. Agent 如何把结果回填给 LLM¶
Agent 会将工具结果包装为一条 tool 消息,并写回对话历史。例如:
{
"role": "tool",
"tool_call_id": "call_001",
"content": "{\"output\":\"Fri Apr 24 01:12:30 UTC 2026\",\"exit_code\":0}"
}
这一步至关重要,因为:
- LLM 不能直接看到真实世界
- 它只能通过 Agent 回填的结构化结果了解“刚才执行了什么”
也就是说,LLM 对工具状态的认知完全依赖回填消息。
9. LLM 拿到结果后如何继续决策¶
当 LLM 看到工具结果后,会重新判断:
- 结果是否足够回答用户?
- 是否需要继续调用其他工具?
- 是否失败了,需要修正参数或改用别的工具?
- 是否需要向用户追问更多信息?
常见三种情况:
9.1 一次调用后直接回答¶
例如:
- 问当前时间
- 调用 date
- 得到结果后直接回答
9.2 串联多个工具¶
例如:
- 先搜索 Python 文件
- 再计算文件大小排序
- 再读取最大文件内容
9.3 调用失败后重试或换策略¶
例如:
- 文件不存在
- 参数格式错误
- 命令超时
模型会基于错误结果继续修正。
10. 三个最重要的“确认点”¶
可以把整个机制压缩为三个关键确认点:
确认点 A:LLM 的调用意图确认¶
由 LLM 完成。
确认内容:
- 要不要调用工具
- 调哪个工具
- 参数是什么
表现形式:
- 返回结构化 tool_call
确认点 B:Agent 的合法性确认¶
由 Agent 完成。
确认内容:
- 工具是否存在
- 参数是否合法
- 当前状态是否允许
- 安全策略是否通过
表现形式:
- 通过校验才执行,否则拒绝或报错
确认点 C:执行结果确认¶
由 Tool + Agent + LLM 共同完成。
确认内容:
- 工具是否真的执行成功
- 返回了什么结果
- 是否还需要下一步调用
表现形式:
- tool result 回填给 LLM,LLM 再决定下一步
11. 一张简化时序图¶
场景:用户问“现在几点了?”
User Agent LLM Tool
| | | |
| 1.提问 | | |
|------------------->| | |
| | 2.发上下文+工具定义给 LLM |
| |------------------->| |
| | | 3.判断需要工具 |
| | | 4.输出 tool_call |
| |<-------------------| |
| | 5.校验调用是否合法 |
| | 6.执行工具 |
| |----------------------------------------->|
| | 7.得到工具结果 |
| |<-----------------------------------------|
| | 8.将结果回填给 LLM |
| |------------------->| |
| | | 9.生成最终答案 |
| |<-------------------| |
|10.返回给用户 | | |
|<-------------------| | |
12. 失败场景下的闭环¶
如果工具失败,Agent 一般不会静默吞掉错误,而是把失败结果回填给 LLM。例如:
{
"error": "File not found: /abc/def.txt",
"exit_code": 1
}
LLM 看到失败结果后,可能:
- 修正路径
- 改用别的工具
- 先搜索文件再读取
- 或在缺少信息时向用户询问
所以失败不是终点,而是下一轮决策的输入。
成熟 Agent 的一个关键能力,就是失败后还能基于结果自我修正。
13. 多工具串联场景示例¶
任务:
“帮我找出项目里最大的 3 个 Python 文件,并读取最大的那个前 50 行。”
可能的执行链路:
search_files:搜索所有.py文件terminal或execute_code:统计并排序文件大小read_file:读取最大文件前 50 行- LLM 基于这些结果给出总结
这个场景说明:
- 每一步 tool result 都会影响下一步 tool call
- 工具调用确认不是单点,而是连续多轮的状态推进
14. 为什么说 LLM 并不知道工具是否真的执行成功¶
LLM 自己只能做两件事:
- 发起调用请求
- 阅读回填结果
它不能直接知道:
- 子进程是否真运行了
- 文件是否真存在
- 浏览器是否真点到了按钮
- API 是否返回了 200
这些都属于 Agent 和 Tool 所接触的真实世界。
因此:
LLM 对工具执行状态的了解,完全依赖 Agent 返回给它的结构化结果。
15. 从代码视角看最小闭环¶
最简思路如下:
while True:
response = llm(messages=messages, tools=tools)
if response.tool_calls:
messages.append(response.assistant_message)
for call in response.tool_calls:
result = execute(call)
messages.append(tool_result(call.id, result))
else:
return response.text
这段代码表达的就是:
- 先问 LLM 下一步怎么做
- 如果 LLM 请求工具,就去执行
- 把执行结果塞回对话上下文
- 再次调用 LLM
- 直到不再需要工具,输出最终答案
16. 更接近 Hermes/Agent 框架的抽象伪代码¶
def run_conversation(user_input):
messages = build_initial_messages(user_input)
tools = discover_available_tools()
for _ in range(max_iterations):
response = call_llm(messages, tools)
if response.tool_calls:
messages.append(response.assistant_message)
for tool_call in response.tool_calls:
validation_error = validate_tool_call(tool_call, tools)
if validation_error:
tool_result = {
"success": False,
"error": validation_error
}
else:
tool_result = dispatch_tool(tool_call)
messages.append(make_tool_message(tool_call.id, tool_result))
continue
return response.text
return "任务未能在最大迭代次数内完成。"
这里多了两个关键动作:
validate_tool_call(...):合法性确认dispatch_tool(...):分发到具体工具实现
这正是成熟 Agent 框架的核心逻辑。
17. 可以把它理解成一个状态机¶
工具调用过程通常会在以下状态之间循环:
WaitingForUserReasoningToolRequestedToolValidatedToolRunningToolReturnedReasoningAgainFinalAnswer
如果失败,还可能进入:
- Retry
- AskUser
- Abort
这说明工具调用不是一次性动作,而是一个受控的状态机循环。
18. 一个工程类比¶
可以把整个机制理解成:
- LLM = 项目经理
- Agent = 调度员 + 审核员
- Tool = 执行员工
- Tool result = 回执单
链路如下:
- 项目经理提出工单(tool call)
- 调度员审核是否合法(validate)
- 执行员工去干活(execute)
- 回执单返回(tool result)
- 项目经理根据回执决定下一步(继续调度或给用户结论)
这个类比很适合理解“LLM 为什么不直接接触真实世界”。
19. 最终总结¶
如果只记一条主线,可以记这 8 步:
- Agent 把工具定义发给 LLM
- LLM 阅读用户需求和工具 schema
- LLM 选择某个工具
- LLM 输出结构化 tool call
- Agent 校验调用是否合法
- Agent 执行工具
- Agent 把结果回填给 LLM
- LLM 基于结果决定继续调用还是最终回答
一句话版:
工具调用的确认,不是“模型说一句要调用工具”,而是“模型提出结构化调用请求,Agent 校验并执行,再把真实结果回传给模型”的闭环。
20. 适合后续继续扩展的方向¶
如果后续继续深入,可以在这份文档基础上扩展:
- 真实 JSON 请求/响应样例
- OpenAI 风格 vs 其他函数调用协议差异
- Hermes 源码中的 tool registry / dispatch 结构
- 浏览器工具的状态依赖与前置条件管理
- 高风险工具调用中的审批与安全策略
- 多工具并行调用、串行依赖与错误恢复模式
附:一句最短记忆版¶
while True:
resp = llm(messages, tools)
if resp.tool_calls:
execute_tools_and_append_results()
else:
return resp.text
核心思想只有一句:
LLM 负责决定,Agent 负责确认与执行,工具结果再反哺 LLM 完成闭环。