动手实践OpenHands系列学习笔记5:代理系统架构概述
笔记5:代理系统架构概述
一、引言
AI代理系统是一种能够自主执行任务的智能软件架构,OpenHands作为AI驱动的软件开发代理平台,拥有完整的代理系统架构设计。本笔记将探讨AI代理架构的基本原理,并通过分析OpenHands核心架构,实现一个简化版的代理框架。
二、AI代理架构设计原则
2.1 AI代理系统的核心组件
- 感知模块(Perception): 接收和处理外部输入
- 认知模块(Cognition): 分析信息并进行决策
- 执行模块(Action): 实施决策并与环境交互
- 记忆模块(Memory): 存储历史信息和经验
- 工具模块(Tools): 扩展代理能力的功能集合
2.2 代理系统设计原则
- 自主性原则: 代理应能在有限监督下自主完成任务
- 可观察性原则: 代理的状态和决策过程应当透明可见
- 安全性原则: 代理应有明确的操作边界和安全保障
- 适应性原则: 代理应能根据反馈调整行为
- 模块化原则: 系统应由解耦合的组件构成,便于扩展
- 可靠性原则: 代理应能处理异常情况,避免灾难性失败
2.3 AI代理架构模式
- 反应式代理(Reactive Agent): 基于简单规则响应环境
- 基于目标的代理(Goal-Based Agent): 根据目标规划行动
- 基于效用的代理(Utility-Based Agent): 优化行动的预期效用
- 学习型代理(Learning Agent): 通过经验优化自身行为
- 混合架构(Hybrid Architecture): 结合多种架构模式的优势
三、OpenHands代理架构分析
从README_CN.md中,我们可以推断OpenHands采用了复杂的混合代理架构,具有以下特点:
3.1 OpenHands核心架构组件
- LLM决策引擎: 由Claude Sonnet 4等大型语言模型提供核心智能
- 工具集成系统: 允许代理使用各种开发工具
- 沙箱执行环境: 安全隔离的代码执行环境
- 对话式界面: 用户与代理交互的主要接口
- 上下文管理系统: 维护代理的工作状态和历史
- 权限与安全控制: 管理代理的操作权限和安全边界
3.2 OpenHands架构特性
- 容器化设计: 使用Docker实现环境隔离和可移植性
- 多模态交互: 支持文本、代码和命令行交互
- 无头模式支持: 可在无GUI环境下以编程方式使用
- 云原生架构: 支持云端部署和本地运行
- 可扩展设计: 支持添加新工具和自定义功能
3.3 OpenHands架构图(推断)
┌─────────────────────────────────────────┐
│ OpenHands系统架构 │
└─────────────────────────────────────────┘│┌───────────┼───────────┐│ │ │
┌───────▼───────┐ ┌─▼─┐ ┌───────▼───────┐
│ 用户界面层 │ │ │ │ 安全层 │
│ (Web/CLI/API) │ │ │ │(权限/隔离/审计)│
└───────┬───────┘ │ │ └───────┬───────┘│ │ │ │
┌───────▼───────┐ │核 │ ┌───────▼───────┐
│ 对话管理层 │ │心 │ │ 工具集成层 │
│(上下文/历史) │ │控 │ │(工具定义/调用)│
└───────┬───────┘ │制 │ └───────┬───────┘│ │层 │ │
┌───────▼───────┐ │ │ ┌───────▼───────┐
│ LLM决策层 │ │ │ │ 执行环境层 │
│(思维/规划/推理)│ │ │ │(沙箱/运行时) │
└───────┬───────┘ └─┬─┘ └───────┬───────┘│ │ │└───────────┼───────────┘│┌───────────▼───────────┐│ 持久化存储层 ││ (配置/历史/工作区) │└───────────────────────┘
四、实践项目:实现简化版代理框架
4.1 代理系统核心结构设计
// agent-core.js
const EventEmitter = require('events');
const LLMClient = require('./llm-client'); // 前一笔记中的LLM客户端
const PromptManager = require('./prompt-manager'); // 前一笔记中的提示管理器class AgentCore extends EventEmitter {constructor(config = {}) {super();this.config = {llmProvider: config.llmProvider || 'claude',apiKey: config.apiKey,systemPrompt: config.systemPrompt || 'You are a helpful AI assistant.',maxIterations: config.maxIterations || 10,...config};this.llmClient = null;this.promptManager = new PromptManager();this.memory = {shortTerm: [], // 当前会话记忆workingMemory: {}, // 工作记忆(变量、状态等)longTerm: [] // 长期记忆(可选,用于跨会话)};this.tools = {}; // 可用工具映射this.state = 'idle'; // 代理状态:idle, thinking, executing, waiting}// 初始化代理系统async initialize() {try {// 初始化提示管理器await this.promptManager.initialize();// 初始化LLM客户端if (!this.config.apiKey) {throw new Error('API key is required');}this.llmClient = new LLMClient({apiKey: this.config.apiKey,model: this.config.model,baseURL: this.config.baseURL});// 加载默认工具await this._registerDefaultTools();this.emit('initialized');return true;} catch (error) {this.emit('error', error);throw error;}}// 注册默认工具async _registerDefaultTools() {// 这里可以注册默认工具// 例如文件操作、命令执行等const defaultTools = [{name: 'search_web',description: '搜索互联网获取信息',handler: async (query) => {// 简化的web搜索实现return { results: [`模拟搜索结果: ${query}`] };}},{name: 'execute_command',description: '执行shell命令',handler: async (command) => {// 在实际实现中,这里应该有安全检查// 简化的命令执行实现return { output: `模拟命令执行: ${command}` };}}];defaultTools.forEach(tool => {this.registerTool(tool.name, tool.description, tool.handler);});}// 注册工具registerTool(name, description, handler) {if (this.tools[name]) {throw new Error(`Tool ${name} already exists`);}this.tools[name] = {name,description,handler};this.emit('tool_registered', name);return this;}// 获取工具信息getTools() {return Object.values(this.tools).map(tool => ({name: tool.name,description: tool.description}));}// 处理用户输入async processInput(userInput, options = {}) {try {this.state = 'thinking';this.emit('state_change', this.state);// 记录用户输入到短期记忆this._addToMemory('user', userInput);// 构建系统提示const systemPrompt = this._buildSystemPrompt();// 准备提示上下文const context = this._prepareContext();// 使用提示管理器增强用户输入const enhancedInput = this.promptManager.enhanceUserPrompt(userInput, context);// 调用LLM获取响应const response = await this.llmClient.complete(enhancedInput, systemPrompt, options);// 解析响应const parsedResponse = this._parseResponse(response);// 记录助手响应到短期记忆this._addToMemory('assistant', parsedResponse.content);// 处理工具调用if (parsedResponse.toolCalls && parsedResponse.toolCalls.length > 0) {this.state = 'executing';this.emit('state_change', this.state);// 执行工具调用const toolResults = await this._executeToolCalls(parsedResponse.toolCalls);// 记录工具调用结果到短期记忆toolResults.forEach(result => {this._addToMemory('tool_result', result);});// 如果需要继续对话,递归处理if (parsedResponse.needsFollowUp && this.memory.shortTerm.length < this.config.maxIterations * 2) {return this.processInput(`请处理以下工具调用结果并继续任务: ${JSON.stringify(toolResults)}`, options);}}this.state = 'idle';this.emit('state_change', this.state);return parsedResponse;} catch (error) {this.state = 'error';this.emit('error', error);throw error;}}// 构建系统提示_buildSystemPrompt() {// 获取所有工具的描述const tools = this.getTools();// 使用提示管理器生成系统提示return this.promptManager.generateSystemPrompt(null, tools, {AGENT_CAPABILITIES: "You can search the web, execute commands, and more."});}// 准备上下文_prepareContext() {return {memory: this.memory.shortTerm.slice(-10), // 最近10条记忆workingMemory: this.memory.workingMemory,tools: Object.keys(this.tools)};}// 解析LLM响应_parseResponse(response) {// 简化的响应解析// 在实际实现中,需要根据具体LLM API响应格式进行解析const content = response.content?.[0]?.text || '';// 简单的工具调用解析const toolCallRegex = /\[\[TOOL:(\w+)\]\[(.*?)\]\]/g;const toolCalls = [];let match;while ((match = toolCallRegex.exec(content)) !== null) {toolCalls.push({tool: match[1],args: JSON.parse(match[2])});}return {content: content.replace(toolCallRegex, '').trim(),toolCalls,needsFollowUp: toolCalls.length > 0};}// 执行工具调用async _executeToolCalls(toolCalls) {const results = [];for (const call of toolCalls) {const tool = this.tools[call.tool];if (!tool) {results.push({tool: call.tool,status: 'error',error: `Tool not found: ${call.tool}`});continue;}try {const result = await tool.handler(call.args);results.push({tool: call.tool,status: 'success',result});} catch (error) {results.push({tool: call.tool,status: 'error',error: error.message});}}return results;}// 添加到记忆_addToMemory(role, content) {this.memory.shortTerm.push({role,content,timestamp: Date.now()});this.emit('memory_updated', role, content);}// 设置工作记忆setWorkingMemory(key, value) {this.memory.workingMemory[key] = value;this.emit('working_memory_updated', key, value);}// 获取工作记忆getWorkingMemory(key) {return this.memory.workingMemory[key];}// 清除短期记忆clearShortTermMemory() {this.memory.shortTerm = [];this.emit('memory_cleared', 'shortTerm');}// 保存代理状态async saveState(filePath) {// 简化的状态保存const state = {memory: this.memory,timestamp: Date.now()};// 在实际实现中,这里应该写入文件return state;}// 加载代理状态async loadState(filePath) {// 简化的状态加载// 在实际实现中,这里应该读取文件// 假设已经加载了状态this.emit('state_loaded');}
}module.exports = AgentCore;
4.2 实现工具管理系统
// tool-manager.js
const EventEmitter = require('events');
const fs = require('fs').promises;
const path = require('path');class ToolManager extends EventEmitter {constructor(config = {}) {super();this.toolsDir = config.toolsDir || path.join(process.cwd(), 'tools');this.tools = {};this.toolSchemas = {}; // 用于存储工具的JSON Schemathis.toolCategories = {'file': [],'command': [],'web': [],'code': [],'other': []};}// 初始化工具管理器async initialize() {try {// 创建工具目录(如果不存在)await fs.mkdir(this.toolsDir, { recursive: true });// 尝试加载工具const files = await fs.readdir(this.toolsDir);for (const file of files) {if (file.endsWith('.js') || file.endsWith('.json')) {try {await this.loadToolFromFile(path.join(this.toolsDir, file));} catch (error) {console.warn(`Failed to load tool from ${file}:`, error.message);}}}console.log(`Loaded ${Object.keys(this.tools).length} tools`);} catch (error) {console.warn('Error initializing tool manager:', error.message);}}// 从文件加载工具async loadToolFromFile(filePath) {try {let tool;if (filePath.endsWith('.js')) {// 加载JavaScript工具模块const toolModule = require(filePath);tool = toolModule.tool || toolModule.default || toolModule;} else if (filePath.endsWith('.json')) {// 加载JSON工具定义const content = await fs.readFile(filePath, 'utf8');const toolDef = JSON.parse(content);if (!toolDef.name || !toolDef.handler) {throw new Error('Invalid tool definition: missing name or handler');}// 创建动态处理函数const handlerFn = new Function('args', `return ${toolDef.handler}`);tool = {name: toolDef.name,description: toolDef.description || '',category: toolDef.category || 'other',handler: async (args) => handlerFn(args),schema: toolDef.schema || {}};}if (tool && tool.name) {this.registerTool(tool);}} catch (error) {throw new Error(`Failed to load tool from ${filePath}: ${error.message}`);}}// 注册工具registerTool(tool) {if (!tool.name || typeof tool.handler !== 'function') {throw new Error('Invalid tool: must have name and handler function');}if (this.tools[tool.name]) {throw new Error(`Tool ${tool.name} already exists`);}this.tools[tool.name] = tool;// 存储工具schemaif (tool.schema) {this.toolSchemas[tool.name] = tool.schema;}// 分类工具const category = tool.category || 'other';if (this.toolCategories[category]) {this.toolCategories[category].push(tool.name);} else {this.toolCategories[category] = [tool.name];}this.emit('tool_registered', tool.name);return this;}// 执行工具async executeTool(name, args = {}) {const tool = this.tools[name];if (!tool) {throw new Error(`Tool not found: ${name}`);}try {this.emit('tool_execution_start', name, args);const result = await tool.handler(args);this.emit('tool_execution_complete', name, args, result);return result;} catch (error) {this.emit('tool_execution_error', name, args, error);throw error;}}// 获取工具列表getTools(category = null) {if (category && this.toolCategories[category]) {return this.toolCategories[category].map(name => ({name,description: this.tools[name].description,category}));}return Object.values(this.tools).map(tool => ({name: tool.name,description: tool.description,category: tool.category || 'other'}));}// 获取工具描述getToolDescription(name) {const tool = this.tools[name];return tool ? {name: tool.name,description: tool.description,category: tool.category || 'other',schema: tool.schema} : null;}// 获取所有工具的JSON SchemasgetToolSchemas() {return this.toolSchemas;}// 创建并保存新工具async createTool(toolDef) {if (!toolDef.name || !toolDef.handler) {throw new Error('Invalid tool definition: missing name or handler');}// 生成工具文件名const fileName = `${toolDef.name.toLowerCase().replace(/\s+/g, '_')}.json`;const filePath = path.join(this.toolsDir, fileName);// 保存工具定义await fs.writeFile(filePath,JSON.stringify(toolDef, null, 2),'utf8');// 加载工具await this.loadToolFromFile(filePath);return toolDef.name;}
}module.exports = ToolManager;
4.3 实现代理执行引擎
// agent-executor.js
const EventEmitter = require('events');
const AgentCore = require('./agent-core');
const ToolManager = require('./tool-manager');class AgentExecutor extends EventEmitter {constructor(config = {}) {super();this.config = config;this.agentCore = new AgentCore(config);this.toolManager = new ToolManager(config);this.isRunning = false;this.taskQueue = []; // 任务队列this.currentTask = null; // 当前正在执行的任务}// 初始化执行引擎async initialize() {try {// 初始化工具管理器await this.toolManager.initialize();// 将工具管理器中的工具注册到代理核心const tools = this.toolManager.getTools();for (const tool of tools) {this.agentCore.registerTool(tool.name, tool.description, async (args) => this.toolManager.executeTool(tool.name, args));}// 初始化代理核心await this.agentCore.initialize();// 连接事件处理this._connectEvents();this.emit('initialized');return true;} catch (error) {this.emit('error', error);throw error;}}// 连接事件处理_connectEvents() {// 监听代理核心事件this.agentCore.on('error', (error) => {this.emit('error', error);});this.agentCore.on('state_change', (state) => {this.emit('state_change', state);});// 监听工具管理器事件this.toolManager.on('tool_execution_start', (name, args) => {this.emit('tool_execution_start', name, args);});this.toolManager.on('tool_execution_complete', (name, args, result) => {this.emit('tool_execution_complete', name, args, result);});this.toolManager.on('tool_execution_error', (name, args, error) => {this.emit('tool_execution_error', name, args, error);});}// 添加任务到队列addTask(task) {if (!task.input) {throw new Error('Task must have input');}const taskWithId = {id: `task_${Date.now()}_${Math.floor(Math.random() * 1000)}`,input: task.input,options: task.options || {},status: 'queued',createdAt: Date.now(),...task};this.taskQueue.push(taskWithId);this.emit('task_queued', taskWithId);// 如果没有正在执行的任务,启动执行if (!this.isRunning) {this._processNextTask();}return taskWithId.id;}// 处理下一个任务async _processNextTask() {if (this.taskQueue.length === 0) {this.isRunning = false;this.emit('queue_empty');return;}this.isRunning = true;this.currentTask = this.taskQueue.shift();this.currentTask.status = 'running';this.currentTask.startedAt = Date.now();this.emit('task_started', this.currentTask);try {// 执行任务const result = await this.agentCore.processInput(this.currentTask.input,this.currentTask.options);this.currentTask.status = 'completed';this.currentTask.completedAt = Date.now();this.currentTask.result = result;this.emit('task_completed', this.currentTask);} catch (error) {this.currentTask.status = 'failed';this.currentTask.error = error.message;this.currentTask.completedAt = Date.now();this.emit('task_failed', this.currentTask, error);}// 处理下一个任务this._processNextTask();}// 执行单个任务async executeTask(task) {const taskId = this.addTask(task);return new Promise((resolve, reject) => {const onCompleted = (completedTask) => {if (completedTask.id === taskId) {this.removeListener('task_completed', onCompleted);this.removeListener('task_failed', onFailed);resolve(completedTask.result);}};const onFailed = (failedTask, error) => {if (failedTask.id === taskId) {this.removeListener('task_completed', onCompleted);this.removeListener('task_failed', onFailed);reject(error);}};this.on('task_completed', onCompleted);this.on('task_failed', onFailed);});}// 取消任务cancelTask(taskId) {// 从队列中移除任务const index = this.taskQueue.findIndex(task => task.id === taskId);if (index !== -1) {const canceledTask = this.taskQueue.splice(index, 1)[0];canceledTask.status = 'canceled';this.emit('task_canceled', canceledTask);return true;}// 如果是当前任务,无法取消(简化版)if (this.currentTask && this.currentTask.id === taskId) {return false;}return false;}// 获取当前状态getStatus() {return {isRunning: this.isRunning,queueLength: this.taskQueue.length,currentTask: this.currentTask};}// 清空任务队列clearTaskQueue() {const canceledTasks = [...this.taskQueue];this.taskQueue = [];canceledTasks.forEach(task => {task.status = 'canceled';this.emit('task_canceled', task);});return canceledTasks.length;}
}module.exports = AgentExecutor;
4.4 创建简单的代理应用框架
// agent-app.js
const express = require('express');
const bodyParser = require('body-parser');
const AgentExecutor = require('./agent-executor');class AgentApp {constructor(config = {}) {this.config = {port: config.port || 3000,apiKey: config.apiKey,llmProvider: config.llmProvider || 'claude',...config};this.app = express();this.agentExecutor = new AgentExecutor({apiKey: this.config.apiKey,llmProvider: this.config.llmProvider});}// 初始化应用async initialize() {// 配置Expressthis.app.use(bodyParser.json());// 初始化代理执行器await this.agentExecutor.initialize();// 设置路由this._setupRoutes();return this;}// 设置API路由_setupRoutes() {// 健康检查this.app.get('/health', (req, res) => {res.json({ status: 'ok' });});// 获取工具列表this.app.get('/api/tools', (req, res) => {const tools = this.agentExecutor.toolManager.getTools(req.query.category);res.json({ tools });});// 执行任务this.app.post('/api/tasks', async (req, res) => {try {const { input, options } = req.body;if (!input) {return res.status(400).json({ error: 'Input is required' });}const taskId = this.agentExecutor.addTask({ input, options });res.status(202).json({ taskId });} catch (error) {res.status(500).json({ error: error.message });}});// 获取任务状态this.app.get('/api/tasks/:taskId', (req, res) => {// 简化版:实际实现需要任务存储const { currentTask } = this.agentExecutor.getStatus();if (currentTask && currentTask.id === req.params.taskId) {res.json(currentTask);} else {res.status(404).json({ error: 'Task not found' });}});// 取消任务this.app.delete('/api/tasks/:taskId', (req, res) => {const success = this.agentExecutor.cancelTask(req.params.taskId);if (success) {res.status(204).send();} else {res.status(404).json({ error: 'Task not found or cannot be canceled' });}});// 对话接口this.app.post('/api/chat', async (req, res) => {try {const { message, options } = req.body;if (!message) {return res.status(400).json({ error: 'Message is required' });}const response = await this.agentExecutor.executeTask({ input: message, options });res.json(response);} catch (error) {res.status(500).json({ error: error.message });}});}// 启动应用async start() {return new Promise((resolve) => {this.server = this.app.listen(this.config.port, () => {console.log(`Agent app running on port ${this.config.port}`);resolve(this.server);});});}// 停止应用async stop() {if (this.server) {return new Promise((resolve) => {this.server.close(() => {console.log('Agent app stopped');resolve();});});}}
}module.exports = AgentApp;
4.5 使用示例
// index.js
require('dotenv').config(); // 加载环境变量
const AgentApp = require('./agent-app');async function main() {try {const agentApp = new AgentApp({port: process.env.PORT || 3000,apiKey: process.env.API_KEY,llmProvider: process.env.LLM_PROVIDER || 'claude'});await agentApp.initialize();await agentApp.start();console.log('Agent app started successfully');} catch (error) {console.error('Failed to start agent app:', error);process.exit(1);}
}main();
五、总结与思考
-
AI代理架构的关键组成:
- 决策引擎(LLM)是核心智能来源
- 工具系统扩展代理能力边界
- 记忆系统维持连贯性和上下文
- 执行引擎协调各组件工作
- 安全层确保操作在可控范围内
-
OpenHands的架构特点:
- 容器化隔离执行环境
- 强大的工具集成能力
- 灵活的部署模式(云端/本地)
- 多模态交互界面
- 安全性与功能性平衡
-
代理系统设计考量:
- 如何平衡自主性与控制
- 如何处理复杂任务分解
- 如何维护长期上下文
- 如何确保执行安全
- 如何优化性能和资源使用
六、下一步学习方向
- 探索更复杂的工具集成机制
- 研究多代理协作系统
- 实现更高级的记忆和规划机制
- 开发更完善的安全防护措施
- 优化资源使用和性能表现
七、参考资源
- OpenHands文档
- AI Agent架构论文
- 反应式代理与认知代理
- 工具学习研究
- 安全代理设计原则