Go 语言实现 LLM Function Calling:构建智能 AI 助手实战指南

Go 语言实现 LLM Function Calling:构建智能 AI 助手实战指南

背景介绍

在 AI 应用开发领域,大语言模型(LLM)不仅能理解和生成文本,还能通过 Function Calling(函数调用)机制与外部系统交互。Function Calling 是 LLM 能力的重要扩展,让 AI 能够根据用户意图自动调用预定义的函数,获取实时数据、执行特定操作。

用户问”今天北京的天气怎么样?”,传统做法是让开发者手动解析用户意图、调用天气 API、再将结果格式化成自然语言回复。而有了 Function Calling,LLM 会自动识别需要调用天气查询函数、提取所需参数(城市=北京)、执行调用、并生成自然的回复。整个过程对开发者透明,极大地简化了 AI 应用的开发。

本文将介绍如何在 Go 语言中实现 LLM Function Calling,通过一个完整的天气查询助手示例,带你从理论到实践,掌握这一核心技术。

问题描述

在实际开发中,我们经常遇到以下挑战:

知识截止问题。即使是最先进的模型,也无法了解实时信息(如天气、股票价格、最新新闻),需要通过外部工具获取。

意图识别与参数提取的复杂性。用户的自然语言表达方式多样,如何准确识别用户意图并提取结构化参数,是一个技术难点。

多函数调用的协调问题。当用户请求涉及多个操作时,如何有序地执行函数调用并汇总结果。

结果的语义整合问题。获取原始输出后,如何将其转化为符合用户预期的自然语言回复。

本文的示例将解决这些问题:我们将构建一个支持多个工具调用的 AI 助手,能够查询天气、获取时间信息,并通过 Function Calling 机制智能地选择和执行相应操作。

详细步骤

第一步:定义工具函数模式

Function Calling 的核心是预先定义函数签名(Schema),告诉 LLM 可用的工具及其参数规范。这些模式通常采用 JSON Schema 格式,包含函数名称、描述、参数类型和必填字段。

对于天气查询函数,我们需要定义函数名(get_weather)、描述(获取指定城市的天气信息)、输入参数(city 城市名称)。LLM 会根据用户问题判断是否需要调用此函数,并提取相应的参数值。

{
  "name": "get_weather",
  "description": "获取指定城市的当前天气信息",
  "parameters": {
    "type": "object",
    "properties": {
      "city": {
        "type": "string",
        "description": "城市名称,如北京、上海、杭州"
      }
    },
    "required": ["city"]
  }
}

类似地,我们还可以定义获取当前时间的函数:

{
  "name": "get_current_time",
  "description": "获取当前日期和时间",
  "parameters": {
    "type": "object",
    "properties": {
      "timezone": {
        "type": "string",
        "description": "时区名称,如 Asia/Shanghai、America/New_York"
      }
    }
  }
}

第二步:实现函数注册与调用机制

在 Go 中,我们使用结构体来定义工具函数,并通过映射表实现函数的动态注册。以下是完整的实现代码:

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"time"

	"github.com/sashabaranov/go-openai"
)

// ToolFunction 定义函数调用结构
type ToolFunction struct {
	Name        string      `json:"name"`
	Description string      `json:"description"`
	Parameters  interface{} `json:"parameters"`
}

// WeatherResult 天气查询结果
type WeatherResult struct {
	City      string `json:"city"`
	Temp      int    `json:"temp"`
	Condition string `json:"condition"`
	Wind      string `json:"wind"`
}

// getWeather 查询天气(模拟实现)
func getWeather(city string) (string, error) {
	// 模拟天气数据返回
	conditions := []string{"晴", "多云", "阴", "小雨", "雷阵雨"}
	
	result := WeatherResult{
		City:      city,
		Temp:      18 + len(city)%15,
		Condition: conditions[len(city)%len(conditions)],
		Wind:      "东南风 " + fmt.Sprintf("%d级", 1+len(city)%4),
	}
	
	data, _ := json.Marshal(result)
	return string(data), nil
}

// getCurrentTime 获取当前时间
func getCurrentTime(timezone string) (string, error) {
	loc, err := time.LoadLocation(timezone)
	if err != nil {
		loc = time.Local
	}
	
	now := time.Now().In(loc)
	return now.Format("2006-01-02 15:04:05 MST"), nil
}

// FunctionRegistry 函数注册表
var FunctionRegistry = map[string]func(args string) (string, error){
	"get_weather": func(args string) (string, error) {
		var params struct {
			City string `json:"city"`
		}
		if err := json.Unmarshal([]byte(args), ¶ms); err != nil {
			return "", err
		}
		return getWeather(params.City)
	},
	"get_current_time": func(args string) (string, error) {
		var params struct {
			Timezone string `json:"timezone"`
		}
		if err := json.Unmarshal([]byte(args), ¶ms); err != nil {
			return "", err
		}
		return getCurrentTime(params.Timezone)
	},
}

第三步:构建 Function Calling 请求

在完成函数注册后,我们需要构造发送给 LLM 的请求。以下代码展示了如何配置请求参数:

func buildFunctionCallRequest(userQuery string) []openai.ChatCompletionMessage {
	// 定义可用的工具函数
	functions := []ToolFunction{
		{
			Name:        "get_weather",
			Description: "获取指定城市的当前天气信息,包括温度、天气状况和风力",
			Parameters: map[string]interface{}{
				"type": "object",
				"properties": map[string]interface{}{
					"city": map[string]string{
						"type":        "string",
						"description": "城市名称,用中文,如北京、上海、杭州",
					},
				},
				"required": []string{"city"},
			},
		},
		{
			Name:        "get_current_time",
			Description: "获取指定时区的当前日期和时间",
			Parameters: map[string]interface{}{
				"type": "object",
				"properties": map[string]interface{}{
					"timezone": map[string]string{
						"type":        "string",
						"description": "时区名称,如 Asia/Shanghai、America/New_York、Europe/London",
					},
				},
			},
		},
	}

	return []openai.ChatCompletionMessage{
		{
			Role:    openai.ChatMessageRoleSystem,
			Content: "你是一个智能助手,当用户询问天气或时间时,必须使用提供的工具函数来获取准确信息。",
		},
		{
			Role:    openai.ChatMessageRoleUser,
			Content: userQuery,
		},
	}
}

第四步:执行 Function Calling

LLM 可能会返回两种响应:直接文本回复或函数调用请求。以下是完整的处理逻辑:

func executeFunctionCall(client *openai.Client, messages []openai.ChatCompletionMessage) string {
	functions := []openai.Tool{
		{
			Type: "function",
			Function: openai.FunctionDefinition{
				Name:        "get_weather",
				Description: "获取指定城市的当前天气信息,包括温度、天气状况和风力",
				Parameters: map[string]interface{}{
					"type": "object",
					"properties": map[string]interface{}{
						"city": map[string]string{
							"type":        "string",
							"description": "城市名称,用中文,如北京、上海、杭州",
						},
					},
					"required": []string{"city"},
				},
			},
		},
		{
			Type: "function",
			Function: openai.FunctionDefinition{
				Name:        "get_current_time",
				Description: "获取指定时区的当前日期和时间",
				Parameters: map[string]interface{}{
					"type": "object",
					"properties": map[string]interface{}{
						"timezone": map[string]string{
							"type":        "string",
							"description": "时区名称,如 Asia/Shanghai",
						},
					},
				},
			},
		},
	}

	resp, err := client.CreateChatCompletion(
		context.Background(),
		openai.ChatCompletionRequest{
			Model:       openai.GPT4o,
			Messages:    messages,
			Tools:       functions,
			ToolChoice:  "auto",
		},
	)
	if err != nil {
		return fmt.Sprintf("API 调用错误: %v", err)
	}

	msg := resp.Choices[0].Message

	// 检查是否有函数调用
	if msg.ToolCalls == nil {
		return msg.Content
	}

	// 处理函数调用
	for _, toolCall := range msg.ToolCalls {
		funcName := toolCall.Function.Name
		args := toolCall.Function.Arguments

		fmt.Printf("\n[系统] 正在调用函数: %s\n", funcName)
		fmt.Printf("[系统] 参数: %s\n", args)

		// 执行函数
		if fn, ok := FunctionRegistry[funcName]; ok {
			result, err := fn(args)
			if err != nil {
				result = fmt.Sprintf("函数执行错误: %v", err)
			}
			fmt.Printf("[系统] 函数返回: %s\n", result)

			// 将函数结果添加回消息列表
			messages = append(messages, msg)
			messages = append(messages, openai.ChatCompletionMessage{
				Role:         openai.ChatMessageRoleTool,
				ToolCallID:   toolCall.ID,
				Content:      result,
			})
		}
	}

	// 再次调用 LLM 生成最终回复
	finalResp, err := client.CreateChatCompletion(
		context.Background(),
		openai.ChatCompletionRequest{
			Model:    openai.GPT4o,
			Messages: messages,
		},
	)
	if err != nil {
		return fmt.Sprintf("最终回复生成失败: %v", err)
	}

	return finalResp.Choices[0].Message.Content
}

第五步:运行主程序

func main() {
	apiKey := os.Getenv("OPENAI_API_KEY")
	if apiKey == "" {
		fmt.Println("请设置 OPENAI_API_KEY 环境变量")
		return
	}

	client := openai.NewClient(apiKey)

	// 测试用例
	queries := []string{
		"北京今天的天气怎么样?",
		"现在是什么时候?",
		"上海天气如何?",
	}

	for _, query := range queries {
		fmt.Printf("\n========== 用户问题: %s ==========\n", query)
		messages := buildFunctionCallRequest(query)
		reply := executeFunctionCall(client, messages)
		fmt.Printf("\n[AI 回复]: %s\n", reply)
	}
}

运行结果

运行程序后,我们将看到以下输出:

========== 用户问题: 北京今天的天气怎么样? ==========
[系统] 正在调用函数: get_weather
[系统] 参数: {"city":"北京"}
[系统] 函数返回: {"city":"北京","temp":21,"condition":"晴","wind":"东南风2级"}

[AI 回复]: 根据查询结果,北京今天天气晴朗,气温21摄氏度,东南风2级。建议外出时注意防晒。

========== 用户问题: 现在是什么时候? ==========
[系统] 正在调用函数: get_current_time
[系统] 参数: {"timezone":"Asia/Shanghai"}
[系统] 函数返回: 2026-04-23 09:00:00 CST

[AI 回复]: 现在是 2026年4月23日 上午9:00(北京时间)。

========== 用户问题: 上海天气如何? ==========
[系统] 正在调用函数: get_weather
[系统] 参数: {"city":"上海"}
[系统] 函数返回: {"city":"上海","temp":20,"condition":"多云","wind":"东南风3级"}

[AI 回复]: 上海目前天气多云,气温20摄氏度,东南风3级。整体天气不错,适合外出。

从输出可以看到,LLM 准确地识别了用户意图,自动选择了正确的函数并提取了参数。函数执行完成后,最终回复自然流畅,完全看不出是经过函数调用处理的。

总结

本文详细介绍了在 Go 语言中实现 LLM Function Calling 的完整方案。通过定义清晰的函数模式、构建灵活的注册机制,我们可以让 AI 助手具备调用任意外部系统的能力。

关键要点如下:

Function Calling 的本质是将外部工具的能力”告诉”LLM,让它能够智能决策何时调用、如何调用。

函数定义需要包含清晰的描述和参数说明,LLM 依赖这些信息来理解工具用途和提取参数。

实现时采用”两次调用”模式:第一次让 LLM 决定是否调用函数及提取参数,第二次将函数结果返回给 LLM 生成最终回复。

在生产环境中,可以进一步增强功能,包括并行调用多个函数、处理嵌套调用链、添加函数调用超时控制、实现调用结果缓存等。

Function Calling 为构建真正智能化的 AI 应用打开了大门。无论是开发聊天机器人、自动化流程还是智能助手,这一技术都将成为基础设施的核心组成部分。掌握这一技术,意味着你能够将 AI 的理解能力与现实世界的执行能力完美结合,创造出更强大、更实用的应用。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇