Go 与 AI:构建智能应用

深入探索如何用 Go 构建 AI 驱动的应用程序。从调用 OpenAI/Claude API、流式响应处理、Function Calling,到向量嵌入、RAG 实现、AI Agent 构建,全面覆盖 Go 与 AI 结合的最佳实践。

引言

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 的道路上一路顺风!🚀

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页