Skip to content

[feat] 普通模式 Session Summary 增强与 CC Session 隔离验证 #79

@hrygo

Description

@hrygo

问题描述

功能 1:普通模式缺少 Block 级别 Session Summary

当前 Unified Block Model 已经为每个 Block 存储 session_stats 字段,但普通模式(normal mode)的会话统计信息不完整。

当前行为

  • Geek/Evolution 模式:显示完整的 SessionSummary(duration, tokens, tools, cost)
  • 普通模式:仅显示基础 duration,缺少 token 统计、工具调用详情等

期望行为

  • 普通模式也应显示完整的 SessionSummary,包括:
    • 思考时间 (thinking_duration_ms)
    • 生成时间 (generation_duration_ms)
    • Token 使用量 (input/output tokens)
    • 工具调用统计 (tools_used, tool_call_count)

功能 2:Geek 与 Evolution 模式 CC Session 隔离验证

后端已实现独立的 CC Session ID 生成逻辑,需要验证前端是否正确区分和显示两个模式的会话统计。

当前实现

  • Geek Mode: UUID v5(null_namespace, "conversation_<id>")
  • Evolution Mode: UUID v5(user_namespace, "evolution_<id>")

需要验证

  • 前端 SessionSummaryPanel 是否正确区分两个模式的统计
  • 模式切换时是否正确加载对应的 session stats

解决方案

架构原则:统计收集下沉到 LLM 层

错误方案:每个 Agent 重复实现 SessionStatsProvider

// 不要这样做
type MemoParrot struct { stats *SessionStats }  // 重复实现
type ScheduleParrot struct { stats *SessionStats }  // 重复实现
type AmazingParrot struct { stats *SessionStats }  // 重复实现

正确方案:LLM Client 层统一收集,Agent 直接获取

// LLM 层统一收集
type LLMClient struct {
    stats *LLMCallStats  // 一次调用完整的统计
}

// Agent 从 LLM 获取
func (p *MemoParrot) GetSessionStats() *SessionStats {
    return p.llm.GetStats()  // 直接使用 LLM 层的统计
}

Phase 1:LLM 层统一统计收集

  1. 扩展 ai/core/llm/ 接口

    • 添加 GetStats() 方法到 LLMClient
    • 记录每次 LLM 调用的:
      • input/output/cache tokens
      • thinking 时间(首字延迟)
      • generation 时间(流式输出总时长)
  2. 定义统一的 LLM 统计结构

    type LLMCallStats struct {
        InputTokens       int
        OutputTokens      int
        CacheWriteTokens  int
        CacheReadTokens   int
        ThinkingDurationMs int64  // 首字延迟
        GenerationDurationMs int64 // 流式输出总时长
        StartTime         time.Time
        EndTime           time.Time
    }
  3. 为所有 Parrot Agent 添加统一的 SessionStatsProvider 实现

    • ai/agent/base_parrot.gotypes.go 中提供基础实现
    • 各 Agent 组合 LLM stats + tool stats

Phase 2:验证 CC Session 隔离

  1. 验证后端 SessionID 生成

    • 确认 Geek 和 Evolution 使用不同的 namespace
    • 确认生成的 SessionID 不会冲突
  2. 前端显示验证

    • 检查 SessionSummaryPanel.tsx 是否正确处理两种模式
    • 确保 Block 的 cc_session_id 字段正确存储和读取

验收标准

  • LLM 层统一收集 token 和时间统计
  • 所有普通 Parrot 通过组合方式实现 SessionStatsProvider
  • 普通模式 Block 显示完整的 SessionSummary
  • Token 统计准确(与 LLM 提供商计费一致)
  • Geek 和 Evolution 模式使用独立的 CC Session
  • 前端 UI 正确显示各自模式的统计信息
  • make test-ai 通过
  • 前端 pnpm lint 通过

技术细节

架构分层

┌─────────────────────────────────────────────────────────┐
│                    Parrot Agents                        │
│  MemoParrot  |  ScheduleParrot  |  AmazingParrot       │
│       │             │                  │                │
│       └─────────────┴──────────────────┘                │
│                     │                                   │
│           GetSessionStats() {                          │
│               return llm.GetStats() + toolStats         │
│           }                                             │
├─────────────────────────────────────────────────────────┤
│                    LLM Layer                            │
│  ┌─────────────────────────────────────────────────┐   │
│  │  LLMClient.GetStats()                           │   │
│  │    - InputTokens, OutputTokens                  │   │
│  │    - ThinkingDurationMs (首字延迟)              │   │
│  │    - GenerationDurationMs (流式总时长)           │   │
│  └─────────────────────────────────────────────────┘   │
├─────────────────────────────────────────────────────────┤
│              SessionStatsProvider (Interface)           │
│  GetSessionStats() *SessionStats                        │
└─────────────────────────────────────────────────────────┘

相关文件

LLM 层(核心修改)

  • ai/core/llm/client.go - 添加统计收集
  • ai/core/llm/stream.go - 记录流式时间
  • ai/core/llm/types.go - LLMCallStats 定义

Agent 层(组合实现)

  • ai/agent/base_parrot.go - 提供基础 SessionStatsProvider 实现(新建)
  • ai/agent/types.go - 接口定义

Handler 层(无需修改,已支持)

  • server/router/api/v1/ai/handler.go:669-706

前端

  • web/src/components/AIChat/SessionSummaryPanel.tsx

接口定义

// LLM 层统计
type LLMCallStats struct {
    InputTokens       int
    OutputTokens      int
    CacheWriteTokens  int
    CacheReadTokens   int
    ThinkingDurationMs int64  // 首字时间
    GenerationDurationMs int64 // 总生成时间
    StartTime         time.Time
    EndTime           time.Time
}

type LLMClient interface {
    // 现有方法...
    GetStats() *LLMCallStats
    ResetStats()  // 开始新一轮对话时重置
}

// Agent 层组合
type BaseParrot struct {
    llm LLMClient
    tools []Tool
}

func (b *BaseParrot) GetSessionStats() *SessionStats {
    llmStats := b.llm.GetStats()
    
    return &SessionStats{
        InputTokens:          llmStats.InputTokens,
        OutputTokens:         llmStats.OutputTokens,
        CacheWriteTokens:     llmStats.CacheWriteTokens,
        CacheReadTokens:      llmStats.CacheReadTokens,
        ThinkingDurationMs:   llmStats.ThinkingDurationMs,
        GenerationDurationMs: llmStats.GenerationDurationMs,
        TotalDurationMs:      llmStats.EndTime.Sub(llmStats.StartTime).Milliseconds(),
        ToolCallCount:        b.countToolCalls(),
        ToolsUsed:            b.getToolsUsed(),
    }
}

优先级

P1 - 功能增强(用户体验提升)+ 架构优化


Co-Authored-By: Claude Opus 4.5 noreply@anthropic.com

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions