引言
2025 年,如果你还觉得"AI 是 Python 的事",那可就大错特错了。诚然,Python 在模型训练和数据科学领域占据统治地位,但当你需要把 AI 能力部署到生产环境、构建高性能的推理服务、或者开发面向用户的 AI 应用时,Go 的优势就凸显出来了:并发能力强、内存占用小、编译成单一二进制、部署简单。
更重要的是,目前主流的 AI 服务(OpenAI、Anthropic Claude、Google Gemini)都提供了完善的 REST API,而 Go 在调用 HTTP API 方面可以说是行家里手。今天这篇文章,我们就来系统地学习如何用 Go 构建各种 AI 应用,从最基础的 API 调用,到完整的 RAG 系统和 AI Agent,一步一个脚印。
一、基础准备:AI API 客户端封装
在开始之前,我们需要一个健壮的 AI API 客户端。虽然社区有一些第三方库,但为了灵活性和学习目的,我们自己封装一个:
package ai
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
// Client 是 AI API 的通用客户端
type Client struct {
apiKey string
baseURL string
httpClient *http.Client
model string
}
// ClientOption 是客户端配置选项
type ClientOption func(*Client)
func WithHTTPClient(c *http.Client) ClientOption {
return func(client *Client) {
client.httpClient = c
}
}
func WithTimeout(d time.Duration) ClientOption {
return func(client *Client) {
client.httpClient.Timeout = d
}
}
func WithBaseURL(url string) ClientOption {
return func(client *Client) {
client.baseURL = url
}
}
// NewOpenAIClient 创建 OpenAI 兼容的客户端
func NewOpenAIClient(apiKey string, opts ...ClientOption) *Client {
c := &Client{
apiKey: apiKey,
baseURL: "https://api.openai.com/v1",
httpClient: &http.Client{
Timeout: 120 * time.Second,
},
model: "gpt-4o",
}
for _, opt := range opts {
opt(c)
}
return c
}
// NewClaudeClient 创建 Anthropic Claude 客户端
func NewClaudeClient(apiKey string, opts ...ClientOption) *Client {
c := &Client{
apiKey: apiKey,
baseURL: "https://api.anthropic.com/v1",
httpClient: &http.Client{
Timeout: 120 * time.Second,
},
model: "claude-sonnet-4-20250514",
}
for _, opt := range opts {
opt(c)
}
return c
}
// Message 表示一条对话消息
type Message struct {
Role string `json:"role"`
Content string `json:"content,omitempty"`
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
ToolCallID string `json:"tool_call_id,omitempty"`
}
// ToolCall 表示模型发起的工具调用
type ToolCall struct {
ID string `json:"id"`
Type string `json:"type"`
Function FunctionCall `json:"function"`
}
// FunctionCall 表示函数调用的详情
type FunctionCall struct {
Name string `json:"name"`
Arguments string `json:"arguments"`
}
// ChatRequest 是聊天请求
type ChatRequest struct {
Model string `json:"model"`
Messages []Message `json:"messages"`
Temperature float64 `json:"temperature,omitempty"`
MaxTokens int `json:"max_tokens,omitempty"`
Stream bool `json:"stream,omitempty"`
Tools []Tool `json:"tools,omitempty"`
Stop []string `json:"stop,omitempty"`
}
// Tool 定义一个可供模型调用的工具
type Tool struct {
Type string `json:"type"`
Function FunctionDef `json:"function"`
}
// FunctionDef 定义函数的参数和描述
type FunctionDef struct {
Name string `json:"name"`
Description string `json:"description"`
Parameters json.RawMessage `json:"parameters"`
}
// ChatResponse 是聊天响应
type ChatResponse struct {
ID string `json:"id"`
Choices []Choice `json:"choices"`
Usage Usage `json:"usage"`
}
// Choice 是响应中的一个选择
type Choice struct {
Index int `json:"index"`
Message Message `json:"message"`
FinishReason string `json:"finish_reason"`
}
// Usage 记录 token 使用量
type Usage struct {
PromptTokens int `json:"prompt_tokens"`
CompletionTokens int `json:"completion_tokens"`
TotalTokens int `json:"total_tokens"`
}
// Chat 发送聊天请求并获取响应
func (c *Client) Chat(ctx context.Context, req *ChatRequest) (*ChatResponse, error) {
if req.Model == "" {
req.Model = c.model
}
body, err := json.Marshal(req)
if err != nil {
return nil, fmt.Errorf("序列化请求失败: %w", err)
}
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost,
c.baseURL+"/chat/completions", bytes.NewReader(body))
if err != nil {
return nil, fmt.Errorf("创建请求失败: %w", err)
}
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("Authorization", "Bearer "+c.apiKey)
resp, err := c.httpClient.Do(httpReq)
if err != nil {
return nil, fmt.Errorf("发送请求失败: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
respBody, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("API 错误 (status=%d): %s", resp.StatusCode, string(respBody))
}
var chatResp ChatResponse
if err := json.NewDecoder(resp.Body).Decode(&chatResp); err != nil {
return nil, fmt.Errorf("解析响应失败: %w", err)
}
return &chatResp, nil
}
有了这个基础客户端,调用 AI API 就变得非常简单:
package main
import (
"context"
"fmt"
"log"
"os"
"example.com/aiapp/ai"
)
func main() {
client := ai.NewOpenAIClient(os.Getenv("OPENAI_API_KEY"))
resp, err := client.Chat(context.Background(), &ai.ChatRequest{
Messages: []ai.Message{
{Role: "system", Content: "你是一个专业的 Go 语言导师,回答简洁且有深度。"},
{Role: "user", Content: "Go 语言的 goroutine 和 Java 的线程有什么本质区别?"},
},
Temperature: 0.7,
MaxTokens: 1000,
})
if err != nil {
log.Fatalf("调用失败: %v", err)
}
fmt.Println(resp.Choices[0].Message.Content)
fmt.Printf("\n--- Token 使用量 ---\n")
fmt.Printf("Prompt: %d, Completion: %d, Total: %d\n",
resp.Usage.PromptTokens, resp.Usage.CompletionTokens, resp.Usage.TotalTokens)
}
二、流式响应:像打字机一样输出
用过 ChatGPT 吧?那种一个字一个字"蹦出来"的效果,技术上叫做流式响应(Streaming)。它的原理是使用 Server-Sent Events (SSE),服务器持续推送数据片段,而不是等全部生成完再一次性返回。
在 Go 中实现流式响应,需要处理 SSE 协议:
package ai
import (
"bufio"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
)
// StreamChunk 表示流式响应中的一个片段
type StreamChunk struct {
ID string `json:"id"`
Choices []StreamChoice `json:"choices"`
Usage *Usage `json:"usage,omitempty"`
}
// StreamChoice 是流式片段中的选择
type StreamChoice struct {
Index int `json:"index"`
Delta DeltaMessage `json:"delta"`
FinishReason *string `json:"finish_reason"`
}
// DeltaMessage 是增量消息内容
type DeltaMessage struct {
Role string `json:"role,omitempty"`
Content string `json:"content,omitempty"`
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
}
// StreamHandler 处理每个流式片段的回调函数
type StreamHandler func(chunk StreamChunk) error
// ChatStream 以流式方式获取 AI 响应
func (c *Client) ChatStream(ctx context.Context, req *ChatRequest, handler StreamHandler) error {
req.Stream = true
if req.Model == "" {
req.Model = c.model
}
body, err := json.Marshal(req)
if err != nil {
return fmt.Errorf("序列化请求失败: %w", err)
}
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost,
c.baseURL+"/chat/completions", bytes.NewReader(body))
if err != nil {
return fmt.Errorf("创建请求失败: %w", err)
}
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("Authorization", "Bearer "+c.apiKey)
httpReq.Header.Set("Accept", "text/event-stream")
resp, err := c.httpClient.Do(httpReq)
if err != nil {
return fmt.Errorf("发送请求失败: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
respBody, _ := io.ReadAll(resp.Body)
return fmt.Errorf("API 错误 (status=%d): %s", resp.StatusCode, string(respBody))
}
// 解析 SSE 流
scanner := bufio.NewScanner(resp.Body)
for scanner.Scan() {
line := scanner.Text()
// SSE 格式:以 "data: " 开头
if !strings.HasPrefix(line, "data: ") {
continue
}
data := strings.TrimPrefix(line, "data: ")
// [DONE] 表示流结束
if data == "[DONE]" {
break
}
var chunk StreamChunk
if err := json.Unmarshal([]byte(data), &chunk); err != nil {
// 跳过解析失败的行
continue
}
if err := handler(chunk); err != nil {
return fmt.Errorf("处理片段失败: %w", err)
}
}
if err := scanner.Err(); err != nil {
return fmt.Errorf("读取流失败: %w", err)
}
return nil
}
现在来做一个漂亮的流式聊天界面:
package main
import (
"context"
"fmt"
"log"
"os"
"strings"
"time"
"example.com/aiapp/ai"
)
func main() {
client := ai.NewOpenAIClient(os.Getenv("OPENAI_API_KEY"))
messages := []ai.Message{
{Role: "system", Content: "你是一个创意故事作家。请用生动的语言写一个简短的故事。"},
{Role: "user", Content: "写一个关于一个程序员穿越到古代的故事,要幽默。"},
}
fmt.Println("🤖 AI 正在创作中...")
fmt.Println(strings.Repeat("─", 60))
start := time.Now()
tokenCount := 0
// 收集完整响应用于后续对话
var fullResponse strings.Builder
err := client.ChatStream(context.Background(), &ai.ChatRequest{
Messages: messages,
Temperature: 0.9,
MaxTokens: 2000,
}, func(chunk ai.StreamChunk) error {
if len(chunk.Choices) > 0 {
content := chunk.Choices[0].Delta.Content
if content != "" {
fmt.Print(content)
fullResponse.WriteString(content)
tokenCount++
}
// 检查是否结束
if chunk.Choices[0].FinishReason != nil {
fmt.Println()
}
}
// 打印最终的 token 使用统计
if chunk.Usage != nil {
fmt.Println(strings.Repeat("─", 60))
fmt.Printf("📊 Token 统计: Prompt=%d, Completion=%d, Total=%d\n",
chunk.Usage.PromptTokens, chunk.Usage.CompletionTokens, chunk.Usage.TotalTokens)
}
return nil
})
if err != nil {
log.Fatalf("流式调用失败: %v", err)
}
elapsed := time.Since(start)
fmt.Printf("⏱ 耗时: %v\n", elapsed)
}
三、Function Calling:让 AI 调用你的代码
Function Calling(也叫 Tool Use)是 AI 应用中最强大的特性之一。它让 AI 模型能够"决定"何时调用你预定义的函数,并传递参数。这就像给 AI 装了一双手,它可以操作你的系统了。
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"math"
"net/http"
"os"
"strconv"
"time"
"example.com/aiapp/ai"
)
// 定义工具函数
var tools = []ai.Tool{
{
Type: "function",
Function: ai.FunctionDef{
Name: "get_weather",
Description: "获取指定城市的当前天气信息,包括温度、湿度和天气状况",
Parameters: json.RawMessage(`{
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称,例如 '北京' 或 '上海'"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "温度单位"
}
},
"required": ["city"]
}`),
},
},
{
Type: "function",
Function: ai.FunctionDef{
Name: "calculate",
Description: "执行数学计算,支持基本运算和常用数学函数",
Parameters: json.RawMessage(`{
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "数学表达式,例如 'sqrt(144) + 2^10'"
}
},
"required": ["expression"]
}`),
},
},
{
Type: "function",
Function: ai.FunctionDef{
Name: "search_database",
Description: "在数据库中搜索用户信息",
Parameters: json.RawMessage(`{
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词"
},
"limit": {
"type": "integer",
"description": "返回结果数量上限",
"default": 5
}
},
"required": ["query"]
}`),
},
},
}
// 模拟天气 API
func getWeather(city, unit string) string {
// 实际应用中这里会调用真实的天气 API
weatherData := map[string]map[string]interface{}{
"北京": {"temp": 28, "humidity": 45, "condition": "晴", "wind": "东北风 3级"},
"上海": {"temp": 32, "humidity": 78, "condition": "多云", "wind": "东南风 2级"},
"广州": {"temp": 35, "humidity": 85, "condition": "雷阵雨", "wind": "南风 1级"},
}
data, ok := weatherData[city]
if !ok {
return fmt.Sprintf("抱歉,未找到 %s 的天气数据", city)
}
tempUnit := "°C"
if unit == "fahrenheit" {
data["temp"] = data["temp"].(int)*9/5 + 32
tempUnit = "°F"
}
return fmt.Sprintf("📍 %s 当前天气:%s,温度 %d%s,湿度 %d%%,%s",
city, data["condition"], data["temp"], tempUnit, data["humidity"], data["wind"])
}
// 模拟数学计算
func calculate(expression string) string {
// 简单的表达式求值(实际应用中应使用安全的表达式解析器)
result, err := strconv.ParseFloat(expression, 64)
if err != nil {
// 尝试一些预定义的计算
switch expression {
case "sqrt(144)":
return fmt.Sprintf("sqrt(144) = %.2f", math.Sqrt(144))
case "2^10":
return fmt.Sprintf("2^10 = %d", int(math.Pow(2, 10)))
}
return fmt.Sprintf("无法计算表达式: %s", expression)
}
return fmt.Sprintf("%s = %.4f", expression, result)
}
// 模拟数据库搜索
func searchDatabase(query string, limit int) string {
// 模拟数据库结果
users := []map[string]string{
{"name": "张三", "email": "zhangsan@example.com", "role": "工程师"},
{"name": "李四", "email": "lisi@example.com", "role": "产品经理"},
{"name": "王五", "email": "wangwu@example.com", "role": "设计师"},
}
results := users
if limit < len(results) {
results = results[:limit]
}
jsonData, _ := json.MarshalIndent(results, "", " ")
return string(jsonData)
}
// 执行工具调用并返回结果
func executeToolCall(call ai.ToolCall) string {
var args map[string]interface{}
if err := json.Unmarshal([]byte(call.Function.Arguments), &args); err != nil {
return fmt.Sprintf("参数解析失败: %v", err)
}
switch call.Function.Name {
case "get_weather":
city, _ := args["city"].(string)
unit, _ := args["unit"].(string)
if unit == "" {
unit = "celsius"
}
return getWeather(city, unit)
case "calculate":
expr, _ := args["expression"].(string)
return calculate(expr)
case "search_database":
query, _ := args["query"].(string)
limit := 5
if l, ok := args["limit"].(float64); ok {
limit = int(l)
}
return searchDatabase(query, limit)
default:
return fmt.Sprintf("未知的工具: %s", call.Function.Name)
}
}
func main() {
client := ai.NewOpenAIClient(os.Getenv("OPENAI_API_KEY"))
ctx := context.Background()
messages := []ai.Message{
{Role: "system", Content: "你是一个智能助手,可以查天气、做计算和查询数据库。请根据用户的需要调用相应的工具。"},
{Role: "user", Content: "帮我查一下北京和上海今天的天气怎么样?另外帮我算一下 sqrt(144) 是多少。"},
}
// 第一轮:发送用户消息,获取 AI 的工具调用决策
fmt.Println("=== 第一轮:AI 决定调用哪些工具 ===")
resp, err := client.Chat(ctx, &ai.ChatRequest{
Messages: messages,
Tools: tools,
Temperature: 0,
})
if err != nil {
log.Fatalf("调用失败: %v", err)
}
assistantMessage := resp.Choices[0].Message
messages = append(messages, assistantMessage)
// 检查是否有工具调用
if len(assistantMessage.ToolCalls) == 0 {
fmt.Println("AI 直接回答:", assistantMessage.Content)
return
}
fmt.Printf("AI 决定调用 %d 个工具:\n", len(assistantMessage.ToolCalls))
for _, tc := range assistantMessage.ToolCalls {
fmt.Printf(" 🔧 %s(%s)\n", tc.Function.Name, tc.Function.Arguments)
}
// 第二轮:执行所有工具调用,把结果返回给 AI
fmt.Println("\n=== 第二轮:执行工具并返回结果 ===")
for _, tc := range assistantMessage.ToolCalls {
result := executeToolCall(tc)
fmt.Printf(" 📋 %s 返回: %s\n", tc.Function.Name, result)
messages = append(messages, ai.Message{
Role: "tool",
Content: result,
ToolCallID: tc.ID,
})
}
// 第三轮:AI 根据工具结果生成最终回答
fmt.Println("\n=== 第三轮:AI 生成最终回答 ===")
finalResp, err := client.Chat(ctx, &ai.ChatRequest{
Messages: messages,
Tools: tools,
Temperature: 0.7,
})
if err != nil {
log.Fatalf("调用失败: %v", err)
}
fmt.Println(finalResp.Choices[0].Message.Content)
}
四、向量嵌入与语义搜索
向量嵌入(Embedding)是 RAG 系统的基石。它把文本转换为高维向量,使得语义相近的文本在向量空间中距离更近。
package ai
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"math"
"net/http"
)
// EmbeddingRequest 是嵌入请求
type EmbeddingRequest struct {
Model string `json:"model"`
Input []string `json:"input"`
}
// EmbeddingResponse 是嵌入响应
type EmbeddingResponse struct {
Data []EmbeddingData `json:"data"`
Usage Usage `json:"usage"`
}
// EmbeddingData 包含单个文本的向量
type EmbeddingData struct {
Embedding []float64 `json:"embedding"`
Index int `json:"index"`
}
// GetEmbeddings 获取文本的向量嵌入
func (c *Client) GetEmbeddings(ctx context.Context, texts []string) (*EmbeddingResponse, error) {
req := EmbeddingRequest{
Model: "text-embedding-3-small",
Input: texts,
}
body, err := json.Marshal(req)
if err != nil {
return nil, err
}
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost,
c.baseURL+"/embeddings", bytes.NewReader(body))
if err != nil {
return nil, err
}
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("Authorization", "Bearer "+c.apiKey)
resp, err := c.httpClient.Do(httpReq)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
respBody, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("API 错误: %s", string(respBody))
}
var embResp EmbeddingResponse
if err := json.NewDecoder(resp.Body).Decode(&embResp); err != nil {
return nil, err
}
return &embResp, nil
}
// CosineSimilarity 计算两个向量的余弦相似度
func CosineSimilarity(a, b []float64) float64 {
if len(a) != len(b) {
return 0
}
var dotProduct, normA, normB float64
for i := range a {
dotProduct += a[i] * b[i]
normA += a[i] * a[i]
normB += b[i] * b[i]
}
if normA == 0 || normB == 0 {
return 0
}
return dotProduct / (math.Sqrt(normA) * math.Sqrt(normB))
}
// EuclideanDistance 计算两个向量的欧几里得距离
func EuclideanDistance(a, b []float64) float64 {
if len(a) != len(b) {
return math.MaxFloat64
}
var sum float64
for i := range a {
diff := a[i] - b[i]
sum += diff * diff
}
return math.Sqrt(sum)
}
接下来实现一个内存向量存储(生产环境应该用专业的向量数据库如 Pinecone、Weaviate 或 pgvector):
package main
import (
"context"
"fmt"
"log"
"os"
"sort"
"example.com/aiapp/ai"
)
// Document 表示一个可搜索的文档
type Document struct {
ID string
Content string
Metadata map[string]string
Embedding []float64
}
// VectorStore 简单的内存向量存储
type VectorStore struct {
documents []Document
}
func NewVectorStore() *VectorStore {
return &VectorStore{}
}
// SearchResult 搜索结果
type SearchResult struct {
Document Document
Score float64
}
// AddDocuments 向存储中添加文档
func (vs *VectorStore) AddDocuments(docs []Document) {
vs.documents = append(vs.documents, docs...)
}
// Search 根据查询向量搜索相似文档
func (vs *VectorStore) Search(queryEmbedding []float64, topK int) []SearchResult {
results := make([]SearchResult, len(vs.documents))
for i, doc := range vs.documents {
results[i] = SearchResult{
Document: doc,
Score: ai.CosineSimilarity(queryEmbedding, doc.Embedding),
}
}
// 按相似度降序排序
sort.Slice(results, func(i, j int) bool {
return results[i].Score > results[j].Score
})
if topK < len(results) {
results = results[:topK]
}
return results
}
func main() {
client := ai.NewOpenAIClient(os.Getenv("OPENAI_API_KEY"))
ctx := context.Background()
// 准备知识库文档
texts := []string{
"Go 语言由 Google 开发,于 2009 年开源。它的设计目标是简洁、高效、并发安全。",
"goroutine 是 Go 语言的轻量级线程,创建成本仅为几 KB 内存,可以轻松创建数十万个。",
"channel 是 Go 语言中 goroutine 之间通信的管道,遵循 CSP(Communicating Sequential Processes)模型。",
"Go 的垃圾回收器使用三色标记法,Go 1.19 引入了 Pacer 改进,大幅降低了 GC 暂停时间。",
"interface 是 Go 语言实现多态的核心机制,空接口 interface{} 可以接受任何类型的值。",
"context 包提供了在 goroutine 树中传递截止时间、取消信号和请求级别数据的方法。",
"Go modules 是 Go 1.11 引入的依赖管理系统,取代了之前的 GOPATH 模式。",
"Go 1.18 引入了泛型(Generics),使用类型参数和类型约束来实现类型安全的代码复用。",
}
// 批量获取嵌入向量
fmt.Println("📝 正在生成文档嵌入向量...")
embResp, err := client.GetEmbeddings(ctx, texts)
if err != nil {
log.Fatalf("获取嵌入失败: %v", err)
}
// 构建文档存储
store := NewVectorStore()
docs := make([]Document, len(texts))
for i, text := range texts {
docs[i] = Document{
ID: fmt.Sprintf("doc-%d", i),
Content: text,
Embedding: embResp.Data[i].Embedding,
Metadata: map[string]string{"source": "go-faq"},
}
}
store.AddDocuments(docs)
fmt.Printf("✅ 已索引 %d 个文档(维度: %d)\n", len(docs), len(docs[0].Embedding))
// 语义搜索
queries := []string{
"Go 语言的并发机制是什么?",
"如何处理 Go 项目的依赖?",
"Go 的性能优化有哪些手段?",
}
for _, query := range queries {
fmt.Printf("\n🔍 查询: %s\n", query)
queryEmb, err := client.GetEmbeddings(ctx, []string{query})
if err != nil {
log.Fatalf("获取查询嵌入失败: %v", err)
}
results := store.Search(queryEmb.Data[0].Embedding, 3)
for i, r := range results {
fmt.Printf(" [%d] 相似度: %.4f | %s\n", i+1, r.Score, r.Document.Content[:60])
}
}
}
五、RAG 系统:检索增强生成
RAG(Retrieval-Augmented Generation)是当前最实用的 AI 应用模式之一。它的核心思想很简单:先从知识库中检索相关文档,再把这些文档作为上下文传给 AI 来生成回答。
package main
import (
"context"
"fmt"
"log"
"os"
"strings"
"example.com/aiapp/ai"
)
// RAGSystem 检索增强生成系统
type RAGSystem struct {
client *ai.Client
vectorStore *VectorStore
chunkSize int
overlap int
}
// NewRAGSystem 创建 RAG 系统实例
func NewRAGSystem(client *ai.Client, store *VectorStore) *RAGSystem {
return &RAGSystem{
client: client,
vectorStore: store,
chunkSize: 500, // 每个文本块的最大字符数
overlap: 100, // 文本块之间的重叠字符数
}
}
// ChunkText 将长文本切分成小块
func (r *RAGSystem) ChunkText(text string, source string) []Document {
var chunks []Document
words := strings.Split(text, "")
for i := 0; i < len(words); i += r.chunkSize - r.overlap {
end := i + r.chunkSize
if end > len(words) {
end = len(words)
}
chunk := strings.Join(words[i:end], "")
if len(chunk) < 50 {
continue // 跳过过短的文本块
}
chunks = append(chunks, Document{
ID: fmt.Sprintf("%s-chunk-%d", source, i),
Content: chunk,
Metadata: map[string]string{
"source": source,
"chunk_idx": fmt.Sprintf("%d", i),
"total_len": fmt.Sprintf("%d", len(words)),
},
})
if end == len(words) {
break
}
}
return chunks
}
// IngestDocument 处理并索引一个文档
func (r *RAGSystem) IngestDocument(ctx context.Context, text, source string) error {
// 1. 文本切分
chunks := r.ChunkText(text, source)
fmt.Printf(" 📄 '%s' 切分为 %d 个文本块\n", source, len(chunks))
// 2. 批量生成嵌入向量
texts := make([]string, len(chunks))
for i, chunk := range chunks {
texts[i] = chunk.Content
}
embResp, err := r.client.GetEmbeddings(ctx, texts)
if err != nil {
return fmt.Errorf("生成嵌入失败: %w", err)
}
// 3. 关联向量和文档块
for i := range chunks {
chunks[i].Embedding = embResp.Data[i].Embedding
}
// 4. 添加到向量存储
r.vectorStore.AddDocuments(chunks)
return nil
}
// Query 执行 RAG 查询
func (r *RAGSystem) Query(ctx context.Context, question string, topK int) (string, error) {
fmt.Printf("\n🧠 RAG 查询: %s\n", question)
// 1. 将问题转换为向量
queryEmb, err := r.client.GetEmbeddings(ctx, []string{question})
if err != nil {
return "", fmt.Errorf("生成查询嵌入失败: %w", err)
}
// 2. 检索相关文档
results := r.vectorStore.Search(queryEmb.Data[0].Embedding, topK)
fmt.Printf(" 📚 检索到 %d 个相关文档块:\n", len(results))
for i, r := range results {
fmt.Printf(" [%d] %.4f | %s...\n", i+1, r.Score,
truncate(r.Document.Content, 80))
}
// 3. 构建带有上下文的 prompt
var contextBuilder strings.Builder
contextBuilder.WriteString("以下是从知识库中检索到的相关信息:\n\n")
for i, result := range results {
if result.Score < 0.3 {
continue // 过滤掉相关度太低的结果
}
contextBuilder.WriteString(fmt.Sprintf("--- 文档 %d (相关度: %.2f) ---\n", i+1, result.Score))
contextBuilder.WriteString(result.Document.Content)
contextBuilder.WriteString("\n\n")
}
systemPrompt := `你是一个专业的技术问答助手。请根据提供的上下文信息回答用户的问题。
如果上下文信息不足以回答问题,请明确说明哪些信息来自知识库,哪些来自你的通用知识。
回答要准确、有深度,并且易于理解。`
userPrompt := fmt.Sprintf("上下文信息:\n%s\n\n用户问题:%s", contextBuilder.String(), question)
// 4. 调用 LLM 生成回答
resp, err := r.client.Chat(ctx, &ai.ChatRequest{
Messages: []ai.Message{
{Role: "system", Content: systemPrompt},
{Role: "user", Content: userPrompt},
},
Temperature: 0.3, // RAG 场景通常使用较低的温度以提高准确性
MaxTokens: 2000,
})
if err != nil {
return "", fmt.Errorf("LLM 调用失败: %w", err)
}
return resp.Choices[0].Message.Content, nil
}
func truncate(s string, maxLen int) string {
if len(s) <= maxLen {
return s
}
return s[:maxLen] + "..."
}
func main() {
client := ai.NewOpenAIClient(os.Getenv("OPENAI_API_KEY"))
ctx := context.Background()
// 创建 RAG 系统
store := NewVectorStore()
rag := NewRAGSystem(client, store)
// 导入知识库文档
documents := map[string]string{
"goroutine-basics": `
goroutine 是 Go 语言中最核心的并发原语。每个 Go 程序至少有一个 goroutine(main goroutine)。
创建 goroutine 非常简单,只需在函数调用前加上 go 关键字。goroutine 的栈是动态伸缩的,
初始大小仅为 2KB(Go 1.4 之后),可以根据需要自动增长或缩小。
Go 的调度器使用 GMP 模型:G 代表 goroutine,M 代表操作系统线程,P 代表处理器(逻辑 CPU)。
默认情况下,P 的数量等于 CPU 核心数,可以通过 GOMAXPROCS 环境变量或 runtime.GOMAXPROCS() 函数调整。
goroutine 之间的通信主要通过 channel 实现,遵循"不要通过共享内存来通信,而要通过通信来共享内存"的哲学。
`,
"channel-patterns": `
Go 的 channel 有多种使用模式。无缓冲 channel(make(chan T))要求发送方和接收方同时就绪。
有缓冲 channel(make(chan T, N))允许发送方在缓冲区未满时不阻塞。
常见的模式包括:fan-in(多路复用)、fan-out(扇出分发)、pipeline(管道处理)和 worker pool(工作池)。
select 语句用于在多个 channel 操作之间进行选择,类似于多路复用器。
使用 close() 关闭 channel 后,接收方可以通过 v, ok := <-ch 的形式检测 channel 是否已关闭。
注意:只能由发送方关闭 channel,向已关闭的 channel 发送数据会触发 panic。
`,
"context-package": `
context 包是 Go 并发编程中不可或缺的工具。它主要解决三个问题:超时控制、取消传播和请求级别数据传递。
context.Background() 创建根上下文,context.TODO() 用于还不确定使用哪个上下文时。
context.WithCancel 创建可取消的上下文,调用 cancel() 后所有子上下文都会被取消。
context.WithTimeout 和 context.WithDeadline 创建带超时的上下文,超时后自动取消。
context.WithValue 用于在上下文中传递键值对数据,但不应滥用——它不是用来传递函数参数的。
最佳实践:context 应该作为函数的第一个参数传递,且不应该存储在结构体中。
`,
}
fmt.Println("📚 正在导入知识库...")
for source, text := range documents {
if err := rag.IngestDocument(ctx, text, source); err != nil {
log.Fatalf("导入文档 '%s' 失败: %v", source, err)
}
}
fmt.Println("✅ 知识库导入完成!")
// 测试 RAG 查询
questions := []string{
"goroutine 的 GMP 调度模型是怎么工作的?",
"如何优雅地关闭 channel?有什么注意事项?",
"context.WithTimeout 和 context.WithCancel 有什么区别?",
}
for _, q := range questions {
answer, err := rag.Query(ctx, q, 2)
if err != nil {
log.Printf("查询失败: %v", err)
continue
}
fmt.Printf("\n💬 回答:\n%s\n", answer)
fmt.Println(strings.Repeat("═", 60))
}
}
六、AI Agent:让 AI 自主行动
AI Agent 是当前最热门的应用模式。一个 Agent 不仅能对话,还能自主规划、使用工具、观察结果并迭代决策,直到完成任务。
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"strings"
"time"
"example.com/aiapp/ai"
)
// Agent 是一个自主决策的 AI 代理
type Agent struct {
client *ai.Client
tools []ai.Tool
executors map[string]ToolExecutor
memory []ai.Message
maxSteps int
}
// ToolExecutor 是工具执行函数
type ToolExecutor func(args map[string]interface{}) (string, error)
// AgentConfig Agent 配置
type AgentConfig struct {
SystemPrompt string
Tools []ai.Tool
Executors map[string]ToolExecutor
MaxSteps int
Model string
}
// NewAgent 创建新的 Agent
func NewAgent(client *ai.Client, config AgentConfig) *Agent {
if config.MaxSteps == 0 {
config.MaxSteps = 10
}
return &Agent{
client: client,
tools: config.Tools,
executors: config.Executors,
maxSteps: config.MaxSteps,
memory: []ai.Message{
{Role: "system", Content: config.SystemPrompt},
},
}
}
// AgentStep Agent 的单步执行结果
type AgentStep struct {
StepNumber int
Thought string
ToolCalls []ai.ToolCall
Results []string
FinalAnswer string
Done bool
}
// Run 执行 Agent 的主循环
func (a *Agent) Run(ctx context.Context, task string) (*AgentStep, error) {
a.memory = append(a.memory, ai.Message{
Role: "user",
Content: task,
})
fmt.Println(strings.Repeat("━", 60))
fmt.Printf("🎯 任务: %s\n", task)
fmt.Println(strings.Repeat("━", 60))
for step := 1; step <= a.maxSteps; step++ {
fmt.Printf("\n🔄 步骤 %d/%d\n", step, a.maxSteps)
// 调用 LLM
resp, err := a.client.Chat(ctx, &ai.ChatRequest{
Messages: a.memory,
Tools: a.tools,
Temperature: 0.2,
MaxTokens: 4000,
})
if err != nil {
return nil, fmt.Errorf("LLM 调用失败: %w", err)
}
msg := resp.Choices[0].Message
a.memory = append(a.memory, msg)
agentStep := &AgentStep{
StepNumber: step,
Thought: msg.Content,
}
// 如果没有工具调用,说明 Agent 已经准备好最终回答
if len(msg.ToolCalls) == 0 {
agentStep.Done = true
agentStep.FinalAnswer = msg.Content
fmt.Printf("✅ 最终回答:\n%s\n", msg.Content)
return agentStep, nil
}
// 执行工具调用
agentStep.ToolCalls = msg.ToolCalls
for _, tc := range msg.ToolCalls {
fmt.Printf(" 🔧 调用: %s\n", tc.Function.Name)
var args map[string]interface{}
if err := json.Unmarshal([]byte(tc.Function.Arguments), &args); err != nil {
result := fmt.Sprintf("参数解析失败: %v", err)
a.memory = append(a.memory, ai.Message{
Role: "tool",
Content: result,
ToolCallID: tc.ID,
})
agentStep.Results = append(agentStep.Results, result)
continue
}
executor, ok := a.executors[tc.Function.Name]
if !ok {
result := fmt.Sprintf("未知工具: %s", tc.Function.Name)
a.memory = append(a.memory, ai.Message{
Role: "tool",
Content: result,
ToolCallID: tc.ID,
})
agentStep.Results = append(agentStep.Results, result)
continue
}
result, err := executor(args)
if err != nil {
result = fmt.Sprintf("执行失败: %v", err)
}
fmt.Printf(" 📋 结果: %s\n", truncateStr(result, 200))
a.memory = append(a.memory, ai.Message{
Role: "tool",
Content: result,
ToolCallID: tc.ID,
})
agentStep.Results = append(agentStep.Results, result)
}
return agentStep, nil
}
return nil, fmt.Errorf("超过最大步骤数 %d", a.maxSteps)
}
func truncateStr(s string, maxLen int) string {
s = strings.ReplaceAll(s, "\n", " ")
if len(s) <= maxLen {
return s
}
return s[:maxLen] + "..."
}
// 定义工具
var agentTools = []ai.Tool{
{
Type: "function",
Function: ai.FunctionDef{
Name: "read_file",
Description: "读取指定路径的文件内容",
Parameters: json.RawMessage(`{
"type": "object",
"properties": {
"path": {"type": "string", "description": "文件路径"}
},
"required": ["path"]
}`),
},
},
{
Type: "function",
Function: ai.FunctionDef{
Name: "analyze_code",
Description: "分析代码的质量、复杂度和潜在问题",
Parameters: json.RawMessage(`{
"type": "object",
"properties": {
"code": {"type": "string", "description": "要分析的代码"},
"language": {"type": "string", "description": "编程语言"}
},
"required": ["code"]
}`),
},
},
{
Type: "function",
Function: ai.FunctionDef{
Name: "run_tests",
Description: "运行指定文件或包的测试",
Parameters: json.RawMessage(`{
"type": "object",
"properties": {
"target": {"type": "string", "description": "测试目标(文件或包路径)"}
},
"required": ["target"]
}`),
},
},
}
// 模拟工具执行器
var executors = map[string]ToolExecutor{
"read_file": func(args map[string]interface{}) (string, error) {
path, _ := args["path"].(string)
// 模拟读取文件
return fmt.Sprintf(`package main
import "fmt"
// Fibonacci 计算斐波那契数列
func Fibonacci(n int) int {
if n <= 1 {
return n
}
return Fibonacci(n-1) + Fibonacci(n-2)
}
func main() {
for i := 0; i < 10; i++ {
fmt.Printf("Fibonacci(%%d) = %%d\n", i, Fibonacci(i))
}
}`, ), nil
},
"analyze_code": func(args map[string]interface{}) (string, error) {
code, _ := args["code"].(string)
lines := strings.Count(code, "\n") + 1
return fmt.Sprintf(`分析结果:
- 代码行数: %d
- 时间复杂度: O(2^n)(递归实现,存在大量重复计算)
- 建议: 使用动态规划或备忘录模式优化
- 潜在问题: n 较大时会导致栈溢出
- 安全评分: 良好(无安全漏洞)`, lines), nil
},
"run_tests": func(args map[string]interface{}) (string, error) {
target, _ := args["target"].(string)
return fmt.Sprintf(`=== RUN TestFibonacci
--- PASS: TestFibonacci (0.00s)
=== RUN TestFibonacciLarge
--- FAIL: TestFibonacciLarge (2.31s)
fibonacci_test.go:15: Fibonacci(50) 超时
FAIL
coverage: 66.7%% of statements
FAIL %s 2.315s`, target), nil
},
}
func main() {
client := ai.NewOpenAIClient(os.Getenv("OPENAI_API_KEY"),
ai.WithTimeout(5*time.Minute))
systemPrompt := `你是一个专业的代码审查和优化 Agent。你的工作流程是:
1. 读取需要审查的代码文件
2. 分析代码的质量、性能和安全性
3. 运行测试以验证代码的正确性
4. 根据分析和测试结果,给出综合评审报告
请一步一步地完成这些任务,每一步都要详细说明你的发现和建议。`
agent := NewAgent(client, AgentConfig{
SystemPrompt: systemPrompt,
Tools: agentTools,
Executors: executors,
MaxSteps: 5,
})
ctx := context.Background()
// 启动 Agent 执行任务
result, err := agent.Run(ctx, "请审查 fibonacci.go 文件,分析代码质量并运行测试。")
if err != nil {
log.Fatalf("Agent 执行失败: %v", err)
}
if result.Done {
fmt.Println("\n" + strings.Repeat("━", 60))
fmt.Println("🎉 Agent 任务完成!")
}
}
七、AI API 的限流与重试
在生产环境中调用 AI API,限流(Rate Limiting)和重试机制是必不可少的。AI API 通常按请求数和 token 数进行限制:
package main
import (
"context"
"fmt"
"log"
"math"
"math/rand"
"net/http"
"os"
"sync"
"time"
"example.com/aiapp/ai"
)
// RateLimiter 令牌桶限流器
type RateLimiter struct {
mu sync.Mutex
tokens float64
maxTokens float64
refillRate float64 // 每秒补充的令牌数
lastRefill time.Time
}
// NewRateLimiter 创建限流器
func NewRateLimiter(requestsPerMinute int) *RateLimiter {
rate := float64(requestsPerMinute) / 60.0
return &RateLimiter{
tokens: float64(requestsPerMinute),
maxTokens: float64(requestsPerMinute),
refillRate: rate,
lastRefill: time.Now(),
}
}
// Wait 等待获取一个令牌
func (rl *RateLimiter) Wait(ctx context.Context) error {
for {
rl.mu.Lock()
rl.refill()
if rl.tokens >= 1 {
rl.tokens--
rl.mu.Unlock()
return nil
}
// 计算需要等待的时间
waitTime := time.Duration(float64(time.Second) * (1 - rl.tokens) / rl.refillRate)
rl.mu.Unlock()
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(waitTime):
// 重试
}
}
}
func (rl *RateLimiter) refill() {
now := time.Now()
elapsed := now.Sub(rl.lastRefill).Seconds()
rl.tokens = math.Min(rl.maxTokens, rl.tokens+elapsed*rl.refillRate)
rl.lastRefill = now
}
// RetryConfig 重试配置
type RetryConfig struct {
MaxRetries int
InitialDelay time.Duration
MaxDelay time.Duration
RetryOnStatus []int
}
// DefaultRetryConfig 默认重试配置
var DefaultRetryConfig = RetryConfig{
MaxRetries: 3,
InitialDelay: 1 * time.Second,
MaxDelay: 30 * time.Second,
RetryOnStatus: []int{429, 500, 502, 503, 504},
}
// RetryableClient 带限流和重试的 AI 客户端
type RetryableClient struct {
client *ai.Client
rateLimiter *RateLimiter
retryConfig RetryConfig
}
// NewRetryableClient 创建可重试的客户端
func NewRetryableClient(apiKey string, rpm int) *RetryableClient {
return &RetryableClient{
client: ai.NewOpenAIClient(apiKey),
rateLimiter: NewRateLimiter(rpm),
retryConfig: DefaultRetryConfig,
}
}
// ChatWithRetry 带限流和重试的聊天请求
func (rc *RetryableClient) ChatWithRetry(ctx context.Context, req *ai.ChatRequest) (*ai.ChatResponse, error) {
var lastErr error
for attempt := 0; attempt <= rc.retryConfig.MaxRetries; attempt++ {
// 限流等待
if err := rc.rateLimiter.Wait(ctx); err != nil {
return nil, fmt.Errorf("限流等待失败: %w", err)
}
// 发送请求
resp, err := rc.client.Chat(ctx, req)
if err == nil {
if attempt > 0 {
log.Printf("✅ 第 %d 次重试成功", attempt)
}
return resp, nil
}
lastErr = err
// 判断是否需要重试
if attempt < rc.retryConfig.MaxRetries {
delay := rc.calculateDelay(attempt)
log.Printf("⚠️ 请求失败 (尝试 %d/%d): %v,%v 后重试...",
attempt+1, rc.retryConfig.MaxRetries+1, err, delay)
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-time.After(delay):
}
}
}
return nil, fmt.Errorf("重试 %d 次后仍然失败: %w", rc.retryConfig.MaxRetries, lastErr)
}
// 指数退避 + 抖动
func (rc *RetryableClient) calculateDelay(attempt int) time.Duration {
delay := rc.retryConfig.InitialDelay * time.Duration(math.Pow(2, float64(attempt)))
if delay > rc.retryConfig.MaxDelay {
delay = rc.retryConfig.MaxDelay
}
// 添加随机抖动,避免惊群效应
jitter := time.Duration(rand.Int63n(int64(delay) / 2))
return delay + jitter
}
func main() {
// 每分钟最多 10 个请求
client := NewRetryableClient(os.Getenv("OPENAI_API_KEY"), 10)
ctx := context.Background()
// 模拟并发请求
var wg sync.WaitGroup
results := make(chan string, 5)
questions := []string{
"Go 1.24 有什么新特性?",
"解释一下 Go 的逃逸分析",
"什么是 Go 的 GC Pacer?",
"Go 中如何实现优雅关闭?",
"如何用 Go 实现一个简易的消息队列?",
}
for i, q := range questions {
wg.Add(1)
go func(idx int, question string) {
defer wg.Done()
start := time.Now()
resp, err := client.ChatWithRetry(ctx, &ai.ChatRequest{
Messages: []ai.Message{
{Role: "system", Content: "简洁回答,不超过 100 字。"},
{Role: "user", Content: question},
},
MaxTokens: 200,
Temperature: 0.5,
})
elapsed := time.Since(start)
if err != nil {
results <- fmt.Sprintf("[Q%d] ❌ 失败 (%v): %v", idx+1, elapsed, err)
return
}
results <- fmt.Sprintf("[Q%d] ✅ 成功 (%v, tokens=%d): %s",
idx+1, elapsed, resp.Usage.TotalTokens,
truncate(resp.Choices[0].Message.Content, 80))
}(i, q)
}
go func() {
wg.Wait()
close(results)
}()
for result := range results {
fmt.Println(result)
}
}
func truncate(s string, maxLen int) string {
if len(s) <= maxLen {
return s
}
return s[:maxLen] + "..."
}
八、Prompt Engineering in Go
好的 Prompt 是 AI 应用的灵魂。让我们封装一个 Prompt 模板系统:
package main
import (
"bytes"
"context"
"fmt"
"log"
"os"
"text/template"
"example.com/aiapp/ai"
)
// PromptTemplate 是 prompt 模板
type PromptTemplate struct {
Name string
System string
User string
Variables map[string]string
}
// Render 渲染模板
func (pt *PromptTemplate) Render(vars map[string]string) (system, user string, err error) {
sysTmpl, err := template.New("system").Parse(pt.System)
if err != nil {
return "", "", fmt.Errorf("解析系统模板失败: %w", err)
}
usrTmpl, err := template.New("user").Parse(pt.User)
if err != nil {
return "", "", fmt.Errorf("解析用户模板失败: %w", err)
}
var sysBuf, usrBuf bytes.Buffer
if err := sysTmpl.Execute(&sysBuf, vars); err != nil {
return "", "", err
}
if err := usrTmpl.Execute(&usrBuf, vars); err != nil {
return "", "", err
}
return sysBuf.String(), usrBuf.String(), nil
}
// 预定义模板集合
var Templates = map[string]*PromptTemplate{
"code_review": {
Name: "代码审查",
System: `你是一位资深的 {{.Language}} 代码审查专家。请从以下维度审查代码:
1. 正确性:逻辑是否正确,边界条件是否处理
2. 性能:是否有明显的性能问题
3. 可读性:命名是否清晰,结构是否合理
4. 安全性:是否有潜在的安全漏洞
5. 最佳实践:是否遵循 {{.Language}} 的惯用写法`,
User: `请审查以下代码:
` + "```{{.Language}}" + `
{{.Code}}
` + "```" + `
{{if .Context}}额外上下文:{{.Context}}{{end}}`,
},
"summarize": {
Name: "文本摘要",
System: `你是一个专业的文本摘要助手。请用{{.Style}}的风格,
将以下内容总结为{{.Length}},包含关键要点和结论。`,
User: `待总结的文本:
{{.Text}}
请提供结构化的摘要。`,
},
"translate": {
Name: "技术翻译",
System: `你是一位专业的技术文档翻译专家。请将以下{{.SourceLang}}技术文本翻译成{{.TargetLang}}。
要求:
- 保持技术术语的准确性
- 符合{{.TargetLang}}的表达习惯
- 保留代码示例和格式
- 专业术语首次出现时在括号中注明原文`,
User: `{{.Text}}`,
},
}
func main() {
client := ai.NewOpenAIClient(os.Getenv("OPENAI_API_KEY"))
ctx := context.Background()
// 使用代码审查模板
reviewTemplate := Templates["code_review"]
system, user, err := reviewTemplate.Render(map[string]string{
"Language": "go",
"Code": `func ProcessUsers(users []User) error {
for i := 0; i <= len(users); i++ {
go func(u User) {
db.Save(u)
if u.Email != "" {
SendEmail(u.Email, "Welcome!")
}
}(users[i])
}
return nil
}`,
"Context": "这是用户注册流程中的一部分",
})
if err != nil {
log.Fatalf("渲染模板失败: %v", err)
}
resp, err := client.Chat(ctx, &ai.ChatRequest{
Messages: []ai.Message{
{Role: "system", Content: system},
{Role: "user", Content: user},
},
Temperature: 0.3,
MaxTokens: 2000,
})
if err != nil {
log.Fatalf("调用失败: %v", err)
}
fmt.Println("📝 代码审查结果:")
fmt.Println(resp.Choices[0].Message.Content)
// 使用摘要模板
fmt.Println("\n" + "═" + "\n")
summarizeTemplate := Templates["summarize"]
system2, user2, err := summarizeTemplate.Render(map[string]string{
"Style": "简洁专业",
"Length": "3-5 个要点",
"Text": `Go 1.24 于 2025 年 2 月正式发布。这个版本带来了多项重要更新:
weak 包提供了弱引用支持,允许在不阻止垃圾回收的情况下引用对象。
新的 rand/v2 包取代了旧的 math/rand,提供了更好的随机数生成器。
工具链方面的改进包括更快的编译速度和改进的 PGO 支持。
net/http 的 ServeMux 获得了更多的功能增强。
此外,Go 1.24 还改进了错误消息,使得调试更加容易。
性能方面,GC 暂停时间进一步降低,对于延迟敏感的应用来说是个好消息。`,
})
if err != nil {
log.Fatalf("渲染模板失败: %v", err)
}
resp2, err := client.Chat(ctx, &ai.ChatRequest{
Messages: []ai.Message{
{Role: "system", Content: system2},
{Role: "user", Content: user2},
},
Temperature: 0.3,
MaxTokens: 500,
})
if err != nil {
log.Fatalf("调用失败: %v", err)
}
fmt.Println("📋 文本摘要:")
fmt.Println(resp2.Choices[0].Message.Content)
}
结语
Go 与 AI 的结合正在变得越来越自然。虽然 Python 在模型训练领域一骑绝尘,但在AI 应用开发这个更大的舞台上,Go 有着不可替代的优势:
- 部署简单:一个二进制文件搞定,不需要复杂的 Python 环境
- 性能卓越:高并发场景下的 AI 网关、代理服务,Go 是天然的选择
- 类型安全:编译期就能发现很多 AI API 调用的参数错误
- 生态丰富:HTTP 客户端、数据库驱动、消息队列等基础设施成熟
本文从 API 调用到 RAG 系统再到 AI Agent,系统地展示了用 Go 构建智能应用的完整路径。希望这些代码示例能成为你 AI 项目的起点。
记住,最好的 AI 应用不是最复杂的模型,而是最懂用户需求的应用。技术只是手段,解决问题才是目的。祝你在 Go + AI 的道路上一路顺风!🚀
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。