initial: project structure and documentation setup
- Create project directory structure - Initialize Go module - Set up development documentation (why.md, taolun.md, changelog.md, memory.md, AGENTS.md) - Configure YAML config template - Set up .gitignore and .env.example - Design OOP architecture with factory and strategy patterns Version: 0.0.1
This commit is contained in:
767
AGENTS.md
Normal file
767
AGENTS.md
Normal file
@@ -0,0 +1,767 @@
|
||||
# AGENTS.md - YOYO翻译工具开发指南
|
||||
|
||||
## 项目概述
|
||||
YOYO是一个命令行翻译工具,使用Go语言编写,采用面向对象设计模式。它通过调用在线大模型API,结合不同的Prompt配置,实现多样化的翻译特色。
|
||||
|
||||
## OOP设计模式
|
||||
|
||||
### 核心类设计
|
||||
项目采用以下面向对象设计,结合工厂模式和策略模式:
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ Config │ │ Provider │ │ Translator │
|
||||
│ (全局配置) │──────│ (厂商接口) │──────│ (核心翻译) │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
│ │ │
|
||||
│ │ │
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ ConfigLoader │ │ ProviderFactory │ │ TranslationTask │
|
||||
│ (配置加载) │ │ (工厂模式) │ │ (任务管理) │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
```
|
||||
|
||||
### 1. 全局配置类 (Config)
|
||||
负责读取YAML配置文件,提供默认值。
|
||||
|
||||
```go
|
||||
// internal/config/config.go
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Config 全局配置结构
|
||||
type Config struct {
|
||||
// 全局设置
|
||||
DefaultProvider string `yaml:"default_provider"`
|
||||
DefaultModel string `yaml:"default_model"`
|
||||
Timeout int `yaml:"timeout"` // 秒
|
||||
|
||||
// 厂商配置
|
||||
Providers map[string]ProviderConfig `yaml:"providers"`
|
||||
|
||||
// Prompt配置
|
||||
Prompts map[string]string `yaml:"prompts"`
|
||||
}
|
||||
|
||||
// ProviderConfig 厂商配置
|
||||
type ProviderConfig struct {
|
||||
APIHost string `yaml:"api_host"`
|
||||
APIKey string `yaml:"api_key"`
|
||||
Model string `yaml:"model"`
|
||||
Enabled bool `yaml:"enabled"`
|
||||
}
|
||||
|
||||
// ConfigLoader 配置加载器接口
|
||||
type ConfigLoader interface {
|
||||
Load(path string) (*Config, error)
|
||||
Save(config *Config, path string) error
|
||||
}
|
||||
|
||||
// YAMLConfigLoader YAML配置加载器实现
|
||||
type YAMLConfigLoader struct{}
|
||||
|
||||
// Load 加载YAML配置文件
|
||||
func (l *YAMLConfigLoader) Load(path string) (*Config, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取配置文件失败: %w", err)
|
||||
}
|
||||
|
||||
config := &Config{}
|
||||
if err := yaml.Unmarshal(data, config); err != nil {
|
||||
return nil, fmt.Errorf("解析配置文件失败: %w", err)
|
||||
}
|
||||
|
||||
// 设置默认值
|
||||
config.setDefaults()
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// setDefaults 设置默认值
|
||||
func (c *Config) setDefaults() {
|
||||
if c.DefaultProvider == "" {
|
||||
c.DefaultProvider = "siliconflow"
|
||||
}
|
||||
if c.Timeout <= 0 {
|
||||
c.Timeout = 30
|
||||
}
|
||||
// 为每个厂商设置默认值
|
||||
for name, provider := range c.Providers {
|
||||
if provider.Model == "" {
|
||||
provider.Model = "gpt-3.5-turbo"
|
||||
c.Providers[name] = provider
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 大模型厂商接口 (Provider)
|
||||
定义统一的厂商接口,采用策略模式。
|
||||
|
||||
```go
|
||||
// internal/provider/provider.go
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// Provider 厂商接口
|
||||
type Provider interface {
|
||||
// Translate 调用厂商API进行翻译
|
||||
Translate(ctx context.Context, req *TranslateRequest) (*TranslateResponse, error)
|
||||
|
||||
// Name 返回厂商名称
|
||||
Name() string
|
||||
|
||||
// Validate 验证配置是否有效
|
||||
Validate() error
|
||||
}
|
||||
|
||||
// TranslateRequest 翻译请求
|
||||
type TranslateRequest struct {
|
||||
Text string `json:"text"`
|
||||
FromLang string `json:"from_lang"`
|
||||
ToLang string `json:"to_lang"`
|
||||
Prompt string `json:"prompt"`
|
||||
Model string `json:"model"`
|
||||
Options map[string]interface{} `json:"options"`
|
||||
}
|
||||
|
||||
// TranslateResponse 翻译响应
|
||||
type TranslateResponse struct {
|
||||
Text string `json:"text"`
|
||||
FromLang string `json:"from_lang"`
|
||||
ToLang string `json:"to_lang"`
|
||||
Model string `json:"model"`
|
||||
Usage *Usage `json:"usage"`
|
||||
RawResponse []byte `json:"raw_response,omitempty"`
|
||||
}
|
||||
|
||||
// Usage 用量统计
|
||||
type Usage struct {
|
||||
PromptTokens int `json:"prompt_tokens"`
|
||||
CompletionTokens int `json:"completion_tokens"`
|
||||
TotalTokens int `json:"total_tokens"`
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 具体厂商实现示例
|
||||
|
||||
```go
|
||||
// internal/provider/siliconflow.go
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// SiliconFlowProvider 硅基流动厂商实现
|
||||
type SiliconFlowProvider struct {
|
||||
config ProviderConfig
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
// ProviderConfig 厂商配置(从config包导入)
|
||||
type ProviderConfig struct {
|
||||
APIHost string
|
||||
APIKey string
|
||||
Model string
|
||||
}
|
||||
|
||||
// NewSiliconFlowProvider 创建硅基流动厂商实例
|
||||
func NewSiliconFlowProvider(config ProviderConfig) *SiliconFlowProvider {
|
||||
return &SiliconFlowProvider{
|
||||
config: config,
|
||||
client: &http.Client{},
|
||||
}
|
||||
}
|
||||
|
||||
// Name 返回厂商名称
|
||||
func (p *SiliconFlowProvider) Name() string {
|
||||
return "siliconflow"
|
||||
}
|
||||
|
||||
// Validate 验证配置
|
||||
func (p *SiliconFlowProvider) Validate() error {
|
||||
if p.config.APIKey == "" {
|
||||
return fmt.Errorf("siliconflow: API key 不能为空")
|
||||
}
|
||||
if p.config.APIHost == "" {
|
||||
p.config.APIHost = "https://api.siliconflow.cn/v1"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Translate 调用硅基流动API
|
||||
func (p *SiliconFlowProvider) Translate(ctx context.Context, req *TranslateRequest) (*TranslateResponse, error) {
|
||||
// 实现具体的API调用逻辑
|
||||
// 1. 构建请求体
|
||||
// 2. 发送HTTP请求
|
||||
// 3. 解析响应
|
||||
|
||||
// 示例代码(简化)
|
||||
url := p.config.APIHost + "/chat/completions"
|
||||
requestBody := map[string]interface{}{
|
||||
"model": p.config.Model,
|
||||
"messages": []map[string]string{
|
||||
{"role": "user", "content": req.Text},
|
||||
},
|
||||
}
|
||||
|
||||
// 实际实现需要完整的HTTP客户端代码
|
||||
return &TranslateResponse{
|
||||
Text: "翻译结果",
|
||||
FromLang: req.FromLang,
|
||||
ToLang: req.ToLang,
|
||||
Model: p.config.Model,
|
||||
}, nil
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 工厂模式 - ProviderFactory
|
||||
|
||||
```go
|
||||
// internal/provider/factory.go
|
||||
package provider
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ProviderFactory 厂商工厂
|
||||
type ProviderFactory struct {
|
||||
providers map[string]func(ProviderConfig) Provider
|
||||
}
|
||||
|
||||
// NewProviderFactory 创建工厂实例
|
||||
func NewProviderFactory() *ProviderFactory {
|
||||
factory := &ProviderFactory{
|
||||
providers: make(map[string]func(ProviderConfig) Provider),
|
||||
}
|
||||
|
||||
// 注册所有厂商
|
||||
factory.Register("siliconflow", NewSiliconFlowProvider)
|
||||
factory.Register("volcano", NewVolcanoEngineProvider)
|
||||
factory.Register("national", NewNationalSupercomputingProvider)
|
||||
factory.Register("qwen", NewQwenProvider)
|
||||
factory.Register("openai", NewOpenAICompatibleProvider)
|
||||
|
||||
return factory
|
||||
}
|
||||
|
||||
// Register 注册厂商构造函数
|
||||
func (f *ProviderFactory) Register(name string, creator func(ProviderConfig) Provider) {
|
||||
f.providers[name] = creator
|
||||
}
|
||||
|
||||
// Create 创建厂商实例
|
||||
func (f *ProviderFactory) Create(name string, config ProviderConfig) (Provider, error) {
|
||||
creator, exists := f.providers[name]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("不支持的厂商: %s", name)
|
||||
}
|
||||
|
||||
provider := creator(config)
|
||||
if err := provider.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("厂商配置验证失败: %w", err)
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 核心翻译类 (Translator)
|
||||
|
||||
```go
|
||||
// internal/translator/translator.go
|
||||
package translator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Translator 核心翻译类
|
||||
type Translator struct {
|
||||
config *config.Config
|
||||
provider provider.Provider
|
||||
prompt *PromptManager
|
||||
}
|
||||
|
||||
// NewTranslator 创建翻译器实例
|
||||
func NewTranslator(config *config.Config, provider provider.Provider) *Translator {
|
||||
return &Translator{
|
||||
config: config,
|
||||
provider: provider,
|
||||
prompt: NewPromptManager(config.Prompts),
|
||||
}
|
||||
}
|
||||
|
||||
// Translate 执行翻译
|
||||
func (t *Translator) Translate(ctx context.Context, text string, options *TranslateOptions) (*TranslateResult, error) {
|
||||
// 设置超时
|
||||
timeoutCtx, cancel := context.WithTimeout(ctx, time.Duration(t.config.Timeout)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// 构建请求
|
||||
req := &provider.TranslateRequest{
|
||||
Text: text,
|
||||
FromLang: options.FromLang,
|
||||
ToLang: options.ToLang,
|
||||
Prompt: t.prompt.GetPrompt(options.PromptName),
|
||||
Model: t.selectModel(options.Model),
|
||||
}
|
||||
|
||||
// 调用厂商API
|
||||
resp, err := t.provider.Translate(timeoutCtx, req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("翻译失败: %w", err)
|
||||
}
|
||||
|
||||
// 构建结果
|
||||
return &TranslateResult{
|
||||
Original: text,
|
||||
Translated: resp.Text,
|
||||
FromLang: resp.FromLang,
|
||||
ToLang: resp.ToLang,
|
||||
Model: resp.Model,
|
||||
Usage: resp.Usage,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// selectModel 选择模型
|
||||
func (t *Translator) selectModel(model string) string {
|
||||
if model != "" {
|
||||
return model
|
||||
}
|
||||
return t.config.DefaultModel
|
||||
}
|
||||
|
||||
// TranslateOptions 翻译选项
|
||||
type TranslateOptions struct {
|
||||
FromLang string
|
||||
ToLang string
|
||||
PromptName string
|
||||
Model string
|
||||
Temperature float64
|
||||
}
|
||||
|
||||
// TranslateResult 翻译结果
|
||||
type TranslateResult struct {
|
||||
Original string
|
||||
Translated string
|
||||
FromLang string
|
||||
ToLang string
|
||||
Model string
|
||||
Usage *provider.Usage
|
||||
}
|
||||
```
|
||||
|
||||
### 6. 设计模式应用
|
||||
|
||||
#### 工厂模式
|
||||
- `ProviderFactory` 用于创建不同厂商实例
|
||||
- 统一创建接口,隐藏具体实现细节
|
||||
|
||||
#### 策略模式
|
||||
- `Provider` 接口定义统一行为
|
||||
- 每个厂商实现不同的API调用策略
|
||||
- 运行时动态选择策略
|
||||
|
||||
#### 单例模式
|
||||
- 全局配置通常只需要一个实例
|
||||
- 可通过包级变量或sync.Once实现
|
||||
|
||||
#### 依赖注入
|
||||
- `Translator` 依赖 `Provider` 接口,而不是具体实现
|
||||
- 通过构造函数注入依赖
|
||||
|
||||
## 项目结构
|
||||
```
|
||||
yoyo/
|
||||
├── cmd/
|
||||
│ └── yoyo/ # CLI入口
|
||||
├── internal/
|
||||
│ ├── config/ # 全局配置
|
||||
│ │ ├── config.go
|
||||
│ │ └── loader.go
|
||||
│ ├── provider/ # 厂商实现
|
||||
│ │ ├── provider.go # 接口定义
|
||||
│ │ ├── factory.go # 工厂模式
|
||||
│ │ ├── siliconflow.go
|
||||
│ │ ├── volcano.go
|
||||
│ │ ├── national.go
|
||||
│ │ ├── qwen.go
|
||||
│ │ └── openai.go
|
||||
│ ├── translator/ # 核心翻译
|
||||
│ │ ├── translator.go
|
||||
│ │ └── prompt.go
|
||||
│ └── prompt/ # Prompt管理
|
||||
├── pkg/ # 公共工具
|
||||
├── configs/ # 配置文件目录
|
||||
│ └── config.yaml
|
||||
├── go.mod
|
||||
└── go.sum
|
||||
```
|
||||
|
||||
## 配置文件示例 (YAML)
|
||||
```yaml
|
||||
# config.yaml
|
||||
default_provider: "siliconflow"
|
||||
default_model: "gpt-3.5-turbo"
|
||||
timeout: 30
|
||||
|
||||
providers:
|
||||
siliconflow:
|
||||
api_host: "https://api.siliconflow.cn/v1"
|
||||
api_key: "${SILICONFLOW_API_KEY}"
|
||||
model: "siliconflow-base"
|
||||
enabled: true
|
||||
|
||||
volcano:
|
||||
api_host: "https://api.volcengine.com/v1"
|
||||
api_key: "${VOLCANO_API_KEY}"
|
||||
model: "volcano-chat"
|
||||
enabled: true
|
||||
|
||||
national:
|
||||
api_host: "https://api.nsc.gov.cn/v1"
|
||||
api_key: "${NATIONAL_API_KEY}"
|
||||
model: "nsc-base"
|
||||
enabled: false
|
||||
|
||||
qwen:
|
||||
api_host: "https://dashscope.aliyuncs.com/compatible-mode/v1"
|
||||
api_key: "${QWEN_API_KEY}"
|
||||
model: "qwen-turbo"
|
||||
enabled: true
|
||||
|
||||
openai:
|
||||
api_host: "https://api.openai.com/v1"
|
||||
api_key: "${OPENAI_API_KEY}"
|
||||
model: "gpt-3.5-turbo"
|
||||
enabled: true
|
||||
|
||||
prompts:
|
||||
technical: "你是一位专业的技术翻译,请准确翻译以下技术文档,保持专业术语的准确性。"
|
||||
creative: "你是一位富有创造力的翻译家,请用优美流畅的语言翻译以下内容。"
|
||||
academic: "你是一位学术翻译专家,请用严谨的学术语言翻译以下内容。"
|
||||
simple: "请用简单易懂的语言翻译以下内容。"
|
||||
```
|
||||
|
||||
## 开发顺序建议
|
||||
1. 实现 `Config` 和 `ConfigLoader`
|
||||
2. 实现 `Provider` 接口和工厂
|
||||
3. 实现至少一个厂商(如SiliconFlow)
|
||||
4. 实现 `Translator` 核心类
|
||||
5. 集成测试
|
||||
6. 实现其他厂商
|
||||
|
||||
## 测试策略
|
||||
```go
|
||||
// 内部测试示例
|
||||
func TestTranslator_Translate(t *testing.T) {
|
||||
// Mock provider
|
||||
mockProvider := &MockProvider{
|
||||
TranslateFunc: func(ctx context.Context, req *provider.TranslateRequest) (*provider.TranslateResponse, error) {
|
||||
return &provider.TranslateResponse{Text: "翻译结果"}, nil
|
||||
},
|
||||
}
|
||||
|
||||
translator := NewTranslator(testConfig, mockProvider)
|
||||
result, err := translator.Translate(context.Background(), "Hello", &TranslateOptions{
|
||||
ToLang: "zh",
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("翻译失败: %v", err)
|
||||
}
|
||||
|
||||
if result.Translated != "翻译结果" {
|
||||
t.Errorf("期望 '翻译结果', 得到 '%s'", result.Translated)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 错误处理最佳实践
|
||||
- 每个厂商实现应定义具体的错误类型
|
||||
- 使用 `%w` 包装错误以保留原始错误信息
|
||||
- 提供清晰的错误消息帮助调试
|
||||
|
||||
## 安全注意事项
|
||||
- API密钥使用环境变量
|
||||
- 配置文件中不存储真实密钥
|
||||
- 使用 `os.Getenv` 读取敏感信息
|
||||
- 定期轮换API密钥
|
||||
|
||||
## 开发规范
|
||||
|
||||
### 文档管理
|
||||
|
||||
#### 文档文件列表
|
||||
1. **why.md** - 项目初衷文档(仅用户编辑)
|
||||
2. **taolun.md** - 讨论记录(时间轴格式)
|
||||
3. **changelog.md** - 版本记录(包含讨论链接)
|
||||
4. **memory.md** - 知识纠正(踩坑记录)
|
||||
|
||||
#### 文档协作规范
|
||||
| 文档 | 编辑者 | 内容 | 更新频率 |
|
||||
|------|--------|------|----------|
|
||||
| why.md | 用户 | 项目初衷、愿景、目标 | 随时 |
|
||||
| taolun.md | AI+用户 | 讨论记录、决策过程 | 每次重要讨论后 |
|
||||
| changelog.md | AI | 版本变更、任务状态 | 每次版本更新 |
|
||||
| memory.md | AI | 经验总结、术语定义 | 遇到问题后 |
|
||||
|
||||
#### 特殊文档说明
|
||||
**why.md**:
|
||||
- 项目初衷文档,只能由用户编辑
|
||||
- AI不应修改此文件内容
|
||||
- 用于记录创始人的个人想法和项目愿景
|
||||
- 位置:项目根目录
|
||||
|
||||
**AI责任边界**:
|
||||
- ✅ 可以编辑:taolun.md、changelog.md、memory.md
|
||||
- ❌ 不应编辑:why.md、用户个人配置文件
|
||||
- ✅ 可以建议:why.md的内容结构(但不强制)
|
||||
|
||||
### 文档链接
|
||||
- 项目初衷: [why.md](why.md)
|
||||
- 讨论记录: [taolun.md](taolun.md)
|
||||
- 版本记录: [changelog.md](changelog.md)
|
||||
- 知识纠正: [memory.md](memory.md)
|
||||
|
||||
### 版本号管理
|
||||
- 格式:主版本.次版本.修订版本(00-99)
|
||||
- 更新时机:测试完成后,git操作前
|
||||
- 递增规则:小修复第三位+1,新功能第二位+1,重大变更第一位+1
|
||||
|
||||
### 分支策略
|
||||
- main: 稳定上线版
|
||||
- dev: 开发分支
|
||||
- 功能开发:从dev创建功能分支
|
||||
|
||||
### 提交规范
|
||||
- 提交前更新相关文档
|
||||
- 使用清晰的提交信息
|
||||
- 版本更新时打标签
|
||||
|
||||
## 开发命令
|
||||
|
||||
### 构建
|
||||
```bash
|
||||
# 构建二进制文件
|
||||
go build -o yoyo ./cmd/yoyo
|
||||
|
||||
# 交叉编译(Linux)
|
||||
GOOS=linux GOARCH=amd64 go build -o yoyo-linux ./cmd/yoyo
|
||||
|
||||
# 交叉编译(macOS)
|
||||
GOOS=darwin GOARCH=amd64 go build -o yoyo-mac ./cmd/yoyo
|
||||
```
|
||||
|
||||
### 测试
|
||||
```bash
|
||||
# 运行所有测试
|
||||
go test ./...
|
||||
|
||||
# 运行特定包的测试
|
||||
go test ./internal/translator
|
||||
|
||||
# 运行单个测试函数
|
||||
go test -run TestTranslate ./internal/translator
|
||||
|
||||
# 运行测试并显示详细输出
|
||||
go test -v ./...
|
||||
|
||||
# 运行基准测试
|
||||
go test -bench=. ./...
|
||||
```
|
||||
|
||||
### 代码检查与格式化
|
||||
```bash
|
||||
# 代码格式化(必须)
|
||||
gofmt -w .
|
||||
|
||||
# 代码检查
|
||||
go vet ./...
|
||||
|
||||
# 使用golangci-lint(推荐)
|
||||
golangci-lint run
|
||||
|
||||
# 生成依赖图
|
||||
go mod graph
|
||||
```
|
||||
|
||||
### 运行工具
|
||||
```bash
|
||||
# 直接运行
|
||||
go run ./cmd/yoyo "This is translation content..."
|
||||
|
||||
# 使用构建的二进制
|
||||
./yoyo "This is translation content..."
|
||||
|
||||
# 指定翻译模式
|
||||
./yoyo --mode=technical "API documentation text"
|
||||
```
|
||||
|
||||
## 代码风格指南
|
||||
|
||||
### 命名约定
|
||||
- **包名**:小写单词,简洁明了(如`translator`、`config`)
|
||||
- **函数/方法名**:驼峰命名,动词开头(如`TranslateText`、`LoadConfig`)
|
||||
- **变量名**:驼峰命名,简洁(如`inputText`、`apiResponse`)
|
||||
- **常量**:全大写加下划线(如`MAX_RETRY_COUNT`)
|
||||
- **接口名**:以`-er`结尾或描述性名称(如`Translator`、`ConfigLoader`)
|
||||
|
||||
### 导入顺序
|
||||
```go
|
||||
import (
|
||||
// 标准库
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
// 第三方包
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
// 项目内部包
|
||||
"github.com/username/yoyo/internal/api"
|
||||
"github.com/username/yoyo/internal/config"
|
||||
)
|
||||
```
|
||||
|
||||
### 错误处理
|
||||
```go
|
||||
// 包装错误以提供更多上下文
|
||||
if err := loadConfig(); err != nil {
|
||||
return fmt.Errorf("failed to load config: %w", err)
|
||||
}
|
||||
|
||||
// 定义哨兵错误
|
||||
var (
|
||||
ErrInvalidAPIKey = errors.New("invalid API key")
|
||||
ErrNetworkFailed = errors.New("network request failed")
|
||||
)
|
||||
|
||||
// 错误检查使用errors.Is
|
||||
if errors.Is(err, ErrInvalidAPIKey) {
|
||||
// 处理特定错误
|
||||
}
|
||||
```
|
||||
|
||||
### 类型使用
|
||||
- 优先使用具体类型,必要时使用接口
|
||||
- 避免使用`interface{}`,使用泛型或具体类型
|
||||
- 结构体字段使用大写开头(导出)或小写开头(私有)
|
||||
- 为复杂类型添加文档注释
|
||||
|
||||
### 并发处理
|
||||
```go
|
||||
// 使用context传递取消信号
|
||||
func Translate(ctx context.Context, text string) (string, error) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return "", ctx.Err()
|
||||
default:
|
||||
// 继续翻译
|
||||
}
|
||||
}
|
||||
|
||||
// 使用goroutine和channel时注意资源清理
|
||||
go func() {
|
||||
defer close(doneCh)
|
||||
// 执行任务
|
||||
}()
|
||||
```
|
||||
|
||||
### 注释规范
|
||||
- 包注释:描述包的功能和用途
|
||||
- 函数注释:描述函数功能、参数、返回值
|
||||
- 导出类型/函数必须有注释
|
||||
- 使用Godoc格式
|
||||
|
||||
## 测试指南
|
||||
- 每个公共函数都应有对应的测试
|
||||
- 测试函数命名:`TestFunctionName`
|
||||
- 使用表格驱动测试
|
||||
- Mock外部依赖
|
||||
- 测试文件放在同一目录,以`_test.go`结尾
|
||||
|
||||
```go
|
||||
// 示例测试
|
||||
func TestTranslator_Translate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{"simple text", "hello", "你好"},
|
||||
{"empty string", "", ""},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result, err := translator.Translate(tt.input)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if result != tt.expected {
|
||||
t.Errorf("got %v, want %v", result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 依赖管理
|
||||
- 使用Go Modules管理依赖
|
||||
- 保持`go.mod`和`go.sum`整洁
|
||||
- 定期更新依赖:`go get -u ./...`
|
||||
- 移除未使用的依赖:`go mod tidy`
|
||||
|
||||
## 提交前检查清单
|
||||
1. ✅ 运行`gofmt -w .`格式化代码
|
||||
2. ✅ 运行`go vet ./...`检查代码
|
||||
3. ✅ 运行`go test ./...`确保测试通过
|
||||
4. ✅ 确保所有导出函数有注释
|
||||
5. ✅ 检查错误处理是否完善
|
||||
6. ✅ 验证API密钥等敏感信息已忽略
|
||||
|
||||
## 敏感信息处理
|
||||
- API密钥使用环境变量或配置文件
|
||||
- 将`.env`添加到`.gitignore`
|
||||
- 不要在日志中打印敏感信息
|
||||
- 使用`os.Getenv`读取环境变量
|
||||
|
||||
## 构建和发布
|
||||
```bash
|
||||
# 发布版本
|
||||
git tag v1.0.0
|
||||
git push origin v1.0.0
|
||||
|
||||
# 使用GoReleaser(推荐)
|
||||
goreleaser release --clean
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
### Q: 如何添加新的翻译模式?
|
||||
A: 在`internal/prompt/`目录下创建新的Prompt配置,然后在`internal/translator/`中注册。
|
||||
|
||||
### Q: 如何处理API限流?
|
||||
A: 使用指数退避重试,并在`internal/api/`中实现限流器。
|
||||
|
||||
### Q: 如何支持更多语言?
|
||||
A: 在配置文件中添加语言映射,并更新翻译逻辑。
|
||||
|
||||
## 参考资源
|
||||
- [Effective Go](https://go.dev/doc/effective_go)
|
||||
- [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments)
|
||||
- [Go Style Guide](https://google.github.io/styleguide/go/)
|
||||
Reference in New Issue
Block a user