LlamaIndex Workflows 上手:用事件驱动把 RAG 和 Agent 串成状态机
RAG 项目做到后面,往往不是「检索一次然后生成」这么简单——要先判断问题类型,事实类走向量检索,计算类走代码工具,拿不准的再走多路召回,最后汇总。用一串 if/else 加函数调用写,三个分支之后就没人敢改了。LlamaIndex 的 Workflows 模块就是给这种复杂度准备的:它把流程拆成「事件」在节点间流动,每个节点是一个被装饰器标记的异步函数。 心智模型 不用记太多,抓住两个概念: Event(事件):在节点之间传的数据对象,比如 QueryReceived、RetrievalDone、ResponseReady; Step(步骤):一个异步函数,用 @step 标记,声明它监听哪些事件、又发出哪些事件。 框架帮你做的是并发调度:多个步骤可以同时跑,事件到了就触发。比起手写 asyncio.gather 加回调,这种声明式写法让流程拓扑一眼看清。 一个多分支问答的骨架 @step async def route(self, ev: QueryReceived) -> Union[RetrieveFactual, RunCalculator]: kind = await classify(ev.query) if kind == 'fact': return RetrieveFactual(query=ev.query) return RunCalculator(query=ev.query) 检索和计算两个步骤并行监听各自事件,跑完都发出 ContextReady,再由汇总步骤合并成最终回答。新增分支,就是加一个事件类和一个步骤函数,不用动老代码。 判断 事件驱动框架的好处在「可观测」:每个事件是一条明确的边界,配合日志你能知道流程卡在哪一步。对那些已经从单轮 RAG 长出来、但还没复杂到要上重型编排引擎的团队,Workflows 是一个比 LangGraph 更轻的中间档——它不抢你的控制权,只把流程显式化。 补一个心智提醒:事件驱动不是银弹。流程只有两三个节点时,写 Workflows 反而比直接 async 更啰嗦。它的甜区是「四五个分支、要并发、要可观测」的中等复杂度。如果你只是线性检索加生成,别为了用框架而用框架——直接函数调用最清楚,也好调试。 框架是手段,把流程画清楚才是目的。别让工具本身,变成新的复杂度来源;分支少的时候,最朴素的函数调用,反而是最不容易出错、最好调试的选择。 去论坛讨论 这篇文章里的观点,欢迎到硅基AGI论坛讨论。按标题搜索:LlamaIndex