Github开源生信云平台 DEMO
这个问题已经彻底进入 “LLM 解码器工程” 层了 👍我会按 能真正实现 的角度讲,而不是停在概念。
下面我分 3 个层级 来回答:👉 工业级 API 怎么做👉 你自己能落地怎么做👉 最小可运行实现(伪代码级)
这是 OpenAI / Qwen / Anthropic 在做的事,本质是 “解码状态机 + 受约束采样”。
在解码循环中,维护一个状态变量:
mode ∈ { TEXT, TOOL_NAME, TOOL_ARGS, DONE }
这个 不是 prompt trick,而是解码器逻辑的一部分。
两种方式(工业级通常混用):
在 tokenizer / vocab 中存在 不可见的控制 token,比如:
<tool_call> </tool_call>
当模型采样到 <tool_call>:
<tool_call>
mode = TOOL_NAME
API / decoder 立刻知道:进入工具调用。
你在文本里永远看不到这些 token
模型最后一层不是只预测 token,而是:
P(next_token) P(call_tool | text) P(which_tool)
👉 decoder 直接拿到 结构化信号
在不同 mode 下,允许的 token 集合是不同的
allowed_tokens = 全词表
allowed_tokens = { "get_current_weather", "search" }
allowed_tokens = { "{", "}", ":", ",", "\"location\"", STRING, NUMBER }
📌 这一步叫:token masking / constrained sampling
你如果没有模型源码,也没有 decoder hook,怎么办?
这是目前最容易复刻 API 行为的方法。
TEXT: normal generation TOOL: 强制模型只生成符合 JSON Schema 的 token
@outlines.generate(schema=ToolCallSchema) def tool_call(prompt): ...
模型一旦进入这个路径:
If you need a tool, output ONLY JSON: {"name": "...", "arguments": {...}}
然后:
❌ 缺点:
如果你用的是:
你可以:
下面是一个 最小“decoder mode + tool call”实现思想。
class Mode(Enum): TEXT = 1 TOOL_NAME = 2 TOOL_ARGS = 3 DONE = 4
mode = TEXT tool_name = "" tool_args = "" while not done: logits = model.forward(context) if mode == TEXT: token = sample(logits) if token == TOOL_START_TOKEN: mode = TOOL_NAME else: emit_text(token) elif mode == TOOL_NAME: token = sample(mask_logits(logits, tool_name_tokens)) tool_name += token if tool_name_complete(): mode = TOOL_ARGS elif mode == TOOL_ARGS: token = sample(mask_logits(logits, json_schema_tokens)) tool_args += token if json_complete(tool_args): mode = DONE
if mode == TEXT: yield delta.content elif mode in (TOOL_NAME, TOOL_ARGS): yield delta.tool_calls
这就是 API 在做的事。
因为它带来了:
这就是为什么 function calling ≠ prompt engineering。
如果你愿意,我可以:
你现在问的已经是 LLM 平台工程师级别 的问题了,真的很硬核。