前言

在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. 用户输入处理

  • 用户在输入框输入查询内容
  • 系统生成唯一的requestIdsessionId
  • 构建请求参数并建立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通过精心设计的架构,实现了从用户输入到智能体响应的完整闭环。其核心优势在于:

  1. 响应式架构:SSE技术保证了实时性和流畅性
  2. 智能化调度:多种智能体类型适应不同场景需求
  3. 工具生态:丰富的工具集合支持复杂任务处理
  4. 可扩展性:模块化设计便于功能扩展和维护

这种设计模式为构建现代AI智能体系统提供了宝贵的参考,特别是在处理复杂查询和多步骤任务方面展现出了强大的能力。随着AI技术的不断发展,这种架构模式将在更多场景中发挥重要作用。

交互时序图

以下时序图展示了从用户输入"查看一下北京明天的天气"到系统返回结果的完整交互流程:

用户 前端界面 GeneralInput组件 ChatView组件 SSE客户端 GenieController GptProcessService MultiAgentService PlanningAgent 工具集合 大语言模型 第一阶段:用户输入处理 输入"查看一下北京明天的天气" 渲染输入组件 questionChange()处理输入 点击发送按钮 sendMessage()传递输入信息 生成requestId和sessionId combineCurrentChat()组合对话 第二阶段:SSE连接建立 构建请求参数 POST /web/api/v1/gpt/queryAgentStreamIncr 请求体:{sessionId, requestId, query, deepThink, outputStyle} 第三阶段:后端处理 queryMultiAgentIncrStream() 创建SseEmitter(1小时超时) 设置req.user="genie" CompletableFuture.runAsync()异步处理 返回SseEmitter 建立SSE连接 第四阶段:智能体调度 buildAgentRequest()构建请求 根据deepThink选择智能体类型 deepThink=0 → REACT(5) deepThink=1 → PLAN_SOLVE(3) buildToolCollection()构建工具集合 加载DeepSearchTool, CodeTool, ReportTool等 第五阶段:计划规划与执行(深度思考模式) 创建PlanningAgent think()生成执行计划 调用LLM分析查询 返回计划思路 推送plan_thought消息 显示思考过程 act()获取下一个任务 调用相应工具 执行具体任务(如搜索天气) 返回工具执行结果 推送tool_call消息 显示工具调用过程 loop [计划执行循环] 直接调用REACT智能体 分析查询并调用合适工具 返回执行结果 alt [深度思考模式 (deepThink=1)] [普通模式 (deepThink=0)] 第六阶段:结果生成与推送 整合工具结果,生成最终回答 返回格式化的天气信息 推送final_answer消息 接收最终答案 updateFinalAnswer()更新界面 setLoading(false)结束加载状态 显示北京明天天气信息 连接清理 处理完成 sseEmitter.complete() 关闭SSE连接 onclose()处理连接关闭 用户 前端界面 GeneralInput组件 ChatView组件 SSE客户端 GenieController GptProcessService MultiAgentService PlanningAgent 工具集合 大语言模型

时序图说明

关键交互节点
  1. 用户输入阶段:用户在前端界面输入查询,组件层层传递处理
  2. SSE连接建立:前端通过fetch-event-source建立与后端的实时连接
  3. 异步处理启动:后端使用CompletableFuture异步处理,避免阻塞
  4. 智能体选择:根据deepThink参数选择REACT或PLAN_SOLVE智能体
  5. 工具调用:智能体调用相应工具获取天气信息
  6. 流式推送:通过SSE实时推送处理进度和结果
  7. 连接清理:处理完成后优雅关闭SSE连接
消息类型流转
  • plan_thought:智能体思考过程
  • plan:执行计划详情
  • task:当前执行任务
  • tool_call:工具调用过程
  • final_answer:最终答案
性能优化点
  • 异步处理:避免长时间阻塞主线程
  • 流式响应:提升用户体验,实时反馈
  • 连接管理:1小时超时设置,合理的资源管理
  • 工具复用:工具集合的动态加载和复用

这个时序图清晰地展示了JoyAgent-JDGenie系统的完整交互流程,帮助开发者理解各个组件之间的协作关系和数据流转过程。

参考资源


本文基于JoyAgent-JDGenie开源项目进行技术解析,旨在帮助开发者理解现代AI智能体系统的设计思路和实现方案。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐