文件:ai_research/llm-tool-calling-confirmation.md 大小:14.0 KB 编码:UTF-8

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. 工具调用确认的本质

“确认工具调用”不是一句自然语言上的“我来查一下”,而是一个结构化闭环:

  1. Agent 将工具定义(名称、描述、参数 schema)发给 LLM
  2. LLM 判断是否需要调用工具
  3. LLM 返回结构化 tool call
  4. Agent 校验这次调用是否合法
  5. Agent 真实执行工具
  6. Agent 将执行结果作为 tool message 回填给 LLM
  7. LLM 基于结果决定:继续调用、重试修正、还是输出最终答案

可以压缩成一句话:

LLM 负责“决定调用什么”,Agent 负责“确认能不能调、真的去执行、并把结果带回来”。


3. 工具调用前:Agent 如何把工具介绍给 LLM

在调用模型时,Agent 不只发送普通文本消息,还会附带工具定义。每个工具通常包含:

  • 工具名,例如 terminal
  • 工具描述,例如“运行 shell 命令”
  • 参数 schema,例如 command 必须是字符串
  • 调用约束,例如某些浏览器操作需要先 navigate 再 click

这一步的作用是告诉 LLM:

  • 你现在有哪些外部能力可以用
  • 每个能力应该以什么格式调用
  • 哪些字段是必填
  • 哪些值是合法的

这相当于把一份“可调用 API 文档”注入到模型上下文中。


4. LLM 如何决定要不要调用工具

当 LLM 同时看到:
- 系统规则
- 用户问题
- 工具定义

它会判断:

  1. 这个问题能否直接回答?
  2. 是否必须使用工具才能保证正确性?
  3. 当前是否存在合适的工具?
  4. 是否需要先获取外部信息再回答?

例如:
- “现在几点了?”属于实时信息,应该使用工具
- “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 看到工具结果后,会重新判断:

  1. 结果是否足够回答用户?
  2. 是否需要继续调用其他工具?
  3. 是否失败了,需要修正参数或改用别的工具?
  4. 是否需要向用户追问更多信息?

常见三种情况:

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 行。”

可能的执行链路:

  1. search_files:搜索所有 .py 文件
  2. terminalexecute_code:统计并排序文件大小
  3. read_file:读取最大文件前 50 行
  4. LLM 基于这些结果给出总结

这个场景说明:
- 每一步 tool result 都会影响下一步 tool call
- 工具调用确认不是单点,而是连续多轮的状态推进


14. 为什么说 LLM 并不知道工具是否真的执行成功

LLM 自己只能做两件事:

  1. 发起调用请求
  2. 阅读回填结果

它不能直接知道:
- 子进程是否真运行了
- 文件是否真存在
- 浏览器是否真点到了按钮
- 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

这段代码表达的就是:

  1. 先问 LLM 下一步怎么做
  2. 如果 LLM 请求工具,就去执行
  3. 把执行结果塞回对话上下文
  4. 再次调用 LLM
  5. 直到不再需要工具,输出最终答案

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. 可以把它理解成一个状态机

工具调用过程通常会在以下状态之间循环:

  1. WaitingForUser
  2. Reasoning
  3. ToolRequested
  4. ToolValidated
  5. ToolRunning
  6. ToolReturned
  7. ReasoningAgain
  8. FinalAnswer

如果失败,还可能进入:
- Retry
- AskUser
- Abort

这说明工具调用不是一次性动作,而是一个受控的状态机循环。


18. 一个工程类比

可以把整个机制理解成:

  • LLM = 项目经理
  • Agent = 调度员 + 审核员
  • Tool = 执行员工
  • Tool result = 回执单

链路如下:

  1. 项目经理提出工单(tool call)
  2. 调度员审核是否合法(validate)
  3. 执行员工去干活(execute)
  4. 回执单返回(tool result)
  5. 项目经理根据回执决定下一步(继续调度或给用户结论)

这个类比很适合理解“LLM 为什么不直接接触真实世界”。


19. 最终总结

如果只记一条主线,可以记这 8 步:

  1. Agent 把工具定义发给 LLM
  2. LLM 阅读用户需求和工具 schema
  3. LLM 选择某个工具
  4. LLM 输出结构化 tool call
  5. Agent 校验调用是否合法
  6. Agent 执行工具
  7. Agent 把结果回填给 LLM
  8. LLM 基于结果决定继续调用还是最终回答

一句话版:

工具调用的确认,不是“模型说一句要调用工具”,而是“模型提出结构化调用请求,Agent 校验并执行,再把真实结果回传给模型”的闭环。


20. 适合后续继续扩展的方向

如果后续继续深入,可以在这份文档基础上扩展:

  1. 真实 JSON 请求/响应样例
  2. OpenAI 风格 vs 其他函数调用协议差异
  3. Hermes 源码中的 tool registry / dispatch 结构
  4. 浏览器工具的状态依赖与前置条件管理
  5. 高风险工具调用中的审批与安全策略
  6. 多工具并行调用、串行依赖与错误恢复模式

附:一句最短记忆版

while True:
    resp = llm(messages, tools)
    if resp.tool_calls:
        execute_tools_and_append_results()
    else:
        return resp.text

核心思想只有一句:

LLM 负责决定,Agent 负责确认与执行,工具结果再反哺 LLM 完成闭环。