大模型支持「工具调用」后,对话里可以查天气、查库、调 API;模型会返回该调用哪个函数、传什么参数。服务端要做的,就是解析模型返回的 tool_calls、执行对应逻辑、把结果塞回对话。用 Go 实现,结构清晰、易维护。下面说清楚解析、执行与一整轮怎么串起来。
Function Calling 与 tool_calls 是什么?
Function Calling(Tool Use)指:你把可调用的函数列表以 JSON Schema 等形式传给大模型,模型在需要时会在回复里带 tool_calls,指明函数名和参数。服务端解析后执行真实逻辑,把结果再发给模型,由模型生成最终回答。
流程:用户发消息 → 带 tools 请求模型 → 模型可能返回 tool_calls(id、函数名、参数 JSON)→ 服务端解析并执行 → 把工具结果按约定格式追加到 messages → 再请求模型得到最终回复。
定义结构体与解析
OpenAI 兼容接口里,choices[0].message.tool_calls 里每个元素含 id、function.name、function.arguments(arguments 是 JSON 字符串)。Go 里定义结构体便于解析:
type ToolCall struct {
ID string `json:"id"`
Function FunctionCall `json:"function"`
}
type FunctionCall struct {
Name string `json:"name"`
Arguments string `json:"arguments"`
}
从响应取出 message.ToolCalls,逐个把 Arguments 用 json.Unmarshal 解成 map[string]interface{} 或具体类型,再交给执行层。
执行逻辑:注册表与统一分发
执行层做成「工具名 → 执行函数」的注册表,按 name 查找并执行:
type ToolFunc func(args string) (string, error)
type ToolRegistry struct { handlers map[string]ToolFunc }
func (r *ToolRegistry) Execute(name, args string) (string, error) {
fn, ok := r.handlers[name]
if !ok { return "", fmt.Errorf("unknown tool: %s", name) }
return fn(args)
}
收到 tool_calls 后循环调用 Execute(tc.Function.Name, tc.Function.Arguments),把每个结果收集起来。
把工具结果塞回对话
模型要求工具结果以固定格式追加到 messages:role: "tool",并带上 tool_call_id:
type ToolResultMessage struct {
Role string `json:"role"`
Content string `json:"content"`
ToolCallID string `json:"tool_call_id"`
}
把本轮 assistant 消息和每条 tool 结果 append 到 messages,再发请求。若仍有 tool_calls,可循环「解析 → 执行 → 回写」直到没有或达到最大轮数。
一轮流程伪代码
msg := resp.Choices[0].Message
if len(msg.ToolCalls) == 0 { return msg.Content, nil }
var toolResults []ToolResultMessage
for _, tc := range msg.ToolCalls {
result, _ := registry.Execute(tc.Function.Name, tc.Function.Arguments)
toolResults = append(toolResults, ToolResultMessage{
Role: "tool", Content: result, ToolCallID: tc.ID,
})
}
// append 到 messages 后再次请求;有 tool_calls 则继续循环
注意事项
- 安全:对参数校验、鉴权,避免误调危险接口。
- 超时与错误:Execute 建议带 context/超时;失败时把错误信息写入 Content 返回给模型。
- 轮数与 token:控制最大 tool 轮数和总 token。
写在最后
用 Go 做 Function Calling 服务端,核心三步:解析 tool_calls(含 arguments 的 JSON)、执行(注册表分发)、回写(role: "tool" + tool_call_id 再请求)。定义好结构体、做一个 ToolRegistry,按上述消息格式塞回对话,即可完成一轮工具调用。