--- outline: deep --- # 开发外部 AI Agent 本文档将指导你如何开发一个外部 AI Agent,并通过 WebSocket 协议接入 Mythra Agent Studio。 ## 概述 外部 AI Agent 允许你使用自己的服务来实现 AI Agent 的逻辑,通过 WebSocket 与 Mythra Agent Studio 进行通信。这种方式适合需要更复杂自定义逻辑、使用特定 AI 模型或框架的场景。 ## 技术选型 你可以使用任何支持 WebSocket 的编程语言和框架来开发外部 Agent。以下是一些流行的选择: | 语言 | 推荐框架 | 特点 | |------|----------|------| | **Node.js** | ws、Socket.io、uWebSockets.js | 生态丰富、开发效率高、适合快速原型 | | **Python** | websockets、FastAPI + WebSocket | AI 生态完善、适合机器学习场景 | | **Go** | gorilla/websocket、melody | 高性能、适合高并发场景 | | **Java** | Spring WebSocket、Netty | 企业级应用、稳定性强 | 本文以 Node.js + `ws` 库为例,演示如何开发一个简单的外部 Agent。 ## 开发步骤 ### 1. 初始化项目 首先创建一个新的 Node.js 项目: ```bash mkdir my-external-agent cd my-external-agent npm init -y npm install ws openai ``` ### 2. 创建 WebSocket 服务 创建 `server.js` 文件,实现一个基本的 WebSocket 服务: ```javascript const WebSocket = require('ws'); const PORT = 8080; const wss = new WebSocket.Server({ port: PORT }); console.log(`External Agent WebSocket server is running on ws://localhost:${PORT}`); wss.on('connection', (ws) => { console.log('Client connected'); ws.on('message', (data) => { handleMessage(ws, data); }); ws.on('close', () => { console.log('Client disconnected'); }); }); ``` ### 3. 处理用户输入 当客户端连接后,会发送 `user-input` 消息。我们需要解析这个消息并处理: ```javascript function handleMessage(ws, data) { try { const message = JSON.parse(data.toString()); if (message.type === 'user-input') { handleUserInput(ws, message); } } catch (error) { console.error('Failed to parse message:', error); } } function handleUserInput(ws, message) { const { userInput, sessionId, workDir } = message; console.log(`Received user input: ${userInput}`); console.log(`Session ID: ${sessionId}, Work directory: ${workDir}`); // 开始处理用户请求 processUserRequest(ws, userInput); } ``` ### 4. 发送响应消息 根据 [Agent 消息协议](./agent-message-protocol.md),我们需要按照特定的格式发送响应消息。 #### 4.1 发送响应开始消息 ```javascript function sendMessage(ws, type, event, content = '', extra = {}) { const message = { id: generateId(), type, event, ...extra, }; if (content) { message.content = content; } ws.send(JSON.stringify(message)); } function generateId() { return Math.random().toString(36).substring(2, 15); } function processUserRequest(ws, userInput) { // 发送响应开始消息 sendMessage(ws, 'response-state', 'start'); // 发送迭代开始消息(可选) sendMessage(ws, 'iteration-state', 'start', 'iteration 0'); // 发送模型调用开始消息(可选) sendMessage(ws, 'model-invocation-state', 'start'); // 发送聊天内容 sendChatOutput(ws, userInput); // 发送模型调用结束消息(可选) sendMessage(ws, 'model-invocation-state', 'end'); // 发送迭代结束消息(可选) sendMessage(ws, 'iteration-state', 'end', 'iteration 0'); // 发送响应结束消息 sendMessage(ws, 'response-state', 'end'); } ``` #### 4.2 发送聊天内容 聊天内容需要分多次发送,使用 `start`、`delta`、`end` 事件。这是因为大语言模型(LLM)API 通常以流式输出(streaming)的方式返回内容,每生成一个 token 就发送给客户端,让用户能够实时看到输出。 首先安装 OpenAI SDK: ```bash npm install openai ``` 然后实现流式聊天输出: ```javascript const OpenAI = require('openai'); const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY, baseURL: process.env.OPENAI_API_BASE_URL || 'https://api.openai.com/v1', }); async function sendChatOutput(ws, userInput) { const chatId = generateId(); // 发送聊天内容开始 ws.send(JSON.stringify({ id: chatId, type: 'chat-output', event: 'start', })); // 调用 OpenAI API(流式输出) const stream = await openai.chat.completions.create({ model: 'gpt-4', messages: [{ role: 'user', content: userInput }], stream: true, }); // 逐个 token 发送 for await (const chunk of stream) { const content = chunk.choices[0]?.delta?.content || ''; if (content) { ws.send(JSON.stringify({ id: chatId, type: 'chat-output', event: 'delta', content, })); } } // 发送聊天内容结束 ws.send(JSON.stringify({ id: chatId, type: 'chat-output', event: 'end', })); } ``` ### 5. 完整示例代码 以下是完整的 `server.js` 文件: ```javascript const WebSocket = require('ws'); const OpenAI = require('openai'); const PORT = 8080; const wss = new WebSocket.Server({ port: PORT }); const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY, baseURL: process.env.OPENAI_API_BASE_URL || 'https://api.openai.com/v1', }); console.log(`External Agent WebSocket server is running on ws://localhost:${PORT}`); wss.on('connection', (ws) => { console.log('Client connected'); ws.on('message', (data) => { handleMessage(ws, data); }); ws.on('close', () => { console.log('Client disconnected'); }); }); function handleMessage(ws, data) { try { const message = JSON.parse(data.toString()); if (message.type === 'user-input') { handleUserInput(ws, message); } } catch (error) { console.error('Failed to parse message:', error); } } function handleUserInput(ws, message) { const { userInput, sessionId, workDir } = message; console.log(`Received user input: ${userInput}`); console.log(`Session ID: ${sessionId}, Work directory: ${workDir}`); processUserRequest(ws, userInput); } function generateId() { return Math.random().toString(36).substring(2, 15); } function sendMessage(ws, type, event, content = '', extra = {}) { const message = { id: generateId(), type, event, ...extra, }; if (content) { message.content = content; } ws.send(JSON.stringify(message)); } async function processUserRequest(ws, userInput) { // 发送响应开始消息 sendMessage(ws, 'response-state', 'start'); // 发送迭代开始消息(可选) sendMessage(ws, 'iteration-state', 'start', 'iteration 0'); // 发送模型调用开始消息(可选) sendMessage(ws, 'model-invocation-state', 'start'); // 发送聊天内容 await sendChatOutput(ws, userInput); // 发送模型调用结束消息(可选) sendMessage(ws, 'model-invocation-state', 'end'); // 发送迭代结束消息(可选) sendMessage(ws, 'iteration-state', 'end', 'iteration 0'); // 发送响应结束消息 sendMessage(ws, 'response-state', 'end'); } async function sendChatOutput(ws, userInput) { const chatId = generateId(); // 发送聊天内容开始 ws.send(JSON.stringify({ id: chatId, type: 'chat-output', event: 'start', })); // 调用 OpenAI API(流式输出) const stream = await openai.chat.completions.create({ model: 'gpt-4', messages: [{ role: 'user', content: userInput }], stream: true, }); // 逐个 token 发送 for await (const chunk of stream) { const content = chunk.choices[0]?.delta?.content || ''; if (content) { ws.send(JSON.stringify({ id: chatId, type: 'chat-output', event: 'delta', content, })); } } // 发送聊天内容结束 ws.send(JSON.stringify({ id: chatId, type: 'chat-output', event: 'end', })); } ``` ### 6. 启动服务 在启动服务之前,需要设置 OpenAI API Key 环境变量: ```bash export OPENAI_API_KEY='your-api-key-here' ``` 然后启动服务: ```bash node server.js ``` 服务启动后,你会在控制台看到: ``` External Agent WebSocket server is running on ws://localhost:8080 ``` ## 接入 Mythra Agent Studio 服务启动后,你可以按照 [创建自定义 Agent](./create-custom-agent.md) 文档中的说明,在 Agent Manager 中创建一个外部 Agent: 1. 选择「External AI Agent」类型 2. 在 URL 字段中填写:`ws://localhost:8080` 3. 保存配置 现在你可以在 Mythra Agent Studio 中与你的外部 Agent 进行对话了! ## 进阶功能 ### 调用工具 如果你的 Agent 需要调用工具(如文件操作、命令执行等),可以发送 `tool-call` 消息: ```javascript function callTool(ws, func, args) { const toolCallId = generateId(); // 发送工具调用开始 ws.send(JSON.stringify({ id: toolCallId, type: 'tool-call', event: 'start', func, args, })); // 执行工具调用(这里需要实现具体的工具逻辑) const result = executeTool(func, args); // 发送工具调用结束 ws.send(JSON.stringify({ id: toolCallId, type: 'tool-call', event: 'end', func, args, })); return result; } ``` ### 发送思考内容 如果你想让 Agent 展示思考过程,可以发送 `think-output` 消息。思考内容通常来自 LLM 的推理过程(如 OpenAI 的 o1 模型)或 Agent 自己的逻辑: ```javascript async function sendThinkOutput(ws, thinkPrompt) { const thinkId = generateId(); // 发送思考内容开始 ws.send(JSON.stringify({ id: thinkId, type: 'think-output', event: 'start', })); // 调用 LLM 获取思考过程(以 OpenAI o1 模型为例) const stream = await openai.chat.completions.create({ model: 'o1-preview', messages: [{ role: 'user', content: thinkPrompt }], stream: true, }); // 逐个 token 发送思考内容 for await (const chunk of stream) { const content = chunk.choices[0]?.delta?.content || ''; if (content) { ws.send(JSON.stringify({ id: thinkId, type: 'think-output', event: 'delta', content, })); } } // 发送思考内容结束 ws.send(JSON.stringify({ id: thinkId, type: 'think-output', event: 'end', })); } ``` ### 错误处理 当发生错误时,可以发送 `error` 消息: ```javascript function sendError(ws, errorMessage) { ws.send(JSON.stringify({ id: generateId(), type: 'error', event: 'error', content: errorMessage, })); } ``` ## 最佳实践 1. **使用环境变量**:将端口号、API 密钥等配置信息放在环境变量中 2. **日志记录**:记录关键操作和错误信息,便于调试 3. **连接管理**:处理客户端断开连接的情况,释放相关资源 4. **流式响应**:对于长文本响应,使用 `delta` 事件分块发送,提升用户体验 5. **超时处理**:设置合理的超时时间,避免长时间等待 ## 相关文档 - [创建自定义 Agent](./create-custom-agent.md) - 了解如何在 Agent Manager 中创建和管理 Agent - [开发 CLI Agent](./develop-cli-agent.md) - 学习如何开发一个命令行 Agent 程序 - [Agent 消息协议](./agent-message-protocol.md) - 详细了解外部 Agent 与 Mythra Agent Studio 之间的通信协议