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的教学改革案例》
- 下一篇:《基于MonkeyCode二次开发:定制企业专属AI助手》
- 系列目录:[MonkeyCode开源完全指南(2026版)— 30篇系列索引](待整理)
本文基于MonkeyCode开源项目( https://github.com/chaitin/monkeycode )v1.2.x版本的源码进行分析。源码结构可能随版本演进有所变化,请以最新版本为准。
关键词:#MonkeyCode #Agent引擎 #源码分析 #架构设计 #AI编程 #TypeScript #MCP协议 #多Agent协作 #开源源码 #软件架构
浙公网安备 33010602011771号