Golang 编写简单的 AI Agent Demo
✨ 文章介绍了一个使用 Go 语言实现的简单 AI Agent 示例,阐述了 AI Agent 的基本结构,即“循环 + AI(LLM) + 工具(Tools)”。文章详细说明了如何通过 Go 语言连接 OpenAI 的大型语言模型(LLM),以及如何定义和整合可供 AI 调用的工具函数(Function Calling),最终形成一个完整的 AI Agent 工作闭环。
AI Agent 并不复杂,粗略来说就是 循环 + AI(LLM) + 工具(Tools) 。
这里整理记录下之前用 Golang 写的 AI Agent Demo,可以当作一个最简的 AI Agent 示例,快速学习理解 AI Agent 的基本结构。
AI 的发展,有时候会让人觉得这些东西没必要再写了,毕竟有啥都可以直接问 AI ,回答又快又好。但 AI 的毕竟是 AI 的,自己动手整理记录的才能算是自己的。
创建 Go 项目
mkdir example-agent
cd example-agent
go mod init example-agent
touch main.go循环输入输出
// main.go
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
// 创建一个读取器,用来读取用户的输入
reader := bufio.NewReader(os.Stdin)
for {
fmt.Print("You: ")
// 读取用户输入,直到遇到换行符
input, _ := reader.ReadString('\n')
// 如果用户输入 "exit" 或 "quit",就退出循环
if strings.TrimSpace(input) == "exit" || strings.TrimSpace(input) == "quit" {
fmt.Println("再见!")
break
}
fmt.Println("Agent: 你说的是:", input)
}
}连接 AI (LLM)
这里使用 OpenAI 的 SDK 来连接 AI(LLM) 。
注意: 需要一个 OpenAI API Key。或者使用第三方的 API 代理服务,修改 API 的 BaseURL 。
# 这个 star 多,就不用 OpenAI 官方 SDK 了
go get github.com/sashabaranov/go-openai// main.go
package main
import (
"bufio"
"context"
"fmt"
"log"
"os"
"strings"
"github.com/sashabaranov/go-openai"
)
func main() {
// --- 初始化 AI 客户端 ---
// 实际使用时,请务必使用安全的方式管理你的 API Key
config := openai.DefaultConfig(os.Getenv("API_KEY"))
// 使用第三方 API 代理,需设置 BaseURL
config.BaseURL = "http://localhost:8080/v1"
client := openai.NewClientWithConfig(config)
// 创建一个列表来存储整个对话的上下文(历史消息)
messages := []openai.ChatCompletionMessage{}
reader := bufio.NewReader(os.Stdin)
for {
fmt.Print("You: ")
input, _ := reader.ReadString('\n')
if strings.TrimSpace(input) == "exit" || strings.TrimSpace(input) == "quit" {
fmt.Println("再见!")
break
}
// 将用户的消息添加到对话历史中
messages = append(messages, openai.ChatCompletionMessage{
Role: openai.ChatMessageRoleUser,
Content: input,
})
// --- 调用 LLM API ---
resp, err := client.CreateChatCompletion(
context.Background(),
openai.ChatCompletionRequest{
Model: openai.GPT4Turbo,
Messages: messages,
},
)
if err != nil {
log.Printf("ChatCompletion error: %v\n", err)
continue
}
aiResponse := resp.Choices[0].Message
// 将 AI 的回复也添加到对话历史中
messages = append(messages, aiResponse)
}
}Agent 工具(Tools)
如下这些函数就是 AI Agent 可调用的工具(Tools),也就是 Function Calling 。
// ... 其他代码 ...
/*
readFile 读取指定文件的内容
*/
func readFile(filename string) (string, error) {
data, err := os.ReadFile(filename)
if err != nil {
return "", err
}
return string(data), nil
}
/*
listFiles 列出指定目录中的所有文件和子目录
*/
func listFiles(path string) (string, error) {
files, err := os.ReadDir(path)
if err != nil {
return "", err
}
var fileNames []string
for _, file := range files {
fileNames = append(fileNames, file.Name())
}
return fmt.Sprintf("%v", fileNames), nil
}
/*
editFile 编辑文件内容,通过字符串替换的方式
*/
func editFile(filename, old, new string) error {
data, err := os.ReadFile(filename)
if err != nil {
return err
}
newData := strings.Replace(string(data), old, new, -1)
return os.WriteFile(filename, []byte(newData), 0644)
}
// ... main 函数 ...整合调用
有了以上工具函数后,接下来在 client.CreateChatCompletion 的请求中,将这些可用工具信息发给 AI。
// 在 for 循环中
resp, err := client.CreateChatCompletion(
context.Background(),
openai.ChatCompletionRequest{
Model: openai.GPT4Turbo,
Messages: messages,
// 定义可用工具(Function Calling)告诉 AI 它可以调用哪些函数
Tools: []openai.Tool{
{
Type: openai.ToolTypeFunction,
Function: &openai.FunctionDefinition{
Name: "read_file",
Description: "Reads the content of a file.",
// JSON Schema 定义参数格式
Parameters: json.RawMessage(`{
"type": "object",
"properties": {
"filename": {
"type": "string",
"description": "The path to the file to read"
}
},
"required": ["filename"]
}`),
},
},
{
Type: openai.ToolTypeFunction,
Function: &openai.FunctionDefinition{
Name: "list_files",
Description: "Lists files in a directory.",
Parameters: json.RawMessage(`{
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "The directory path to list files from"
}
},
"required": ["path"]
}`),
},
},
{
Type: openai.ToolTypeFunction,
Function: &openai.FunctionDefinition{
Name: "edit_file",
Description: "Edits a file by replacing a string.",
Parameters: json.RawMessage(`{
"type": "object",
"properties": {
"filename": {
"type": "string",
"description": "The path to the file to edit"
},
"old": {
"type": "string",
"description": "The string to be replaced"
},
"new": {
"type": "string",
"description": "The new string to replace with"
}
},
"required": ["filename", "old", "new"]
}`),
},
},
},
},
)这样 AI 就知道了有 read_file、list_files、edit_file 这些工具了,当 AI 处理用户输入后决定使用这些工具时,其回复中会包含一个 ToolCalls 字段,根据 ToolCalls 中的信息来执行相应的工具函数,再将结果一起发送给 AI ,由 AI 根据工具执行结果生成最终响应。
// ... 其他代码 ...
func main() {
// ...
for {
// ...
messages = append(messages, openai.ChatCompletionMessage{
Role: openai.ChatMessageRoleUser,
Content: input,
})
resp, err := client.CreateChatCompletion(
// ... (请求信息,包含 list_files 等工具) ...
)
// ... (错误处理) ...
message := resp.Choices[0].Message
messages = append(messages, message)
// --- 检查 AI 是否要调用工具 ---
if message.ToolCalls != nil {
fmt.Println("Agent: 正在调用工具...")
for _, toolCall := range message.ToolCalls {
var result string // 存储工具执行结果
var err error
// 根据工具名称执行相应的函数
switch toolCall.Function.Name {
case "read_file":
var args struct {
Filename string `json:"filename"`
}
json.Unmarshal([]byte(toolCall.Function.Arguments), &args)
result, err = readFile(args.Filename)
case "list_files":
var args struct {
Path string `json:"path"`
}
json.Unmarshal([]byte(toolCall.Function.Arguments), &args)
result, err = listFiles(args.Path)
case "edit_file":
var args struct {
Filename string `json:"filename"`
Old string `json:"old"`
New string `json:"new"`
}
json.Unmarshal([]byte(toolCall.Function.Arguments), &args)
err = editFile(args.Filename, args.Old, args.New)
if err == nil {
result = "File edited successfully."
}
}
if err != nil {
result = err.Error()
}
// 将工具执行结果添加到历史消息
messages = append(messages, openai.ChatCompletionMessage{
Role: openai.ChatMessageRoleTool,
Content: result,
Name: toolCall.Function.Name,
ToolCallID: toolCall.ID,
})
}
// 发送给 AI ,根据工具执行结果生成最终回复
resp, err = client.CreateChatCompletion(
context.Background(),
openai.ChatCompletionRequest{
Model: openai.GPT4Turbo,
Messages: messages,
},
)
if err != nil {
log.Printf("Tool Result ChatCompletion error: %v\n", err)
continue
}
// 显示 AI 的最终回复
fmt.Println("Agent:", resp.Choices[0].Message.Content)
// 将最终回复也添加到历史消息
messages = append(messages, resp.Choices[0].Message)
} else {
// 如果 AI 没有调用工具,直接显示回复
fmt.Println("Agent:", message.Content)
}
}
}以上整合后就形成了一个简单完整的 AI Agent 闭环:
用户提问 -> AI 响应 -> 工具调用 -> 执行函数 -> 将结果再发给 AI -> AI 生成最终回复