前言

前文已经完成了Java+Python的基础设施构建,接下来进入核心业务:构建AI模块逻辑

阶段新目标

基于当前家政项目的特点,决定从性价比最高的方案入手:

  • 聊天界面询问订单状况
  • 评价总结(像电商平台那种在评论区顶上的AI评论总结)
  • 智能客服(基于RAG构建知识库)

本文先搭建聊天界面询问订单功能

开始设计

一、需求澄清与领域建模

1.1 核心需求

用AI查询用户的订单相关信息,返回结果

1.2 列出核心实体名词

  • 订单:
    用户、服务人员/企业、订单状态、服务项目、数量、价格、地址、联系电话

1.3 梳理主要流程

正常流程: 收到查询意向→llm分析意向→llm调用tool→返回查询数据→llm整理→返回结果
异常流程:
输入意向不包含查询→llm正常回答
模型超时→返回友好提示
数据库没对应数据→llm返回友好提示
ai应用层宕机→返回友好提示

1.4 确定模块边界

Python端不做:
  • 查询数据库
  • 用户鉴权
Java端不做:
  • 决策执行

二、架构选型与模块划分

2.1 当前已有功能

  • 基于OpenAI SDK实现聊天功能
  • Java↔Python websocket双向通信
  • Java⇢前端 SSE;前端→Java HTTP
  • 前端支持AI消息渲染

2.2 基于已有功能升级

  • Python端(ai-engine模块)引入LangChain,配合本地tool(Python端)和远程tool(调用Java端),复用之前的websocket,方便后续扩展更多实时交互功能
  • Java端复用查询接口,定义工具类调用接口

2.3 技术选型

  • Python端:LangChain , LangGraph
  • Java端和前端无需新增技术栈

2.4 新增模块分层

jzo2o-ai-engine/
├── app/
│   ├── agent/
│   │   ├── graph.py #StateGraph 编排 LLM 调用和工具执行
│   │   ├── state.py #Agent 状态定义
│   │   ├── tool_context.py #远程工具执行的异步桥接
│   │   └── tool.py  #本地工具(Python直接执行)和远程工具(Java通过WebSocket执行)
│   ├── api/
│   │   └── ws_chat.py #LangGraph Agent 集成架构
│   ├── core/
│   │   └── config.py # 工具配置
│   ├── schemas/
│       └── ws_message.py # 远程工具调用消息模型
├── requirements.txt
├── .env

三、详细设计

1. 外部接口设计(WebSocket 协议)

1.1 WebSocket 路径

/ws/chat/{session_id}
一个 session_id 对应单独一条通道(支持多用户并发)

1.2 消息类型
Java → Python:
  • "type": "user_message"
    • messages: [{role, content}, ...],触发新一轮会话/推理。
  • "type": "cancel"
    • 立即中断当前推理/工具调用。
  • "type": "tool_result"
    • Java 端 business tool 执行结果,随 tool_call_id 注入。
Python → Java:
  • "type": "token"
    • LLM 逐 token 输出(支持流式推理)。
  • "type": "agent_finish"
    • 当前 Agent/LLM 一轮推理全部完成。
  • "type": "error"
    • 当前轮异常信息。
  • "type": "tool_call"
    • (新增) 触发 Java 端特定工具执行。含 tool_call_id。
  • "type": "tool_start"
    • (新增) 工具开始执行,供前端进度反馈。
  • "type": "tool_end"
    • (新增) 工具结束。
1.3 典型流程举例
  1. Java 发送 user_message,Python Agent 开始推理流式回 token。
  2. Agent 需要调用外部工具时,发送 tool_call,Java 响应 tool_result。
  3. 推理被用户中止时,cancel 收到后 agent 立刻终止。
  4. 所有信令均为 {"type": ..., ...} 结构化 JSON 帧。

2. 数据模型(app/schemas/ws_message.py)

新旧消息格式详细对照,并且为每一类 JSON 帧定义强类型 Pydantic 数据模型,便于 IDE 类型提示和后续协议扩展。

2.1 Java → Python
class WsClientMessage(BaseModel):
    type: Literal["user_message", "cancel"]
    messages: Optional[List[dict]]

class WsToolResultPayload(BaseModel):
    type: Literal["tool_result"]
    tool_call_id: str
    content: str
2.2 Python → Java
class WsTokenPayload(BaseModel):
    type: Literal["token"]
    content: str

class WsToolCallPayload(BaseModel):
    type: Literal["tool_call"]
    tool_call_id: str
    tool_name: str
    args: dict[str, Any]

class WsToolStartPayload(BaseModel):
    type: Literal["tool_start"]
    tool_call_id: str
    tool_name: str

class WsToolEndPayload(BaseModel):
    type: Literal["tool_end"]
    tool_call_id: str

class WsErrorPayload(BaseModel):
    type: Literal["error"]
    message: str

3. 新增/重构核心模块

3.1 Agent相关(jzo2o-ai-engine/app/agent/*)
3.1.1 agent/state.py

定义 Agent 运行状态(会话上下文),主要包含 messages:

class AgentState(TypedDict):
    messages: Annotated[Sequence[AnyMessage], add_messages]
3.1.2 agent/tool_context.py

管理 Agent 与 Java 之间远程工具调用的生命周期,实现:

  • 调用工具时,通过 send_callback 把 tool_call 发给 Java
  • 等待 Java 结果,异步回调
  • 工具调用支持超时、取消

核心接口:

class ToolExecutionContext:
    async def execute(self, tool_call_id, tool_name, args): ...
    async def on_tool_result(self, msg): ...
    def cancel_all(self): ...
    def set_send_callback(self, cb): ...
3.1.3 agent/graph.py

用 StateGraph/LangGraph 封装 Agent 推理主流程,抽象成 call_model、tool_executor 两类节点并可扩展:

  • call_model:单步推理,确定是否有工具调用
  • tool_executor:本地工具直接执行,远程工具异步发给 Java
  • 多轮对话/工具调用通过 StateGraph condition 分支与循环实现
3.1.4 agent/tool.py

新增远程工具

@tool
async def customer_order_query(order_id: str) -> str:
    """查询客户订单详情。根据订单ID查询完整订单信息,包括订单状态、金额、服务类型、服务人员、地址等。"""
    raise NotImplementedError("Remote tool executed via Java WebSocket")
3.2 LLM 相关(jzo2o-ai-engine/app/core/llm_client.py)
3.2.1 支持原始 OpenAI、ChatDeepSeek
  • OpenAI SDK 用于传统 HTTP 单向流兼容
  • LangChain-DeepSeek 支持多步推理/工具调用
  • 配置项(model/key/base、温度、最大token),新增环境变量支持
3.2.2 关键补丁
  • Monkey-patch ChatDeepSeek 的 message 序列化,额外透传 reasoning_content 字段,保证工具链场景简单切换。
3.3 WebSocket端点(jzo2o-ai-engine/app/api/ws_chat.py)

完全重构,采用三任务并发模型

  • reader_task:循环从 WebSocket 收消息(解析类型后分发)
    • user_message 入自身队列
    • cancel 事件、断连事件分别处理
    • tool_result 传入 ToolExecutionContext
  • sender_task:主动从 send_queue 推送信令到 WebSocket
  • agent_task(动态):处理一次 user_message 触发的 Agent 任务
    • 运行 LangGraph Agent,流式将 LLM token、工具信令推到 send_queue
    • 能被 cancel_event、会话断开安全中断
代码核心片段
@router.websocket("/ws/chat/{session_id}")
async def ws_chat(...):
    ...
    # reader_task, sender_task, agent_task 并发协作
    ...
    while True:
        msg = await incoming_queue.get()
        if msg["type"] == "user_message":
            # 启动新的 agent_task,结束此前对话
        elif msg["type"] == "cancel":
            # cancel 当前 agent_task
        elif ...
3.4 Java端
WebSocket客户端

handleWsMessage方法新增:

                case "tool_call":
                    // Python Agent 请求执行远程工具 → 调用业务服务 → WS 回传 tool_result
                    handleToolCall(frame, sessionId);
                    break;
                case "tool_start":
                    // 透传工具开始事件给前端 (展示进度)
                    emitter.send(SseEmitter.event().name("tool_start").data(payload));
                    break;
                case "tool_end":
                    // 透传工具完成事件给前端
                    emitter.send(SseEmitter.event().name("tool_end").data(payload));
                    break;

新增支持tool_call/tool_start/tool_end帧处理

    /**
     * 处理 tool_call 帧 — 异步执行远程工具, 结果通过 WebSocket 回传给 Python
     */
    private void handleToolCall(Map<String, Object> frame, String sessionId) {
        String toolName = (String) frame.get("tool_name");
        String toolCallId = (String) frame.get("tool_call_id");
        @SuppressWarnings("unchecked")
        Map<String, Object> args = (Map<String, Object>) frame.getOrDefault("args", Map.of());

        CompletableFuture.supplyAsync(() -> toolExecutor.execute(toolName, toolCallId, args))
                .thenAccept(result -> sendToolResult(sessionId, toolCallId, result))
                .exceptionally(e -> {
                    log.error("远程工具执行失败, sessionId={}, toolName={}: {}",
                            sessionId, toolName, e.getMessage());
                    // 失败时也回传 tool_result, 避免 Python 侧干等超时
                    sendToolResult(sessionId, toolCallId,
                            "{\"error\": \"" + e.getMessage().replace("\"", "'") + "\"}");
                    return null;
                });
    }

    /** 通过 WebSocket session 向 Python 发送 tool_result 帧 */
    private void sendToolResult(String sessionId, String toolCallId, String content) {
        WebSocketSession wsSession = activeWsSessions.get(sessionId);
        if (wsSession != null && wsSession.isOpen()) {
            try {
                String resultJson = objectMapper.writeValueAsString(
                        Map.of("type", "tool_result",
                                "tool_call_id", toolCallId,
                                "content", content));
                wsSession.send(Mono.just(wsSession.textMessage(resultJson)))
                        .subscribe(
                                null,
                                e -> log.error("发送 tool_result 失败, sessionId={}: {}",
                                        sessionId, e.getMessage()));
            } catch (Exception e) {
                log.error("序列化 tool_result 失败, sessionId={}: {}",
                        sessionId, e.getMessage());
            }
        } else {
            log.warn("WebSocket 会话已关闭, 无法发送 tool_result, sessionId={}", sessionId);
        }
    }

验收

验收指标:

  • 程序运行正常
  • 核心业务:AI查询订单能跑通

开始验收

所有服务启动启动启动启动
启动
还有这个
前端

工具调用:
工具调用
完整回答:

好的,我来查询您的订单信息。以下是您查询的订单 2605200000000000129 的详细信息:
📋 订单基本信息
项目	内容
订单编号	2605200000000000129
服务类型	🧹 保洁清
服务项目	日常保洁
数量	1 份
订单金额	¥58.00(实付)
订单状态	已下单(待处理)
支付状态	已支付
评价状态	未评价
👤 客户信息
项目	内容
联系人	哈喽哈喽
联系电话	18999665522
服务地址	重庆市重庆市11街11号
🕐 时间信息
项目	时间
创建时间	2026年5月20日
| 服务开始时间 | 2026年5月21日 |
订单目前已支付成功,服务安排在了 2026年5月21日,请您按时到达服务地址。如果您还需要其他帮助,请随时告诉我!😊

悬浮窗窗口界面:
悬浮窗
注:本篇省去了前端的开发流程,主要就是新增了侧边栏独立聊天界面,状态可在悬浮窗和侧边栏之间转换,升级了下TDesign版本和做点适配

验证其有的工具:
验证工具
注:这里把这篇文章没涉及到的功能也列出来了,后面的文章会提一下

验收通过


总结

本文基于家政项目,使用 Java + Python 混合架构 成功实现了 AI 查询订单功能。核心成果如下:

  • Python 端:引入 LangChain + LangGraph 编排 Agent 推理流程,通过 WebSocket 与 Java 端双向通信,实现了远程工具调用的完整生命周期管理。
  • Java 端:复用现有查询接口,新增 tool_call/tool_start/tool_end 帧处理,异步执行业务工具并通过 WebSocket 回传结果。
  • 通信协议:设计了结构化的 JSON 帧协议,覆盖 user_messagecanceltokentool_calltool_result 等全链路信令。

验收环节已跑通完整链路,AI 能够准确查询订单详情并以结构化表格形式返回结果,悬浮窗和侧边栏界面运行正常。

下一个功能:评价总结

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐