Appearance
开发外部 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 openai2. 创建 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 消息协议,我们需要按照特定的格式发送响应消息。
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 文档中的说明,在 Agent Manager 中创建一个外部 Agent:
- 选择「External AI Agent」类型
- 在 URL 字段中填写:
ws://localhost:8080 - 保存配置
现在你可以在 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,
}));
}最佳实践
- 使用环境变量:将端口号、API 密钥等配置信息放在环境变量中
- 日志记录:记录关键操作和错误信息,便于调试
- 连接管理:处理客户端断开连接的情况,释放相关资源
- 流式响应:对于长文本响应,使用
delta事件分块发送,提升用户体验 - 超时处理:设置合理的超时时间,避免长时间等待
相关文档
- 创建自定义 Agent - 了解如何在 Agent Manager 中创建和管理 Agent
- 开发 CLI Agent - 学习如何开发一个命令行 Agent 程序
- Agent 消息协议 - 详细了解外部 Agent 与 Mythra Agent Studio 之间的通信协议