一、 性能瓶颈:ReAct 架构带来的“延时梦魇”

在前面的架构手记中,我们利用 Vue3 状态机和 ReAct 单智能体,构建了严密的用药安全防线。但在内部测试时,一个严峻的性能问题暴露了出来:长耗时请求导致的 I/O 阻塞与极差的用户体验(UX)。

由于我们采用了 ReAct 框架,Agent 在内部需要经历 Thought(思考) -> Action(查图谱) -> Observation(接收规则) -> Final Answer(输出结果) 的完整链路。这意味着,从前端发起 HTTP 请求到拿到最终的 JSON,往往需要等待 5 到 8 秒。

如果是传统的同步框架(如 Django 或 Flask 默认模式),这 8 秒钟内当前的 Worker 线程会被完全挂起(Block)。如果此时门诊有多位医生同时开药,服务器的并发能力会瞬间触顶,导致后续请求排队甚至超时崩溃。

为了打破这个僵局,我从两个维度对系统底层架构进行了彻底的重构:后端底层的异步化(Asyncio)通信协议的流式改造(SSE)

二、 后端重构:FastAPI 的原生异步(Asyncio)释放并发能力

首先,在技术选型之初,我力排众议选择了 FastAPI,正是看中了它基于 Starlette 的原生异步特性。

在重构代码时,我将所有涉及网络请求(如调用大模型 API)和磁盘读取的 I/O 密集型函数,全部使用 async defawait 进行了重写。

Python

# FastAPI 异步非阻塞处理示例
@app.post("/api/analyze_prescription")
async def analyze_prescription(request: PrescriptionRequest):
    # 使用 await 挂起当前协程,释放底层的 Event Loop 去处理其他医生的请求
    agent_response = await llm_client.agenerate(
        prompt=build_react_prompt(request.query)
    )
    
    # 图谱检索属于 CPU 密集型/内存访问,速度极快 (O(1)),同步执行即可
    conflict_rules = check_graph_conflict(agent_response.extracted_entities)
    
    return build_response(conflict_rules)

架构收益: 通过这种非阻塞(Non-blocking)的设计,当 Agent 在漫长地等待大模型厂商服务器返回结果时,FastAPI 的事件循环(Event Loop)会自动把线程让出来,去接收其他医生的网络请求。这让我们的单台轻量级服务器也能轻松扛住极高的门诊并发量。

三、 协议升级:引入 SSE 协议实现 ReAct “思维流”的可视化

虽然异步解决了后端的并发问题,但前端医生依然需要对着屏幕干等 8 秒。为了降低用户的“首字节感知延迟(TTFB)”,我决定抛弃传统的 HTTP 一次性返回模式。

针对 LLM 单向高频吐字的特征,我没有使用沉重且维持成本高的 WebSocket,而是采用了更轻量、基建成本更低的 SSE(Server-Sent Events)协议 来实现流式交互。

我将后端的响应改写为 Generator(生成器),通过 StreamingResponse 持续向前端推送大模型的 ReAct 状态:

Python

from fastapi.responses import StreamingResponse
import asyncio

async def react_agent_stream(query: str):
    # 模拟 ReAct 工作流的流式推送
    yield "event: thought\ndata: 正在解析主诉,提取药物实体...\n\n"
    await asyncio.sleep(1) # 模拟解析耗时
    
    yield "event: action\ndata: 调用本地知识图谱进行安全核查...\n\n"
    # ... 执行图谱检索 ...
    
    yield "event: result\ndata: {\"action_type\": \"BLOCK\", \"risk_level\": \"High\"}\n\n"

@app.get("/api/stream_analyze")
async def stream_analyze(query: str):
    return StreamingResponse(react_agent_stream(query), media_type="text/event-stream")

四、 前端状态机的丝滑衔接

在前端 Vue3 中,我们利用原生 EventSource API 接收这个流式数据。这与我们在【手记(五)】中设计的状态机完美契合。

ANALYZING(分析态)下,界面不再是一个枯燥的转圈 Loading,而是像拥有了人类思维一样,实时渲染 Agent 的心智过程:

  1. 界面闪烁:“正在提取患者特征:孕妇...”

  2. 界面闪烁:“正在检索底层图谱:利巴韦林 ↔ 孕妇...”

  3. 状态突变:最终收到 event: result,前端状态机瞬间切入 SUCCESS_BLOCKCLARIFYING 状态,弹出耀眼的红色阻断卡或追问弹窗!

五、 结语

通过 FastAPI 的底层异步化与轻量级 SSE 协议的引入,我们不仅彻底打通了长耗时请求造成的 I/O 阻塞,还极其优雅地将大模型“黑盒”的 ReAct 思考过程透明化,将原本令人焦躁的等待时间,转化为了一场极具科技感的视觉演示。

目前,系统的主干道已经完全畅通,高并发与高可用性得到了保障。但在真实的联调开发中,团队还是踩了不少暗坑。在下一篇【架构手记(七)】中,我将复盘我们在前后端联调时遇到的跨域问题、大模型 JSON 解析失败的边缘场景(Edge Cases),以及我们是如何用工程手段逐一化解的。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐