2026-03-28 23:19:13 +08:00
|
|
|
|
# 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: 在配置文件中添加语言映射,并更新翻译逻辑。
|
|
|
|
|
|
|
feat: add language support and onboard configuration wizard (v0.2.0)
- Add language code intelligent parsing module (internal/lang)
- Support --lang parameter for target language specification
- Support multiple language code formats (BCP47, aliases, Chinese names)
- Implement interactive onboard configuration wizard
- Update Config struct with language fields
- Add survey library dependency for interactive UI
- Improve CLI command interface
- Add comprehensive unit tests for language module
- Update documentation (AGENTS.md, changelog.md, taolun.md, memory.md)
Supported language codes:
- Standard: zh-CN, zh-TW, en-US, en-GB, ja, ko, es, fr, de
- Aliases: cn, en, jp, kr, es, fr, de
- Chinese names: chinese, english, japanese
Commands:
- yoyo "Hello world" - basic translation
- yoyo --lang=cn "Hello world" - specify target language
- yoyo onboard - start configuration wizard
- yoyo onboard --force - force reconfiguration
Version: 0.2.0
2026-03-29 01:30:42 +08:00
|
|
|
|
## 语言代码处理
|
|
|
|
|
|
|
|
|
|
|
|
### 支持的语言代码格式
|
|
|
|
|
|
项目支持多种语言代码格式,通过 `internal/lang` 模块处理:
|
|
|
|
|
|
|
|
|
|
|
|
1. **标准BCP47格式**: `zh-CN`, `zh-TW`, `en-US`, `en-GB`, `ja`, `ko` 等
|
|
|
|
|
|
2. **简短别名**: `cn`(中文), `en`(英文), `jp`(日文), `kr`(韩文) 等
|
|
|
|
|
|
3. **中文名称**: `chinese`(中文), `english`(英文), `japanese`(日文) 等
|
|
|
|
|
|
|
|
|
|
|
|
### 语言解析函数
|
|
|
|
|
|
```go
|
|
|
|
|
|
// 解析语言代码
|
|
|
|
|
|
lang.ParseLanguageCode("cn") // 返回 "zh-CN"
|
|
|
|
|
|
lang.ParseLanguageCode("en") // 返回 "en-US"
|
|
|
|
|
|
lang.ParseLanguageCode("zh-TW") // 返回 "zh-TW"
|
|
|
|
|
|
|
|
|
|
|
|
// 获取语言名称(用于显示)
|
|
|
|
|
|
lang.GetLanguageName("zh-CN") // 返回 "中文(简体)"
|
|
|
|
|
|
lang.GetLanguageName("en-US") // 返回 "English (US)"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## Onboard配置向导
|
|
|
|
|
|
|
|
|
|
|
|
### 配置流程
|
|
|
|
|
|
1. 选择主要翻译厂商
|
|
|
|
|
|
2. 配置厂商API密钥、HOST、模型
|
|
|
|
|
|
3. 设置全局配置(默认语言、超时)
|
|
|
|
|
|
4. 保存配置到 `configs/config.yaml`
|
|
|
|
|
|
|
|
|
|
|
|
### 使用方法
|
|
|
|
|
|
```bash
|
|
|
|
|
|
yoyo onboard # 启动配置向导
|
|
|
|
|
|
yoyo onboard --force # 强制重新配置
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 配置向导实现
|
|
|
|
|
|
- 使用 `github.com/AlecAivazis/survey/v2` 实现交互式界面
|
|
|
|
|
|
- 支持厂商选择、API配置、语言设置
|
|
|
|
|
|
- 生成标准YAML配置文件
|
|
|
|
|
|
|
|
|
|
|
|
## 分阶段迁移策略
|
|
|
|
|
|
|
|
|
|
|
|
### 第一阶段:开发阶段(当前)
|
|
|
|
|
|
- API密钥存储在 `.env` 文件
|
|
|
|
|
|
- 复杂配置存储在 `configs/config.yaml`
|
|
|
|
|
|
- 支持环境变量替换
|
|
|
|
|
|
|
|
|
|
|
|
### 第二阶段:上线前
|
|
|
|
|
|
- 实现配置文件路径查找机制
|
|
|
|
|
|
- 支持用户配置目录 `~/.config/yoo/yoo.yml`
|
|
|
|
|
|
- 提供配置迁移工具
|
|
|
|
|
|
|
|
|
|
|
|
### 第三阶段:最终优化
|
|
|
|
|
|
- 移除对 `.env` 文件依赖
|
|
|
|
|
|
- 完全使用配置文件
|
|
|
|
|
|
|
2026-03-28 23:19:13 +08:00
|
|
|
|
## 参考资源
|
|
|
|
|
|
- [Effective Go](https://go.dev/doc/effective_go)
|
|
|
|
|
|
- [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments)
|
feat: add language support and onboard configuration wizard (v0.2.0)
- Add language code intelligent parsing module (internal/lang)
- Support --lang parameter for target language specification
- Support multiple language code formats (BCP47, aliases, Chinese names)
- Implement interactive onboard configuration wizard
- Update Config struct with language fields
- Add survey library dependency for interactive UI
- Improve CLI command interface
- Add comprehensive unit tests for language module
- Update documentation (AGENTS.md, changelog.md, taolun.md, memory.md)
Supported language codes:
- Standard: zh-CN, zh-TW, en-US, en-GB, ja, ko, es, fr, de
- Aliases: cn, en, jp, kr, es, fr, de
- Chinese names: chinese, english, japanese
Commands:
- yoyo "Hello world" - basic translation
- yoyo --lang=cn "Hello world" - specify target language
- yoyo onboard - start configuration wizard
- yoyo onboard --force - force reconfiguration
Version: 0.2.0
2026-03-29 01:30:42 +08:00
|
|
|
|
- [Go Style Guide](https://google.github.io/styleguide/go/)
|
|
|
|
|
|
- [Survey库文档](https://github.com/AlecAivazis/survey)
|