Sooua
登录
返回文章列表
OpenCode··5 分钟阅读

Go SDK 使用

flowchart TB

目标:在 Go 程序中程序化调用 OpenCode,构建自动化工具


SDK 架构


安装

go get github.com/anomalyco/opencode/sdk

基础用法

创建客户端

package main
 
import (
    "context"
    "fmt"
    "log"
    
    "github.com/anomalyco/opencode/sdk"
)
 
func main() {
    // 创建客户端
    client, err := sdk.New(sdk.Config{
        APIKey:    "your-api-key",
        BaseURL:   "https://api.opencode.ai", // 可选
        Model:     "anthropic/claude-sonnet-4-5",
        Timeout:   60 * time.Second,
    })
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()
    
    // 发送消息
    resp, err := client.Send(context.Background(), sdk.Message{
        Role:    sdk.RoleUser,
        Content: "请解释什么是依赖注入",
    })
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Println(resp.Content)
}

流式响应

func streamExample() {
    client, _ := sdk.New(sdk.Config{APIKey: "your-key"})
    
    stream, err := client.SendStream(context.Background(), sdk.Message{
        Content: "写一个快速排序算法",
    })
    if err != nil {
        log.Fatal(err)
    }
    
    // 实时接收片段
    for chunk := range stream {
        fmt.Print(chunk.Content)
    }
}

工具调用

注册自定义工具

// 注册工具
client.RegisterTool(sdk.Tool{
    Name:        "search_docs",
    Description: "搜索内部文档",
    Parameters: map[string]interface{}{
        "type": "object",
        "properties": map[string]interface{}{
            "query": map[string]interface{}{
                "type":        "string",
                "description": "搜索关键词",
            },
        },
        "required": []string{"query"},
    },
    Handler: func(ctx context.Context, args map[string]interface{}) (string, error) {
        query := args["query"].(string)
        results := searchInternalDocs(query)
        return fmt.Sprintf("搜索结果: %v", results), nil
    },
})
 
// 使用工具
resp, err := client.Send(context.Background(), sdk.Message{
    Content: "搜索关于认证的最新文档",
})

工具调用流程


并发使用

func concurrentRequests() {
    client, _ := sdk.New(sdk.Config{APIKey: "your-key"})
    
    questions := []string{
        "解释 REST API",
        "什么是 GraphQL",
        "比较 gRPC 和 REST",
    }
    
    var wg sync.WaitGroup
    results := make([]string, len(questions))
    
    for i, q := range questions {
        wg.Add(1)
        go func(index int, question string) {
            defer wg.Done()
            
            resp, err := client.Send(context.Background(), sdk.Message{
                Content: question,
            })
            if err != nil {
                results[index] = fmt.Sprintf("错误: %v", err)
                return
            }
            results[index] = resp.Content
        }(i, q)
    }
    
    wg.Wait()
    
    for i, r := range results {
        fmt.Printf("Q%d: %s\nA: %s\n\n", i+1, questions[i], r)
    }
}

完整示例:代码审查工具

package main
 
import (
    "context"
    "fmt"
    "os"
    "path/filepath"
    "strings"
    
    "github.com/anomalyco/opencode/sdk"
)
 
// CodeReviewer 自动代码审查工具
type CodeReviewer struct {
    client *sdk.Client
}
 
func NewCodeReviewer(apiKey string) (*CodeReviewer, error) {
    client, err := sdk.New(sdk.Config{
        APIKey: apiKey,
        Model:  "anthropic/claude-sonnet-4-5",
    })
    if err != nil {
        return nil, err
    }
    
    return &CodeReviewer{client: client}, nil
}
 
func (r *CodeReviewer) ReviewFile(ctx context.Context, filePath string) (string, error) {
    // 读取文件
    content, err := os.ReadFile(filePath)
    if err != nil {
        return "", err
    }
    
    // 构建审查提示
    prompt := fmt.Sprintf(`请审查以下代码,关注:
1. 潜在的 bug 和边界情况
2. 性能问题
3. 安全漏洞
4. 代码可读性和规范
 
文件: %s
 
代码:
%s
 
请用中文回复,格式:
- 🔴 严重: ...
- 🟡 警告: ...
- 🟢 建议: ...`, filePath, string(content))
    
    resp, err := r.client.Send(ctx, sdk.Message{Content: prompt})
    if err != nil {
        return "", err
    }
    
    return resp.Content, nil
}
 
func (r *CodeReviewer) ReviewDirectory(ctx context.Context, dir string, extensions []string) error {
    return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }
        
        if info.IsDir() {
            return nil
        }
        
        // 检查扩展名
        ext := filepath.Ext(path)
        for _, e := range extensions {
            if ext == e {
                fmt.Printf("正在审查: %s\n", path)
                review, err := r.ReviewFile(ctx, path)
                if err != nil {
                    fmt.Printf("  错误: %v\n", err)
                    continue
                }
                fmt.Println(review)
                fmt.Println(strings.Repeat("-", 50))
            }
        }
        
        return nil
    })
}
 
func main() {
    reviewer, err := NewCodeReviewer(os.Getenv("OPENCODE_API_KEY"))
    if err != nil {
        panic(err)
    }
    
    ctx := context.Background()
    
    // 审查单个文件
    result, _ := reviewer.ReviewFile(ctx, "src/auth.ts")
    fmt.Println(result)
    
    // 审查整个目录
    reviewer.ReviewDirectory(ctx, "./src", []string{".ts", ".tsx"})
}

错误处理

func robustExample() {
    client, err := sdk.New(sdk.Config{
        APIKey:  "your-key",
        Timeout: 30 * time.Second,
        Retries: 3, // 自动重试
    })
    if err != nil {
        log.Fatal("创建客户端失败:", err)
    }
    
    ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
    defer cancel()
    
    resp, err := client.Send(ctx, sdk.Message{
        Content: "分析这段代码",
    })
    if err != nil {
        switch {
        case errors.Is(err, sdk.ErrRateLimited):
            log.Println("请求过于频繁,请稍后再试")
        case errors.Is(err, sdk.ErrInvalidAPIKey):
            log.Fatal("API 密钥无效")
        case errors.Is(err, context.DeadlineExceeded):
            log.Println("请求超时")
        default:
            log.Printf("未知错误: %v\n", err)
        }
        return
    }
    
    fmt.Println(resp.Content)
}

使用场景

场景实现思路
CI/CD 代码审查GitHub Action 调用 SDK,PR 自动审查
IDE 插件编辑器插件调用 SDK 提供 AI 补全
自动化文档扫描代码自动生成 API 文档
智能测试根据代码生成测试用例
代码迁移批量将代码从一种语言转为另一种

性能优化

// 连接池
client, _ := sdk.New(sdk.Config{
    APIKey:      "your-key",
    MaxConns:    10,
    IdleTimeout: 30 * time.Second,
})
 
// 批量请求(节省 Token)
batchResp, _ := client.SendBatch(context.Background(), []sdk.Message{
    {Content: "问题1"},
    {Content: "问题2"},
    {Content: "问题3"},
})

下一篇:19. 服务器部署

分享

评论

登录 后参与讨论。

加载中…

相关文章