Skip to content

feat: LLM 模块重构计划 #293

Description

@ModerRAS

LLM 模块重构计划

TL;DR

Quick Summary: 重构 LLM 模块,消除代码重复、移除静态状态、解耦 Agent/Bot 进程,建立可持续维护的架构。

Deliverables:

  • AbstractLlmService 基类,消除 4 服务重复代码
  • 工具能力标记系统,支持子 Agent 工具控制
  • McpToolHelper 从静态类改为 DI 服务
  • GeneralLLMService 依赖接口而非具体类
  • 所有现有测试通过

Estimated Effort: Medium-Large (3-4 周)
Parallel Execution: YES - 3 waves
Critical Path: Wave 1 → Wave 2 → Wave 3 → Final Verification


Context

项目背景

用户需要一个 Telegram 搜索机器人,核心功能包括:

  1. 消息存储与搜索(Lucene + FAISS)
  2. LLM 对话(OpenAI/Gemini/Ollama/Anthropic)
  3. MCP 工具支持
  4. LLM Agent 独立进程运行,未来可被沙箱隔离

架构目标

┌─────────────────────────────────────────────────────────────────────────┐
│                        Agent 进程 (可沙箱隔离)                           │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   LLM 对话 + 工具决策                                                    │
│                                                                          │
│   ├─► Agent 端工具 (沙箱内执行):                                        │
│   │     - Echo / Calculate                                            │
│   │     - Bash / File / SearchText                                    │
│   │     - MCP 外部工具                                                  │
│   │                                                                     │
│   └─► Bot API 工具 (通过 IPC 调用 Bot 进程):                           │
│         - SendPhoto / SendVideo / SendDocument                         │
│         - Search / QueryMessage                                        │
│         - Todo / Memory / ShortUrl                                    │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘
                                   │ IPC (Garnet RPC)
                                   ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                        Bot 进程 (安全环境,不需要沙箱)                   │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   Telegram 交互 | 消息存储 | 搜索 | 向量生成                            │
│                                                                          │
│   Bot API 工具执行:                                                     │
│   - SendPhoto / SendVideo / SendDocument                               │
│   - Search / QueryMessage                                              │
│   - Todo / Memory / ShortUrl / McpInstaller                            │
│                                                                          │
│   权限控制: 统一校验调用者身份和参数                                     │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

现状分析

代码规模:

  • LLM 模块共 14 个核心服务文件
  • 总计约 5,000+ 行代码
  • 最大文件: OpenAIService.cs (1,309 行), McpToolHelper.cs (982 行)

问题清单:

问题 严重性 位置 解决方案
GetChatHistory 重复 4 份 🔴 高 4 个服务 AbstractLlmService 基类
TryLoadMessagePhoto 重复 3 份 🟠 中 3 个服务 基类或工具类
CheckVisionSupport 重复 🟠 中 3 个服务 基类
静态状态线程不安全 🟠 中 McpToolHelper, OpenAIService._botName 改为 DI 服务
God Class (McpToolHelper 982行) 🟡 低 McpToolHelper.cs 清理非必要代码
过长方法 (100+ 行) 🟡 低 ExecAsync 等 拆分
违反依赖反转 🟡 低 GeneralLLMService 依赖 ILLMService 接口

关键依赖关系:

  • GeneralLLMController 直接依赖 OpenAIService
  • McpToolHelper 是静态类,工具注册在启动时完成
  • LLM 工具通过 Redis/Garnet IPC 调用 Bot 进程

测试覆盖:

  • 有基础单元测试 (GeneralLLMServiceTests, LLMFactoryTests, McpToolHelperTests)
  • 向量服务通过 Mock IGeneralLLMService 测试

Work Objectives

Core Objective

将 LLM 模块重构为:

  1. 消除代码重复,降低维护成本
  2. 支持 Agent/Bot 进程解耦
  3. 支持子 Agent 工具控制
  4. 移除静态状态,提高可测试性
  5. 保持向后兼容

Concrete Deliverables

  1. AbstractLlmService 基类

    • 公共方法: GetChatHistory, TryLoadMessagePhoto, CheckVisionSupport, InferModelCapabilities
    • 抽象方法: BuildChatRequest, ParseResponse, ConvertToProviderFormat
    • 4 个 LLM 服务继承它
  2. 工具能力标记系统

    • ToolCapability 枚举 (BotApi, FileSystem, Network, Admin, Dangerous)
    • [BuiltInTool] 扩展支持能力标记
    • Prompt 生成时按配置过滤
    • 子 Agent 配置支持能力/工具白名单
  3. McpToolHelper 清理

    • 移除不必要的职责
    • 清理过长方法
    • 可选:转为 DI 服务
  4. 依赖反转修复

    • GeneralLLMService 依赖 ILLMService 接口
    • GeneralLLMController 依赖 IBotConfigurationProvider 接口

Definition of Done

  • 所有现有 dotnet test 通过
  • 重构后功能行为完全一致(无逻辑变更)
  • 4 个 LLM 服务继承 AbstractLlmService
  • 工具能力标记系统工作正常
  • Agent/Bot IPC 调用路径不变

Must Have

  • 向后兼容现有工具定义格式(Redis schema 不变)
  • 保留现有 Prompt 模板语义
  • 保持相同错误处理行为
  • 所有 LLM 服务仍可通过 [Injectable] 自动注册

Must NOT Have

  • 任何静态状态(static 字段除非是真正的常量)
  • 代码重复(DRY 原则)
  • 单个类超过 500 行(特殊情况需评审)
  • 破坏现有测试
  • 破坏 Agent/Bot IPC 调用

Architecture Design

1. 模板方法模式 - AbstractLlmService

┌─────────────────────────────────────────────────────────────────────────┐
│                         AbstractLlmService                               │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  依赖注入 (所有子类共享):                                                 │
│  • DataDbContext - 聊天历史查询                                           │
│  • IBotConfigurationProvider - BotName                                   │
│  • IMessageExtensionService - 消息扩展查询                                │
│  • IConnectionMultiplexer - Redis 连接                                  │
│                                                                          │
│  ────────────────────────────────────────────────────────────────────── │
│                                                                          │
│  模板方法 (通用实现,子类可选 override):                                  │
│                                                                          │
│  GetChatHistory(chatId, inputToken, supportsVision)                      │
│    ├─► 查询 DataDbContext.Messages (最近1小时 或 10条)                   │
│    ├─► 查询 DataDbContext.UserData (用户名)                              │
│    ├─► 查询 MessageExtensions (图片等)                                    │
│    ├─► BuildChatHistoryForProvider() ──────────────────────────────┐    │
│    └─► 合并连续同用户消息                                               │    │
│                                                                          │
│  TryLoadMessagePhoto(messageId, supportsVision)                          │
│    ├─► 查询 MessageExtensions (图片扩展)                                │
│    └─► 加载图片并转为 Base64                                             │
│                                                                          │
│  CheckVisionSupport(modelName)                                          │
│    ├─► 查询 DataDbContext.ModelCapabilities                             │
│    └─► 返回是否支持 vision                                              │
│                                                                          │
│  InferModelCapabilities(modelName, provider)                            │
│    ├─► 查询已知模型列表                                                  │
│    └─► 返回能力 (vision, function_calling, etc)                         │
│                                                                          │
│  ────────────────────────────────────────────────────────────────────── │
│                                                                          │
│  抽象方法 (子类必须实现):                                                 │
│                                                                          │
│  protected abstract List<ChatMessage> BuildChatHistoryForProvider(       │
│      List<Message> messages, Dictionary<long, string> userNames)         │
│    - OpenAIService: Message[] → OpenAI SDK ChatMessage                   │
│    - GeminiService: Message[] → Gemini SDK Content                       │
│    - AnthropicService: Message[] → Anthropic SDK Message                 │
│    - OllamaService: Message[] → OllamaSharp.Chat                          │
│                                                                          │
│  protected abstract ChatRequest BuildRequest(...)                        │
│  protected abstract Response ParseResponse(...)                          │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘
                           ▲
           ┌───────────────┼───────────────┬───────────────┐
           │               │               │               │
    ┌──────┴──────┐ ┌──────┴──────┐ ┌──────┴──────┐ ┌──────┴──────┐
    │ OpenAIService│ │GeminiService│ │OllamaService│ │AnthropicSvc │
    ├─────────────┤ ├─────────────┤ ├─────────────┤ ├─────────────┤
    │ + BuildChat│ │ + BuildChat│ │ + BuildChat│ │ + BuildChat│
    │   History  │ │   History  │ │   History  │ │   History  │
    │ + BuildReq │ │ + BuildReq │ │ + BuildReq │ │ + BuildReq │
    │ + ParseResp│ │ + ParseResp│ │ + ParseResp│ │ + ParseResp│
    └────────────┘ └────────────┘ └────────────┘ └────────────┘

2. 工具能力标记系统

┌─────────────────────────────────────────────────────────────────────────┐
│                          ToolCapability 枚举                             │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  [Flags]                                                                 │
│  public enum ToolCapability {                                            │
│      None = 0,                    // 无能力                              │
│      FileSystem = 1,              // 文件系统操作 (Bash, File)           │
│      Network = 2,                  // 网络操作 (MCP, HTTP)               │
│      BotApi = 4,                   // Bot API 操作 (SendPhoto, Search)   │
│      Admin = 8,                    // 管理操作 (McpInstaller)            │
│      Dangerous = 16,               // 危险操作 (需要沙箱)                 │
│  }                                                                           │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────┐
│                      扩展 [BuiltInTool] 特性                             │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  public class BuiltInToolAttribute : Attribute {                         │
│      public string Name { get; set; }                                    │
│      public string Description { get; set; }                             │
│      public ToolCapability Capabilities { get; set; } = ToolCapability.None│
│      public bool RequiresAdmin { get; set; } = false                     │
│  }                                                                          │
│                                                                          │
│  // 使用示例                                                             │
│  [BuiltInTool(Name = "send_photo", Description = "...",                  │
│      Capabilities = ToolCapability.BotApi)]                              │
│  public async Task<string> SendPhotoBase64(...) { ... }                  │
│                                                                          │
│  [BuiltInTool(Name = "bash", Description = "...",                        │
│      Capabilities = ToolCapability.FileSystem | ToolCapability.Dangerous)]│
│  public async Task<string> ExecuteCommand(...) { ... }                   │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

3. 子 Agent 配置

┌─────────────────────────────────────────────────────────────────────────┐
│                        SubAgentConfig 配置                               │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  public class SubAgentConfig {                                           │
│      public string AgentId { get; set; }                                 │
│      public string Description { get; set; }                             │
│                                                                          │
│      // 方式1: 按能力过滤                                                │
│      public ToolCapability AllowedCapabilities { get; set; }             │
│                                                                          │
│      // 方式2: 精确工具列表 (优先级更高)                                  │
│      public HashSet<string> AllowedTools { get; set; }                   │
│                                                                          │
│      // 方式3: 排除特定工具                                              │
│      public HashSet<string> BlockedTools { get; set; }                   │
│  }                                                                          │
│                                                                          │
│  public List<ToolDefinition> GetToolsForAgent(SubAgentConfig config) {  │
│      var allTools = ToolRegistry.GetAll();                               │
│                                                                          │
│      if (config.AllowedTools?.Count > 0) {                               │
│          return allTools                                                  │
│              .Where(t => config.AllowedTools.Contains(t.Name))           │
│              .Where(t => !config.BlockedTools?.Contains(t.Name))         │
│              .ToList();                                                  │
│      }                                                                   │
│                                                                          │
│      return allTools                                                      │
│          .Where(t => config.AllowedCapabilities.HasFlag(t.Capabilities)) │
│          .Where(t => !config.BlockedTools?.Contains(t.Name))             │
│          .ToList();                                                       │
│  }                                                                          │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

4. 执行分发

┌─────────────────────────────────────────────────────────────────────────┐
│                           工具执行流程                                    │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   LLM 响应工具调用                                                       │
│         │                                                                │
│         ▼                                                                │
│   ┌─────────────────┐                                                    │
│   │ McpToolHelper   │                                                    │
│   │ 解析工具调用    │                                                    │
│   └────────┬────────┘                                                    │
│            │                                                             │
│            ▼                                                             │
│   ┌─────────────────────────────────────────────────────────────┐        │
│   │ 判断执行位置                                                   │        │
│   │                                                               │        │
│   │ if (tool.Capabilities.HasFlag(ToolCapability.BotApi)) {      │        │
│   │     // Bot API 工具 → IPC 调用 Bot 进程                      │        │
│   │     await _ipcClient.CallAsync("ToolExecutor.Execute", req); │        │
│   │ } else {                                                     │        │
│   │     // Agent 端工具 → 本地执行                                 │        │
│   │     await ExecuteLocal(toolName, args);                      │        │
│   │ }                                                             │        │
│   └─────────────────────────────────────────────────────────────┘        │
│            │                                                             │
│            ├──────────────────────┐                                      │
│            ▼                      ▼                                      │
│   ┌─────────────┐          ┌─────────────┐                               │
│   │ 本地执行    │          │ IPC 调用    │                               │
│   │ (Agent进程) │          │ (Bot进程)   │                               │
│   ├─────────────┤          ├─────────────┤                               │
│   │ Echo        │          │ SendPhoto   │                               │
│   │ Calculate   │          │ Search      │                               │
│   │ Bash        │          │ Todo        │                               │
│   │ File        │          │ Memory      │                               │
│   └─────────────┘          └─────────────┘                               │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

Verification Strategy

Test Decision

  • Infrastructure exists: YES
  • Automated tests: YES (Tests-after)
  • Framework: xUnit + Moq
  • Strategy: 先确保现有测试通过,再逐步重构,每步验证

QA Policy

Every task includes agent-executed QA scenarios. Evidence saved to .sisyphus/evidence/task-{N}-{scenario-slug}.{ext}.


Execution Strategy

Parallel Execution Waves

Wave 1 (基础设施 - 所有任务可并行):
├── Task 1: 创建 IBotConfigurationProvider 接口 + 实现
├── Task 2: 创建 ToolCapability 枚举
├── Task 3: 扩展 [BuiltInTool] 特性支持能力标记
└── Task 4: 创建 AbstractLlmService 基类骨架

Wave 2 (核心重构 - 独立服务可并行):
├── Task 5: 重构 OpenAIService (继承基类)
├── Task 6: 重构 AnthropicService (继承基类)
├── Task 7: 重构 GeminiService (继承基类)
├── Task 8: 重构 OllamaService (继承基类)
├── Task 9: 实现工具能力过滤 (Prompt 生成)
└── Task 10: 实现执行分发 (BotApi vs 本地)

Wave 3 (集成修复):
├── Task 11: 重构 GeneralLLMService (依赖接口)
├── Task 12: 重构 GeneralLLMController (依赖接口)
├── Task 13: 清理 McpToolHelper (可选)
└── Task 14: 添加子 Agent 配置支持

Final Verification:
└── Task F1: 全量测试 + 功能验证

Dependency Matrix

Task Blocks Blocked By
1 11, 12 -
2 3, 9, 10 -
3 9 2
4 5, 6, 7, 8 1
5 11 4
6 11 4
7 11 4
8 11 4
9 14 2, 3
10 - 2
11 12 5, 6, 7, 8
12 14 1, 11
13 - -
14 - 9, 12

Agent Dispatch Summary

  • Wave 1: 4 tasks → deep (architecture design)
  • Wave 2: 6 tasks → deep (core refactoring)
  • Wave 3: 4 tasks → unspecified-high (integration)
  • FINAL: 1 task → unspecified-high (verification)

TODOs

  • 1. 创建 IBotConfigurationProvider 接口 + 实现

    What to do:

    • 创建 IBotConfigurationProvider 接口
      public interface IBotConfigurationProvider {
          string GetBotName();
          string GetDefaultModel();
          bool IsVisionSupported(string model);
          IReadOnlyList<string> GetKnownVisionModels();
      }
    • 创建 BotConfigurationProvider 实现,从现有 OpenAIService._botName 等静态字段迁移
    • 修改 GeneralLLMController 使用接口而非直接注入 OpenAIService

    Must NOT do:

    • 不要修改 OpenAIService 的其他逻辑
    • 不要创建静态字段

    Recommended Agent Profile:

    • Category: deep
      • Reason: 需要理解现有静态字段的使用场景,设计合适的接口抽象
    • Skills: []

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 1 (with Tasks 2, 3, 4)
    • Blocks: Tasks 11, 12 (GeneralLLMService/Controller 重构)
    • Blocked By: None

    References:

    Pattern References:

    • TelegramSearchBot.LLM/Interface/ILLMService.cs - 接口定义模式
    • TelegramSearchBot.LLM/Service/AI/LLM/GeneralLLMService.cs:1-40 - 现有配置获取方式

    API/Type References:

    • TelegramSearchBot.LLM/Service/AI/LLM/OpenAIService.cs:48 - _botName 静态字段
    • TelegramSearchBot.LLM/Service/AI/LLM/AnthropicService.cs:36 - _botName 静态字段
    • TelegramSearchBot/Controller/AI/LLM/GeneralLLMController.cs:26 - 当前依赖方式

    Test References:

    • TelegramSearchBot.LLM.Test/Service/AI/LLM/GeneralLLMServiceTests.cs - Mock 模式参考

    Acceptance Criteria:

    • IBotConfigurationProvider 接口定义完成
    • BotConfigurationProvider 实现完成
    • GeneralLLMController 改为使用接口
    • dotnet build TelegramSearchBot.sln 编译通过

    QA Scenarios:

    Scenario: Bot 配置正确获取
      Tool: Bash (dotnet build)
      Preconditions: 代码修改完成
      Steps:
        1. dotnet build TelegramSearchBot.sln
        2. 验证无编译错误
      Expected Result: 编译成功,无警告
      Failure Indicators: 编译错误、类型不匹配
      Evidence: .sisyphus/evidence/task-1-build.{ext}
    
  • 2. 创建 ToolCapability 枚举

    What to do:

    • 创建 ToolCapability 枚举
      [Flags]
      public enum ToolCapability {
          None = 0,
          FileSystem = 1,      // Bash, File, SearchText
          Network = 2,          // MCP, HTTP
          BotApi = 4,           // SendPhoto, Search, Todo
          Admin = 8,             // McpInstaller
          Dangerous = 16,        // 需要沙箱
      }
    • 放在 TelegramSearchBot.Common/Attributes/ 目录

    Must NOT do:

    • 不要修改现有代码

    Recommended Agent Profile:

    • Category: quick
      • Reason: 简单枚举定义
    • Skills: []

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 1 (with Tasks 1, 3, 4)
    • Blocks: Tasks 3, 9, 10
    • Blocked By: None

    References:

    Pattern References:

    • TelegramSearchBot.Common/Attributes/BuiltInToolAttributes.cs - 现有特性定义

    Acceptance Criteria:

    • ToolCapability 枚举创建完成
    • dotnet build 通过

    QA Scenarios:

    Scenario: ToolCapability 枚举编译通过
      Tool: Bash (dotnet build)
      Preconditions: 枚举创建完成
      Steps:
        1. dotnet build TelegramSearchBot.sln
      Expected Result: 编译成功
      Failure Indicators: 编译错误
      Evidence: .sisyphus/evidence/task-2-build.{ext}
    
  • 3. 扩展 [BuiltInTool] 特性支持能力标记

    What to do:

    • 扩展 BuiltInToolAttribute
      [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
      public class BuiltInToolAttribute : Attribute {
          public string Name { get; set; }
          public string Description { get; set; }
          public ToolCapability Capabilities { get; set; } = ToolCapability.None;
          public bool RequiresAdmin { get; set; } = false;
      }
    • 更新 ToolDefinition 模型,添加 Capabilities 字段
    • 更新工具注册逻辑,保存能力信息

    Must NOT do:

    • 不要改变工具定义格式(向后兼容)
    • 不要改变执行逻辑

    Recommended Agent Profile:

    • Category: deep
      • Reason: 需要理解现有特性系统,添加新字段
    • Skills: []

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 1 (with Tasks 1, 2, 4)
    • Blocks: Task 9 (Prompt 过滤)
    • Blocked By: Task 2 (ToolCapability 枚举)

    References:

    Pattern References:

    • TelegramSearchBot.Common/Attributes/BuiltInToolAttributes.cs - 现有特性
    • TelegramSearchBot.LLM/Service/AI/LLM/McpToolHelper.cs:116-169 - 工具注册逻辑

    Acceptance Criteria:

    • [BuiltInTool] 特性扩展完成
    • ToolDefinition 更新完成
    • 工具注册逻辑更新完成
    • dotnet build 通过

    QA Scenarios:

    Scenario: BuiltInTool 特性扩展后编译通过
      Tool: Bash (dotnet build)
      Preconditions: 特性扩展完成
      Steps:
        1. dotnet build TelegramSearchBot.sln
      Expected Result: 编译成功
      Failure Indicators: 编译错误
      Evidence: .sisyphus/evidence/task-3-build.{ext}
    
  • 4. 创建 AbstractLlmService 基类

    What to do:

    • 创建 AbstractLlmService 基类,继承 ILLMService
    • 实现模板方法:
      • GetChatHistoryCore() - 从 DataDbContext 查询聊天历史
      • TryLoadMessagePhotoCore() - 加载消息图片
      • CheckVisionSupportCore() - 检查视觉支持
      • InferModelCapabilitiesCore() - 推断模型能力
    • 声明抽象方法:
      • BuildChatHistoryForProvider() - 转换为 Provider 格式
      • BuildRequest() - 构建请求
      • ParseResponse() - 解析响应
    • 将 _botName 改为实例字段,通过 IBotConfigurationProvider 获取
    public abstract class AbstractLlmService : ILLMService {
        protected readonly DataDbContext _db;
        protected readonly IBotConfigurationProvider _config;
        protected readonly IMessageExtensionService _extensions;
        protected readonly ILogger _logger;
        
        // 模板方法 (通用实现)
        protected virtual async Task<List<ChatMessage>> GetChatHistoryCore(
            long chatId, 
            Message inputToken,
            bool supportsVision) {
            // 1. 查询最近1小时或10条消息
            var messages = await _db.Messages
                .Where(m => m.GroupId == chatId)
                .Where(m => m.DateTime > cutoff)
                .ToListAsync();
            
            // 2. 不足10条则回退
            if (messages.Count < 10) { ... }
            
            // 3. 查询用户名
            var userIds = messages.Select(m => m.FromUserId).Distinct();
            var users = await _db.UserData
                .Where(u => userIds.Contains(u.Id))
                .ToDictionaryAsync(u => u.Id, u => u.Name);
            
            // 4. 转换格式
            return BuildChatHistoryForProvider(messages, users);
        }
        
        // 抽象方法 (子类实现)
        protected abstract List<ChatMessage> BuildChatHistoryForProvider(
            List<Message> messages,
            Dictionary<long, string> userNames);
        
        protected abstract ChatRequest BuildRequest(...);
        protected abstract Response ParseResponse(...);
    }

    Must NOT do:

    • 不要实现 ExecAsync(各服务自己实现)
    • 不要改变接口契约

    Recommended Agent Profile:

    • Category: deep
      • Reason: 需要分析 4 个服务的相似代码,提取最佳实现
    • Skills: []

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 1 (with Tasks 1, 2, 3)
    • Blocks: Tasks 5, 6, 7, 8 (各服务重构)
    • Blocked By: Task 1 (IBotConfigurationProvider)

    References:

    Pattern References:

    • TelegramSearchBot.LLM/Service/AI/LLM/OpenAIService.cs:731-807 - GetChatHistory 最佳实现
    • TelegramSearchBot.LLM/Service/AI/LLM/GeminiService.cs:79-149 - GetChatHistory 变体
    • TelegramSearchBot.LLM/Service/AI/LLM/AnthropicService.cs:142-229 - GetChatHistory 变体
    • TelegramSearchBot.LLM/Service/AI/LLM/OllamaService.cs:89-140 - GetChatHistory 变体

    Test References:

    • TelegramSearchBot.LLM.Test/Service/AI/LLM/GeneralLLMServiceTests.cs - 测试模式

    Acceptance Criteria:

    • AbstractLlmService 基类创建完成
    • 模板方法实现完成
    • 抽象方法声明完成
    • 4 个服务仍可正常编译

    QA Scenarios:

    Scenario: 基类创建后编译通过
      Tool: Bash (dotnet build)
      Preconditions: AbstractLlmService 创建完成
      Steps:
        1. dotnet build TelegramSearchBot.LLM.sln
      Expected Result: 编译成功
      Failure Indicators: 基类与接口不兼容
      Evidence: .sisyphus/evidence/task-4-build.{ext}
    
  • 5. 重构 OpenAIService

    What to do:

    • 继承 AbstractLlmService 基类
    • 移除 _botName 静态字段,改为从 IBotConfigurationProvider 获取
    • 实现抽象方法:
      • BuildChatHistoryForProvider() → OpenAI SDK 格式
      • BuildRequest() → OpenAI 请求格式
      • ParseResponse() → OpenAI 响应解析
    • 保留 ExecAsync 的 Provider 特有逻辑

    Must NOT do:

    • 不要改变 API 响应格式
    • 不要改变错误处理行为
    • 不要修改与 GeneralLLMService 的接口契约

    Recommended Agent Profile:

    • Category: deep
      • Reason: 最大最复杂的文件,需要仔细处理
    • Skills: []

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 2 (with Tasks 6, 7, 8)
    • Blocks: Task 11 (GeneralLLMService 重构)
    • Blocked By: Task 4 (AbstractLlmService)

    References:

    Pattern References:

    • TelegramSearchBot.LLM/Service/AI/LLM/OpenAIService.cs - 完整文件需要重构
    • TelegramSearchBot.LLM/Service/AI/LLM/McpToolHelper.cs:700-900 - 工具相关逻辑

    Acceptance Criteria:

    • OpenAIService 继承 AbstractLlmService
    • 静态字段移除
    • dotnet build 和 dotnet test 通过

    QA Scenarios:

    Scenario: OpenAIService 重构后功能正常
      Tool: Bash (dotnet build + dotnet test)
      Preconditions: 重构完成
      Steps:
        1. dotnet build TelegramSearchBot.LLM.sln
        2. dotnet test TelegramSearchBot.LLM.Test --filter "OpenAI"
      Expected Result: 编译和测试通过
      Failure Indicators: 编译错误、测试失败
      Evidence: .sisyphus/evidence/task-5-test.{ext}
    
  • 6. 重构 AnthropicService

    What to do:

    • 继承 AbstractLlmService 基类
    • 移除 _botName 静态字段
    • 实现抽象方法:
      • BuildChatHistoryForProvider() → Anthropic SDK 格式
      • 注意:Anthropic 要求以 user 消息开始
    • 保留 ExecAsync 的 Provider 特有逻辑

    Must NOT do:

    • 不要改变 API 响应格式
    • 不要改变错误处理行为

    Recommended Agent Profile:

    • Category: deep
      • Reason: 需要与 OpenAIService 重构保持一致
    • Skills: []

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 2 (with Tasks 5, 7, 8)
    • Blocks: Task 11
    • Blocked By: Task 4

    References:

    Pattern References:

    • TelegramSearchBot.LLM/Service/AI/LLM/AnthropicService.cs - 873 行
    • 参考 Task 5 的重构模式

    Acceptance Criteria:

    • AnthropicService 继承 AbstractLlmService
    • 静态字段移除
    • dotnet build 和 dotnet test 通过

    QA Scenarios:

    Scenario: AnthropicService 重构后功能正常
      Tool: Bash (dotnet build + dotnet test)
      Preconditions: 重构完成
      Steps:
        1. dotnet build TelegramSearchBot.LLM.sln
        2. dotnet test TelegramSearchBot.LLM.Test --filter "Anthropic"
      Expected Result: 编译和测试通过
      Failure Indicators: 编译错误、测试失败
      Evidence: .sisyphus/evidence/task-6-test.{ext}
    
  • 7. 重构 GeminiService

    What to do:

    • 继承 AbstractLlmService 基类
    • 简化 InferGeminiModelCapabilities 方法(~95 行)
    • 实现抽象方法:
      • BuildChatHistoryForProvider() → Gemini SDK 格式
    • 保留 ExecAsync 的 Provider 特有逻辑

    Must NOT do:

    • 不要改变 API 响应格式

    Recommended Agent Profile:

    • Category: deep
      • Reason: 需要处理 Gemini 特有的 API 差异
    • Skills: []

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 2 (with Tasks 5, 6, 8)
    • Blocks: Task 11
    • Blocked By: Task 4

    References:

    Pattern References:

    • TelegramSearchBot.LLM/Service/AI/LLM/GeminiService.cs - 422 行
    • 参考 Task 5 的重构模式

    Acceptance Criteria:

    • GeminiService 继承 AbstractLlmService
    • 方法拆分完成
    • dotnet build 和 dotnet test 通过

    QA Scenarios:

    Scenario: GeminiService 重构后功能正常
      Tool: Bash (dotnet build + dotnet test)
      Preconditions: 重构完成
      Steps:
        1. dotnet build TelegramSearchBot.LLM.sln
        2. dotnet test TelegramSearchBot.LLM.Test --filter "Gemini"
      Expected Result: 编译和测试通过
      Failure Indicators: 编译错误、测试失败
      Evidence: .sisyphus/evidence/task-7-test.{ext}
    
  • 8. 重构 OllamaService

    What to do:

    • 继承 AbstractLlmService 基类
    • 实现抽象方法:
      • BuildChatHistoryForProvider() → OllamaSharp 格式
    • 保留 ExecAsync 的 Provider 特有逻辑
    • 保持与 OllamaSharp 的集成

    Must NOT do:

    • 不要改变 API 响应格式

    Recommended Agent Profile:

    • Category: deep
      • Reason: 需要处理 OllamaSharp 库的集成
    • Skills: []

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 2 (with Tasks 5, 6, 7)
    • Blocks: Task 11
    • Blocked By: Task 4

    References:

    Pattern References:

    • TelegramSearchBot.LLM/Service/AI/LLM/OllamaService.cs - 371 行
    • 参考 Task 5 的重构模式

    Acceptance Criteria:

    • OllamaService 继承 AbstractLlmService
    • dotnet build 和 dotnet test 通过

    QA Scenarios:

    Scenario: OllamaService 重构后功能正常
      Tool: Bash (dotnet build + dotnet test)
      Preconditions: 重构完成
      Steps:
        1. dotnet build TelegramSearchBot.LLM.sln
        2. dotnet test TelegramSearchBot.LLM.Test --filter "Ollama"
      Expected Result: 编译和测试通过
      Failure Indicators: 编译错误、测试失败
      Evidence: .sisyphus/evidence/task-8-test.{ext}
    
  • 9. 实现工具能力过滤 (Prompt 生成)

    What to do:

    • 实现 GetToolsForAgent() 方法
      public List<ToolDefinition> GetToolsForAgent(SubAgentConfig config) {
          var allTools = ToolRegistry.GetAll();
          
          // 精确列表优先
          if (config.AllowedTools?.Count > 0) {
              return allTools
                  .Where(t => config.AllowedTools.Contains(t.Name))
                  .Where(t => !config.BlockedTools?.Contains(t.Name))
                  .ToList();
          }
          
          // 按能力过滤
          return allTools
              .Where(t => config.AllowedCapabilities.HasFlag(t.Capabilities))
              .Where(t => !config.BlockedTools?.Contains(t.Name))
              .ToList();
      }
    • 更新 Prompt 生成逻辑,使用过滤后的工具列表
    • 确保 BotApi 工具的 Prompt 也正确生成(供 IPC 调用)

    Must NOT do:

    • 不要改变工具定义格式
    • 不要改变执行逻辑

    Recommended Agent Profile:

    • Category: deep
      • Reason: 需要理解 Prompt 生成逻辑
    • Skills: []

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 2
    • Blocks: Task 14 (子 Agent 配置支持)
    • Blocked By: Tasks 2, 3

    References:

    Pattern References:

    • TelegramSearchBot.LLM/Service/AI/LLM/McpToolHelper.cs:116-169 - 工具注册
    • TelegramSearchBot.LLM/Service/AI/LLM/McpToolHelper.cs:299-374 - Prompt 生成

    Acceptance Criteria:

    • 工具能力过滤实现完成
    • Prompt 生成逻辑更新
    • dotnet build 和 dotnet test 通过

    QA Scenarios:

    Scenario: 工具能力过滤功能正常
      Tool: Bash (dotnet build + dotnet test)
      Preconditions: 实现完成
      Steps:
        1. dotnet build TelegramSearchBot.LLM.sln
        2. dotnet test TelegramSearchBot.LLM.Test
      Expected Result: 编译和测试通过
      Failure Indicators: 编译错误、测试失败
      Evidence: .sisyphus/evidence/task-9-test.{ext}
    
  • 10. 实现执行分发 (BotApi vs 本地)

    What to do:

    • 在工具执行逻辑中添加执行位置判断
      public async Task<string> ExecuteToolAsync(
          ToolCall call, 
          ToolContext context) {
          
          var tool = ToolRegistry.Get(call.Name);
          
          if (tool.Capabilities.HasFlag(ToolCapability.BotApi)) {
              // Bot API 工具 → IPC 调用 Bot 进程
              return await _ipcClient.CallAsync<ToolResult>(
                  "ToolExecutor.Execute",
                  new ToolCallRequest {
                      ToolName = call.Name,
                      Arguments = call.Arguments,
                      Context = context
                  }
              ).Result;
          }
          
          // Agent 端工具 → 本地执行
          return await ExecuteLocalToolAsync(call.Name, call.Arguments, context);
      }
    • 复用现有的 IPC 调用逻辑(ToolExecutor)

    Must NOT do:

    • 不要改变 IPC 协议格式
    • 不要破坏现有的 Bot API 工具执行

    Recommended Agent Profile:

    • Category: deep
      • Reason: 需要理解现有 IPC 调用逻辑
    • Skills: []

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 2
    • Blocked By: Task 2

    References:

    Pattern References:

    • TelegramSearchBot.LLM/Service/AI/LLM/McpToolHelper.cs:693-784 - 工具执行
    • TelegramSearchBot.LLMAgent/Service/ToolExecutor.cs - Agent 端工具执行

    Acceptance Criteria:

    • 执行分发逻辑实现完成
    • BotApi 工具走 IPC
    • 非 BotApi 工具走本地
    • dotnet build 和 dotnet test 通过

    QA Scenarios:

    Scenario: 执行分发功能正常
      Tool: Bash (dotnet build + dotnet test)
      Preconditions: 实现完成
      Steps:
        1. dotnet build TelegramSearchBot.sln
        2. dotnet test
      Expected Result: 编译和测试通过
      Failure Indicators: 编译错误、测试失败
      Evidence: .sisyphus/evidence/task-10-test.{ext}
    
  • 11. 重构 GeneralLLMService

    What to do:

    • 将直接依赖具体服务改为依赖 ILLMService 接口
      // Before
      private readonly OpenAIService _openaiService;
      // After
      private readonly ILLMService _openaiService; // 通过 LLMFactory 获取
    • 或引入 ILLMServiceFactory 工厂接口
    • 确保渠道优先级调度逻辑不变

    Must NOT do:

    • 不要改变渠道调度行为
    • 不要改变错误处理逻辑

    Recommended Agent Profile:

    • Category: deep
      • Reason: 需要正确处理多服务协调逻辑
    • Skills: []

    Parallelization:

    • Can Run In Parallel: NO
    • Blocks: Task 12
    • Blocked By: Tasks 5, 6, 7, 8

    References:

    Pattern References:

    • TelegramSearchBot.LLM/Service/AI/LLM/GeneralLLMService.cs:37-56 - 当前依赖方式
    • TelegramSearchBot.LLM/Service/AI/LLM/LLMFactory.cs - 工厂模式

    Acceptance Criteria:

    • GeneralLLMService 依赖 ILLMService 接口
    • 渠道调度行为不变
    • dotnet build 和 dotnet test 通过

    QA Scenarios:

    Scenario: GeneralLLMService 重构后渠道调度正常
      Tool: Bash (dotnet build + dotnet test)
      Preconditions: 重构完成
      Steps:
        1. dotnet build TelegramSearchBot.LLM.sln
        2. dotnet test TelegramSearchBot.LLM.Test --filter "GeneralLLM"
      Expected Result: 编译和测试通过
      Failure Indicators: 编译错误、测试失败
      Evidence: .sisyphus/evidence/task-11-test.{ext}
    
  • 12. 重构 GeneralLLMController

    What to do:

    • 将直接依赖 OpenAIService 改为依赖 IBotConfigurationProvider
    • 移除获取 BotName 的直接依赖

    Must NOT do:

    • 不要改变 Controller 的输入输出契约

    Recommended Agent Profile:

    • Category: deep
      • Reason: Controller 是关键入口,需要谨慎
    • Skills: []

    Parallelization:

    • Can Run In Parallel: NO
    • Blocks: Task 14
    • Blocked By: Tasks 1, 11

    References:

    Pattern References:

    • TelegramSearchBot/Controller/AI/LLM/GeneralLLMController.cs:26 - 当前依赖方式

    Acceptance Criteria:

    • GeneralLLMController 依赖 IBotConfigurationProvider
    • dotnet build 通过

    QA Scenarios:

    Scenario: Controller 重构后编译通过
      Tool: Bash (dotnet build)
      Preconditions: 重构完成
      Steps:
        1. dotnet build TelegramSearchBot.sln
      Expected Result: 编译成功
      Failure Indicators: 编译错误
      Evidence: .sisyphus/evidence/task-12-build.{ext}
    
  • 13. 清理 McpToolHelper (可选)

    What to do:

    • 拆分过长方法(100+ 行)
    • 移除不必要的职责
    • 可选:转为 DI 服务
    • 清理注释掉的代码(如果有)

    Must NOT do:

    • 不要改变公开接口
    • 不要破坏现有功能

    Recommended Agent Profile:

    • Category: unspecified-high
      • Reason: 需要全面评估静态方法的调用者
    • Skills: []

    Parallelization:

    • Can Run In Parallel: YES
    • Blocked By: None

    References:

    Pattern References:

    • TelegramSearchBot.LLM/Service/AI/LLM/McpToolHelper.cs - 完整文件

    Acceptance Criteria:

    • 方法拆分完成
    • dotnet build 和 dotnet test 通过

    QA Scenarios:

    Scenario: McpToolHelper 清理后功能正常
      Tool: Bash (dotnet test)
      Preconditions: 清理完成
      Steps:
        1. dotnet test
      Expected Result: 所有测试通过
      Failure Indicators: 测试失败
      Evidence: .sisyphus/evidence/task-13-test.{ext}
    
  • 14. 添加子 Agent 配置支持

    What to do:

    • 创建 SubAgentConfig 配置类
    • 实现配置加载(从数据库或配置文件)
    • 与任务 9 的工具过滤集成

    Must NOT do:

    • 不要破坏默认行为(无配置时使用所有工具)

    Recommended Agent Profile:

    • Category: deep
      • Reason: 需要设计灵活的配置系统
    • Skills: []

    Parallelization:

    • Can Run In Parallel: NO
    • Blocked By: Tasks 9, 12

    References:

    Pattern References:

    • TelegramSearchBot.Common/Model/AI/ - 现有模型

    Acceptance Criteria:

    • SubAgentConfig 配置类创建完成
    • 配置加载实现完成
    • 与工具过滤集成
    • dotnet build 和 dotnet test 通过

    QA Scenarios:

    Scenario: 子 Agent 配置功能正常
      Tool: Bash (dotnet build + dotnet test)
      Preconditions: 实现完成
      Steps:
        1. dotnet build TelegramSearchBot.sln
        2. dotnet test
      Expected Result: 编译和测试通过
      Failure Indicators: 编译错误、测试失败
      Evidence: .sisyphus/evidence/task-14-test.{ext}
    

Final Verification Wave

  • F1. 全量测试 + 功能验证 — unspecified-high
    运行 dotnet test 确保所有测试通过。
    手动测试核心场景:
    • LLM 对话功能正常
    • 工具调用正常
    • Bot API 工具走 IPC 调用
    • 子 Agent 配置正确过滤工具
    • 向量生成正常
      Output: Tests [N/N pass] | Integration [PASS/FAIL] | VERDICT

Commit Strategy

  • Wave 1: refactor(llm): add interfaces and tool capability system
  • Wave 2: refactor(llm): migrate services to base class and add execution dispatch
  • Wave 3: refactor(llm): fix dependency inversion and add sub-agent config
  • Pre-commit: dotnet test

Success Criteria

Verification Commands

dotnet build TelegramSearchBot.sln
dotnet test

Final Checklist

  • 4 个 LLM 服务继承 AbstractLlmService
  • GetChatHistory 重复代码消除
  • 静态字段移除
  • 工具能力标记系统工作正常
  • 执行分发正确路由 BotApi 工具
  • GeneralLLMService 依赖接口
  • 子 Agent 配置支持
  • 所有测试通过
  • 功能行为与重构前一致

未来规划 (不在本次范围)

  1. 沙箱接口设计

    • 预留 ISandbox 接口
    • 支持不同沙箱实现(Sandboxie, Docker 等)
    • Agent 进程通过接口调用沙箱
  2. McpToolHelper 完全转为 DI

    • 改为 IToolManager 服务
    • 支持更好的测试 Mock
  3. 完整 Agent 沙箱隔离

    • 使用 Sandboxie 同款方案
    • 限制 Agent 进程的系统调用

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions