How I Build AI Agents: 从 Token 到 Tool Call

引言

LLM 本质上就是输出一串 token。那么问题来了:一串 token 是怎么变成 tool call 的?

这个问题看似简单,背后却涉及从模型训练、输出格式设计、到解析引擎的完整工程链路。本文将从底层原理出发,介绍四种主流的 tool use 解析方式,并结合 Hermes Agent 的实践经验,聊聊如何构建一个可靠的 AI agent。

什么是 AI Agent?

一个 AI agent = LLM + Tools + Memory + Loop。它能感知环境、推理决策、使用工具、并从反馈中学习。

核心循环很简单:

while not done:
    observation = get_observation()
    thought = llm.think(observation, memory)
    action = llm.decide_action(thought)
    result = execute_action(action)
    memory.update(observation, thought, action, result)
    done = is_task_complete(result)

但魔鬼在细节中。llm.decide_action() 返回的到底是纯文本还是结构化 tool call?这就是关键所在。

方式一:API 层的 Structured Output

OpenAI、Anthropic 等商业 API 提供了原生的 function calling 能力。模型在训练时就被微调过,遇到 tool use 场景时会输出特殊格式的 token。

API 服务端在返回给用户之前,会提前终止流式输出,将 tool call 部分截断并解析为 JSON,然后放到 response 的 tool_calls 字段里。

你拿到的不是原始 token 流,而是已经解析好的结构化对象。本质上就是:特殊 token 检测 → 截断 → JSON parse。简单粗暴,但需要模型在训练阶段就学会这种格式。

方式二:Prompt-based 解析

如果模型没有原生 function calling 能力(比如很多开源模型),就只能靠 prompt 约束输出格式,然后程序端用正则提取。

比如让模型输出 XML 格式:

<function=read_file>
<parameter=path>foo.txt</parameter>
</function>

然后用正则 <function=(\w+)>(.*?)</function> 提取。token 流先拼成完整 text,再做 pattern matching。优势是灵活——换格式只需换正则。劣势是模型可能输出不一致,需要 robust 的 fallback。

方式三:Grammar-constrained Decoding

更优雅的方案是在 token 生成阶段就约束输出,保证生成的 token 序列一定符合某个 JSON schema。

代表工具有 Outlines、Guidance、llama.cpp 的 grammar 功能。原理是对下一个 token 的 logits 做 mask,只允许符合 schema 的 token 被采样。这样 output 天然就是合法 JSON,不需要事后修复。代价是推理速度略慢。

方式四:Special Token 触发

一些专为 agent 场景微调的模型(如 Hermes 系列)在 SFT 阶段加入了大量 tool use 数据,模型学会了在需要调用工具时输出特定 token 序列。

推理引擎检测到特殊 token(如 tool)就进入 tool call 解析模式。这是一种混合方案——训练保证格式稳定性,解析层做兜底处理。

实践:Hermes Agent 的做法

Hermes Agent 采用方式二 + 方式四的混合策略。对于支持原生 tool use 的模型(如 Claude、GPT-4),直接依赖 API 层解析。对于开源模型,则用 prompt + 正则兜底。

关键工程考量:

  • 容错性:模型输出格式可能不完美,解析层需要 robust 处理
  • 流式支持:工具调用应支持流式输出,不能等整个 response 生成完再解析
  • 并行调用:一个 response 可能包含多个 tool call,需要并行执行
  • 错误恢复:解析失败时 fallback 到纯文本,而不是直接报错

总结

从 token 到 tool call,核心就是格式化 + 解析。选择哪种方式取决于你的场景:用商业 API → 原生 function calling;用开源模型 → prompt + 正则或 special token;需要严格输出保证 → grammar constraint。最好的实践是多种方式组合,根据模型能力自动选择。


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *