nkds

导航

 

MonkeyCode Agent引擎源码深度剖析:从架构设计到实现细节(2026版)

"Agent不是会调API的Chatbot。真正的Agent有记忆、有规划、能使用工具、能在失败时自我修正。本文将带你深入MonkeyCode Agent引擎的每一个核心模块。"


一、Agent引擎全景架构

1.1 什么是Agent?MonkeyCode的Agent是什么?

┌─────────────────────────────────────────────────────┐
│           Chatbot vs Agent vs MonkeyCode Agent        │
│                                                      │
│  Chatbot(聊天机器人):                               │
│  ┌───────────┐                                      │
│  │ 用户输入    │ → [LLM] → 文本输出                   │
│  └───────────┘                                      │
│  特点: 无状态、无工具、单轮对话                        │
│  例子: 早期的客服机器人                               │
│                                                      │
│  Agent(智能体):                                     │
│  ┌──────────────────────────────────┐               │
│  │  用户输入                          │               │
│  │    ↓                              │               │
│  │  [感知] → [规划] → [执行] → [反思] │               │
│  │              ↓                    │               │
│  │         [工具调用]                  │               │
│  │              ↓                    │               │
│  │         输出 + 记忆更新             │               │
│  └──────────────────────────────────┘               │
│  特点: 有状态、有工具、多步推理、可循环                 │
│  例子: AutoGPT / GPT-4 with Plugins                  │
│                                                      │
│  MonkeyCode Agent(编程专用Agent):                    │
│  ┌──────────────────────────────────────────┐       │
│  │                                          │       │
│  │  SDD输入 ──→ [Planner Agent] ──→ 任务计划   │       │
│  │                    ↓                     │       │
│  │           [Architect Agent] ──→ 架构设计     │       │
│  │                    ↓                     │       │
│  │            [Coder Agent] ──→ 代码生成       │       │
│  │           ↙         ↓          ↘            │       │
│  │  [Scanner Agent]  [Tester Agent]  [Reviewer]  │       │
│  │       ↓              ↓            ↓         │       │
│  │  安全扫描      测试执行      代码审查        │       │
│  │       └──────────┴────────────┘              │       │
│  │                    ↓                          │       │
│  │           [Orchestrator 编排器]                │       │
│  │                    ↓                          │       │
│  │              PR / 部署 / 反馈                  │       │
│  │                                          │       │
│  └──────────────────────────────────────────┘       │
│                                                      │
│  特点: 多Agent协作、SDD驱动、MCP工具链、               │
│        安全扫描内置、完整DevOps闭环                    │
└─────────────────────────────────────────────────────┘

1.2 核心源码目录结构

monkeycode/
├── packages/
│   ├── agent-core/                  # 🔥 Agent引擎核心
│   │   ├── src/
│   │   │   ├── index.ts             # 导出入口
│   │   │   ├── engine/
│   │   │   │   ├── Engine.ts        # 主引擎(编排器)
│   │   │   │   ├── Pipeline.ts      # Pipeline定义与执行
│   │   │   │   └── Context.ts       # 执行上下文(状态管理)
│   │   │   ├── agents/
│   │   │   │   ├── BaseAgent.ts     # Agent基类(抽象)
│   │   │   │   ├── PlannerAgent.ts  # 规划Agent
│   │   │   │   ├── ArchitectAgent.ts# 架构Agent
│   │   │   │   ├── CoderAgent.ts    # 编码Agent
│   │   │   │   ├── TesterAgent.ts   # 测试Agent
│   │   │   │   ├── ReviewerAgent.ts # 审查Agent
│   │   │   │   ├── ScannerAgent.ts  # 安全扫描Agent
│   │   │   │   └── DevOpsAgent.ts   # 运维Agent
│   │   │   ├── memory/
│   │   │   │   ├── MemoryStore.ts   # 记忆存储接口
│   │   │   │   ├── ShortTermMemory.ts  # 短期记忆
│   │   │   │   ├── LongTermMemory.ts   # 长期记忆
│   │   │   │   └── EpisodicMemory.ts   # 情景记忆
│   │   │   ├── tools/
│   │   │   │   ├── ToolRegistry.ts  # 工具注册表
│   │   │   │   ├── BaseTool.ts      # 工具基类
│   │   │   │   └── mcp/
│   │   │   │       ├── McpClient.ts    # MCP客户端
│   │   │   │       ├── McpTransport.ts # 传输层
│   │   │   │       └── McpToolAdapter.ts # MCP工具适配
│   │   │   ├── llm/
│   │   │   │   ├── LlmProvider.ts   # LLM抽象层
│   │   │   │   ├── OpenAICompat.ts  # OpenAI兼容接口
│   │   │   │   ├── TokenCounter.ts  # Token计数
│   │   │   │   └── PromptTemplate.ts # 提示词模板
│   │   │   └── types/
│   │   │       ├── agent.types.ts   # Agent类型定义
│   │   │       ├── message.types.ts # 消息类型
│   │   │       ├── tool.types.ts    # 工具类型
│   │   │       └── pipeline.types.ts# Pipeline类型
│   │   ├── package.json
│   │   └── tsconfig.json
│   │
│   ├── sdd-engine/                   # SDD规范引擎
│   │   ├── src/
│   │   │   ├── parser/
│   │   │   │   ├── YamlParser.ts    # YAML解析器
│   │   │   │   ├── SchemaValidator.ts# Schema校验
│   │   │   │   └── SddTransformer.ts# SDD转换器
│   │   │   ├── template/
│   │   │   └── TemplateEngine.ts   # 模板引擎
│   │   └── ...
│   │
│   ├── monkey-scan/                  # MonkeyScan安全扫描
│   │   ├── src/
│   │   │   ├── core/
│   │   │   │   ├── Scanner.ts       # 扫描器主入口
│   │   │   │   ├── RuleEngine.ts    # 规则引擎
│   │   │   │   ├── Analyzer.ts      # 代码分析器
│   │   │   │   └── Reporter.ts      # 报告生成器
│   │   │   ├── rules/
│   │   │   │   ├── sql-injection.ts
│   │   │   │   ├── xss.ts
│   │   │   │   ├── hardcoded-secrets.ts
│   │   │   │   └── ... (20+规则文件)
│   │   │   └── ...
│   │   └── ...
│   │
│   └── mcp-sdk/                      # MCP协议SDK(封装)
│       └── ...
│
├── apps/
│   ├── web/                         # Web UI前端
│   └── server/                      # 后端服务
│
├── scripts/                         # 构建脚本
├── tests/                           # 测试套件
├── examples/                        # 示例项目
└── package.json                     # Monorepo根配置

二、核心模块源码解读

2.1 Engine — 主引擎(编排器)

// packages/agent-core/src/engine/Engine.ts
// MonkeyCode Agent主引擎 —— 所有Agent的指挥官

import { EventEmitter } from 'events';
import { 
  AgentContext, 
  PipelineDefinition, 
  PipelineResult,
  AgentMessage,
  ExecutionPhase,
  ToolCall
} from '../types';
import { BaseAgent } from '../agents/BaseAgent';
import { MemoryStore } from '../memory/MemoryStore';
import { ToolRegistry } from '../tools/ToolRegistry';
import { LlmProvider } from '../llm/LlmProvider';

/**
 * MonkeyCode Agent Engine
 * 
 * 核心职责:
 * 1. 管理Pipeline的生命周期(创建→执行→完成/失败)
 * 2. 协调多个Agent之间的协作
 * 3. 维护全局执行上下文(Context)
 * 4. 处理错误恢复和重试逻辑
 * 5. 发出生命周期事件供外部监听
 */
export class AgentEngine extends EventEmitter {
  
  // === 依赖注入 ===
  private context: AgentContext;          // 全局上下文
  private agents: Map<string, BaseAgent>; // 已注册的Agent实例
  private memory: MemoryStore;            // 记忆存储
  private tools: ToolRegistry;            // 工具注册表
  private llm: LlmProvider;               // LLM提供者
  
  // === 状态管理 ===
  private currentPhase: ExecutionPhase = ExecutionPhase.IDLE;
  private pipelineHistory: PipelineResult[] = [];
  private maxRetries: number = 3;
  private timeoutMs: number = 300000; // 默认5分钟超时

  constructor(config: EngineConfig) {
    super();
    this.context = new AgentContext(config);
    this.agents = new Map();
    this.memory = config.memory || new MemoryStore();
    this.tools = new ToolRegistry();
    this.llm = config.llm;
    
    this.registerBuiltinAgents();
    this.setupErrorHandling();
  }

  /**
   * 🎯 核心方法:执行一个完整的Pipeline
   * 
   * 这是整个Agent引擎最核心的方法。
   * 它接收一个Pipeline定义(通常来自SDD文件),
   * 按照预定义的阶段顺序执行每个步骤,
   * 并在每一步之间传递和更新上下文。
   */
  async executePipeline(pipeline: PipelineDefinition): Promise<PipelineResult> {
    const startTime = Date.now();
    const result: PipelineResult = {
      pipelineId: pipeline.id,
      status: 'running',
      phases: [],
      artifacts: {},
      metrics: { durationMs: 0, tokenUsage: {} },
    };

    try {
      this.emit('pipeline:start', { pipeline });
      this.currentPhase = ExecutionPhase.PLANNING;

      // ===== Phase 1: 规划阶段 =====
      this.emit('phase:start', { phase: 'planning' });
      const planner = this.agents.get('planner')!;
      const plan = await planner.execute({
        input: pipeline.input,
        context: this.context.getState(),
        memory: this.memory.getRecent(50),
      }, this.tools);
      
      result.phases.push({ name: 'planning', output: plan, duration: Date.now() - startTime });
      this.context.updatePlan(plan);
      this.emit('phase:complete', { phase: 'planning', plan });

      // ===== Phase 2: 架构设计阶段 =====
      this.currentPhase = ExecutionPhase.ARCHITECTING;
      this.emit('phase:start', { phase: 'architecting' });
      
      const architect = this.agents.get('architect')!;
      const architecture = await architect.execute({
        input: plan,
        context: this.context.getState(),
        sdd: pipeline.sdd,  // 传入SDD作为架构依据
      }, this.tools);
      
      result.phases.push({ name: 'architecting', output: architecture });
      this.context.updateArchitecture(architecture);
      this.emit('phase:complete', { phase: 'architecting', architecture });

      // ===== Phase 3: 编码阶段(核心!可能包含多次迭代)=====
      this.currentPhase = ExecutionPhase.CODING;
      this.emit('phase:start', { phase: 'coding' });
      
      const coder = this.agents.get('coder')!;
      let codeArtifacts = await this.executeWithRetry(
        () => coder.execute({
          input: architecture,
          context: this.context.getState(),
          sdd: pipeline.sdd,
        }, this.tools),
        'coding',
        maxRetries: this.maxRetries
      );
      
      result.phases.push({ name: 'coding', output: codeArtifacts });
      result.artifacts.code = codeArtifacts;
      this.context.updateCode(codeArtifacts);

      // ===== Phase 4: 安装扫描阶段(并行执行!)=====
      this.currentPhase = ExecutionPhase.SCANNING;
      this.emit('phase:start', { phase: 'scanning' });
      
      const [scanResult, testResult] = await Promise.all([
        // 安全扫描
        this.agents.get('scanner')!.execute({
          input: codeArtifacts,
          context: this.context.getState(),
        }, this.tools),
        
        // 测试生成与执行
        this.agents.get('tester')!.execute({
          input: codeArtifacts,
          context: this.context.getState(),
          architecture,
        }, this.tools),
      ]);
      
      result.phases.push(
        { name: 'scanning', output: scanResult },
        { name: 'testing', output: testResult }
      );
      
      // 如果扫描发现问题,自动触发修复循环
      if (scanResult.issues.length > 0) {
        this.currentPhase = ExecutionPhase.FIXING;
        const fixedCode = await this.autoFixIssues(
          codeArtifacts, scanResult.issues
        );
        result.artifacts.code = fixedCode;
        result.artifacts.fixesApplied = scanResult.issues.length;
      }

      // ===== Phase 5: 审查阶段 =====
      this.currentPhase = ExecutionPhase.REVIEWING;
      this.emit('phase:start', { phase: 'reviewing' });
      
      const reviewer = this.agents.get('reviewer')!;
      const reviewResult = await reviewer.execute({
        input: result.artifacts.code,
        context: this.context.getState(),
        scanResult,
        testResult,
        sdd: pipeline.sdd,  // 用SDD做符合度检查
      }, this.tools);
      
      result.phases.push({ name: 'reviewing', output: reviewResult });

      // ===== Phase 6: 输出阶段 =====
      this.currentPhase = ExecutionPhase.FINALIZING;
      const devops = this.agents.get('devops')!;
      const finalOutput = await devops.execute({
        input: {
          code: result.artifacts.code,
          review: reviewResult,
          scan: scanResult,
          test: testResult,
        },
        context: this.context.getState(),
        pipelineConfig: pipeline.config,
      }, this.tools);
      
      result.phases.push({ name: 'finalizing', output: finalOutput });
      result.status = 'success';
      result.artifacts.final = finalOutput;

      // ===== 完成!保存到记忆 =====
      await this.memory.saveEpisode({
        pipelineId: pipeline.id,
        input: pipeline.input,
        output: finalOutput,
        phases: result.phases,
        timestamp: new Date(),
        success: true,
      });

      this.currentPhase = ExecutionPhase.IDLE;
      result.metrics.durationMs = Date.now() - startTime;
      this.pipelineHistory.push(result);
      this.emit('pipeline:complete', { result });
      
      return result;

    } catch (error) {
      // 错误处理与优雅降级
      result.status = 'failed';
      result.error = error.message;
      result.metrics.durationMs = Date.now() - startTime;
      
      this.emit('pipeline:error', { error, result });
      
      // 尝试保存部分结果(即使失败了也可能有有用的中间产物)
      if (result.artifacts.code) {
        await this.memory.savePartial(result);
      }
      
      throw new AgentExecutionError(
        `Pipeline ${pipeline.id} failed at phase ${this.currentPhase}: ${error.message}`,
        { phase: this.currentPhase, partialResult: result }
      );
    }
  }

  /**
   * 带重试的执行包装器
   * 
   * 对于可能因LLM不确定性而失败的步骤,
   * 自动重试并逐步调整策略
   */
  private async executeWithRetry<T>(
    fn: () => Promise<T>,
    phaseName: string,
    options: { maxRetries?: number } = {}
  ): Promise<T> {
    const maxAttempts = options.maxRetries ?? this.maxRetries;
    let lastError: Error | null = null;
    
    for (let attempt = 0; attempt < maxAttempts; attempt++) {
      try {
        return await fn();
      } catch (error) {
        lastError = error as Error;
        this.emit('agent:retry', { 
          phase: phaseName, 
          attempt: attempt + 1, 
          error: error.message 
        });
        
        // 每次重试前调整策略
        if (attempt < maxAttempts - 1) {
          await this.adjustStrategy(phaseName, attempt, error);
        }
      }
    }
    
    throw lastError!;
  }

  /**
   * 自动修复安全问题的核心方法
   */
  private async autoFixIssues(
    code: CodeArtifact, 
    issues: ScanIssue[]
  ): Promise<CodeArtifact> {
    // 只自动修复Medium及以下级别的问题
    const fixableIssues = issues.filter(
      i => i.severity !== 'CRITICAL'
    );
    
    if (fixableIssues.length === 0) return code;
    
    this.emit('auto-fix:start', { count: fixableIssues.length });
    
    // 使用Coder Agent进行定向修复
    const coder = this.agents.get('coder')!;
    const fixedCode = await coder.execute({
      action: 'fix_issues',
      targetCode: code,
      issues: fixableIssues,
      context: this.context.getState(),
    }, this.tools);
    
    // 修复后重新扫描验证
    const scanner = this.agents.get('scanner')!;
    const reScan = await scanner.execute({
      input: fixedCode,
      context: this.context.getState(),
    }, this.tools);
    
    if (reScan.issues.some(i => i.severity === 'CRITICAL')) {
      // 仍有严重问题,需要人工介入
      this.emit('auto-fix:needs-human', { remaining: reScan.issues });
      throw new AutoFixError('Critical issues remain after auto-fix', reScan.issues);
    }
    
    this.emit('auto-fix:complete', { 
      fixed: fixableIssues.length, 
      remaining: reScan.issues.length 
    });
    
    return fixedCode;
  }

  /**
   * 注册所有内置Agent
   */
  private registerBuiltinAgents(): void {
    const builtinAgents = [
      { name: 'planner', cls: PlannerAgent },
      { name: 'architect', cls: ArchitectAgent },
      { name: 'coder', cls: CoderAgent },
      { name: 'tester', cls: TesterAgent },
      { name: 'reviewer', cls: ReviewerAgent },
      { name: 'scanner', cls: ScannerAgent },
      { name: 'devops', cls: DevOpsAgent },
    ];
    
    for (const { name, cls } of builtinAgents) {
      const instance = new cls({
        llm: this.llm,
        memory: this.memory,
        tools: this.tools,
      });
      this.agents.set(name, instance);
      this.emit('agent:registered', { name });
    }
  }

  // ... 其他辅助方法省略 ...
}

// === 类型定义 ===

export interface EngineConfig {
  llm: LlmProvider;
  memory?: MemoryStore;
  maxRetries?: number;
  timeoutMs?: number;
  enableAutoFix?: boolean;
  verbose?: boolean;
}

export enum ExecutionPhase {
  IDLE = 'idle',
  PLANNING = 'planning',
  ARCHITECTING = 'architecting',
  CODING = 'coding',
  SCANNING = 'scanning',
  TESTING = 'testing',
  REVIEWING = 'reviewing',
  FIXING = 'fixing',
  FINALIZING = 'finalizing',
}

export class AgentExecutionError extends Error {
  constructor(message: string, public meta: any) {
    super(message);
    this.name = 'AgentExecutionError';
  }
}

2.2 BaseAgent — Agent基类

// packages/agent-core/src/agents/BaseAgent.ts
// 所有MonkeyCode Agent的基类

import { v4 as uuidv4 } from 'uuid';
import { 
  AgentMessage, 
  AgentOutput, 
  ToolCall,
  ToolResult,
  PromptContext
} from '../types';
import { LlmProvider } from '../llm/LlmProvider';
import { MemoryStore } from '../memory/MemoryStore';
import { ToolRegistry } from '../tools/ToolRegistry';

/**
 * BaseAgent - 所有Agent的抽象基类
 * 
 * 设计原则:
 * 1. 模板方法模式:定义固定的执行流程骨架,子类只填充特定步骤
 * 2. 每个Agent有明确的角色定位和能力边界
 * 3. 统一的输入输出格式,便于Agent之间协作
 * 4. 内置思维链(Chain-of-Thought)支持
 */
export abstract class BaseAgent {
  
  // === 身份信息 ===
  abstract readonly name: string;        // Agent名称(唯一标识)
  abstract readonly description: string; // 能力描述
  abstract readonly role: string;        // 角色定位(用于System Prompt)
  
  // === 依赖 ===
  protected llm: LlmProvider;
  protected memory: MemoryStore;
  protected tools: ToolRegistry;
  
  // === 配置 ===
  protected temperature: number = 0.3;   // 低温度保证稳定性
  protected maxTokens: number = 8192;
  protected enableCoT: boolean = true;   // 启用思维链

  constructor(config: AgentConfig) {
    this.llm = config.llm;
    this.memory = config.memory;
    this.tools = config.tools;
  }

  /**
   * 🔑 核心执行方法(模板方法模式)
   * 
   * 执行流程:
   * 1. buildPrompt() → 构建提示词(子类实现)
   * 2. preExecute()  → 前置处理(可选,子类可覆盖)
   * 3. callLLM()    → 调用LLM
   * 4. postProcess()→ 后处理(可选,子类可覆盖)
   * 5. 返回结构化输出
   */
  async execute(
    input: any,
    tools: ToolRegistry,
    options?: ExecuteOptions
  ): Promise<AgentOutput> {
    const sessionId = uuidv4();
    const startTime = Date.now();
    
    try {
      // Step 1: 构建提示词
      const promptContext = await this.buildPrompt(input, options);
      
      // Step 2: 前置处理(如加载额外上下文)
      await this.preExecute(input, promptContext);
      
      // Step 3: 调用LLM(可能包含工具调用循环)
      const rawResponse = await this.callLLM(promptContext, tools);
      
      // Step 4: 后处理(解析、校验、格式化)
      const output = await this.postProcess(rawResponse, input);
      
      // Step 5: 记录到记忆
      await this.recordToMemory(sessionId, input, output, Date.now() - startTime);
      
      return {
        agentName: this.name,
        sessionId,
        output,
        metadata: {
          duration: Date.now() - startTime,
          promptTokens: promptContext.tokenCount,
          model: this.llm.modelName,
        },
        timestamp: new Date(),
      };
      
    } catch (error) {
      throw new AgentError(
        `${this.name} execution failed: ${(error as Error).message}`,
        { agent: this.name, sessionId, input }
      );
    }
  }

  /**
   * 🔧 子类必须实现:构建提示词
   * 
   * 这是每个Agent差异化的核心。
   * 不同Agent通过不同的Prompt来实现不同的能力。
   */
  protected abstract buildPrompt(
    input: any,
    options?: ExecuteOptions
  ): Promise<PromptContext>;

  /**
   * 🔧 子类可选覆盖:前置处理
   * 用于在调用LLM前做一些准备工作
   */
  protected async preExecute(
    input: any, 
    promptCtx: PromptContext
  ): Promise<void> {
    // 默认空实现
    // 子类可以覆盖来添加特殊的前置逻辑
    // 例如:Coder Agent 可能需要先读取相关代码文件
  }

  /**
   * 🔧 子类可选覆盖:后处理
   * 用于对LLM输出进行解析和校验
   */
  protected async postProcess(
    rawResponse: LLMResponse,
    originalInput: any
  ): Promise<any> {
    // 默认尝试JSON解析
    if (typeof rawResponse.content === 'string') {
      try {
        return JSON.parse(rawResponse.content);
      } catch {
        // 不是JSON,直接返回文本
        return rawResponse.content;
      }
    }
    return rawResponse.content;
  }

  /**
   * 🔄 LLM调用(含工具使用循环)
   * 
   * 这是Agent能力的核心——让LLM能够使用工具。
   * 实现了ReAct(Reasoning + Acting)模式:
   * 1. LLM决定是否需要调用工具
   * 2. 如果需要,执行工具调用
   * 3. 将工具结果反馈给LLM
   * 4. 重复直到LLM给出最终答案
   */
  protected async callLLM(
    promptCtx: PromptContext,
    tools: ToolRegistry
  ): Promise<LLMResponse> {
    const messages: AgentMessage[] = [
      // System Message: 角色设定
      {
        role: 'system',
        content: this.buildSystemPrompt(tools),
      },
      // User Message: 具体任务
      {
        role: 'user',
        content: promptCtx.userMessage,
      },
    ];

    // 工具使用循环(最多10轮,防止无限循环)
    const MAX_TOOL_ROUNDS = 10;
    
    for (let round = 0; round < MAX_TOOL_ROUNDS; round++) {
      // 调用LLM
      const response = await this.llm.chatCompletion({
        messages,
        temperature: this.temperature,
        maxTokens: this.maxTokens,
        tools: tools.getToolDefinitions(),  // 传入可用工具定义
      });

      // 检查是否要调用工具
      const toolCalls = response.toolCalls;
      
      if (!toolCalls || toolCalls.length === 0) {
        // LLM给出了最终回答,不需要再调用工具
        return response;
      }

      // 执行工具调用
      const toolResults: ToolResult[] = [];
      for (const toolCall of toolCalls) {
        try {
          const result = await tools.execute(toolCall);
          toolResults.push({
            toolCallId: toolCall.id,
            result: result.output,
          });
        } catch (error) {
          toolResults.push({
            toolCallId: toolCall.id,
            isError: true,
            result: `Tool execution error: ${(error as Error).message}`,
          });
        }
      }

      // 将工具结果追加到消息历史中
      messages.push({
        role: 'assistant',
        content: response.content,
        toolCalls,
      });
      messages.push({
        role: 'tool',
        toolResults,
      });

      // 继续下一轮循环,让LLM根据工具结果继续推理
    }

    // 达到最大轮次限制
    throw new Error(`Agent ${this.name} exceeded maximum tool call rounds (${MAX_TOOL_ROUNDS})`);
  }

  /**
   * 构建System Prompt
   * 包含:角色描述 + 可用工具列表 + 输出格式要求 + 思维链指令
   */
  protected buildSystemPrompt(tools: ToolRegistry): string {
    const toolDescriptions = tools.listTools()
      .map(t => `- ${t.name}: ${t.description}`)
      .join('\n');
    
    return `你是一个专业的${this.role}。

## 你的身份
${this.description}

## 你的任务
根据用户的需求,利用你的专业知识和可用工具,高质量地完成任务。

## 你可以使用的工具
${toolDescriptions}

## 输出要求
- 使用JSON格式输出结构化结果
- 确保输出的代码符合最佳实践
- 对不确定的部分,标注[NEEDS_REVIEW]

${this.enableCoT ? `
## 思考方式
请按照以下步骤思考:
1. 分析需求,理解目标
2. 制定行动计划
3. 逐步执行计划
4. 验证结果
5. 输出最终答案

请将你的思考过程放在<think_tags>标签中。
` : ''}`;
  }

  /**
   * 记录到记忆系统
   */
  private async recordToMemory(
    sessionId: string,
    input: any,
    output: any,
    duration: number
  ): Promise<void> {
    await this.memory.store({
      agent: this.name,
      sessionId,
      input: this.sanitizeForStorage(input),
      output: this.sanitizeForStorage(output),
      duration,
      timestamp: new Date(),
    });
  }

  private sanitizeForStorage(data: any): any {
    // 移除敏感信息和过大的字段
    // 简化实现:实际项目中需要更精细的处理
    if (typeof data === 'string' && data.length > 10000) {
      return data.substring(0, 10000) + '... [truncated]';
    }
    return data;
  }
}

// === 类型定义 ===

export interface AgentConfig {
  llm: LlmProvider;
  memory: MemoryStore;
  tools: ToolRegistry;
  temperature?: number;
  maxTokens?: number;
}

export interface ExecuteOptions {
  verbose?: boolean;
  forceRefresh?: boolean;
  additionalContext?: Record<string, any>;
}

export interface PromptContext {
  userMessage: string;
  systemContext?: string;
  tokenCount: number;
  relevantMemory?: any[];
}

export interface LLMResponse {
  content: string;
  toolCalls?: ToolCall[];
  finishReason: 'stop' | 'tool_calls' | 'length';
  usage?: { promptTokens: number; completionTokens: number };
}

2.3 CoderAgent — 编码Agent(最复杂的Agent)

// packages/agent-core/src/agents/CoderAgent.ts
// CoderAgent —— 负责代码生成的核心Agent

import { BaseAgent } from './BaseAgent';

/**
 * CoderAgent
 * 
 * 这是整个MonkeyCode系统中最重要的Agent之一,
 * 也是代码量最大、逻辑最复杂的Agent。
 * 
 * 核心能力:
 * 1. 根据SDD和架构设计生成高质量代码
 * 2. 支持多种编程语言
 * 3. 遵循项目的编码规范
 * 4. 处理复杂的多文件生成场景
 * 5. 自我检查生成代码的质量
 */
export class CoderAgent extends BaseAgent {
  readonly name = 'coder';
  readonly description = '专业代码生成Agent,根据SDD规范和架构设计生成高质量的工程代码';
  readonly role = '高级软件工程师';

  // Coder特有配置
  private supportedLanguages = [
    'typescript', 'javascript', 'python', 'java', 'go', 'rust'
  ];
  private maxFilesPerGeneration = 20; // 单次最多生成20个文件

  /**
   * 构建Coder专用的Prompt
   * 
   * 关键设计决策:
   * - 注入SDD内容作为"需求规格"
   * - 注入架构设计作为"技术约束"
   * - 注入编码规范作为"风格指南"
   * - 注入已有代码作为"上下文参考"
   */
  protected async buildPrompt(
    input: CoderInput,
    options?: ExecuteOptions
  ): Promise<PromptContext> {
    const { architecture, sdd, existingCode, action } = input;
    
    // 构建User Message
    let userMessage = '';

    // 根据action类型构建不同的prompt
    switch (action) {
      case 'generate':
        userMessage = this.buildGeneratePrompt(sdd, architecture, existingCode);
        break;
      case 'modify':
        userMessage = this.buildModifyPrompt(sdd, architecture, existingCode, input.modifications);
        break;
      case 'fix_issues':
        userMessage = this.buildFixPrompt(existingCode, input.issues);
        break;
      default:
        userMessage = this.buildGeneratePrompt(sdd, architecture, existingCode);
    }

    // 从记忆中检索相关的历史代码片段
    const relevantMemory = await this.memory.search({
      query: sdd?.meta?.name || '',
      agent: this.name,
      limit: 5,
    });

    return {
      userMessage,
      tokenCount: this.estimateTokens(userMessage),
      relevantMemory,
    };
  }

  /**
   * 构建"生成新代码"模式的Prompt
   */
  private buildGeneratePrompt(
    sdd: SDDDocument,
    architecture: ArchitectureDesign,
    existingCode?: CodebaseSnapshot
  ): string {
    return `请根据以下SDD规范和架构设计,生成完整的代码。

## SDD规范
\`\`\`yaml
${JSON.stringify(sdd, null, 2)}
\`\`\`

## 架构设计
\`\`\`json
${JSON.stringify(architecture, null, 2)}
\`\`\`

${existingCode ? `
## 已有代码参考
以下是目前项目中已有的相关代码,请保持风格一致:

### 目录结构
${existingCode.directoryTree}

### 相关文件摘要
${existingCode.relevantFiles.map(f => 
  `#### ${f.path}\n\`\`\`${f.language}\n${f.content}\n\`\`\``
).join('\n\n')}
` : ''}

## 生成要求
1. 严格按照SDD中的techStack指定的语言和框架
2. 遵循架构设计中定义的模块结构和分层
3. 每个文件必须有清晰的注释说明其用途
4. 导出/公开的API必须有JSDoc/TSDoc注释
5. 错误处理要完善,不要留TODO或FIXME
6. 生成的代码应该可以直接运行(不需要手动修改)

## 输出格式
请以以下JSON格式输出:
{
  "files": [
    {
      "path": "相对路径/文件名.扩展名",
      "language": "语言标识",
      "content": "完整的文件内容",
      "description": "这个文件的用途说明"
    }
  ],
  "summary": "整体实现的简要说明",
  "dependencies": ["需要安装的npm/pip依赖"],
  "notes": ["任何需要注意的事项"]
}`;
  }

  /**
   * Coder特有的后处理:代码质量自检
   */
  protected async postProcess(rawResponse: LLMResponse, originalInput: CoderInput): Promise<CodeArtifact> {
    let parsed: CodeGenerationResult;
    
    // 解析LLM输出
    if (typeof rawResponse.content === 'string') {
      // 尝试提取JSON(LLM可能在代码块中返回JSON)
      const jsonMatch = rawResponse.content.match(/```(?:json)?\s*([\s\S]*?)```/);
      if (jsonMatch) {
        parsed = JSON.parse(jsonMatch[1]);
      } else {
        parsed = JSON.parse(rawResponse.content);
      }
    } else {
      parsed = rawResponse.content;
    }

    // 代码质量自检
    const qualityCheck = this.performQualityCheck(parsed.files);
    
    if (!qualityCheck.passed) {
      // 质量不达标,记录警告但不阻断
      console.warn(`[CoderAgent] Quality check warnings:`, qualityCheck.warnings);
    }

    return {
      files: parsed.files,
      summary: parsed.summary,
      dependencies: parsed.dependencies || [],
      notes: parsed.notes || [],
      qualityScore: qualityCheck.score,
      generatedAt: new Date(),
      sourceAgent: this.name,
    };
  }

  /**
   * 代码质量快速检查
   * (注意:这不是MonkeyScan的替代品,而是Coder自身的轻量级检查)
   */
  private performQualityCheck(files: GeneratedFile[]): QualityCheckResult {
    const warnings: string[] = [];
    let score = 100;

    for (const file of files) {
      // 检查1: 文件不能为空
      if (!file.content || file.content.trim().length === 0) {
        warnings.push(`${file.path}: 文件内容为空`);
        score -= 20;
      }

      // 检查2: 不能有明显的占位符
      if (file.content.includes('// TODO') || file.content.includes('# TODO')) {
        warnings.push(`${file.path}: 包含TODO占位符`);
        score -= 5;
      }

      // 检查3: 函数/方法数量合理性
      const funcCount = (file.content.match(/function\s+\w+|def\s+\w+|func\s+\w+/g) || []).length;
      if (funcCount > 30) {
        warnings.push(`${file.path}: 单文件函数过多(${funcCount}个),建议拆分`);
        score -= 5;
      }

      // 检查4: 文件长度合理性
      if (file.content.length > 50000) {
        warnings.push(`${file.path}: 文件过大(${Math.round(file.content.length/1000)}K),建议拆分`);
        score -= 5;
      }
    }

    return {
      passed: score >= 70,  // 70分以上算通过
      score: Math.max(0, score),
      warnings,
    };
  }

  // === 前置处理:加载项目上下文 ===
  protected async preExecute(input: CoderInput, promptCtx: PromptContext): Promise<void> {
    // 如果是修改操作,先加载目标文件的内容
    if (input.action === 'modify' && input.targetPaths) {
      const fileContents = await Promise.all(
        input.targetPaths.map(path => 
          this.tools.execute({
            name: 'filesystem_read_file',
            arguments: { path }
          })
        )
      );
      input.existingCodeContent = fileContents;
    }
  }
}

// === CoderAgent 特有类型 ===

interface CoderInput {
  architecture: ArchitectureDesign;
  sdd?: SDDDocument;
  existingCode?: CodebaseSnapshot;
  action: 'generate' | 'modify' | 'fix_issues';
  modifications?: ModificationRequest[];
  issues?: ScanIssue[];
  targetPaths?: string[];
}

interface CodeArtifact {
  files: GeneratedFile[];
  summary: string;
  dependencies: string[];
  notes: string[];
  qualityScore: number;
  generatedAt: Date;
  sourceAgent: string;
}

interface GeneratedFile {
  path: string;
  language: string;
  content: string;
  description?: string;
}

interface QualityCheckResult {
  passed: boolean;
  score: number;
  warnings: string[];
}

三、关键子系统深度分析

3.1 记忆系统(Memory)

MonkeyCode Agent的记忆体系设计:

┌─────────────────────────────────────────────────────┐
│                Memory Store (记忆存储)                │
│                                                     │
│  ┌───────────────────────────────────────────────┐  │
│  │           Short-Term Memory (短期记忆)         │  │
│  │                                               │  │
│  │  作用域: 当前Pipeline执行过程中的临时信息       │  │
│  │  容量: 最近50条交互                            │  │
│  │  保留时间: Pipeline执行期间                     │  │
│  │  典型内容:                                    │  │
│  │  • 当前的SDD文件内容                           │  │
│  │  • 各阶段的中间产物                             │  │
│  │  • Agent之间的对话上下文                       │  │
│  │  • 当前的错误信息和修复方案                     │  │
│  │                                               │  │
│  │  实现: 内存中的LRU Cache                       │  │
│  └───────────────────────────────────────────────┘  │
│                                                     │
│  ┌───────────────────────────────────────────────┐  │
│  │           Long-Term Memory (长期记忆)           │  │
│  │                                               │  │
│  │  作用域: 跨Pipeline持久化的知识                 │  │
│  │  容量: 无限(受磁盘空间限制)                   │  │
│  │  保留时间: 永久(除非主动删除)                 │  │
│  │  典型内容:                                    │  │
│  │  • 项目的技术栈偏好和历史选择                   │  │
│  │  • 常用的代码模式和惯用法                       │  │
│  │  • 之前解决过的类似问题和方案                   │  │
│  │  • 用户偏好设置                                │  │
│  │                                               │  │
│  │  实现: SQLite / 文件系统                      │  │
│  └───────────────────────────────────────────────┘  │
│                                                     │
│  ┌───────────────────────────────────────────────┐  │
│  │           Episodic Memory (情景记忆)            │  │
│  │                                               │  │
│  │  作用域: 完整的Pipeline执行记录                 │  │
│  │  容量: 按需保留                                 │  │
│  │  保留时间: 可配置(默认90天)                   │  │
│  │  典型内容:                                    │  │
│  │  • 每次Pipeline的完整执行日志                   │  │
│  │  • 输入/输出/各阶段耗时                         │  │
│  │  • 成功/失败状态和原因                          │  │
│  │  • 使用的工具和参数                             │  │
│  │                                               │  │
│  │  实现: 结构化日志文件 (JSONL)                  │  │
│  └───────────────────────────────────────────────┘  │
│                                                     │
│  记忆的使用时机:                                   │
│  ├── 新Pipeline开始时 → 加载Long-Term相关记忆       │
│  ├── Agent执行前 → 注入Short-Term上下文             │
│  ├── Pipeline结束时 → 保存Episodic记录              │
│  ├── 遇到错误时 → 搜索Long-Term找类似解决方案       │
│  └── 用户提问时 → 综合查询所有记忆层级              │
└─────────────────────────────────────────────────────┘

3.2 MCP工具集成

// packages/agent-core/src/tools/mcp/McpToolAdapter.ts
// MCP协议工具适配器 —— 让Agent可以使用任何MCP Server提供的工具

import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';

/**
 * McpToolAdapter
 * 
 * 将MCP Server暴露的工具转换为MonkeyCode Agent可用的内部工具。
 * 
 * MCP(Model Context Protocol)是一个开放标准,
 * 定义了AI模型如何与外部工具和数据源进行交互。
 * 
 * 通过这个适配器,MonkeyCode可以无缝使用任何实现了
 * MCP协议的工具服务。
 */
export class McpToolAdapter {
  private clients: Map<string, Client> = new Map();
  private toolCache: Map<string, McpToolDefinition> = new Map();

  /**
   * 注册一个MCP Server
   * 
   * @param serverName 服务名称(唯一标识)
   * @param config 启动配置(命令、参数、环境变量等)
   */
  async registerServer(serverName: string, config: McpServerConfig): Promise<void> {
    const transport = new StdioClientTransport({
      command: config.command,
      args: config.args,
      env: { ...process.env, ...config.env },
    });

    const client = new Client({
      name: `monkeycode-${serverName}`,
      version: '1.0.0',
    });

    await client.connect(transport);
    
    // 列出该Server提供的所有工具
    const { tools } = await client.listTools();
    
    for (const tool of tools) {
      const fullToolName = `${serverName}.${tool.name}`;
      this.toolCache.set(fullToolName, {
        ...tool,
        serverName,
        client,
      });
    }

    this.clients.set(serverName, client);
    console.log(`[MCP] Registered server '${serverName}' with ${tools.length} tools`);
  }

  /**
   * 获取所有已注册的工具定义
   * 用于在LLM调用时声明可用工具
   */
  listAllTools(): McpToolDefinition[] {
    return Array.from(this.toolCache.values());
  }

  /**
   * 调用一个MCP工具
   * 
   * 这是Agent实际使用MCP工具时的入口
   */
  async callTool(toolName: string, args: Record<string, any>): Promise<any> {
    const tool = this.toolCache.get(toolName);
    if (!tool) {
      throw new Error(`MCP tool not found: ${toolName}`);
    }

    console.log(`[MCP] Calling ${toolName}`, args);
    
    const result = await tool.client.callTool({
      name: tool.name.replace(`${tool.serverName}.`, ''),
      arguments: args,
    });

    // 解析MCP返回的内容
    if (result.content && Array.isArray(result.content)) {
      const textContent = result.content
        .filter(c => c.type === 'text')
        .map(c => c.text)
        .join('\n');
      
      try {
        return JSON.parse(textContent);
      } catch {
        return textContent;
      }
    }

    return result;
  }

  /**
   * 断开所有MCP连接
   */
  async disconnectAll(): Promise<void> {
    for (const [name, client] of this.clients) {
      await client.close();
      console.log(`[MCP] Disconnected server '${name}'`);
    }
    this.clients.clear();
    this.toolCache.clear();
  }
}

// MCP工具的实际使用示例:
//
// 在Coder Agent中调用数据库查询MCP工具:
// const dbResult = await mcpAdapter.callTool('database.query', {
//   sql: 'SELECT * FROM users WHERE id = $1',
//   params: ['123']
// });
//
// 在Architect Agent中调用文档查询MCP工具:
// const apiDoc = await mcpAdapter.callTool('api-docs.getEndpoint', {
//   path: '/api/v1/users',
//   method: 'GET'
// });

四、性能优化与工程设计

4.1 性能优化策略

MonkeyCode Agent引擎的性能优化矩阵:

┌────────────────────┬──────────────────────────────────┬──────────┐
│ 优化维度            │ 具体措施                           │ 效果      │
├────────────────────┼──────────────────────────────────┼──────────┤
│ LLM调用优化         │                                  │          │
│ ├─ Prompt缓存       │ 相同SDD的Prompt复用               │ -60% Token│
│ ├─ 流式输出         │ 首Token延迟降低80%               │ 更快响应 │
│ ├─ 并行Agent执行     │ Scan+Test并行(见Engine.ts)      │ -45% 时间│
│ └─ 小模型预处理     │ 简单任务用7B模型,复杂任务用72B     │ -70% 成本│
│                    │                                  │          │
│ 记忆系统优化        │                                  │          │
│ ├─ 向量索引          │ 语义搜索而非精确匹配               │ 召回率↑35%│
│ ├─ 分级存储          │ 热/温/冷数据分层                  │ 内存↓60% │
│ └─ 增量更新          │ 只变更的部分写入磁盘               │ I/O↓80%  │
│                    │                                  │          │
│ 代码生成优化         │                                  │          │
│ ├─ 增量生成          │ 只生成变更的文件,非全量           │ 速度↑3x  │
│ ├─ 模板加速          │ 常用代码模式从模板生成             │ 速度↑2x  │
│ └─ 缓存编译          │ TypeScript编译结果缓存            │ 重复↓90% │
│                    │                                  │          │
│ 并发控制            │                                  │          │
│ ├─ 信号量限制        │ 最大并发Agent数=CPU核数×2         │ 稳定性↑  │
│ ├─ 队列调度          │ 优先级队列(P0>P1>P2)            │ SLA保障  │
│ └─ 超时熔断          │ 单步超过5min自动中断              │ 避免死锁 │
└────────────────────┴──────────────────────────────────┴──────────┘

4.2 可扩展性设计

Agent引擎的可扩展性架构:

扩展点1: 自定义Agent
  → 继承BaseAgent
  → 实现buildPrompt()方法
  → 注册到Engine
  → 即可在Pipeline中使用
  
  示例: 创建一个专门处理SQL优化的Agent
  class SqlOptimizerAgent extends BaseAgent {
    readonly name = 'sql-optimizer';
    readonly role = '数据库性能优化专家';
    // ... 实现
  }

扩展点2: 自定义工具
  → 实现BaseTool接口
  → 注册到ToolRegistry
  → 所有Agent都可以使用
  
  示例: 创建一个公司内部的工单系统工具
  class TicketSystemTool extends BaseTool {
    name = 'ticket_system';
    // ... 实现
  }

扩展点3: 自定义规则(MonkeyScan)
  → 编写YAML格式的规则文件
  → 放入rules目录
  → 自动加载并生效
  
  示例: 公司特有的安全规则
  # rules/custom/no-hardcoded-ip.yaml
  rule:
    name: "no_hardcoded_ip"
    pattern: "\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"
    severity: MEDIUM
    suggestion: "IP地址应从环境变量读取"

扩展点4: 自定义SDD模板
  → 编写YAML模板
  → 放入templates目录
  → 新项目可直接选用

扩展点5: LLM Provider扩展
  → 实现LlmProvider接口
  → 支持任何兼容OpenAI API的服务
  → 包括本地部署的模型

五、总结与学习建议

5.1 核心架构要点回顾

╔══════════════════════════════════════════════════════╗
║                                                      ║
║  MonkeyCode Agent引擎的核心设计理念:                  ║
║                                                      ║
║  ① Engine(编排器): 不干活,只协调                  ║
║     → 类似乐队指挥,自己不演奏乐器                    ║
║     → 职责是决定谁在什么时候做什么                    ║
║                                                      ║
║  ② Agent(执行者): 专业分工,各司其职               ║
║     → 每个Agent有自己的专长领域                      ║
║     → 通过统一的接口(BaseAgent)协作                 ║
║     → 可以独立测试和优化                             ║
║                                                      ║
║  ③ Memory(记忆): 跨会话的知识积累                  ║
║     → 让Agent越用越聪明                              ║
║     → 三层记忆设计满足不同需求                        ║
║                                                      ║
║  ④ Tools(工具): Agent的手和眼睛                    ║
║     → MCP协议让工具生态无限扩展                      ║
║     → Agent通过工具与真实世界交互                     ║
║                                                      ║
║  ⑤ SDD(规范): 共同的语言                           ║
║     → Agent之间沟通的基础                            ║
║     → 保证输出的一致性和质量                         ║
║                                                      ║
╚══════════════════════════════════════════════════════╝

5.2 给想深入源码的学习者建议

📚 推荐阅读顺序:

  Level 1: 入门(了解整体架构)
  ├── 阅读 BaseAgent.ts(理解Agent的基本工作方式)
  ├── 阅读 Engine.ts 的 executePipeline 方法(理解整体流程)
  └── 跑通一个简单的Hello World示例

  Level 2: 进阶(理解核心机制)
  ├── 阅读 CoderAgent.ts(理解最复杂的Agent)
  ├── 阅读 MemoryStore 的三种记忆实现
  ├── 阅读 McpToolAdapter(理解MCP集成方式)
  └── 尝试编写一个自定义Agent

  Level 3: 高深(理解优化和扩展)
  ├── 研究 Prompt Template 系统
  ├── 学习 Token 计数和成本优化策略
  ├── 阅读并发控制和错误恢复机制
  └── 尝试贡献代码到开源项目

💡 学习技巧:
  1. 先跑起来再看代码(动态调试比静态阅读高效10倍)
  2. 从测试用例入手(tests/目录是最好的文档)
  3. 关注数据流(数据怎么从一个Agent传到另一个)
  4. 动手写代码(最好的学习方式是自己写一个简化版)
  5. 加入社区讨论(GitHub Issues/Discord中有大量有价值讨论)

系列导航


本文基于MonkeyCode开源项目( https://github.com/chaitin/monkeycode )v1.2.x版本的源码进行分析。源码结构可能随版本演进有所变化,请以最新版本为准。

关键词:#MonkeyCode #Agent引擎 #源码分析 #架构设计 #AI编程 #TypeScript #MCP协议 #多Agent协作 #开源源码 #软件架构

posted on 2026-07-08 18:04  MonkeyCode  阅读(24)  评论(0)    收藏  举报