大模型支持「工具调用」后,对话里可以查天气、查库、调 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 里每个元素含 idfunction.namefunction.argumentsarguments 是 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,逐个把 Argumentsjson.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,按上述消息格式塞回对话,即可完成一轮工具调用。