Go 实现 Function Calling 工具调用:从工具定义到参数解析保姆级教程
系列第 1 篇 · Go + Function Calling 写 AI Agent。本文只讲一件事:工具怎么定义、怎么注册给大模型、参数怎么解析。
目录
- Go 实现 Function Calling 工具调用:从工具定义到参数解析保姆级教程
- 一个工具需要四样东西
- 怎么注册给大模型
- 参数解析:最容易踩的坑
- Params 的 JSON Schema 怎么写
- 一句话总结
一个工具需要四样东西
很多 Go 教程把 Function Calling 讲得云里雾里,其实在 Go 里落地,一个工具就是一个结构体:
// tools/tool.gotypeToolDefstruct{Namestring// 工具名,模型靠它点名Descriptionstring// 工具说明,模型靠它决定什么时候用Params openai.FunctionParameters// JSON Schema,告诉模型传什么参数Fnfunc(ctx context.Context,argsmap[string]any)(string,error)// 真正干活的函数}小提醒:这里的
openai和shared都来自OpenAI 官方 Go SDK,不是什么厂商私有库——任何兼容 OpenAI 接口的服务都能用。四个导入一个都不能少:
import("context""encoding/json""github.com/openai/openai-go/v3""github.com/openai/openai-go/v3/shared")就这四样:名字、描述、参数格式、执行函数。缺一不可,后面的无数坑都源于对这四个字段理解不深。
怎么注册给大模型
光有结构体模型不认识,得把它翻译成 OpenAI API 能懂的格式:
func(t*ToolDef)ToChatParam()openai.ChatCompletionToolUnionParam{returnopenai.ChatCompletionToolUnionParam{OfFunction:&openai.ChatCompletionFunctionToolParam{Function:shared.FunctionDefinitionParam{Name:t.Name,Description:openai.String(t.Description),Parameters:t.Params,},},}}注册后,把它塞进请求的Tools字段,模型就知道"我有这些工具可以调用"。
参数解析:最容易踩的坑
模型决定调用工具后,会把参数用JSON 字符串传回来,长这样:
"{\"sql\":\"select * from users\"}"注意:它是字符串,不是对象。里面的引号是转义的。所以必须先json.Unmarshal成 Go 的 map,才能取值:
func(t*ToolDef)Call(ctx context.Context,argsJSONstring)(string,error){varargsmap[string]anyiferr:=json.Unmarshal([]byte(argsJSON),&args);err!=nil{return"",err}returnt.Fn(ctx,args)}很多人卡在这里,直接把argsJSON当对象用,结果永远nil。记住这一句:模型给你的是字符串,先反序列化,再干活。
Params 的 JSON Schema 怎么写
Params字段告诉模型该传什么参数,格式是固定的三段:
Params:openai.FunctionParameters{"type":"object","properties":map[string]any{"sql":map[string]any{"type":"string","description":"要执行的 SQL 语句",},},"required":[]string{"sql"},}type: “object” —— 外层永远是对象properties—— 里面有哪些字段required—— 哪些字段必填
哪怕工具不要参数,这三个键也要存在,内容填空就行。这是很多教程漏讲的细节。
一句话总结
Function Calling 的落地就是:定义结构体 → 翻译成 API 格式 → 反序列化模型传来的 JSON 参数 → 执行真正的函数。把这四步走通,你就已经会写 80% 的工具调用代码了。
下一篇讲:怎么把这些工具串成一个 Agent 主循环——那才是 Agent 真正的骨架。
系列导航:第 1 篇(本文)· [第 2 篇:Go 手写 Agent 主循环] · [第 3 篇:SQL 安全四道锁]