乐于分享
好东西不私藏

Go AI代码助手:自动生成代码的AI工具

Go AI代码助手:自动生成代码的AI工具

AI代码助手正在改变开发者的工作方式,从GitHub Copilot到Cursor,AI辅助编程已成为趋势。本文将详细讲解如何使用Go构建一个AI代码助手,实现代码生成、代码解释、Bug修复等功能。

一、AI代码助手架构

1.1 整体架构设计

/*
AI代码助手核心架构:

┌─────────────────────────────────────────────────────────────────┐
│                         IDE插件/CLI                              │
│                    代码上下文、用户指令                           │
└─────────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────┐
│                      代码助手服务                                │
│              上下文处理、Prompt构建、结果解析                     │
└─────────────────────────────────────────────────────────────────┘
                                │
        ┌───────────────────────┼───────────────────────┐
        ▼                       ▼                       ▼
┌───────────────┐     ┌───────────────┐     ┌───────────────┐
│   OpenAI      │     │   代码索引     │     │   知识库      │
│   GPT-4       │     │   语义搜索     │     │   文档/示例   │
└───────────────┘     └───────────────┘     └───────────────┘

核心功能:
1. 代码生成:根据注释/描述生成代码
2. 代码补全:智能补全代码片段
3. 代码解释:解释代码功能和逻辑
4. Bug修复:分析并修复代码问题
5. 代码重构:优化代码结构和性能
*/

1.2 数据模型

package model

// CodeContext 代码上下文
type CodeContext struct {
 FilePath    string`json:"file_path"`
 Language    string`json:"language"`
 CurrentCode string`json:"current_code"`
 CursorLine  int`json:"cursor_line"`
 Selection   string`json:"selection"`
 Imports     []string`json:"imports"`
 Functions   []string`json:"functions"`
}

// CodeRequest 代码请求
type CodeRequest struct {
 Action      string`json:"action"`// generate, complete, explain, fix, refactor
 Instruction string`json:"instruction"`
 Context     *CodeContext `json:"context"`
}

// CodeResponse 代码响应
type CodeResponse struct {
 Code        string`json:"code"`
 Explanation string`json:"explanation"`
 Suggestions []string`json:"suggestions"`
}

二、代码生成服务

2.1 Prompt构建器

package assistant

import (
"fmt"
"strings"
)

// PromptBuilder Prompt构建器
type PromptBuilder struct {
 templates map[string]string
}

// NewPromptBuilder 创建Prompt构建器
funcNewPromptBuilder() *PromptBuilder {
return &PromptBuilder{
  templates: map[string]string{
"generate"`你是一个专业的%s程序员。请根据以下要求生成代码:

要求:%s

当前文件:%s
已有代码上下文:
%s

请生成符合以下标准的代码:
1. 代码简洁、可读性强
2. 遵循%s最佳实践
3. 包含必要的错误处理
4. 添加适当的注释

只返回代码,不要解释。`
,

"complete"`你是一个代码补全助手。请补全以下%s代码:

文件:%s
当前代码:
%s

光标位置在第%d行。请补全代码,使其完整且正确。
只返回补全的代码部分。`
,

"explain"`请解释以下%s代码的功能和逻辑:

%s

请从以下方面解释:
1. 代码的主要功能
2. 关键逻辑和算法
3. 输入输出说明
4. 潜在的问题或改进建议`
,

"fix"`以下%s代码存在问题,请修复:

%s

错误信息:%s

请:
1. 分析问题原因
2. 提供修复后的代码
3. 解释修复内容`
,

"refactor"`请重构以下%s代码,使其更加优雅和高效:

%s

重构要求:%s

请提供重构后的代码,并说明改进点。`
,
  },
 }
}

// BuildPrompt 构建Prompt
func(b *PromptBuilder)BuildPrompt(req *CodeRequest)string {
 ctx := req.Context

switch req.Action {
case"generate":
return fmt.Sprintf(b.templates["generate"],
   ctx.Language, req.Instruction, ctx.FilePath, ctx.CurrentCode, ctx.Language)
case"complete":
return fmt.Sprintf(b.templates["complete"],
   ctx.Language, ctx.FilePath, ctx.CurrentCode, ctx.CursorLine)
case"explain":
  code := ctx.Selection
if code == "" {
   code = ctx.CurrentCode
  }
return fmt.Sprintf(b.templates["explain"], ctx.Language, code)
case"fix":
return fmt.Sprintf(b.templates["fix"],
   ctx.Language, ctx.CurrentCode, req.Instruction)
case"refactor":
return fmt.Sprintf(b.templates["refactor"],
   ctx.Language, ctx.CurrentCode, req.Instruction)
default:
return req.Instruction
 }
}

2.2 代码助手服务

// CodeAssistant 代码助手
type CodeAssistant struct {
 client        *openai.Client
 promptBuilder *PromptBuilder
 codeIndex     *CodeIndex
}

// NewCodeAssistant 创建代码助手
funcNewCodeAssistant(apiKey string) *CodeAssistant {
return &CodeAssistant{
  client:        openai.NewClient(apiKey),
  promptBuilder: NewPromptBuilder(),
  codeIndex:     NewCodeIndex(),
 }
}

// Process 处理请求
func(a *CodeAssistant)Process(ctx context.Context, req *CodeRequest)(*CodeResponse, error) {
// 构建Prompt
 prompt := a.promptBuilder.BuildPrompt(req)

// 获取相关代码片段
if req.Action == "generate" {
  relatedCode := a.codeIndex.Search(req.Instruction, 3)
iflen(relatedCode) > 0 {
   prompt += "\n\n参考代码示例:\n" + strings.Join(relatedCode, "\n---\n")
  }
 }

// 调用AI
 messages := []openai.ChatMessage{
  {Role: "system", Content: "你是一个专业的编程助手,擅长生成高质量的代码。"},
  {Role: "user", Content: prompt},
 }

 resp, err := a.client.CreateChatCompletion(ctx, &openai.ChatCompletionRequest{
  Model:       "gpt-4",
  Messages:    messages,
  Temperature: 0.2// 低温度保证代码准确性
  MaxTokens:   2000,
 })
if err != nil {
returnnil, err
 }

 content := resp.Choices[0].Message.Content

// 解析响应
return a.parseResponse(req.Action, content), nil
}

// parseResponse 解析响应
func(a *CodeAssistant)parseResponse(action, content string) *CodeResponse {
 resp := &CodeResponse{}

switch action {
case"generate""complete""fix""refactor":
// 提取代码块
  resp.Code = extractCodeBlock(content)
  resp.Explanation = extractExplanation(content)
case"explain":
  resp.Explanation = content
 }

return resp
}

// extractCodeBlock 提取代码块
funcextractCodeBlock(content string)string {
// 匹配 ```language ... ``` 格式
 start := strings.Index(content, "```")
if start == -1 {
return content
 }

// 跳过语言标识
 start = strings.Index(content[start:], "\n") + start + 1
 end := strings.LastIndex(content, "```")

if end > start {
return strings.TrimSpace(content[start:end])
 }

return content
}

三、代码索引与搜索

3.1 代码索引服务

package index

import (
"context"
"go/ast"
"go/parser"
"go/token"
"path/filepath"
"strings"
)

// CodeIndex 代码索引
type CodeIndex struct {
 client     *openai.Client
 embeddings map[string][]float64
 snippets   map[string]*CodeSnippet
}

// CodeSnippet 代码片段
type CodeSnippet struct {
 ID       string
 FilePath string
 Name     string
 Type     string// function, struct, interface
 Code     string
 Doc      string
}

// NewCodeIndex 创建代码索引
funcNewCodeIndex(client *openai.Client) *CodeIndex {
return &CodeIndex{
  client:     client,
  embeddings: make(map[string][]float64),
  snippets:   make(map[string]*CodeSnippet),
 }
}

// IndexDirectory 索引目录
func(idx *CodeIndex)IndexDirectory(ctx context.Context, dir string)error {
return filepath.Walk(dir, func(path string, info os.FileInfo, err error)error {
if err != nil || info.IsDir() || !strings.HasSuffix(path, ".go") {
returnnil
  }
return idx.indexFile(ctx, path)
 })
}

// indexFile 索引文件
func(idx *CodeIndex)indexFile(ctx context.Context, path string)error {
 fset := token.NewFileSet()
 node, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
if err != nil {
return err
 }

// 提取函数
for _, decl := range node.Decls {
if fn, ok := decl.(*ast.FuncDecl); ok {
   snippet := &CodeSnippet{
    ID:       fmt.Sprintf("%s:%s", path, fn.Name.Name),
    FilePath: path,
    Name:     fn.Name.Name,
    Type:     "function",
    Code:     extractFuncCode(fset, fn),
    Doc:      extractDoc(fn.Doc),
   }
   idx.snippets[snippet.ID] = snippet
  }
 }

// 批量生成向量
return idx.generateEmbeddings(ctx)
}

// generateEmbeddings 生成向量
func(idx *CodeIndex)generateEmbeddings(ctx context.Context)error {
var texts []string
var ids []string

for id, snippet := range idx.snippets {
if _, exists := idx.embeddings[id]; !exists {
   texts = append(texts, snippet.Doc+" "+snippet.Code)
   ids = append(ids, id)
  }
 }

iflen(texts) == 0 {
returnnil
 }

// 批量获取向量
 resp, err := idx.client.CreateEmbedding(ctx, texts)
if err != nil {
return err
 }

for i, id := range ids {
  idx.embeddings[id] = resp.Data[i].Embedding
 }

returnnil
}

// Search 搜索相关代码
func(idx *CodeIndex)Search(query string, topK int) []string {
 ctx := context.Background()

// 获取查询向量
 resp, err := idx.client.CreateEmbedding(ctx, []string{query})
if err != nil {
returnnil
 }

 queryEmbedding := resp.Data[0].Embedding

// 计算相似度
type result struct {
  id    string
  score float64
 }
var results []result

for id, embedding := range idx.embeddings {
  score := cosineSimilarity(queryEmbedding, embedding)
  results = append(results, result{id, score})
 }

// 排序
 sort.Slice(results, func(i, j int)bool {
return results[i].score > results[j].score
 })

// 返回代码
var codes []string
for i := 0; i < topK && i < len(results); i++ {
if snippet, exists := idx.snippets[results[i].id]; exists {
   codes = append(codes, snippet.Code)
  }
 }

return codes
}

3.2 AST解析器

// ASTParser AST解析器
type ASTParser struct{}

// ParseGoFile 解析Go文件
func(p *ASTParser)ParseGoFile(path string)(*FileInfo, error) {
 fset := token.NewFileSet()
 node, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
if err != nil {
returnnil, err
 }

 info := &FileInfo{
  Path:      path,
  Package:   node.Name.Name,
  Imports:   make([]string0),
  Functions: make([]*FunctionInfo, 0),
  Structs:   make([]*StructInfo, 0),
 }

// 提取导入
for _, imp := range node.Imports {
  info.Imports = append(info.Imports, strings.Trim(imp.Path.Value, `"`))
 }

// 提取声明
for _, decl := range node.Decls {
switch d := decl.(type) {
case *ast.FuncDecl:
   info.Functions = append(info.Functions, p.parseFuncDecl(fset, d))
case *ast.GenDecl:
for _, spec := range d.Specs {
if ts, ok := spec.(*ast.TypeSpec); ok {
if st, ok := ts.Type.(*ast.StructType); ok {
      info.Structs = append(info.Structs, p.parseStructType(ts.Name.Name, st))
     }
    }
   }
  }
 }

return info, nil
}

type FileInfo struct {
 Path      string
 Package   string
 Imports   []string
 Functions []*FunctionInfo
 Structs   []*StructInfo
}

type FunctionInfo struct {
 Name       string
 Receiver   string
 Parameters []string
 Returns    []string
 Doc        string
}

type StructInfo struct {
 Name   string
 Fields []string
}

四、代码补全

4.1 智能补全服务

package completion

import (
"context"
"strings"
)

// CompletionService 补全服务
type CompletionService struct {
 client *openai.Client
}

// Complete 代码补全
func(s *CompletionService)Complete(ctx context.Context, req *CompletionRequest)(*CompletionResponse, error) {
// 构建上下文
 prefix := req.Code[:req.CursorOffset]
 suffix := req.Code[req.CursorOffset:]

 prompt := fmt.Sprintf(`补全以下%s代码,光标位置用<CURSOR>标记:

%s<CURSOR>%s

只返回需要插入的代码,不要包含已有代码。`
, req.Language, prefix, suffix)

 resp, err := s.client.CreateChatCompletion(ctx, &openai.ChatCompletionRequest{
  Model: "gpt-3.5-turbo",
  Messages: []openai.ChatMessage{
   {Role: "system", Content: "你是一个代码补全助手,只返回需要补全的代码。"},
   {Role: "user", Content: prompt},
  },
  Temperature: 0.1,
  MaxTokens:   500,
 })
if err != nil {
returnnil, err
 }

 completion := extractCodeBlock(resp.Choices[0].Message.Content)

return &CompletionResponse{
  Completion: completion,
  InsertText: completion,
 }, nil
}

type CompletionRequest struct {
 Code         string
 CursorOffset int
 Language     string
 FilePath     string
}

type CompletionResponse struct {
 Completion string
 InsertText string
}

// ❌ 错误示例:不考虑上下文
funcBadComplete(code string)string {
// 问题:没有考虑代码上下文,补全可能不准确
return"// TODO"
}

// ✅ 正确示例:基于上下文智能补全
funcGoodComplete(ctx context.Context, s *CompletionService, req *CompletionRequest)string {
 resp, err := s.Complete(ctx, req)
if err != nil {
return""
 }
return resp.Completion
}

4.2 多候选补全

// MultiComplete 多候选补全
func(s *CompletionService)MultiComplete(ctx context.Context, req *CompletionRequest, n int)([]*CompletionResponse, error) {
 prefix := req.Code[:req.CursorOffset]
 suffix := req.Code[req.CursorOffset:]

 prompt := fmt.Sprintf(`为以下%s代码提供%d个不同的补全选项:

%s<CURSOR>%s

以JSON数组格式返回,每个元素是一个补全选项。`
, req.Language, n, prefix, suffix)

 resp, err := s.client.CreateChatCompletion(ctx, &openai.ChatCompletionRequest{
  Model: "gpt-3.5-turbo",
  Messages: []openai.ChatMessage{
   {Role: "user", Content: prompt},
  },
  Temperature: 0.5// 稍高温度获得多样性
  MaxTokens:   1000,
 })
if err != nil {
returnnil, err
 }

// 解析多个补全选项
var completions []string
 json.Unmarshal([]byte(resp.Choices[0].Message.Content), &completions)

 results := make([]*CompletionResponse, len(completions))
for i, c := range completions {
  results[i] = &CompletionResponse{
   Completion: c,
   InsertText: c,
  }
 }

return results, nil
}

五、Bug修复与代码审查

5.1 Bug修复服务

package fix

import (
"context"
"fmt"
)

// BugFixService Bug修复服务
type BugFixService struct {
 client *openai.Client
}

// FixBug 修复Bug
func(s *BugFixService)FixBug(ctx context.Context, req *BugFixRequest)(*BugFixResponse, error) {
 prompt := fmt.Sprintf(`分析并修复以下%s代码中的Bug:

代码:
%s

错误信息:
%s

请:
1. 分析Bug原因
2. 提供修复后的完整代码
3. 解释修复内容`
, req.Language, req.Code, req.ErrorMessage)

 resp, err := s.client.CreateChatCompletion(ctx, &openai.ChatCompletionRequest{
  Model: "gpt-4",
  Messages: []openai.ChatMessage{
   {Role: "system", Content: "你是一个专业的代码调试专家。"},
   {Role: "user", Content: prompt},
  },
  Temperature: 0.2,
 })
if err != nil {
returnnil, err
 }

 content := resp.Choices[0].Message.Content

return &BugFixResponse{
  FixedCode:   extractCodeBlock(content),
  Explanation: extractExplanation(content),
  RootCause:   extractRootCause(content),
 }, nil
}

type BugFixRequest struct {
 Code         string
 ErrorMessage string
 Language     string
}

type BugFixResponse struct {
 FixedCode   string
 Explanation string
 RootCause   string
}

5.2 代码审查服务

// CodeReviewService 代码审查服务
type CodeReviewService struct {
 client *openai.Client
}

// Review 代码审查
func(s *CodeReviewService)Review(ctx context.Context, code string, language string)(*ReviewResult, error) {
 prompt := fmt.Sprintf(`请审查以下%s代码,从以下方面给出建议:

1. 代码质量和可读性
2. 潜在的Bug和安全问题
3. 性能优化建议
4. 最佳实践遵循情况

代码:
%s

请以JSON格式返回审查结果。`
, language, code)

 resp, err := s.client.CreateChatCompletion(ctx, &openai.ChatCompletionRequest{
  Model: "gpt-4",
  Messages: []openai.ChatMessage{
   {Role: "system", Content: "你是一个资深的代码审查专家。"},
   {Role: "user", Content: prompt},
  },
 })
if err != nil {
returnnil, err
 }

var result ReviewResult
 json.Unmarshal([]byte(resp.Choices[0].Message.Content), &result)

return &result, nil
}

type ReviewResult struct {
 Score       int`json:"score"`// 0-100
 Issues      []Issue  `json:"issues"`
 Suggestions []string`json:"suggestions"`
 Summary     string`json:"summary"`
}

type Issue struct {
 Line     int`json:"line"`
 Severity string`json:"severity"`// error, warning, info
 Message  string`json:"message"`
 Fix      string`json:"fix"`
}

六、完整API服务

6.1 代码助手API

package api

import (
"net/http"
"github.com/gin-gonic/gin"
)

// CodeAssistantAPI 代码助手API
type CodeAssistantAPI struct {
 assistant  *CodeAssistant
 completion *CompletionService
 bugFix     *BugFixService
 review     *CodeReviewService
}

// RegisterRoutes 注册路由
func(api *CodeAssistantAPI)RegisterRoutes(r *gin.RouterGroup) {
 r.POST("/generate", api.Generate)
 r.POST("/complete", api.Complete)
 r.POST("/explain", api.Explain)
 r.POST("/fix", api.Fix)
 r.POST("/review", api.Review)
}

// Generate 生成代码
// POST /api/v1/code/generate
func(api *CodeAssistantAPI)Generate(c *gin.Context) {
var req CodeRequest
if err := c.ShouldBindJSON(&req); err != nil {
  c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
 }

 req.Action = "generate"
 resp, err := api.assistant.Process(c.Request.Context(), &req)
if err != nil {
  c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
 }

 c.JSON(http.StatusOK, resp)
}

// Complete 代码补全
// POST /api/v1/code/complete
func(api *CodeAssistantAPI)Complete(c *gin.Context) {
var req CompletionRequest
if err := c.ShouldBindJSON(&req); err != nil {
  c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
 }

 resp, err := api.completion.Complete(c.Request.Context(), &req)
if err != nil {
  c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
 }

 c.JSON(http.StatusOK, resp)
}

// Fix 修复Bug
// POST /api/v1/code/fix
func(api *CodeAssistantAPI)Fix(c *gin.Context) {
var req BugFixRequest
if err := c.ShouldBindJSON(&req); err != nil {
  c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
 }

 resp, err := api.bugFix.FixBug(c.Request.Context(), &req)
if err != nil {
  c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
 }

 c.JSON(http.StatusOK, resp)
}

AI代码助手架构总结

功能
技术方案
关键点
代码生成
GPT-4 + Prompt
上下文感知、模板化
代码补全
流式响应
低延迟、多候选
代码索引
Embedding
语义搜索、相似代码
Bug修复
错误分析
根因分析、自动修复
代码审查
多维度评估
质量、安全、性能

💡 AI代码助手正在改变开发方式,掌握这套方案让你具备构建Copilot级工具的能力!