使用渐进式,逐步实现一个 AI 服务的关键点。 使用 React+NodeJS实现。
1、接口设计
A. 任务启动接口 (Producer Side)
前端通过此接口发送问题,后端初始化 Agent 并返回票据。
- POST
/api/v1/agent/chat - 请求体:
{ sessionId: string, prompt: string } - 响应体:
{ agentId: string, status: "created" }
B. SSE 订阅接口 (Consumer Side)
前端拿到 agentId 后,立即建立长连接。
- GET
/api/v1/events/subscribe/:agentId - Header:
Connection: keep-alive,Content-Type: text/event-stream
2、核心模块设计
模块一:Connection Manager (SSE 枢纽)
这是你微服务的核心。它像一个“交换机”,负责维护 agentId 到多个 Response 对象的映射。
- 数据结构:
Map<string, Set<Response>> - 职责:
- 管理连接的生命周期(连接、断开、清理)。
- 实现心跳包,防止网关超时。
- 封装
send(agentId, data)方法,实现一对多广播。
模块二:Agent Engine (执行引擎)
负责与 AI 模型(如 OpenAI)交互。
- 职责:
- 实例化 Agent 逻辑。
- 将 AI 返回的 Stream 碎片包装成标准的
AgentEvent格式。 - 每次产生新碎片时,调用
Connection Manager的发送方法。
模块三:Session Store (可选)
- 职责: 记录
sessionId与agentId的历史关系,确保对话上下文的连续性。
1、V0.1 可跑通过的 DEMO
feature:
- AI 模型分发,可以选择使用哪个模型。
- 实现对话框上,AI 返回逐字添加的效果
方案:
1、AI 模型分发的简单版本:
// 通过规则,来选择使用哪个 Model
function agentDispatcher(prompt) {
// 简单的关键词路由:如果包含 "代码"、"函数"、"code",则路由给 Coder Agent
if (prompt.match(/(代码|函数|code|script)/i)) {
console.log(`[Router] Dispatching to Coder Agent: ${prompt}`);
return coderModel(prompt);
}
console.log(`[Router] Dispatching to Chat Agent: ${prompt}`);
return chatModel(prompt);
}2、实现流式推送(逐字效果)
- ❌ 前端轮训:性能问题,也不现实。
- ❌ WebSocket:太重了。
- ✅ Chunked Transfer(http 分块传输)
- ✅ SSE(Server-Sent Events)
最佳方案,一般是 **SSE + Chunked。**这里v0.1版本,先只用chunked实现。
// server
app.post("/api/chat", async (req, res) => {
const { prompt } = req.body;
// 关键头信息:开启流式传输
res.setHeader("Content-Type", "text/html; charset=utf-8");
res.setHeader("Transfer-Encoding", "chunked");
try {
// 获取对应的 Generator
const stream = agentDispatcher(prompt);
// 循环推送数据块
for await (const chunk of stream) {
res.write(chunk);
}
} catch (err) {
console.error(err);
} finally {
res.end(); // 结束响应
}
});
// client
const response = await fetch("http://localhost:3001/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ prompt: currentPrompt }),
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const textChunk = decoder.decode(value, { stream: true });
// 实时更新最后一条消息(即 AI 的回复)
setMessages((prev) =>
prev.map((msg, idx) =>
idx === prev.length - 1
? {
...msg,
content: msg.content + textChunk,
}
: msg
)
);
}