1. 项目背景与动机
作为一名长期关注AI技术发展的开发者,我一直对AI编程助手这类工具充满好奇。市面上已有不少成熟产品,但作为技术人员,我更想亲手构建一个简化版的Code Agent来深入理解其工作原理。这个决定源于三个核心需求:
- 学习需求:通过实践掌握Code Agent的核心架构
- 实用需求:打造一个轻量级但功能完备的本地开发助手
- 探索需求:验证不同开源模型在代码生成场景的表现
选择从零开始构建而不是直接使用现成方案,能让我在以下方面获得更深入的理解:
- ReAct模式的实际工作流程
- 工具系统的设计权衡
- 多模型支持的实现细节
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心架构设计
2.1 整体架构视图
Mini Claude Code采用分层架构设计,各模块职责分明:
code复制┌─────────────────────────────────┐
│ CLI Interface │
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ ReAct Agent Core │
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ Tool System │
│ ┌─────────┐ ┌─────────┐ ┌─────┐ │
│ │ Built-in │ │ MCP │ │ ... │ │
│ │ Tools │ │ Tools │ │ │ │
│ └─────────┘ └─────────┘ └─────┘ │
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ Model Adapters │
│ ┌───────┐ ┌──────┐ ┌──────────┐ │
│ │Ollama │ │ Groq │ │ DeepSeek │ │
│ └───────┘ └──────┘ └──────────┘ │
└─────────────────────────────────┘
2.2 关键技术选型
经过多轮评估,最终确定以下技术栈:
运行时环境:
- Node.js v18+:成熟的异步IO支持
- TypeScript:类型安全提升开发效率
核心库:
- Commander:构建CLI命令体系
- Inquirer:交互式命令行界面
- Zod:参数校验与类型推断
AI相关:
- OpenAI兼容API:统一不同模型的调用方式
- LangChain.js:可选集成,提供更高级的AI功能
辅助工具:
- Chalk:终端输出着色
- Ora:优雅的加载动画
- Boxen:醒目的信息框展示
3. ReAct模式实现细节
3.1 循环控制机制
ReAct循环的核心在于保持状态和控制流程。我们实现了带超时和重试机制的循环:
typescript复制class ReactLoop {
private maxIterations: number = 15;
private timeoutMs: number = 30000;
private retryCount: number = 3;
async execute(task: string): Promise<Result> {
let iteration = 0;
let context = this.initContext(task);
while (iteration < this.maxIterations) {
try {
const result = await this.runIteration(context);
if (result.status === 'COMPLETE') {
return result;
}
context = this.updateContext(context, result);
iteration++;
} catch (error) {
if (this.retryCount <= 0) throw error;
this.retryCount--;
await new Promise(r => setTimeout(r, 1000));
}
}
throw new Error(`Max iterations (${this.maxIterations}) reached`);
}
}
3.2 思考-行动-观察的完整流程
思考阶段:
- 分析当前上下文
- 生成下一步行动计划
- 评估可能的执行风险
行动阶段:
- 解析工具调用指令
- 验证参数有效性
- 执行具体工具操作
观察阶段:
- 捕获工具执行结果
- 提取关键信息
- 格式化反馈内容
典型的工作日志示例:
code复制[THOUGHT] 需要先了解项目结构,应该列出src目录内容
[ACTION] 调用list_files工具 {path: "./src"}
[OBSERVATION] 发现目录包含: index.ts, cli.ts, tools/
[THOUGHT] 需要检查工具实现,应读取tools/index.ts
[ACTION] 调用read_file工具 {path: "./src/tools/index.ts"}
4. 工具系统深度解析
4.1 工具接口设计
工具系统采用插件式架构,每个工具需要实现以下接口:
typescript复制interface Tool {
// 工具唯一标识
name: string;
// 自然语言描述,用于prompt生成
description: string;
// 参数规范(Zod schema)
parameters: z.ZodTypeAny;
// 执行方法
execute(params: unknown, workspace: string): Promise<{
success: boolean;
output: string;
data?: unknown;
}>;
// 安全等级(影响执行权限)
safetyLevel?: 'safe' | 'caution' | 'dangerous';
}
4.2 内置工具实现示例
以edit_file工具为例,展示完整实现:
typescript复制const EditFileTool: Tool = {
name: 'edit_file',
description: 'Perform search-and-replace operations in a file',
parameters: z.object({
filePath: z.string().describe("Relative path to the file"),
replacements: z.array(
z.object({
search: z.string(),
replace: z.string(),
isRegex: z.boolean().optional()
})
).min(1)
}),
safetyLevel: 'caution',
async execute(params, workspace) {
const { filePath, replacements } = params;
const fullPath = path.join(workspace, filePath);
if (!fs.existsSync(fullPath)) {
return {
success: false,
output: `File not found: ${filePath}`
};
}
let content = fs.readFileSync(fullPath, 'utf8');
let changes = 0;
for (const { search, replace, isRegex } of replacements) {
const pattern = isRegex ? new RegExp(search, 'g') : search;
const newContent = content.replace(pattern, replace);
if (newContent !== content) {
changes++;
content = newContent;
}
}
if (changes > 0) {
fs.writeFileSync(fullPath, content);
return {
success: true,
output: `Made ${changes} replacements in ${filePath}`,
data: { changes }
};
}
return {
success: true,
output: `No changes made to ${filePath}`
};
}
};
4.3 工具调用安全机制
为确保安全,实现了多层次的防护:
- 路径限制:所有文件操作限制在工作区内
- 沙盒执行:Shell命令在受限环境中运行
- 权限分级:危险操作需要显式授权
- 输入验证:严格的参数类型检查
- 操作审计:记录所有工具调用日志
5. MCP协议集成实践
5.1 协议连接流程
MCP集成的关键步骤:
-
服务发现:
- 扫描本地网络寻找可用MCP服务
- 验证服务兼容性和版本
-
能力协商:
- 获取服务提供的工具列表
- 检查参数格式和返回值约定
-
会话管理:
- 建立持久化连接
- 处理心跳和超时
-
错误恢复:
- 自动重连机制
- 状态同步恢复
5.2 代码实现要点
typescript复制class MCPClient {
private connection: MCPConnection;
private services: Map<string, MCPService> = new Map();
async connect(endpoint: string) {
this.connection = await establishConnection(endpoint);
// 交换能力信息
const handshake = await this.connection.request('handshake', {
client: 'mini-claude-code',
version: '0.1.0',
capabilities: ['tools', 'filesystem']
});
// 注册服务
for (const service of handshake.services) {
this.services.set(service.name, {
name: service.name,
tools: await this.listTools(service.name),
metadata: service.metadata
});
}
}
async callTool(service: string, tool: string, params: unknown) {
const result = await this.connection.request('tool', {
service,
tool,
params
}, { timeout: 10000 });
if (result.status === 'error') {
throw new MCPError(result.message);
}
return result.data;
}
}
6. 多模型支持方案
6.1 统一API适配层
为不同模型提供一致的调用接口:
typescript复制interface AIModel {
name: string;
streamChat(messages: ChatMessage[], options: {
temperature?: number;
maxTokens?: number;
tools?: ToolDefinition[];
}): Promise<{
content: string;
toolCalls?: ToolCall[];
isComplete: boolean;
}>;
// 其他统一方法...
}
class OllamaAdapter implements AIModel {
// 具体实现...
}
class GroqAdapter implements AIModel {
// 具体实现...
}
6.2 模型性能对比
在实际测试中,各模型表现差异明显:
| 模型 | 响应速度 | 代码质量 | 工具调用准确率 | Token成本 |
|---|---|---|---|---|
| Ollama | 中等 | 良好 | 85% | 低 |
| Groq | 极快 | 优秀 | 92% | 中 |
| DeepSeek | 快 | 优秀 | 90% | 低 |
7. 系统提示工程
7.1 动态提示生成
根据当前上下文生成最优提示:
typescript复制function generateSystemPrompt(tools: Tool[], context: {
workspace: string;
recentFiles: string[];
}): string {
const toolDescriptions = tools.map(t =>
`- ${t.name}: ${t.description}\n Parameters: ${describeParameters(t.parameters)}`
).join('\n');
return `
你是一个专业的编程助手,正在处理位于 ${context.workspace} 的项目。
# 可用工具
${toolDescriptions}
# 工作流程
1. 分析任务需求
2. 选择合适的工具
3. 执行精确的操作
4. 验证结果
# 特别注意
- 每次只调用一个工具
- 检查文件路径是否正确
- 危险操作需要确认
- 保持操作原子性
`;
}
7.2 提示优化技巧
通过实践总结的提示优化方法:
- 分层结构:使用Markdown格式提高可读性
- 示例引导:包含典型调用示例
- 约束明确:清晰列出禁止行为
- 上下文感知:动态注入环境信息
- 风格控制:指定回答语气和格式
8. 性能优化实践
8.1 对话历史管理
高效的历史记录处理策略:
typescript复制class Conversation {
private messages: ChatMessage[] = [];
private tokenCount: number = 0;
private maxTokens: number = 8000;
addMessage(message: ChatMessage) {
this.messages.push(message);
this.tokenCount += estimateTokens(message.content);
// 智能裁剪策略
while (this.tokenCount > this.maxTokens) {
const removed = this.messages.splice(1, 1)[0]; // 保留system prompt
this.tokenCount -= estimateTokens(removed.content);
}
}
// 其他优化方法...
}
8.2 缓存机制
实现多级缓存提升响应速度:
- 工具结果缓存:短期缓存相同参数的调用结果
- 模型响应缓存:存储常见问题的标准回答
- 文件内容缓存:减少重复文件读取
- 验证结果缓存:缓存路径验证等元操作
9. 测试与验证方法
9.1 测试金字塔
构建全面的测试体系:
code复制 E2E测试
/ \
集成测试 集成测试
| |
单元测试 单元测试
9.2 典型测试案例
工具调用测试:
typescript复制describe('read_file tool', () => {
const workspace = setupTestWorkspace();
const tool = getTool('read_file');
it('should read existing file', async () => {
const result = await tool.execute({ filePath: 'test.txt' }, workspace);
expect(result.success).toBe(true);
expect(result.output).toContain('test content');
});
it('should fail for non-existent file', async () => {
const result = await tool.execute({ filePath: 'missing.txt' }, workspace);
expect(result.success).toBe(false);
});
});
ReAct循环测试:
typescript复制describe('React loop', () => {
it('should complete file editing task', async () => {
const agent = setupTestAgent();
const result = await agent.run(
"Replace all 'foo' with 'bar' in src/index.ts"
);
expect(result.status).toBe('complete');
expect(result.actions).toHaveLength(3); // list, read, edit
expect(getFileContent('src/index.ts')).not.toContain('foo');
});
});
10. 部署与使用指南
10.1 安装步骤
bash复制# 1. 安装依赖
npm install -g mini-claude-code
# 2. 配置模型端点
mcc config set ollama.endpoint http://localhost:11434
# 3. 验证安装
mcc --version
10.2 典型工作流
-
初始化项目:
bash复制
mcc init ./my-project -
交互式会话:
bash复制
mcc chat -
批量执行:
bash复制mcc run -t "Refactor all components to use TypeScript" -
工具管理:
bash复制
mcc tools list mcc tools info edit_file
11. 经验总结与避坑指南
11.1 关键收获
- 循环控制:必须设置合理的迭代限制和超时机制
- 错误处理:工具调用需要有完善的错误恢复流程
- 上下文管理:保持任务目标不丢失是最大挑战
- 提示工程:动态提示比静态提示效果提升显著
- 测试覆盖:工具调用需要模拟各种边界情况
11.2 常见问题解决
问题1:Agent陷入无限循环
- 原因:未正确检测任务完成状态
- 解决:添加明确的完成标记检测
问题2:工具调用参数错误
- 原因:模型输出解析不完整
- 解决:强化参数校验和默认值处理
问题3:跨平台路径问题
- 原因:未正确处理路径分隔符
- 解决:使用path模块规范化所有路径
问题4:模型忘记原始任务
- 原因:上下文被后续对话稀释
- 解决:定期重新注入任务目标
12. 未来改进方向
- 可视化调试:开发ReAct循环的实时可视化工具
- 自动提示优化:根据执行结果动态调整提示
- 工具学习:记录成功模式形成工具使用模板
- 多Agent协作:实现多个Agent协同完成复杂任务
- 知识图谱:构建项目专属的知识库增强上下文
这个项目的完整代码已开源在GitHub仓库,包含了更多实现细节和完整文档。通过这次实践,我不仅深入理解了Code Agent的工作原理,还积累了大量关于AI应用开发的实战经验。特别是对工具系统的设计思考,为后续开发更复杂的AI应用打下了坚实基础。
