Skip to content

Conversation

@ljh740
Copy link
Contributor

@ljh740 ljh740 commented Nov 6, 2025

📝 Description | 描述

中文:
增加AI请求耗时记录,让你知道为啥你的决策总是和市场偏离


🎯 Type of Change | 变更类型

  • 🐛 Bug fix | 修复 Bug
  • [x ] ✨ New feature | 新功能
  • 💥 Breaking change | 破坏性变更
  • 📝 Documentation update | 文档更新
  • 🎨 Code style update | 代码样式更新
  • ♻️ Refactoring | 重构
  • ⚡ Performance improvement | 性能优化
  • ✅ Test update | 测试更新
  • 🔧 Build/config change | 构建/配置变更
  • 🔒 Security fix | 安全修复

🧪 Testing | 测试

  • [x ] Tested locally | 本地测试通过
  • Tests pass | 测试通过
  • [x ] Verified no existing functionality broke | 确认没有破坏现有功能

✅ Checklist | 检查清单

Code Quality | 代码质量

  • [ x] Code follows project style | 代码遵循项目风格
  • [ x] Self-review completed | 已完成代码自查
  • [ x] Comments added for complex logic | 已添加必要注释

Documentation | 文档

  • Updated relevant documentation | 已更新相关文档

Git

  • Commits follow conventional format | 提交遵循 Conventional Commits 格式
  • Rebased on latest dev branch | 已 rebase 到最新 dev 分支
  • No merge conflicts | 无合并冲突

By submitting this PR, I confirm | 提交此 PR,我确认:

  • [ x] I have read the Contributing Guidelines | 已阅读贡献指南
  • [ x] I agree to the Code of Conduct | 同意行为准则
  • [ x] My contribution is licensed under AGPL-3.0 | 贡献遵循 AGPL-3.0 许可证

🌟 Thank you for your contribution! | 感谢你的贡献!

@xqliu
Copy link
Contributor

xqliu commented Nov 8, 2025

代码审查报告 - PR #587

📋 基本信息

  • PR标题: feat: 添加AI请求耗时记录,优化性能评估
  • 作者: @ljh740
  • 类型: ✨ New feature (性能监控)
  • 修改文件: decision/engine.go (14行新增), logger/decision_logger.go (2行新增), trader/auto_trader.go (7行新增)

1️⃣ 业务逻辑审查

✅ 问题定义

核心需求: 监控AI API调用耗时,用于性能分析

业务场景:

问题: 用户反馈决策延迟,但不知道是哪个环节慢
- AI API调用?
- 市场数据获取?
- 决策执行?
- 网络延迟?

需求: 精确记录AI API调用耗时,定位性能瓶颈

实际影响:

场景: 高频交易时,决策延迟可能导致:
- 错过最佳入场点
- 价格滑点增加
- 止损不及时

有了耗时记录:
- 可识别慢请求(如>5秒)
- 可对比不同AI模型性能(GPT-4 vs Claude vs DeepSeek)
- 可评估网络质量

✅ 解决方案验证

监控策略: 在AI调用前后记录时间戳

aiCallStart := time.Now()
aiResponse, err := mcpClient.CallWithMessages(systemPrompt, userPrompt)
aiCallDuration := time.Since(aiCallStart)

// 保存到决策记录
decision.AIRequestDurationMs = aiCallDuration.Milliseconds()

数据流向:

1. decision/engine.go: 记录AI调用耗时 → FullDecision.AIRequestDurationMs
2. trader/auto_trader.go: 复制到DecisionRecord.AIRequestDurationMs
3. logger/decision_logger.go: 持久化到JSON文件
4. 后续可用于: 性能分析、慢请求告警、模型对比

业务价值: ✅ 显著提升可观测性

  • 性能诊断: 快速定位慢请求
  • 模型选择: 对比不同模型响应时间
  • 成本优化: 识别需要升级网络的场景

2️⃣ 技术实现审查

✅ 修改1: decision/engine.go - 添加耗时字段

// decision/engine.go:112
type FullDecision struct {
    CoTTrace     string     `json:"cot_trace"`
    Decisions    []Decision `json:"decisions"`
    Timestamp    time.Time  `json:"timestamp"`
    // ✅ 新增: AI API 调用耗时(毫秒)
    AIRequestDurationMs int64 `json:"ai_request_duration_ms,omitempty"`
}

技术评价: ✅ 正确

  • 字段命名清晰: AIRequestDurationMs(毫秒)
  • JSON标签: omitempty(值为0时不序列化,节省空间)
  • 注释完整: 说明用途("方便排查延迟问题")

✅ 修改2: 记录AI调用耗时

修改前(无监控):

// decision/engine.go:131
aiResponse, err := mcpClient.CallWithMessages(systemPrompt, userPrompt)
if err != nil {
    return nil, fmt.Errorf("调用AI API失败: %w", err)
}

修改后(有监控):

// decision/engine.go:134
aiCallStart := time.Now()  // ✅ 记录开始时间
aiResponse, err := mcpClient.CallWithMessages(systemPrompt, userPrompt)
aiCallDuration := time.Since(aiCallStart)  // ✅ 计算耗时
if err != nil {
    return nil, fmt.Errorf("调用AI API失败: %w", err)
}

// 解析AI响应
decision, err := parseFullDecisionResponse(...)

// ✅ 无论是否有错误,都保存耗时
if decision != nil {
    decision.Timestamp = time.Now()
    decision.SystemPrompt = systemPrompt
    decision.UserPrompt = userPrompt
    decision.AIRequestDurationMs = aiCallDuration.Milliseconds()  // ✅ 保存耗时
}

技术亮点:

  1. 时间精度: 使用time.Now()time.Since()(纳秒级精度)
  2. 错误容忍: 即使解析失败,仍记录耗时(有助于诊断API问题)
  3. 单位统一: 统一使用毫秒(.Milliseconds()

✅ 修改3: logger/decision_logger.go - 日志字段

// logger/decision_logger.go:27
type DecisionRecord struct {
    ExecutionLog   []string           `json:"execution_log"`
    Success        bool               `json:"success"`
    ErrorMessage   string             `json:"error_message"`
    // ✅ 新增: AI API 调用耗时
    AIRequestDurationMs int64 `json:"ai_request_duration_ms,omitempty"`
}

技术评价: ✅ 正确

  • 字段一致性: 与FullDecision保持一致
  • 注释说明: "方便评估调用性能"

✅ 修改4: trader/auto_trader.go - 复制耗时并打印日志

// trader/auto_trader.go:451
decision, err := decision.GetFullDecisionWithCustomPrompt(ctx, at.mcpClient, ...)

if decision != nil && decision.AIRequestDurationMs > 0 {
    // ✅ 复制耗时到record
    record.AIRequestDurationMs = decision.AIRequestDurationMs
    
    // ✅ 打印日志(便于实时监控)
    log.Printf("⏱️ AI调用耗时: %.2f 秒", float64(record.AIRequestDurationMs)/1000)
    
    // ✅ 添加到执行日志
    record.ExecutionLog = append(record.ExecutionLog,
        fmt.Sprintf("AI调用耗时: %d ms", record.AIRequestDurationMs))
}

技术亮点:

  1. 双重记录: 既打印到控制台,也写入执行日志
  2. 格式化: 日志显示秒数(更易读),JSON保存毫秒(更精确)
  3. 条件检查: decision.AIRequestDurationMs > 0(避免记录无效数据)

3️⃣ 端到端验证

✅ 核心场景测试

场景1: 正常AI调用

操作: trader执行一个决策周期

流程:
1. auto_trader.runCycle()
2. decision.GetFullDecisionWithCustomPrompt()
   → aiCallStart = time.Now()
   → mcpClient.CallWithMessages() (耗时2.5秒)
   → aiCallDuration = 2500ms
3. decision.AIRequestDurationMs = 2500
4. record.AIRequestDurationMs = 2500
5. 日志输出: "⏱️ AI调用耗时: 2.50 秒"
6. JSON文件: {"ai_request_duration_ms": 2500}

验证:
- ✅ 日志正确显示
- ✅ JSON正确保存
- ✅ ExecutionLog包含耗时

场景2: AI调用失败

操作: AI API返回错误(如网络超时)

流程:
1. aiCallStart = time.Now()
2. mcpClient.CallWithMessages() → 返回错误(耗时30秒)
3. aiCallDuration = 30000ms
4. parseFullDecisionResponse() → 返回nil decision
5. decision == nil → 不记录耗时

问题: ❌ AI调用失败时,耗时丢失(无法分析慢请求)

建议: 即使decision为nil,也记录耗时到record

场景3: 慢请求识别

场景: GPT-4调用耗时15秒(异常慢)

日志输出:
"⏱️ AI调用耗时: 15.00 秒"  ← 用户可立即看到

后续分析:
1. 查询decision_logs/*.json
2. 过滤 ai_request_duration_ms > 10000
3. 识别慢请求模式:
   - 特定时间段(如高峰期)
   - 特定模型(如GPT-4 vs Claude)
   - 特定prompt长度

✅ 边界条件

边界1: 极快请求(<1ms)

场景: 本地模拟AI,响应时间<1ms

aiCallDuration.Milliseconds() = 0

JSON输出:
- 有omitempty: {} (字段省略)✅
- 无omitempty: {"ai_request_duration_ms": 0} (占用空间)

当前实现: ✅ 使用omitempty,节省空间

边界2: 极慢请求(>60s)

场景: 网络超时,AI API耗时90秒

aiCallDuration.Milliseconds() = 90000

JSON输出: {"ai_request_duration_ms": 90000} ✅

建议: 添加超时告警
if record.AIRequestDurationMs > 60000 {
    log.Printf("⚠️ AI调用耗时过长: %.2f 秒,可能存在性能问题", ...)
}

边界3: decision为nil

场景: AI返回错误,decision为nil

当前实现:
if decision != nil && decision.AIRequestDurationMs > 0 {
    record.AIRequestDurationMs = decision.AIRequestDurationMs
}

问题: decision为nil时,耗时丢失 ❌

建议修复:
if decision != nil {
    if decision.AIRequestDurationMs > 0 {
        record.AIRequestDurationMs = decision.AIRequestDurationMs
    }
} else {
    // 即使decision为nil,也从局部变量记录耗时
    record.AIRequestDurationMs = aiCallDuration.Milliseconds()
}

4️⃣ 性能与监控审查

✅ 性能影响

额外开销:

  • time.Now(): ~20ns(纳秒级,可忽略)
  • time.Since(): ~20ns
  • Milliseconds(): 整数除法,<1ns
  • 总开销: <100ns(完全可以忽略)

存储开销:

  • JSON字段: 8字节(int64)
  • 每天100个决策: 800字节
  • 年存储: <300KB(可忽略)

评价: ✅ 性能影响微乎其微

✅ 可观测性提升

日志示例:

2025-11-08 10:30:15 🤖 正在请求AI分析并决策... [模板: default]
2025-11-08 10:30:17 ⏱️ AI调用耗时: 2.35 秒
2025-11-08 10:30:18 ✅ 决策执行完成

JSON示例:

{
  "cycle": 42,
  "timestamp": "2025-11-08T10:30:15Z",
  "ai_request_duration_ms": 2350,
  "execution_log": [
    "开始获取市场数据",
    "AI调用耗时: 2350 ms",
    "执行决策: BTCUSDT LONG"
  ],
  "success": true
}

分析价值:

  1. 实时监控: 控制台日志立即显示
  2. 历史分析: JSON文件可用于离线分析
  3. 性能对比: 对比不同模型、不同时段

5️⃣ 代码质量

✅ 优点

  1. 代码简洁: 仅23行新增,无破坏性变更
  2. 注释清晰: 每个新字段都有中文注释
  3. 向后兼容: omitempty确保旧数据仍可解析
  4. 双重记录: 日志+JSON,满足不同需求

⚠️ 建议改进

1. 修复decision为nil时耗时丢失

// trader/auto_trader.go:448(建议修复)
decision, err := decision.GetFullDecisionWithCustomPrompt(ctx, at.mcpClient, ...)

// ✅ 方案1: 在engine.go中,即使出错也返回耗时
// decision/engine.go:
if err != nil {
    // 仍然返回部分decision(包含耗时)
    return &FullDecision{
        Timestamp: time.Now(),
        AIRequestDurationMs: aiCallDuration.Milliseconds(),
    }, fmt.Errorf("调用AI API失败: %w", err)
}

// ✅ 方案2: 在auto_trader.go中,单独传递耗时
// (需要修改GetFullDecisionWithCustomPrompt返回值)
decision, aiDuration, err := decision.GetFullDecisionWithCustomPrompt(...)
if aiDuration > 0 {
    record.AIRequestDurationMs = aiDuration.Milliseconds()
}

2. 添加慢请求告警

// trader/auto_trader.go:454(建议增强)
if decision != nil && decision.AIRequestDurationMs > 0 {
    record.AIRequestDurationMs = decision.AIRequestDurationMs
    
    durationSec := float64(record.AIRequestDurationMs) / 1000
    log.Printf("⏱️ AI调用耗时: %.2f 秒", durationSec)
    
    // ✅ 添加慢请求告警
    if record.AIRequestDurationMs > 10000 {  // 超过10秒
        log.Printf("⚠️ [慢请求] AI调用耗时过长: %.2f 秒,请检查网络或AI服务状态", durationSec)
    }
    
    record.ExecutionLog = append(record.ExecutionLog,
        fmt.Sprintf("AI调用耗时: %d ms", record.AIRequestDurationMs))
}

3. 添加Prometheus指标(可选)

// 建议添加到trader/metrics.go
var aiRequestDuration = prometheus.NewHistogramVec(
    prometheus.HistogramOpts{
        Name: "nofx_ai_request_duration_seconds",
        Help: "AI API request duration in seconds",
        Buckets: []float64{0.5, 1, 2, 5, 10, 30, 60},
    },
    []string{"trader_id", "model"},
)

// 在auto_trader.go中记录
aiRequestDuration.WithLabelValues(at.id, at.modelName).Observe(durationSec)

4. 添加单元测试

// decision/engine_test.go(建议新增)
func TestAIRequestDurationRecording(t *testing.T) {
    // Mock AI client with 2 second delay
    mockClient := &MockMCPClient{
        Delay: 2 * time.Second,
    }
    
    start := time.Now()
    decision, err := GetFullDecisionWithCustomPrompt(ctx, mockClient, "", false, "default")
    elapsed := time.Since(start)
    
    assert.NoError(t, err)
    assert.NotNil(t, decision)
    
    // 验证耗时记录准确(允许±100ms误差)
    assert.InDelta(t, 2000, decision.AIRequestDurationMs, 100)
    assert.InDelta(t, elapsed.Milliseconds(), decision.AIRequestDurationMs, 100)
}

📊 总结

✅ 审查结果: 通过(建议修复nil处理)

维度 评分 说明
业务价值 ⭐⭐⭐⭐⭐ 显著提升性能可观测性
技术实现 ⭐⭐⭐⭐ 实现正确,但decision为nil时耗时丢失
性能影响 ⭐⭐⭐⭐⭐ 开销可忽略(<100ns)
可维护性 ⭐⭐⭐⭐⭐ 代码简洁,注释清晰

🎯 核心价值

  1. 性能诊断: 快速识别慢请求和性能瓶颈
  2. 模型对比: 对比不同AI模型响应时间(GPT-4 vs Claude vs DeepSeek)
  3. 运维告警: 可设置慢请求阈值告警

🔧 行动建议

必须修复: 无
强烈建议:

  1. 修复decision为nil时耗时丢失问题(方案1或方案2)
  2. 添加慢请求告警(如>10秒)

可选优化:

  1. 添加Prometheus指标(集成到监控系统)
  2. 添加单元测试验证耗时记录准确性
  3. 在Web UI显示AI调用耗时趋势图
  4. 添加耗时百分位统计(P50/P90/P99)

🌟 特别表扬

实用功能: 解决真实用户痛点("为啥决策总是和市场偏离")
实现简洁: 23行代码实现完整监控
向后兼容: 使用omitempty,不影响旧数据

建议后续工作:

  1. 收集1周数据后,分析耗时分布(识别慢请求模式)
  2. 对比不同AI模型的平均响应时间
  3. 根据数据优化模型选择策略(如高频场景选快速模型)

总评: 这是一个高价值的性能监控功能,代码简洁、实现正确、性能影响微乎其微。建议修复decision为nil时的耗时丢失问题,并添加慢请求告警,其他均符合生产级标准。


审查时间: 2025-11-08
审查者: Claude AI Code Reviewer

@hzb1115 hzb1115 merged commit f35f1ad into NoFxAiOS:dev Nov 11, 2025
sparrow211 pushed a commit to sparrow211/nofx that referenced this pull request Nov 11, 2025
@ljh740 ljh740 deleted the feature/dev-aiatime branch November 11, 2025 11:50
bebest2010 pushed a commit to bebest2010/nofx that referenced this pull request Nov 18, 2025
tinkle-community pushed a commit that referenced this pull request Nov 26, 2025
tinkle-community pushed a commit that referenced this pull request Nov 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants