refactor(mcp) (#1042)

* improve(interface): replace with interface

* feat(mcp): 添加构建器模式支持

新增功能:
- RequestBuilder 构建器,支持流式 API
- 多轮对话支持(AddAssistantMessage)
- Function Calling / Tools 支持
- 精细参数控制(temperature, top_p, penalties 等)
- 3个预设场景(Chat, CodeGen, CreativeWriting)
- 完整的测试套件(19个新测试)

修复问题:
- Config 字段未使用(MaxRetries、Temperature 等)
- DeepSeek/Qwen SetAPIKey 的冗余 nil 检查

向后兼容:
- 保留 CallWithMessages API
- 新增 CallWithRequest API

测试:
- 81 个测试全部通过
- 覆盖率 80.6%

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: tinkle-community <tinklefund@gmail.com>

---------

Co-authored-by: zbhan <zbhan@freewheel.tv>
Co-authored-by: tinkle-community <tinklefund@gmail.com>
This commit is contained in:
Shui
2025-11-15 23:04:53 -05:00
committed by GitHub
parent b66fd5fb0a
commit 88b01c8f2a
22 changed files with 6144 additions and 142 deletions

View File

@@ -0,0 +1,572 @@
# RequestBuilder 使用示例
## 📋 目录
1. [基础用法](#基础用法)
2. [多轮对话](#多轮对话)
3. [参数精细控制](#参数精细控制)
4. [Function Calling](#function-calling)
5. [预设场景](#预设场景)
6. [完整示例](#完整示例)
---
## 基础用法
### 简单对话
```go
package main
import (
"fmt"
"nofx/mcp"
)
func main() {
// 创建客户端
client := mcp.NewDeepSeekClientWithOptions(
mcp.WithAPIKey("sk-xxx"),
)
// 使用构建器创建请求
request := mcp.NewRequestBuilder().
WithSystemPrompt("You are a helpful assistant").
WithUserPrompt("What is Go programming language?").
Build()
// 调用 API
result, err := client.CallWithRequest(request)
if err != nil {
panic(err)
}
fmt.Println(result)
}
```
### 与传统方式对比
```go
// 传统方式(仍然可用)
result, err := client.CallWithMessages(
"You are a helpful assistant",
"What is Go?",
)
// 构建器方式新API功能更强大
request := mcp.NewRequestBuilder().
WithSystemPrompt("You are a helpful assistant").
WithUserPrompt("What is Go?").
Build()
result, err := client.CallWithRequest(request)
```
---
## 多轮对话
### 带上下文的对话
```go
// 构建包含历史的多轮对话
request := mcp.NewRequestBuilder().
AddSystemMessage("You are a trading advisor").
AddUserMessage("Analyze BTC price").
AddAssistantMessage("BTC is currently in an upward trend...").
AddUserMessage("What's the best entry point?"). // 继续对话
WithTemperature(0.3). // 低温度,更精确
Build()
result, err := client.CallWithRequest(request)
```
### 从历史记录构建
```go
// 假设你有保存的对话历史
history := []mcp.Message{
mcp.NewUserMessage("Hello"),
mcp.NewAssistantMessage("Hi! How can I help?"),
mcp.NewUserMessage("What's the weather?"),
mcp.NewAssistantMessage("It's sunny today"),
}
// 继续对话
request := mcp.NewRequestBuilder().
AddSystemMessage("You are helpful").
AddConversationHistory(history). // 添加历史
AddUserMessage("What about tomorrow?"). // 新问题
Build()
result, err := client.CallWithRequest(request)
```
---
## 参数精细控制
### 代码生成(低温度、精确)
```go
request := mcp.NewRequestBuilder().
WithSystemPrompt("You are a Go expert").
WithUserPrompt("Generate a HTTP server").
WithTemperature(0.2). // 低温度 = 更确定
WithTopP(0.1). // 低 top_p = 更聚焦
WithMaxTokens(2000).
AddStopSequence("```"). // 遇到代码块结束符停止
Build()
code, err := client.CallWithRequest(request)
```
### 创意写作(高温度、随机)
```go
request := mcp.NewRequestBuilder().
WithSystemPrompt("You are a creative writer").
WithUserPrompt("Write a sci-fi story about AI").
WithTemperature(1.2). // 高温度 = 更创意
WithTopP(0.95). // 高 top_p = 更多样
WithPresencePenalty(0.6). // 避免重复主题
WithFrequencyPenalty(0.5). // 避免重复词汇
WithMaxTokens(4000).
Build()
story, err := client.CallWithRequest(request)
```
### 精确分析(平衡参数)
```go
request := mcp.NewRequestBuilder().
WithSystemPrompt("You are a quantitative analyst").
WithUserPrompt("Analyze BTC/USDT chart pattern").
WithTemperature(0.5). // 中等温度
WithMaxTokens(1500).
WithStopSequences([]string{"---", "END"}). // 多个停止序列
Build()
analysis, err := client.CallWithRequest(request)
```
---
## Function Calling
### 天气查询工具
```go
// 定义工具参数 schemaJSON Schema 格式)
weatherParams := map[string]any{
"type": "object",
"properties": map[string]any{
"location": map[string]any{
"type": "string",
"description": "City name, e.g., Beijing, Shanghai",
},
"unit": map[string]any{
"type": "string",
"enum": []string{"celsius", "fahrenheit"},
},
},
"required": []string{"location"},
}
// 构建请求
request := mcp.NewRequestBuilder().
WithUserPrompt("北京今天天气怎么样?").
AddFunction(
"get_weather", // 函数名
"Get current weather", // 函数描述
weatherParams, // 参数定义
).
WithToolChoice("auto"). // 让 AI 自动决定是否调用
Build()
response, err := client.CallWithRequest(request)
// AI 可能返回 tool_calls你需要执行函数并返回结果
// (具体实现取决于 AI provider 的响应格式)
```
### 多个工具
```go
// 定义多个工具
request := mcp.NewRequestBuilder().
WithUserPrompt("帮我查询北京天气并计算100的平方根").
AddFunction("get_weather", "Get weather", weatherParams).
AddFunction("calculate", "Calculate math", calcParams).
AddFunction("search_web", "Search web", searchParams).
WithToolChoice("auto").
Build()
response, err := client.CallWithRequest(request)
// AI 会选择调用相应的工具
```
### 强制使用特定工具
```go
request := mcp.NewRequestBuilder().
WithUserPrompt("北京").
AddFunction("get_weather", "Get weather", weatherParams).
WithToolChoice(`{"type": "function", "function": {"name": "get_weather"}}`).
Build()
// AI 必须调用 get_weather 函数
```
---
## 预设场景
### ForChat - 聊天场景
```go
// 预设参数temperature=0.7, maxTokens=2000
request := mcp.ForChat().
WithSystemPrompt("You are a friendly chatbot").
WithUserPrompt("Hello!").
Build()
// 等价于
request := mcp.NewRequestBuilder().
WithSystemPrompt("You are a friendly chatbot").
WithUserPrompt("Hello!").
WithTemperature(0.7).
WithMaxTokens(2000).
Build()
```
### ForCodeGeneration - 代码生成场景
```go
// 预设参数temperature=0.2, topP=0.1, maxTokens=2000
request := mcp.ForCodeGeneration().
WithUserPrompt("Generate a REST API in Go").
Build()
// 自动使用低温度和低 top_p确保代码准确性
```
### ForCreativeWriting - 创意写作场景
```go
// 预设参数:
// temperature=1.2, topP=0.95, maxTokens=4000
// presencePenalty=0.6, frequencyPenalty=0.5
request := mcp.ForCreativeWriting().
WithSystemPrompt("You are a novelist").
WithUserPrompt("Write a fantasy story").
Build()
// 自动使用高温度和惩罚参数,增加创意和多样性
```
---
## 完整示例
### 量化交易 AI 顾问
```go
package main
import (
"fmt"
"log"
"nofx/mcp"
"os"
)
func main() {
// 创建客户端
client := mcp.NewDeepSeekClientWithOptions(
mcp.WithAPIKey(os.Getenv("DEEPSEEK_API_KEY")),
mcp.WithMaxRetries(5),
mcp.WithTimeout(60 * time.Second),
)
// 场景1: 市场分析(需要精确)
analysisRequest := mcp.NewRequestBuilder().
WithSystemPrompt("You are a professional quantitative trader").
WithUserPrompt("Analyze BTC/USDT 1H chart, current price $45,000").
WithTemperature(0.3). // 低温度,更精确
WithMaxTokens(1500).
Build()
analysis, err := client.CallWithRequest(analysisRequest)
if err != nil {
log.Fatal(err)
}
fmt.Println("=== Market Analysis ===")
fmt.Println(analysis)
// 场景2: 继续对话,询问入场点
followUpRequest := mcp.NewRequestBuilder().
AddSystemMessage("You are a professional quantitative trader").
AddUserMessage("Analyze BTC/USDT 1H chart, current price $45,000").
AddAssistantMessage(analysis). // 添加之前的回复
AddUserMessage("Based on your analysis, what's the best entry point?").
WithTemperature(0.3).
Build()
entryPoint, err := client.CallWithRequest(followUpRequest)
if err != nil {
log.Fatal(err)
}
fmt.Println("\n=== Entry Point Suggestion ===")
fmt.Println(entryPoint)
}
```
### 代码评审助手
```go
func reviewCode(client mcp.AIClient, code string) (string, error) {
request := mcp.ForCodeGeneration(). // 使用代码场景预设
WithSystemPrompt("You are a senior Go developer reviewing code").
WithUserPrompt(fmt.Sprintf("Review this code:\n\n```go\n%s\n```", code)).
WithMaxTokens(2000).
AddStopSequence("---END---").
Build()
return client.CallWithRequest(request)
}
func main() {
client := mcp.NewDeepSeekClientWithOptions(
mcp.WithAPIKey(os.Getenv("DEEPSEEK_API_KEY")),
)
code := `
func Add(a, b int) int {
return a + b
}
`
review, err := reviewCode(client, code)
if err != nil {
log.Fatal(err)
}
fmt.Println(review)
}
```
### AI 聊天机器人(带历史记录)
```go
type ChatBot struct {
client mcp.AIClient
history []mcp.Message
}
func NewChatBot(client mcp.AIClient, systemPrompt string) *ChatBot {
return &ChatBot{
client: client,
history: []mcp.Message{
mcp.NewSystemMessage(systemPrompt),
},
}
}
func (bot *ChatBot) Chat(userMessage string) (string, error) {
// 添加用户消息到历史
bot.history = append(bot.history, mcp.NewUserMessage(userMessage))
// 构建请求(包含完整历史)
request := mcp.ForChat().
AddMessages(bot.history...).
Build()
// 调用 API
response, err := bot.client.CallWithRequest(request)
if err != nil {
return "", err
}
// 添加 AI 回复到历史
bot.history = append(bot.history, mcp.NewAssistantMessage(response))
return response, nil
}
func main() {
client := mcp.NewDeepSeekClientWithOptions(
mcp.WithAPIKey(os.Getenv("DEEPSEEK_API_KEY")),
)
bot := NewChatBot(client, "You are a friendly and helpful assistant")
// 对话1
resp1, _ := bot.Chat("What is Go?")
fmt.Println("User: What is Go?")
fmt.Println("Bot:", resp1)
// 对话2带上下文
resp2, _ := bot.Chat("What are its main features?")
fmt.Println("\nUser: What are its main features?")
fmt.Println("Bot:", resp2)
// 对话3继续上下文
resp3, _ := bot.Chat("Show me an example")
fmt.Println("\nUser: Show me an example")
fmt.Println("Bot:", resp3)
}
```
### Function Calling 完整示例
```go
package main
import (
"encoding/json"
"fmt"
"nofx/mcp"
"os"
)
// 天气查询函数(模拟)
func getWeather(location string) string {
return fmt.Sprintf("Weather in %s: Sunny, 25°C", location)
}
func main() {
client := mcp.NewDeepSeekClientWithOptions(
mcp.WithAPIKey(os.Getenv("DEEPSEEK_API_KEY")),
)
// 定义工具
weatherParams := map[string]any{
"type": "object",
"properties": map[string]any{
"location": map[string]any{
"type": "string",
"description": "City name",
},
},
"required": []string{"location"},
}
// 第一步:发送带工具的请求
request := mcp.NewRequestBuilder().
WithUserPrompt("北京天气怎么样?").
AddFunction("get_weather", "Get current weather", weatherParams).
WithToolChoice("auto").
Build()
response, err := client.CallWithRequest(request)
if err != nil {
panic(err)
}
fmt.Println("AI Response:", response)
// 第二步:如果 AI 返回了 tool_call实际需要解析 JSON 响应)
// 这里是示例,实际需要根据 provider 的响应格式解析
// toolCall := parseToolCall(response)
// weatherResult := getWeather(toolCall.Arguments.Location)
// 第三步:将工具结果返回给 AI
// followUp := mcp.NewRequestBuilder().
// AddConversationHistory(previousMessages).
// AddToolResult(toolCall.ID, weatherResult).
// Build()
//
// finalResponse, _ := client.CallWithRequest(followUp)
}
```
---
## 最佳实践
### 1. 使用 MustBuild() vs Build()
```go
// Build() - 返回 error需要处理
request, err := NewRequestBuilder().
WithUserPrompt("Hello").
Build()
if err != nil {
log.Fatal(err)
}
// MustBuild() - 如果失败会 panic适用于确定不会错的场景
request := NewRequestBuilder().
WithSystemPrompt("You are helpful").
WithUserPrompt("Hello").
MustBuild() // 构建失败会 panic
```
### 2. 重用构建器
```go
// 创建基础构建器
baseBuilder := mcp.NewRequestBuilder().
WithSystemPrompt("You are a trading advisor").
WithTemperature(0.3)
// 为不同问题添加用户消息
question1 := baseBuilder.
AddUserMessage("Analyze BTC").
Build()
question2 := baseBuilder.
ClearMessages(). // 清空之前的消息
AddSystemMessage("You are a trading advisor").
AddUserMessage("Analyze ETH").
Build()
```
### 3. 选择合适的预设
```go
// ✅ 代码生成 - 使用 ForCodeGeneration
ForCodeGeneration().WithUserPrompt("Generate code")
// ✅ 聊天 - 使用 ForChat
ForChat().WithUserPrompt("Hello")
// ✅ 创意写作 - 使用 ForCreativeWriting
ForCreativeWriting().WithUserPrompt("Write a story")
// ✅ 自定义 - 使用 NewRequestBuilder
NewRequestBuilder().WithTemperature(0.6).WithUserPrompt("...")
```
---
## 迁移指南
### 从旧 API 迁移
```go
// 旧 API仍然可用
result, err := client.CallWithMessages("system", "user")
// 迁移到新 API
request := mcp.NewRequestBuilder().
WithSystemPrompt("system").
WithUserPrompt("user").
Build()
result, err := client.CallWithRequest(request)
// 如果需要更多控制
request := mcp.NewRequestBuilder().
WithSystemPrompt("system").
WithUserPrompt("user").
WithTemperature(0.8). // 新功能
WithMaxTokens(2000). // 新功能
Build()
result, err := client.CallWithRequest(request)
```
---
更多信息请参考:
- [构建器模式价值分析](./BUILDER_PATTERN_BENEFITS.md)
- [MCP 使用指南](./README.md)

View File

@@ -0,0 +1,716 @@
# 构建器模式在 MCP 模块中的应用价值
## 📋 目录
1. [当前实现的局限性](#当前实现的局限性)
2. [构建器模式的好处](#构建器模式的好处)
3. [实际应用场景](#实际应用场景)
4. [对比示例](#对比示例)
5. [是否需要引入](#是否需要引入)
---
## 当前实现的局限性
### 现状分析
**当前 buildMCPRequestBody 实现**:
```go
func (client *Client) buildMCPRequestBody(systemPrompt, userPrompt string) map[string]any {
messages := []map[string]string{}
if systemPrompt != "" {
messages = append(messages, map[string]string{
"role": "system",
"content": systemPrompt,
})
}
messages = append(messages, map[string]string{
"role": "user",
"content": userPrompt,
})
return map[string]interface{}{
"model": client.Model,
"messages": messages,
"temperature": client.config.Temperature,
"max_tokens": client.MaxTokens,
}
}
```
### 存在的限制
1. **只支持简单对话**
- ❌ 无法添加多轮对话历史
- ❌ 无法添加 assistant 回复
- ❌ 无法构建复杂的对话上下文
2. **参数固定**
- ❌ 无法动态添加可选参数(如 top_p、frequency_penalty
- ❌ 无法为单次请求自定义 temperature会影响全局配置
- ❌ 无法添加 function calling、tools 等高级功能
3. **扩展性差**
- ❌ 每次添加新参数都需要修改方法签名
- ❌ 参数列表会越来越长
- ❌ 子类重写时需要处理所有参数
---
## 构建器模式的好处
### 1. 🎯 **灵活性和可读性**
#### 当前方式(参数传递)
```go
// 问题:参数多了会很混乱
client.CallWithCustomParams(
"system prompt",
"user prompt",
0.8, // temperature - 这是什么?
2000, // max_tokens - 这是什么?
0.9, // top_p - 这是什么?
0.5, // frequency_penalty
nil, // stop sequences
false, // stream
)
```
#### 构建器方式
```go
// 清晰、自解释
request := NewRequestBuilder().
WithSystemPrompt("You are a helpful assistant").
WithUserPrompt("Tell me about Go").
WithTemperature(0.8).
WithMaxTokens(2000).
WithTopP(0.9).
Build()
result, err := client.CallWithRequest(request)
```
---
### 2. 📚 **支持复杂场景**
#### 场景1: 多轮对话
**当前方式**: 😢 不支持
```go
// ❌ 无法实现
client.CallWithMessages("system", "user prompt")
```
**构建器方式**: ✅ 支持
```go
request := NewRequestBuilder().
AddSystemMessage("You are a helpful assistant").
AddUserMessage("What is the weather?").
AddAssistantMessage("It's sunny today").
AddUserMessage("What about tomorrow?"). // 继续对话
WithTemperature(0.7).
Build()
```
#### 场景2: 函数调用Function Calling
**当前方式**: 😢 不支持
```go
// ❌ 无法添加 tools/functions
```
**构建器方式**: ✅ 支持
```go
request := NewRequestBuilder().
WithUserPrompt("What's the weather in Beijing?").
AddTool(Tool{
Type: "function",
Function: FunctionDef{
Name: "get_weather",
Description: "Get current weather",
Parameters: weatherParamsSchema,
},
}).
WithToolChoice("auto").
Build()
```
#### 场景3: 流式响应
**当前方式**: 😢 需要修改整个架构
```go
// ❌ CallWithMessages 不支持流式
```
**构建器方式**: ✅ 易于扩展
```go
request := NewRequestBuilder().
WithUserPrompt("Write a long story").
WithStream(true).
Build()
stream, err := client.CallStream(request)
for chunk := range stream {
fmt.Print(chunk)
}
```
---
### 3. 🔧 **易于扩展和维护**
#### 添加新参数
**当前方式**: 😢 破坏性修改
```go
// 需要修改方法签名(破坏现有代码)
func (client *Client) buildMCPRequestBody(
systemPrompt, userPrompt string,
// 新增参数会导致所有调用处都要修改
topP float64,
presencePenalty float64,
) map[string]any
```
**构建器方式**: ✅ 向后兼容
```go
// 只需添加新方法,不影响现有代码
func (b *RequestBuilder) WithPresencePenalty(p float64) *RequestBuilder {
b.presencePenalty = p
return b
}
// 旧代码不受影响
request := builder.WithUserPrompt("Hello").Build()
// 新代码可以使用新功能
request := builder.
WithUserPrompt("Hello").
WithPresencePenalty(0.6). // 新参数
Build()
```
---
### 4. 🎨 **可选参数处理**
**当前方式**: 😢 难以处理可选参数
```go
// 方案1: 传 nil/0 值(不优雅)
client.CallWithParams(system, user, 0, 0, nil, nil)
// 方案2: 使用选项模式(但每次调用都要传)
client.CallWithParams(system, user, WithTopP(0.9), WithPenalty(0.5))
// 方案3: 配置对象(需要创建临时对象)
config := &RequestConfig{
SystemPrompt: system,
UserPrompt: user,
TopP: 0.9,
}
```
**构建器方式**: ✅ 优雅处理
```go
// 只设置需要的参数,其他使用默认值
request := NewRequestBuilder().
WithUserPrompt("Hello").
// 不设置 temperature使用默认值
// 不设置 topP使用默认值
Build()
// 也可以全部自定义
request := NewRequestBuilder().
WithUserPrompt("Hello").
WithTemperature(0.8).
WithTopP(0.9).
WithMaxTokens(2000).
Build()
```
---
### 5. ✅ **类型安全和验证**
**当前方式**: 😢 运行时才发现错误
```go
// ❌ 编译时无法发现问题
client.CallWithMessages("", "") // 空 prompt
client.CallWithMessages("system", "user") // temperature 可能不合法
```
**构建器方式**: ✅ 提前验证
```go
type RequestBuilder struct {
messages []Message
temperature float64
maxTokens int
}
func (b *RequestBuilder) WithTemperature(t float64) *RequestBuilder {
if t < 0 || t > 2 {
panic("temperature must be between 0 and 2") // 或返回 error
}
b.temperature = t
return b
}
func (b *RequestBuilder) Build() (*Request, error) {
if len(b.messages) == 0 {
return nil, errors.New("at least one message is required")
}
if b.maxTokens <= 0 {
return nil, errors.New("maxTokens must be positive")
}
return &Request{...}, nil
}
```
---
## 实际应用场景
### 场景1: 量化交易 AI 顾问(多轮对话)
```go
// 构建包含市场数据的上下文对话
request := NewRequestBuilder().
AddSystemMessage("You are a quantitative trading advisor").
AddUserMessage("Analyze BTC trend").
AddAssistantMessage("BTC is in an upward trend based on...").
AddUserMessage("What about entry points?"). // 继续对话
WithTemperature(0.3). // 低温度,更精确
WithMaxTokens(1000).
Build()
analysis, err := client.CallWithRequest(request)
```
### 场景2: 代码生成(需要精确控制)
```go
request := NewRequestBuilder().
WithSystemPrompt("You are a Go expert").
WithUserPrompt("Generate a HTTP server").
WithTemperature(0.2). // 低温度,更确定性
WithTopP(0.1). // 低 top_p更聚焦
WithMaxTokens(2000).
WithStopSequences([]string{"```"}). // 遇到代码块结束符停止
Build()
```
### 场景3: 创意写作(需要随机性)
```go
request := NewRequestBuilder().
WithSystemPrompt("You are a creative writer").
WithUserPrompt("Write a sci-fi story").
WithTemperature(1.2). // 高温度,更创意
WithTopP(0.95). // 高 top_p更多样性
WithPresencePenalty(0.6). // 避免重复
WithFrequencyPenalty(0.5).
WithMaxTokens(4000).
Build()
```
### 场景4: 函数调用(工具使用)
```go
// 定义工具
weatherTool := Tool{
Type: "function",
Function: FunctionDef{
Name: "get_weather",
Description: "Get current weather for a location",
Parameters: map[string]any{
"type": "object",
"properties": map[string]any{
"location": map[string]any{
"type": "string",
"description": "City name",
},
},
"required": []string{"location"},
},
},
}
request := NewRequestBuilder().
WithUserPrompt("What's the weather in Beijing?").
AddTool(weatherTool).
WithToolChoice("auto").
Build()
response, err := client.CallWithRequest(request)
// 解析 response.ToolCalls 并执行实际的天气查询
```
---
## 对比示例
### 示例1: 基础用法
#### 当前实现
```go
result, err := client.CallWithMessages(
"You are a helpful assistant",
"What is Go?",
)
```
#### 构建器模式
```go
request := NewRequestBuilder().
WithSystemPrompt("You are a helpful assistant").
WithUserPrompt("What is Go?").
Build()
result, err := client.CallWithRequest(request)
```
**分析**: 基础用法下,构建器稍显冗长,但更清晰。
---
### 示例2: 复杂用法
#### 当前实现(假设扩展后)
```go
// 😢 参数太多,难以理解
result, err := client.CallWithMessagesAdvanced(
"system prompt",
"user prompt",
nil, // messages history?
0.8, // temperature
2000, // max_tokens
0.9, // top_p
0.5, // frequency_penalty
0.6, // presence_penalty
nil, // stop sequences
false, // stream
nil, // tools
"", // tool_choice
)
```
#### 构建器模式
```go
// ✅ 清晰、自解释
request := NewRequestBuilder().
WithSystemPrompt("system prompt").
WithUserPrompt("user prompt").
WithTemperature(0.8).
WithMaxTokens(2000).
WithTopP(0.9).
WithFrequencyPenalty(0.5).
WithPresencePenalty(0.6).
Build()
result, err := client.CallWithRequest(request)
```
**分析**: 复杂场景下,构建器模式优势明显。
---
## 是否需要引入?
### ✅ 建议引入的情况
1. **需要支持多轮对话**
- 聊天机器人
- 上下文相关的 AI 助手
2. **需要精细控制 AI 参数**
- 不同任务需要不同 temperature
- 需要使用 top_p、penalty 等高级参数
3. **需要使用 AI 高级功能**
- Function Calling / Tools
- 流式响应
- Vision API图片输入
4. **API 接口可能频繁变化**
- AI 提供商经常添加新参数
- 需要向后兼容
### ⚠️ 可以暂缓的情况
1. **只有简单的单轮对话**
- 当前 `CallWithMessages` 已足够
2. **参数固定不变**
- 所有请求使用相同配置
3. **团队规模小,代码量少**
- 引入新模式的学习成本 > 收益
---
## 推荐方案
### 方案1: 渐进式引入(推荐)
**第一阶段**: 保留现有 API新增构建器
```go
// 旧 API 继续工作(向后兼容)
result, err := client.CallWithMessages("system", "user")
// 新 API 提供高级功能
request := NewRequestBuilder().
WithUserPrompt("user").
WithTemperature(0.8).
Build()
result, err := client.CallWithRequest(request)
```
**第二阶段**: 逐步迁移
```go
// 在文档中推荐使用构建器
// 旧 API 标记为 Deprecated但不删除
```
### 方案2: 仅用于高级场景
只在需要复杂功能时使用构建器:
```go
// 简单场景:使用现有 API
client.CallWithMessages("system", "user")
// 复杂场景:使用构建器
client.CallWithRequest(
NewRequestBuilder().
AddConversationHistory(history).
AddUserMessage("new question").
WithTools(tools).
Build(),
)
```
---
## 实现示例
### 完整的构建器实现
```go
package mcp
type Message struct {
Role string `json:"role"`
Content string `json:"content"`
}
type Tool struct {
Type string `json:"type"`
Function FunctionDef `json:"function"`
}
type Request struct {
Model string `json:"model"`
Messages []Message `json:"messages"`
Temperature float64 `json:"temperature,omitempty"`
MaxTokens int `json:"max_tokens,omitempty"`
TopP float64 `json:"top_p,omitempty"`
FrequencyPenalty float64 `json:"frequency_penalty,omitempty"`
PresencePenalty float64 `json:"presence_penalty,omitempty"`
Stop []string `json:"stop,omitempty"`
Tools []Tool `json:"tools,omitempty"`
ToolChoice string `json:"tool_choice,omitempty"`
Stream bool `json:"stream,omitempty"`
}
type RequestBuilder struct {
model string
messages []Message
temperature *float64
maxTokens *int
topP *float64
frequencyPenalty *float64
presencePenalty *float64
stop []string
tools []Tool
toolChoice string
stream bool
}
func NewRequestBuilder() *RequestBuilder {
return &RequestBuilder{
messages: make([]Message, 0),
}
}
func (b *RequestBuilder) WithModel(model string) *RequestBuilder {
b.model = model
return b
}
func (b *RequestBuilder) WithSystemPrompt(prompt string) *RequestBuilder {
if prompt != "" {
b.messages = append(b.messages, Message{
Role: "system",
Content: prompt,
})
}
return b
}
func (b *RequestBuilder) WithUserPrompt(prompt string) *RequestBuilder {
b.messages = append(b.messages, Message{
Role: "user",
Content: prompt,
})
return b
}
func (b *RequestBuilder) AddUserMessage(content string) *RequestBuilder {
return b.WithUserPrompt(content)
}
func (b *RequestBuilder) AddSystemMessage(content string) *RequestBuilder {
return b.WithSystemPrompt(content)
}
func (b *RequestBuilder) AddAssistantMessage(content string) *RequestBuilder {
b.messages = append(b.messages, Message{
Role: "assistant",
Content: content,
})
return b
}
func (b *RequestBuilder) AddMessage(role, content string) *RequestBuilder {
b.messages = append(b.messages, Message{
Role: role,
Content: content,
})
return b
}
func (b *RequestBuilder) AddConversationHistory(history []Message) *RequestBuilder {
b.messages = append(b.messages, history...)
return b
}
func (b *RequestBuilder) WithTemperature(t float64) *RequestBuilder {
if t < 0 || t > 2 {
panic("temperature must be between 0 and 2")
}
b.temperature = &t
return b
}
func (b *RequestBuilder) WithMaxTokens(tokens int) *RequestBuilder {
b.maxTokens = &tokens
return b
}
func (b *RequestBuilder) WithTopP(p float64) *RequestBuilder {
b.topP = &p
return b
}
func (b *RequestBuilder) WithFrequencyPenalty(p float64) *RequestBuilder {
b.frequencyPenalty = &p
return b
}
func (b *RequestBuilder) WithPresencePenalty(p float64) *RequestBuilder {
b.presencePenalty = &p
return b
}
func (b *RequestBuilder) WithStopSequences(sequences []string) *RequestBuilder {
b.stop = sequences
return b
}
func (b *RequestBuilder) AddTool(tool Tool) *RequestBuilder {
b.tools = append(b.tools, tool)
return b
}
func (b *RequestBuilder) WithToolChoice(choice string) *RequestBuilder {
b.toolChoice = choice
return b
}
func (b *RequestBuilder) WithStream(stream bool) *RequestBuilder {
b.stream = stream
return b
}
func (b *RequestBuilder) Build() (*Request, error) {
if len(b.messages) == 0 {
return nil, errors.New("at least one message is required")
}
req := &Request{
Model: b.model,
Messages: b.messages,
Stop: b.stop,
Tools: b.tools,
ToolChoice: b.toolChoice,
Stream: b.stream,
}
// 只设置非 nil 的可选参数
if b.temperature != nil {
req.Temperature = *b.temperature
}
if b.maxTokens != nil {
req.MaxTokens = *b.maxTokens
}
if b.topP != nil {
req.TopP = *b.topP
}
if b.frequencyPenalty != nil {
req.FrequencyPenalty = *b.frequencyPenalty
}
if b.presencePenalty != nil {
req.PresencePenalty = *b.presencePenalty
}
return req, nil
}
```
### Client 集成
```go
// 新增方法(不影响现有代码)
func (client *Client) CallWithRequest(req *Request) (string, error) {
// 使用 req 中的参数发送请求
// ...
}
```
---
## 总结
### 核心优势
1.**灵活性** - 轻松支持复杂场景
2.**可读性** - 代码自解释,易于理解
3.**可扩展性** - 添加新功能不破坏现有代码
4.**类型安全** - 编译时检查,提前发现错误
5.**向后兼容** - 可以与现有 API 共存
### 建议
- **当前阶段**: 如果只需要简单对话,现有实现已足够
- **未来扩展**: 当需要以下功能时再引入
- 多轮对话
- Function Calling
- 流式响应
- 精细参数控制
### 最佳实践
采用**渐进式引入**策略:
1. 保留现有 `CallWithMessages` API
2. 新增 `CallWithRequest` + 构建器
3. 在文档中推荐新 API但不强制迁移
4. 根据实际需求逐步完善构建器功能
这样既能保持向后兼容,又能为未来的功能扩展做好准备。

View File

@@ -0,0 +1,268 @@
# Logrus 集成指南
本文档展示如何将 MCP 模块与 Logrus 日志库集成。
## 📦 安装 Logrus
```bash
go get github.com/sirupsen/logrus
```
## 🔧 集成步骤
### 1. 创建 Logrus 适配器
创建一个实现 `mcp.Logger` 接口的适配器:
```go
package main
import (
"github.com/sirupsen/logrus"
"nofx/mcp"
)
// LogrusLogger Logrus 日志适配器
type LogrusLogger struct {
logger *logrus.Logger
}
// NewLogrusLogger 创建 Logrus 日志适配器
func NewLogrusLogger(logger *logrus.Logger) *LogrusLogger {
return &LogrusLogger{logger: logger}
}
// Debugf 实现 Debug 日志
func (l *LogrusLogger) Debugf(format string, args ...any) {
l.logger.Debugf(format, args...)
}
// Infof 实现 Info 日志
func (l *LogrusLogger) Infof(format string, args ...any) {
l.logger.Infof(format, args...)
}
// Warnf 实现 Warn 日志
func (l *LogrusLogger) Warnf(format string, args ...any) {
l.logger.Warnf(format, args...)
}
// Errorf 实现 Error 日志
func (l *LogrusLogger) Errorf(format string, args ...any) {
l.logger.Errorf(format, args...)
}
```
### 2. 使用 Logrus Logger
```go
package main
import (
"github.com/sirupsen/logrus"
"nofx/mcp"
)
func main() {
// 1. 创建 Logrus logger
logger := logrus.New()
// 2. 配置 Logrus
logger.SetLevel(logrus.DebugLevel)
logger.SetFormatter(&logrus.JSONFormatter{})
// 3. 创建适配器
logrusAdapter := NewLogrusLogger(logger)
// 4. 使用 MCP 客户端
client := mcp.NewClient(
mcp.WithDeepSeekConfig("sk-xxx"),
mcp.WithLogger(logrusAdapter), // 注入 Logrus 日志器
)
// 5. 调用 AI
result, err := client.CallWithMessages("system", "user")
if err != nil {
logger.Errorf("AI 调用失败: %v", err)
return
}
logger.Infof("AI 响应: %s", result)
}
```
## 🎨 高级配置
### JSON 格式输出
```go
logger := logrus.New()
logger.SetFormatter(&logrus.JSONFormatter{
TimestampFormat: "2006-01-02 15:04:05",
PrettyPrint: true,
})
```
输出示例:
```json
{
"level": "info",
"msg": "📡 [Provider: deepseek, Model: deepseek-chat] Request AI Server: BaseURL: https://api.deepseek.com/v1",
"time": "2024-01-15 10:30:45"
}
```
### 添加固定字段
```go
logger := logrus.New()
logger.WithFields(logrus.Fields{
"service": "trading-bot",
"version": "1.0.0",
})
```
### 不同环境配置
```go
func createLogger(env string) *logrus.Logger {
logger := logrus.New()
switch env {
case "production":
// 生产环境JSON 格式,只记录 Info 以上
logger.SetLevel(logrus.InfoLevel)
logger.SetFormatter(&logrus.JSONFormatter{})
case "development":
// 开发环境:文本格式,记录所有级别
logger.SetLevel(logrus.DebugLevel)
logger.SetFormatter(&logrus.TextFormatter{
FullTimestamp: true,
})
case "test":
// 测试环境:静默模式
logger.SetLevel(logrus.FatalLevel)
}
return logger
}
// 使用
logger := createLogger("production")
mcpClient := mcp.NewClient(
mcp.WithLogger(NewLogrusLogger(logger)),
)
```
## 📝 完整示例
```go
package main
import (
"os"
"github.com/sirupsen/logrus"
"nofx/mcp"
)
// LogrusLogger Logrus 适配器
type LogrusLogger struct {
logger *logrus.Logger
}
func NewLogrusLogger(logger *logrus.Logger) *LogrusLogger {
return &LogrusLogger{logger: logger}
}
func (l *LogrusLogger) Debugf(format string, args ...any) {
l.logger.Debugf(format, args...)
}
func (l *LogrusLogger) Infof(format string, args ...any) {
l.logger.Infof(format, args...)
}
func (l *LogrusLogger) Warnf(format string, args ...any) {
l.logger.Warnf(format, args...)
}
func (l *LogrusLogger) Errorf(format string, args ...any) {
l.logger.Errorf(format, args...)
}
func main() {
// 创建 Logrus logger
logger := logrus.New()
logger.SetLevel(logrus.DebugLevel)
logger.SetFormatter(&logrus.TextFormatter{
FullTimestamp: true,
ForceColors: true,
})
logger.SetOutput(os.Stdout)
// 创建 MCP 客户端
client := mcp.NewDeepSeekClientWithOptions(
mcp.WithAPIKey(os.Getenv("DEEPSEEK_API_KEY")),
mcp.WithLogger(NewLogrusLogger(logger)),
mcp.WithMaxRetries(5),
)
// 调用 AI
logger.Info("开始调用 AI...")
result, err := client.CallWithMessages(
"你是一个专业的量化交易顾问",
"分析 BTC 当前走势",
)
if err != nil {
logger.WithError(err).Error("AI 调用失败")
return
}
logger.WithField("result", result).Info("AI 调用成功")
}
```
## 🔍 输出示例
### 开发环境Text 格式)
```
INFO[2024-01-15 10:30:45] 开始调用 AI...
INFO[2024-01-15 10:30:45] 📡 [Provider: deepseek, Model: deepseek-chat] Request AI Server: BaseURL: https://api.deepseek.com/v1
DEBUG[2024-01-15 10:30:45] [Provider: deepseek, Model: deepseek-chat] UseFullURL: false
DEBUG[2024-01-15 10:30:45] [Provider: deepseek, Model: deepseek-chat] API Key: sk-x...xxx
INFO[2024-01-15 10:30:45] 📡 [MCP Provider: deepseek, Model: deepseek-chat] 请求 URL: https://api.deepseek.com/v1/chat/completions
INFO[2024-01-15 10:30:46] AI 调用成功 result="[AI 响应内容]"
```
### 生产环境JSON 格式)
```json
{"level":"info","msg":"开始调用 AI...","time":"2024-01-15T10:30:45+08:00"}
{"level":"info","msg":"📡 [Provider: deepseek, Model: deepseek-chat] Request AI Server: BaseURL: https://api.deepseek.com/v1","time":"2024-01-15T10:30:45+08:00"}
{"level":"info","msg":"AI 调用成功","result":"[AI 响应内容]","time":"2024-01-15T10:30:46+08:00"}
```
## 🎯 最佳实践
1. **生产环境使用 JSON 格式**,便于日志收集和分析
2. **开发环境使用 Text 格式**,便于阅读
3. **测试环境关闭日志**,提高测试速度
4. **添加请求 ID**,方便追踪请求链路
5. **记录错误堆栈**,便于问题排查
## 📊 性能优化
Logrus 在高并发场景下可能有性能瓶颈,推荐使用 [Zap](https://github.com/uber-go/zap) 获得更好的性能。
MCP 模块也支持 Zap集成方式类似。
## 🔗 相关资源
- [Logrus 官方文档](https://github.com/sirupsen/logrus)
- [Zap 集成示例](./ZAP_INTEGRATION.md)
- [MCP README](./README.md)

View File

@@ -0,0 +1,361 @@
# MCP 模块重构迁移指南
## 📋 重构概览
本次重构采用**渐进式、向前兼容**的设计,现有代码**无需修改**即可继续使用,同时提供了更强大的新 API。
### 重构目标
-**100% 向前兼容** - 所有现有 API 继续工作
-**模块独立** - 可作为独立 Go module 发布
-**依赖可替换** - 日志、HTTP 客户端都可自定义
-**易于测试** - 支持依赖注入和 mock
-**配置灵活** - 支持选项模式 (Functional Options)
---
## 🔄 向前兼容保证
### ✅ 所有现有代码继续工作
```go
// ✅ 这些代码无需修改,继续正常工作
mcpClient := mcp.New()
mcpClient.SetAPIKey(apiKey, url, model)
// ✅ 这些也继续工作
dsClient := mcp.NewDeepSeekClient()
qwenClient := mcp.NewQwenClient()
```
**重要**:虽然标记为 `Deprecated`,但这些函数会一直保留,不会被删除。
---
## 🆕 新特性使用指南
### 1. 基础用法(推荐)
```go
// 新的推荐用法
client := mcp.NewClient(
mcp.WithDeepSeekConfig("sk-xxx"),
mcp.WithTimeout(60 * time.Second),
)
```
### 2. 自定义日志
```go
// 使用自定义日志器(如 zap, logrus
type MyLogger struct {
zapLogger *zap.Logger
}
func (l *MyLogger) Info(msg string, args ...any) {
l.zapLogger.Sugar().Infof(msg, args...)
}
// 注入自定义日志器
client := mcp.NewClient(
mcp.WithLogger(&MyLogger{zapLogger}),
)
```
### 3. 自定义 HTTP 客户端
```go
// 添加代理、追踪、自定义 TLS 等
customHTTP := &http.Client{
Timeout: 30 * time.Second,
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{/* ... */},
},
}
client := mcp.NewClient(
mcp.WithHTTPClient(customHTTP),
)
```
### 4. 测试场景
```go
func TestMyCode(t *testing.T) {
// Mock HTTP 客户端
mockHTTP := &MockHTTPClient{
// 返回预设的响应
}
// 禁用日志
client := mcp.NewClient(
mcp.WithHTTPClient(mockHTTP),
mcp.WithLogger(mcp.NewNoopLogger()),
)
// 测试...
}
```
### 5. 组合多个选项
```go
client := mcp.NewDeepSeekClientWithOptions(
mcp.WithAPIKey("sk-xxx"),
mcp.WithLogger(customLogger),
mcp.WithTimeout(60 * time.Second),
mcp.WithMaxRetries(5),
mcp.WithMaxTokens(4000),
)
```
---
## 📊 API 对比表
### 构造函数对比
| 旧 API (仍可用) | 新 API (推荐) | 说明 |
|----------------|--------------|------|
| `mcp.New()` | `mcp.NewClient(opts...)` | 支持选项模式 |
| `mcp.NewDeepSeekClient()` | `mcp.NewDeepSeekClientWithOptions(opts...)` | 支持自定义配置 |
| `mcp.NewQwenClient()` | `mcp.NewQwenClientWithOptions(opts...)` | 支持自定义配置 |
### 配置选项
| 选项函数 | 说明 | 使用示例 |
|---------|------|---------|
| `WithLogger(logger)` | 自定义日志器 | `WithLogger(zapLogger)` |
| `WithHTTPClient(client)` | 自定义 HTTP 客户端 | `WithHTTPClient(customHTTP)` |
| `WithTimeout(duration)` | 设置超时 | `WithTimeout(60*time.Second)` |
| `WithMaxRetries(n)` | 设置重试次数 | `WithMaxRetries(5)` |
| `WithMaxTokens(n)` | 设置最大 token | `WithMaxTokens(4000)` |
| `WithTemperature(t)` | 设置温度参数 | `WithTemperature(0.7)` |
| `WithAPIKey(key)` | 设置 API Key | `WithAPIKey("sk-xxx")` |
| `WithDeepSeekConfig(key)` | 快速配置 DeepSeek | `WithDeepSeekConfig("sk-xxx")` |
| `WithQwenConfig(key)` | 快速配置 Qwen | `WithQwenConfig("sk-xxx")` |
---
## 🔧 迁移步骤
### Phase 1: 继续使用现有代码(无需改动)
```go
// trader/auto_trader.go 中的现有代码
mcpClient := mcp.New()
if config.AIModel == "qwen" {
mcpClient = mcp.NewQwenClient()
mcpClient.SetAPIKey(config.QwenKey, config.CustomAPIURL, config.CustomModelName)
} else {
mcpClient = mcp.NewDeepSeekClient()
mcpClient.SetAPIKey(config.DeepSeekKey, config.CustomAPIURL, config.CustomModelName)
}
// ✅ 继续工作,无需修改
```
### Phase 2: 可选升级到新 API推荐
```go
// 升级后的代码(可选)
var mcpClient mcp.AIClient
if config.AIModel == "qwen" {
mcpClient = mcp.NewQwenClientWithOptions(
mcp.WithAPIKey(config.QwenKey),
mcp.WithBaseURL(config.CustomAPIURL),
mcp.WithModel(config.CustomModelName),
)
} else {
mcpClient = mcp.NewDeepSeekClientWithOptions(
mcp.WithAPIKey(config.DeepSeekKey),
mcp.WithBaseURL(config.CustomAPIURL),
mcp.WithModel(config.CustomModelName),
)
}
```
### Phase 3: 添加自定义配置(高级)
```go
// 添加自定义日志
customLogger := &MyZapLogger{zap.NewProduction()}
mcpClient := mcp.NewDeepSeekClientWithOptions(
mcp.WithAPIKey(config.DeepSeekKey),
mcp.WithLogger(customLogger), // 自定义日志
mcp.WithTimeout(90 * time.Second), // 自定义超时
mcp.WithMaxRetries(5), // 自定义重试次数
)
```
---
## 🎯 实际使用场景
### 场景 1: 开发环境详细日志
```go
// 开发环境:使用详细日志
devClient := mcp.NewClient(
mcp.WithDeepSeekConfig(apiKey),
mcp.WithLogger(&defaultLogger{}), // 详细日志
)
```
### 场景 2: 生产环境结构化日志
```go
// 生产环境:使用 zap 结构化日志
zapLogger, _ := zap.NewProduction()
prodClient := mcp.NewClient(
mcp.WithDeepSeekConfig(apiKey),
mcp.WithLogger(&ZapLogger{zapLogger}),
)
```
### 场景 3: 测试环境 Mock
```go
// 测试环境Mock HTTP 响应
mockHTTP := &MockHTTPClient{
Response: `{"choices":[{"message":{"content":"test"}}]}`,
}
testClient := mcp.NewClient(
mcp.WithHTTPClient(mockHTTP),
mcp.WithLogger(mcp.NewNoopLogger()), // 禁用日志
)
```
### 场景 4: 需要代理的网络环境
```go
// 使用代理
proxyURL, _ := url.Parse("http://proxy.company.com:8080")
proxyClient := &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyURL(proxyURL),
},
}
client := mcp.NewClient(
mcp.WithDeepSeekConfig(apiKey),
mcp.WithHTTPClient(proxyClient),
)
```
---
## 📦 作为独立模块发布
重构后mcp 模块可以独立发布:
### go.mod
```go
module github.com/yourorg/mcp
go 1.21
// 无外部依赖!
```
### 使用方
```go
import "github.com/yourorg/mcp"
client := mcp.NewClient(
mcp.WithDeepSeekConfig("sk-xxx"),
)
```
---
## 🧪 测试支持
### Mock 示例
```go
package mypackage_test
import (
"testing"
"github.com/stretchr/testify/assert"
"nofx/mcp"
)
type MockHTTPClient struct {
Response string
Error error
}
func (m *MockHTTPClient) Do(req *http.Request) (*http.Response, error) {
if m.Error != nil {
return nil, m.Error
}
return &http.Response{
StatusCode: 200,
Body: io.NopCloser(strings.NewReader(m.Response)),
}, nil
}
func TestAIIntegration(t *testing.T) {
// Arrange
mockHTTP := &MockHTTPClient{
Response: `{"choices":[{"message":{"content":"success"}}]}`,
}
client := mcp.NewClient(
mcp.WithHTTPClient(mockHTTP),
mcp.WithLogger(mcp.NewNoopLogger()),
)
// Act
result, err := client.CallWithMessages("system", "user")
// Assert
assert.NoError(t, err)
assert.Equal(t, "success", result)
}
```
---
## ⚠️ 注意事项
1. **向前兼容性**
- 所有 `Deprecated` 的 API 会永久保留
- 现有代码可以继续使用,不会被破坏
2. **渐进式迁移**
- 不需要一次性迁移所有代码
- 可以逐步采用新 API
3. **配置优先级**
- 用户传入的选项优先级最高
- 环境变量次之
- 默认配置最低
4. **日志器接口**
- 可以适配任何日志库zap, logrus, etc.
- 测试时可以使用 `NewNoopLogger()` 禁用日志
---
## 📚 进一步阅读
- [选项模式详解](https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis)
- [依赖注入最佳实践](https://go.dev/blog/wire)
- [Go 接口设计原则](https://go.dev/blog/laws-of-reflection)
---
## 🤝 贡献
欢迎提交 issue 和 PR
如有问题,请联系:[your-email@example.com]

379
mcp/intro/README.md Normal file
View File

@@ -0,0 +1,379 @@
# MCP - Model Context Protocol Client
一个灵活、可扩展的 AI 模型客户端库,支持 DeepSeek、Qwen 等多种 AI 提供商。
## ✨ 特性
- 🔌 **多 Provider 支持** - DeepSeek、Qwen、OpenAI 兼容 API
- 🎯 **模板方法模式** - 固定流程,可扩展步骤
- 🏗️ **构建器模式** - 支持多轮对话、Function Calling、精细参数控制
- 📦 **零外部依赖** - 仅使用 Go 标准库
- 🔧 **高度可配置** - 支持 Functional Options 模式
- 🧪 **易于测试** - 支持依赖注入和 Mock
-**向前兼容** - 现有代码无需修改
- 📝 **丰富的日志** - 可替换的日志接口
## 🚀 快速开始
### 基础用法
```go
import "nofx/mcp"
// 创建客户端
client := mcp.NewClient(
mcp.WithDeepSeekConfig("sk-xxx"),
)
// 调用 AI
result, err := client.CallWithMessages("system prompt", "user prompt")
if err != nil {
log.Fatal(err)
}
fmt.Println(result)
```
### DeepSeek 客户端
```go
client := mcp.NewDeepSeekClientWithOptions(
mcp.WithAPIKey("sk-xxx"),
mcp.WithTimeout(60 * time.Second),
)
```
### Qwen 客户端
```go
client := mcp.NewQwenClientWithOptions(
mcp.WithAPIKey("sk-xxx"),
mcp.WithMaxTokens(4000),
)
```
### 🏗️ 构建器模式(高级功能)
构建器模式支持多轮对话、精细参数控制、Function Calling 等高级功能。
#### 简单用法
```go
// 使用构建器创建请求
request := mcp.NewRequestBuilder().
WithSystemPrompt("You are helpful").
WithUserPrompt("What is Go?").
WithTemperature(0.8).
Build()
result, err := client.CallWithRequest(request)
```
#### 多轮对话
```go
// 构建包含历史的多轮对话
request := mcp.NewRequestBuilder().
AddSystemMessage("You are a trading advisor").
AddUserMessage("Analyze BTC").
AddAssistantMessage("BTC is bullish...").
AddUserMessage("What about entry point?"). // 继续对话
WithTemperature(0.3).
Build()
result, err := client.CallWithRequest(request)
```
#### 预设场景
```go
// 代码生成(低温度、精确)
request := mcp.ForCodeGeneration().
WithUserPrompt("Generate a HTTP server").
Build()
// 创意写作(高温度、随机)
request := mcp.ForCreativeWriting().
WithUserPrompt("Write a story").
Build()
// 聊天(平衡参数)
request := mcp.ForChat().
WithUserPrompt("Hello").
Build()
```
#### Function Calling
```go
// 定义工具
weatherParams := map[string]any{
"type": "object",
"properties": map[string]any{
"location": map[string]any{"type": "string"},
},
}
request := mcp.NewRequestBuilder().
WithUserPrompt("北京天气怎么样?").
AddFunction("get_weather", "Get weather", weatherParams).
WithToolChoice("auto").
Build()
result, err := client.CallWithRequest(request)
```
## 📖 详细文档
- [构建器模式完整示例](./BUILDER_EXAMPLES.md) - 多轮对话、Function Calling、参数控制
- [构建器模式价值分析](./BUILDER_PATTERN_BENEFITS.md) - 为什么引入构建器模式
- [迁移指南](./MIGRATION_GUIDE.md) - 从旧 API 迁移到新 API
- [Logrus 集成](./LOGRUS_INTEGRATION.md) - 日志框架集成示例
- [代码审查报告](./CODE_REVIEW.md) - 问题分析和修复记录
## 🎛️ 配置选项
### 依赖注入
```go
// 自定义日志器
mcp.WithLogger(customLogger)
// 自定义 HTTP 客户端
mcp.WithHTTPClient(customHTTP)
```
### 超时和重试
```go
mcp.WithTimeout(60 * time.Second)
mcp.WithMaxRetries(5)
mcp.WithRetryWaitBase(3 * time.Second)
```
### AI 参数
```go
mcp.WithMaxTokens(4000)
mcp.WithTemperature(0.7)
```
### Provider 配置
```go
// 快速配置 DeepSeek
mcp.WithDeepSeekConfig("sk-xxx")
// 快速配置 Qwen
mcp.WithQwenConfig("sk-xxx")
// 自定义配置
mcp.WithAPIKey("sk-xxx")
mcp.WithBaseURL("https://api.custom.com")
mcp.WithModel("gpt-4")
```
## 🧪 测试
```go
// 使用 Mock HTTP 客户端
mockHTTP := &MockHTTPClient{
Response: `{"choices":[{"message":{"content":"test"}}]}`,
}
client := mcp.NewClient(
mcp.WithHTTPClient(mockHTTP),
mcp.WithLogger(mcp.NewNoopLogger()), // 禁用日志
)
```
## 🏗️ 架构设计
### 模板方法模式
```
CallWithMessages (固定重试流程)
call (固定调用流程)
hooks (可重写的步骤)
├─ buildMCPRequestBody
├─ marshalRequestBody
├─ buildUrl
├─ setAuthHeader
├─ parseMCPResponse
└─ isRetryableError
```
### 接口分离
```go
// 公开接口(给外部使用)
type AIClient interface {
SetAPIKey(...)
SetTimeout(...)
CallWithMessages(...) (string, error)
}
// 内部钩子接口(供子类重写)
type clientHooks interface {
buildMCPRequestBody(...) map[string]any
buildUrl() string
setAuthHeader(...)
marshalRequestBody(...) ([]byte, error)
parseMCPResponse(...) (string, error)
isRetryableError(...) bool
}
```
## 🔄 向前兼容
所有旧 API 继续工作:
```go
// ✅ 旧代码无需修改
client := mcp.New()
client.SetAPIKey("sk-xxx", "https://api.custom.com", "gpt-4")
dsClient := mcp.NewDeepSeekClient()
dsClient.SetAPIKey("sk-xxx", "", "")
```
## 📦 作为独立模块使用
```go
// go.mod
module github.com/yourorg/yourproject
require github.com/yourorg/mcp v1.0.0
```
```go
// main.go
import "github.com/yourorg/mcp"
client := mcp.NewClient(
mcp.WithDeepSeekConfig("sk-xxx"),
)
```
## 🤝 扩展自定义 Provider
```go
type CustomProvider struct {
*mcp.Client
}
// 重写特定钩子
func (c *CustomProvider) buildUrl() string {
return c.BaseURL + "/custom/endpoint"
}
func (c *CustomProvider) setAuthHeader(headers http.Header) {
headers.Set("X-Custom-Auth", c.APIKey)
}
```
## 📝 日志器适配示例
### Zap 日志器
```go
type ZapLogger struct {
logger *zap.Logger
}
func (l *ZapLogger) Infof(format string, args ...any) {
l.logger.Sugar().Infof(format, args...)
}
func (l *ZapLogger) Debugf(format string, args ...any) {
l.logger.Sugar().Debugf(format, args...)
}
// 使用
client := mcp.NewClient(
mcp.WithLogger(&ZapLogger{zapLogger}),
)
```
### Logrus 日志器
```go
type LogrusLogger struct {
logger *logrus.Logger
}
func (l *LogrusLogger) Infof(format string, args ...any) {
l.logger.Infof(format, args...)
}
func (l *LogrusLogger) Debugf(format string, args ...any) {
l.logger.Debugf(format, args...)
}
```
## 🎯 使用场景
### 开发环境
```go
devClient := mcp.NewClient(
mcp.WithDeepSeekConfig("sk-xxx"),
mcp.WithLogger(&customLogger{}), // 详细日志
)
```
### 生产环境
```go
prodClient := mcp.NewClient(
mcp.WithDeepSeekConfig("sk-xxx"),
mcp.WithLogger(&zapLogger{}), // 结构化日志
mcp.WithTimeout(30*time.Second), // 超时保护
mcp.WithMaxRetries(3), // 重试保护
)
```
### 测试环境
```go
testClient := mcp.NewClient(
mcp.WithHTTPClient(mockHTTP),
mcp.WithLogger(mcp.NewNoopLogger()),
)
```
## 📊 性能特性
- ✅ HTTP 连接复用
- ✅ 智能重试机制
- ✅ 可配置超时
- ✅ 零分配日志(使用 NoopLogger
## 🛡️ 安全性
- ✅ API Key 部分脱敏日志
- ✅ HTTPS 默认启用
- ✅ 支持自定义 TLS 配置
- ✅ 请求超时保护
## 📈 版本兼容性
- Go 1.18+
- 向前兼容保证
- 语义化版本管理
## 🤝 贡献
欢迎提交 Issue 和 Pull Request
## 📄 许可证
MIT License
## 🔗 相关链接
- [DeepSeek API 文档](https://platform.deepseek.com/docs)
- [Qwen API 文档](https://help.aliyun.com/zh/dashscope/)
- [OpenAI API 文档](https://platform.openai.com/docs)