JoyAgent-JDGenie:从用户输入到AI智能体调度的完整技术解析
京东开源的多智能体协作平台JoyAgent-JDGenie采用前后端分离架构,通过SSE技术实现流式响应。文章解析了从用户输入"查看北京明天天气"到系统响应的完整流程:前端使用React+TypeScript处理用户交互,通过fetch-event-source建立SSE连接;后端基于Spring Boot的控制器接收请求,服务层处理核心业务逻辑。
前言
在AI智能体系统的设计中,用户交互的流畅性和系统响应的实时性是衡量产品体验的关键指标。JoyAgent-JDGenie作为京东开源的多智能体协作平台,采用了前后端分离的架构设计,通过Server-Sent Events(SSE)技术实现了流式响应,为用户提供了类似ChatGPT的实时对话体验。
本文将深入解析从用户输入"查看一下北京明天的天气"这样一个简单查询,到系统完成智能体调度、计划规划、工具调用的完整技术流程,帮助开发者理解现代AI智能体系统的核心架构设计。
技术栈概览
前端技术栈
- React 19 + TypeScript:现代化的前端框架组合
- Vite:高性能的构建工具
- Ant Design:企业级UI组件库
- Axios:HTTP客户端库
- @microsoft/fetch-event-source:SSE客户端实现
后端技术栈
- Java 17 + Spring Boot 3.2.2:企业级后端框架
- Maven:项目管理工具
- SSE(Server-Sent Events):流式数据传输
- OkHttp:HTTP客户端
- FastJSON:JSON处理库
工具服务技术栈
- Python 3.11+ + FastAPI:高性能API服务
- LiteLLM:多模型LLM接口统一
- MCP协议:模型控制协议
完整交互流程解析
第一阶段:前端用户交互处理
1.1 用户输入组件初始化
当用户打开JoyAgent-JDGenie界面时,系统首先渲染主页面组件:
// ui/src/pages/Home/index.tsx
const Home: GenieType.FC<HomeProps> = memo(() => {
const [inputInfo, setInputInfo] = useState<CHAT.TInputInfo>({
message: "",
deepThink: false,
outputStyle: defaultProduct.type
});
const renderContent = () => {
if (inputInfo.message.length === 0) {
return (
<div className="pt-[120px] flex flex-col items-center">
<Slogn />
<div className="w-640 rounded-xl shadow-[0_18px_39px_0_rgba(198,202,240,0.1)]">
<GeneralInput
placeholder={product.placeholder}
showBtn={true}
size="big"
disabled={false}
product={product}
send={changeInputInfo}
/>
</div>
</div>
);
}
// 显示对话界面
return <ChatView inputInfo={inputInfo} product={product} />;
};
1.2 输入组件事件处理
GeneralInput组件负责处理用户的输入交互:
// ui/src/components/GeneralInput/index.tsx
const GeneralInput: GenieType.FC<Props> = (props) => {
const { placeholder, showBtn, disabled, product, send } = props;
const [question, setQuestion] = useState<string>("");
const [deepThink, setDeepThink] = useState<boolean>(false);
// 处理用户输入变化
const questionChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
setQuestion(e.target.value);
};
// 切换深度思考模式
const changeThinkStatus = () => {
setDeepThink(!deepThink);
};
// 发送消息处理
const sendMessage = () => {
send({
message: question,
outputStyle: product?.type,
deepThink,
});
setQuestion("");
};
当用户输入"查看一下北京明天的天气"并点击发送按钮时,系统会调用sendMessage函数,将输入信息传递给父组件。
1.3 对话视图组件处理
ChatView组件接收到输入信息后,开始处理对话逻辑:
// ui/src/components/ChatView/index.tsx
const sendMessage = useMemoizedFn((inputInfo: CHAT.TInputInfo) => {
const {message, deepThink, outputStyle} = inputInfo;
const requestId = getUniqId(); // 生成唯一请求ID
let currentChat = combineCurrentChat(inputInfo, sessionId, requestId);
chatList.current = [...chatList.current, currentChat];
if (!chatTitle) {
setChatTitle(message!); // 设置对话标题
}
setLoading(true);
const params = {
sessionId: sessionId,
requestId: requestId,
query: message,
deepThink: deepThink ? 1 : 0,
outputStyle
};
第二阶段:SSE连接建立与请求发送
2.1 SSE客户端配置
系统使用@microsoft/fetch-event-source库建立SSE连接:
// ui/src/utils/querySSE.ts
const DEFAULT_SSE_URL = `${customHost}/web/api/v1/gpt/queryAgentStreamIncr`;
const SSE_HEADERS = {
'Content-Type': 'application/json',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Accept': 'text/event-stream',
};
export default (config: SSEConfig, url: string = DEFAULT_SSE_URL): void => {
const { body = null, handleMessage, handleError, handleClose } = config;
fetchEventSource(url, {
method: 'POST',
credentials: 'include',
headers: SSE_HEADERS,
body: JSON.stringify(body),
openWhenHidden: true,
onmessage(event: EventSourceMessage) {
if (event.data) {
try {
const parsedData = JSON.parse(event.data);
handleMessage(parsedData); // 处理服务器推送的消息
} catch (error) {
console.error('Error parsing SSE message:', error);
handleError(new Error('Failed to parse SSE message'));
}
}
},
onerror(error: Error) {
console.error('SSE error:', error);
handleError(error);
},
onclose() {
console.log('SSE connection closed');
handleClose();
}
});
};
2.2 请求参数构建
前端将用户输入转换为标准的请求参数:
{
"sessionId": "session_1234567890",
"requestId": "req_1234567890",
"query": "查看一下北京明天的天气",
"deepThink": 0,
"outputStyle": "html"
}
第三阶段:后端接口处理
3.1 控制器层处理
请求首先到达Spring Boot控制器:
// GenieController.java
@RequestMapping(value = "/web/api/v1/gpt/queryAgentStreamIncr",
produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter queryAgentStreamIncr(@RequestBody GptQueryReq params) {
return gptProcessService.queryMultiAgentIncrStream(params);
}
3.2 服务层处理
GptProcessServiceImpl负责核心的业务逻辑处理:
// GptProcessServiceImpl.java
@Override
public SseEmitter queryMultiAgentIncrStream(GptQueryReq req) {
// 设置SSE连接超时时间为1小时
long timeoutMillis = TimeUnit.HOURS.toMillis(1);
// 设置请求用户为"genie",标识系统内部调用
req.setUser("genie");
// 设置深度思考参数,如果为空则默认设置为0(不启用深度思考)
req.setDeepThink(req.getDeepThink() == null ? 0: req.getDeepThink());
// 创建SSE发射器,用于向前端推送实时数据
SseEmitter sseEmitter = new SseEmitter(timeoutMillis);
// 异步处理多智能体请求,避免阻塞主线程
CompletableFuture.runAsync(() -> {
try {
// 调用多智能体服务处理请求
multiAgentService.queryMultiAgentIncrStream(req, sseEmitter);
} catch (Exception e) {
log.error("处理多智能体请求时发生异常", e);
// 发生异常时完成SSE连接并传递错误信息
sseEmitter.completeWithError(e);
}
});
return sseEmitter;
}
第四阶段:智能体类型判断与调度
4.1 智能体类型枚举
系统定义了多种智能体类型:
// AgentType.java
public enum AgentType {
COMPREHENSIVE(1), // 综合型智能体
WORKFLOW(2), // 工作流智能体
PLAN_SOLVE(3), // 计划-解决型智能体
ROUTER(4), // 路由智能体
REACT(5); // 反应型智能体
}
4.2 智能体请求构建
MultiAgentServiceImpl根据用户的deepThink参数选择合适的智能体类型:
// MultiAgentServiceImpl.java
private AgentRequest buildAgentRequest(GptQueryReq req) {
AgentRequest request = new AgentRequest();
request.setRequestId(req.getTraceId());
request.setErp(req.getUser());
request.setQuery(req.getQuery());
// 根据deepThink参数选择智能体类型
// deepThink=0 选择REACT(5),deepThink=1 选择PLAN_SOLVE(3)
request.setAgentType(req.getDeepThink() == 0 ? 5: 3);
// 设置对应的提示词
request.setSopPrompt(request.getAgentType() == 3 ?
genieConfig.getGenieSopPrompt(): "");
request.setBasePrompt(request.getAgentType() == 5 ?
genieConfig.getGenieBasePrompt() : "");
request.setIsStream(true);
request.setOutputStyle(req.getOutputStyle());
return request;
}
对于"查看一下北京明天的天气"这个查询,由于用户没有开启深度思考模式,系统会选择REACT(5)智能体进行处理。
第五阶段:计划规划与执行流程
5.1 计划-解决模式(深度思考开启时)
当用户开启深度思考模式时,系统会使用PLAN_SOLVE(3)智能体,采用三阶段处理模式:
// PlanSolveHandlerImpl.java
public void handle(AgentContext agentContext, AgentRequest request) {
int stepIdx = 0;
int maxStepNum = genieConfig.getPlannerMaxSteps(); // 最大迭代步数
// 主要的计划-执行-规划迭代循环
while (stepIdx <= maxStepNum) {
// 阶段1:规划(Planning)
PlanningAgent planningAgent = new PlanningAgent(agentContext);
planningAgent.think(); // 生成执行计划
String nextTask = planningAgent.act(); // 获取下一个任务
if (nextTask == null || nextTask.isEmpty()) {
break; // 所有任务完成
}
// 阶段2:执行(Execution)
ExecutorAgent executorAgent = new ExecutorAgent(agentContext);
executorAgent.setTask(nextTask);
executorAgent.think(); // 思考如何执行任务
executorAgent.act(); // 执行任务
// 阶段3:总结(Summary)
SummaryAgent summaryAgent = new SummaryAgent(agentContext);
TaskSummaryResult summaryResult = summaryAgent.summarize();
stepIdx++;
}
}
5.2 规划智能体(PlanningAgent)
规划智能体负责分析用户查询并生成执行计划:
// PlanningAgent.java
@Override
public boolean think() {
long startTime = System.currentTimeMillis();
// 构建规划提示词,包含可用工具信息
StringBuilder toolPrompt = new StringBuilder();
for (BaseTool tool : context.getToolCollection().getToolMap().values()) {
toolPrompt.append(String.format("工具名:%s 工具描述:%s\n",
tool.getName(), tool.getDescription()));
}
// 调用LLM生成计划
LLMResponse response = llm.callLLM(context, buildPlanningPrompt());
if (response.getContent() != null && !response.getContent().isEmpty()) {
printer.send("plan_thought", response.getContent());
}
return true;
}
private String getNextTask() {
boolean allComplete = true;
for (String status : planningTool.getPlan().getStepStatus()) {
if (!"completed".equals(status)) {
allComplete = false;
break;
}
}
if (allComplete) {
return null; // 所有任务完成
}
// 返回下一个未完成的任务
return planningTool.getPlan().getNextTask();
}
5.3 工具集合构建
系统为智能体配置了丰富的工具集合:
// GenieController.java
private ToolCollection buildToolCollection(AgentContext agentContext,
AgentRequest request) {
ToolCollection toolCollection = new ToolCollection();
toolCollection.setAgentContext(agentContext);
// 文件工具
FileTool fileTool = new FileTool();
fileTool.setAgentContext(agentContext);
toolCollection.addTool(fileTool);
// 默认工具集
List<String> agentToolList = Arrays.asList(
genieConfig.getMultiAgentToolListMap()
.getOrDefault("default", "search,code,report").split(","));
if (agentToolList.contains("search")) {
DeepSearchTool deepSearchTool = new DeepSearchTool();
deepSearchTool.setAgentContext(agentContext);
toolCollection.addTool(deepSearchTool);
}
if (agentToolList.contains("code")) {
CodeInterpreterTool codeTool = new CodeInterpreterTool();
codeTool.setAgentContext(agentContext);
toolCollection.addTool(codeTool);
}
if (agentToolList.contains("report")) {
ReportTool htmlTool = new ReportTool();
htmlTool.setAgentContext(agentContext);
toolCollection.addTool(htmlTool);
}
// MCP工具集成
addMcpTools(toolCollection, agentContext);
return toolCollection;
}
第六阶段:流式响应处理
6.1 SSE消息格式
系统通过SSE向前端推送不同类型的消息:
// BaseAgentResponseHandler.java
switch (agentResponse.getMessageType()) {
case "plan_thought":
message.setMessageType(agentResponse.getMessageType());
message.setMessageOrder(eventResult.getAndIncrOrder(agentResponse.getMessageType()));
message.setResultMap(JSON.parseObject(JSONObject.toJSONString(agentResponse)));
if (isFinal && !eventResult.getResultMap().containsKey("plan_thought")) {
eventResult.getResultMap().put("plan_thought", agentResponse.getPlanThought());
}
break;
case "plan":
// 处理计划消息
break;
case "task":
// 处理任务消息
break;
case "tool_call":
// 处理工具调用消息
break;
case "final_answer":
// 处理最终答案消息
break;
}
6.2 前端消息处理
前端接收到SSE消息后,根据消息类型进行相应处理:
// ChatView/index.tsx
const handleMessage = (data: MESSAGE.Answer) => {
const { finished, resultMap, packageType, status } = data;
if (status === "tokenUseUp") {
modal.info({
title: '您的试用次数已用尽',
content: '如需额外申请,请联系 liyang.1236@jd.com',
});
return;
}
// 处理不同类型的消息
switch (packageType) {
case "plan_thought":
// 显示智能体思考过程
updatePlanThought(resultMap);
break;
case "plan":
// 显示执行计划
updatePlan(resultMap);
break;
case "task":
// 显示当前执行任务
updateCurrentTask(resultMap);
break;
case "tool_call":
// 显示工具调用过程
updateToolCall(resultMap);
break;
case "final_answer":
// 显示最终答案
updateFinalAnswer(resultMap);
setLoading(false);
break;
}
};
实际执行示例
以"查看一下北京明天的天气"为例,完整的执行流程如下:
1. 用户输入处理
- 用户在输入框输入查询内容
- 系统生成唯一的
requestId和sessionId - 构建请求参数并建立SSE连接
2. 智能体选择
- 由于未开启深度思考,选择REACT智能体
- 加载基础提示词和工具集合
3. 工具调用
- 智能体分析查询内容,识别需要天气信息
- 调用
DeepSearchTool搜索北京明天的天气 - 可能调用MCP工具获取实时天气数据
4. 响应生成
- 整合搜索结果和工具调用结果
- 生成结构化的天气信息回复
- 根据
outputStyle参数格式化输出
5. 流式推送
- 通过SSE实时推送处理进度
- 前端动态更新界面显示
- 完成后关闭SSE连接
技术亮点分析
1. 流式响应设计
- 实时性:通过SSE技术实现毫秒级的响应推送
- 用户体验:类似ChatGPT的打字机效果,提升交互体验
- 资源优化:避免长时间的HTTP连接占用
2. 智能体架构
- 模块化设计:不同类型的智能体处理不同复杂度的任务
- 可扩展性:支持新增智能体类型和工具
- 容错机制:完善的异常处理和降级策略
3. 工具生态
- MCP协议集成:支持标准化的工具接口
- 多样化工具:搜索、代码执行、报告生成等
- 动态加载:根据配置动态加载工具集合
4. 前后端分离
- 技术栈独立:前端React+TypeScript,后端Java+Spring Boot
- 接口标准化:RESTful API设计,便于维护和扩展
- 部署灵活:支持Docker容器化部署
总结
JoyAgent-JDGenie通过精心设计的架构,实现了从用户输入到智能体响应的完整闭环。其核心优势在于:
- 响应式架构:SSE技术保证了实时性和流畅性
- 智能化调度:多种智能体类型适应不同场景需求
- 工具生态:丰富的工具集合支持复杂任务处理
- 可扩展性:模块化设计便于功能扩展和维护
这种设计模式为构建现代AI智能体系统提供了宝贵的参考,特别是在处理复杂查询和多步骤任务方面展现出了强大的能力。随着AI技术的不断发展,这种架构模式将在更多场景中发挥重要作用。
交互时序图
以下时序图展示了从用户输入"查看一下北京明天的天气"到系统返回结果的完整交互流程:
时序图说明
关键交互节点
- 用户输入阶段:用户在前端界面输入查询,组件层层传递处理
- SSE连接建立:前端通过fetch-event-source建立与后端的实时连接
- 异步处理启动:后端使用CompletableFuture异步处理,避免阻塞
- 智能体选择:根据deepThink参数选择REACT或PLAN_SOLVE智能体
- 工具调用:智能体调用相应工具获取天气信息
- 流式推送:通过SSE实时推送处理进度和结果
- 连接清理:处理完成后优雅关闭SSE连接
消息类型流转
plan_thought:智能体思考过程plan:执行计划详情task:当前执行任务tool_call:工具调用过程final_answer:最终答案
性能优化点
- 异步处理:避免长时间阻塞主线程
- 流式响应:提升用户体验,实时反馈
- 连接管理:1小时超时设置,合理的资源管理
- 工具复用:工具集合的动态加载和复用
这个时序图清晰地展示了JoyAgent-JDGenie系统的完整交互流程,帮助开发者理解各个组件之间的协作关系和数据流转过程。
参考资源
本文基于JoyAgent-JDGenie开源项目进行技术解析,旨在帮助开发者理解现代AI智能体系统的设计思路和实现方案。
更多推荐



所有评论(0)