
在人工智能应用落地的过程中,仅仅依靠大语言模型(LLM)的单向文本生成能力,已经无法满足复杂的业务场景需求。如果希望 AI 能够主动查询数据库、调用外部 API 甚至执行本地计算,就必须将其升级为具备自主规划和工具调用能力的智能体(Agent)。
Python 生态中的 LangChain 为这类应用提供了繁荣的脚手架,而对于追求高性能、低延迟以及强类型安全的 Go 语言开发者而言,社区版的 langchaingo 同样提供了一套完整的 Agent 构建工具。
这篇文章将展示如何借助 langchaingo 框架,在 Go 项目中快速搭建一个可以自主调用天气查询工具的 AI Agent 智能体。
在 langchaingo 的设计哲学中,Agent 相当于智能体的大脑,而工具(Tool)则是智能体延伸出的双手。让 Agent 拥有调用外部代码的能力,核心在于如何向大模型清晰地描述工具的用途与入参结构。
在 langchaingo/tools 包中,所有的外部工具都必须实现一个统一的 Tool 接口。该接口定义了以下三个核心方法:
type Tool interface {
Name() string
Description() string
Call(ctx context.Context, input string) (string, error)
}接口设计非常符合 Go 语言的简洁美学。Name 方法返回工具的唯一标识;Description 提供工具功能的详细文本描述,大模型将根据这段描述来判断在何种场景下调用该工具;Call 则是工具的核心执行逻辑。
以查询天气为例,定义一个 WeatherTool 结构体:
// WeatherTool 模拟天气查询工具
type WeatherTool struct{}
// 确保 WeatherTool 实现了 tools.Tool 接口
var _ tools.Tool = WeatherTool{}这里通过 var _ tools.Tool = WeatherTool{} 的声明方式,在编译期强制校验接口是否完全实现,这是 Go 语言中非常经典的防御性编程实践。
接下来,为该结构体实现接口定义的三个方法:
func (w WeatherTool) Name() string {
return "WeatherQuery"
}对于 Name 方法,返回一个精简且具有辨识度的英文字符串。
func (w WeatherTool) Description() string {
return "查询指定城市的当前天气情况,输入参数为城市名称(如:北京)"
}Description 的编写至关重要,这是大模型进行语义匹配的唯一依据。在描述中应当包含工具的适用场景、输入参数的具体格式以及预期返回的内容。
最后,编写工具的具体执行逻辑:
func (w WeatherTool) Call(ctx context.Context, input string) (string, error) {
if input == "" {
return "", errors.New("城市名称不能为空")
}
return fmt.Sprintf("%s当前的实时天气为:晴朗,气温 25 摄氏度,微风三级。", input), nil
}在 Call 方法中,大模型传入的参数会作为 input 字符串输入。开发者可以在这里编写 HTTP 请求去调用真实的天气 API,也可以在本地执行特定的计算逻辑。这里使用了一段模拟返回的字符串来演示基本调用流。
工具准备就绪后,需要为智能体配置一颗能够理解指令并做出决策的“大脑”。 langchaingo 提供了对多种主流大模型提供商的底层支持,例如 OpenAI、Ollama、Gemini 等。
这里以最通用的 openai 模型驱动为例。首先需要初始化模型实例:
// 初始化 OpenAI 模型驱动
llm, err := openai.New()
if err != nil {
log.Fatalf("初始化模型失败: %v", err)
}在默认配置下,openai.New() 会自动从系统的环境变量中读取 OPENAI_API_KEY 和 OPENAI_BASE_URL。如果需要对接私有化部署的本地模型(如通过 Ollama 启动的 Llama 3),也可以通过配置 Option 选项来实现:
// 对接本地大模型示例
llm, err := openai.New(
openai.WithBaseURL("http://localhost:11434/v1"),
openai.WithToken("ollama"),
openai.WithModel("llama3"),
)通过传递不同的 Option 选项,开发团队可以在开发阶段轻松地将本地调试模型与生产环境的云端模型进行一键切换,确保了架构的灵活性。
在 langchaingo 中,光有模型大脑和工具双手还不够,还需要一个底座将两者编排在一起,并驱动它们循环运转。这个底座就是 Agent 和执行器(Executor)。
首先,需要将刚才定义的工具放入工具集合中:
// 准备工具集合
agentTools := []tools.Tool{
WeatherTool{},
}接着,利用 agents 包的构造函数来生成一个 Agent 智能体实例。这里选用最经典的 OneShotAgent (即基于 ReAct 思考模式的单次规划智能体):
// 绑定大模型与工具集,构建 Agent
agent := agents.NewOneShotAgent(llm, agentTools)NewOneShotAgent 会自动为大模型注入一段内置的系统提示词(System Prompt)。这段提示词会指导大模型按照特定的逻辑框架运行。
此时的 agent 实例仅包含决策逻辑,但它本身并不会执行循环。还需要通过执行器 Executor 来提供运行时的支撑:
// 运行执行器包裹 Agent
executor := agents.NewExecutor(agent)Executor 的职责是运行一个无限循环:调用大模型 -> 捕获大模型输出的 Thought 与 Action -> 执行对应的 Action 工具 -> 将 Observation 结果返回给大模型 -> 重复此过程,直到大模型给出 Final Answer 或达到最大迭代次数。
最后,使用 chains.Run 来向执行器发送任务指令:
ctx := context.Background()
question := "我想知道北京现在的天气怎么样"
// 执行任务链
result, err := chains.Run(ctx, executor, question)
if err != nil {
log.Fatalf("Agent 运行报错: %v", err)
}
fmt.Println("最终结果:", result)当 chains.Run 被调用时, Executor 内部会触发一系列精妙的决策链条。为了更好地展示其内在原理,可以将大模型底层的对话日志抽象为以下流转步骤:
大模型接收到原始问题,并结合系统内置的 ReAct 模板进行思考:
Thought: 用户想要知道北京的天气。我有一个名为 WeatherQuery 的工具可以查询指定城市的天气。
Action: WeatherQuery
Action Input: 北京执行器捕捉到了 Action: WeatherQuery 的指令,并在本地 Go 程序中匹配到注册的 WeatherTool 实例,随后同步执行 Call 方法。
Observation: 北京当前的实时天气为:晴朗,气温 25 摄氏度,微风三级。执行器将本地执行的返回结果拼装为 Observation 反馈给大模型。大模型拿到天气数据后,再次进行思考:
Thought: 我已经获取到了北京的天气情况。现在可以给出最终回答了。
Final Answer: 北京当前的实时天气为晴朗,气温 25 摄氏度,伴有微风三级。大模型输出以 Final Answer: 开头的行,触发了执行器的退出条件,执行器停止循环并将最终文本返回给外层 Go 调用者。整个过程无需开发者手动解析大模型的 JSON 或文本,所有的编排工作全部由 langchaingo 的 Executor 自动化完成。
利用 langchaingo 开发 Agent 可以帮助 Go 语言开发者以非常规范的工程化范式来设计 AI 应用。
在实际生产落地中,为了构建更为健壮的智能体应用,开发者还需注意以下几个高级实践:
其一,对于长时间挂起的 Agent 执行,务必合理设置 context 的超时与取消逻辑,防止单个请求陷入死循环或因为模型响应过慢导致系统资源枯竭。
其二,在大并发场景下,建议为大模型的 Tool 调用添加速率限制(Rate Limiting)以及错误重试机制,以提升智能体在网络抖动或大模型 API 超限时的鲁棒性。
借助于 Go 语言的强类型优势与 langchaingo 的高度抽象,开发团队可以轻松地将遗留的业务系统接口包装为 Agent 的工具集,让 AI 真正连接并驱动原有的业务流。