☰
openrig:用声明式装配台搞定多Agent工作流编排
2026/10/31 13:37:13 网站建设 项目流程

openrig是我最近一直在折腾的一个开源小框架。这个名字拆开来看就是“打开你的装配台”,它解决的痛点其实挺具体的:当你手上有多个大模型API、多个数据处理工具,还要把它们串成一条可维护的工作流时,怎么不靠硬编码、不靠复制粘贴,而是用一套声明式配置把整个流程“组装”出来。我做这个项目的初衷,就是想给AI应用开发这件事一个相对轻量的脚手架。它能做什么呢,简单说就是:你在配置里画好节点、接好线,调用大模型、检索知识库、调用外部工具、处理中间结果,openrig帮你把这一整套跑起来。适合谁用呢?一是被各种Agent编排折腾过的全栈工程师,二是刚接触AI工作流、想快速出原型的产品研发,三是想把自己的工程经验沉淀成标准化模板的团队。

市面上类似的东西其实不少,但openrig的取舍和它们不太一样。它不追求把所有能力塞进一个引擎里,而是把重点放在“如何让装配过程本身变得可控、可调试、可复用”。下面这几节我尽量把设计思路、核心实现、实操过程和一些常见的坑都摊开来讲。

1. 项目整体设计思路:为什么需要一台“装配台”

1.1 多Agent协作的真正难点不在模型,而在编排

我最早尝试做多Agent应用时,最初的写法是每个工具写一个类,再在主流程里用if-else判断下一步调用谁。流程图很简单的时候,这套写法完全没问题。但一旦加入检索、生成、校验、改写这些环节,代码会迅速退化成一大团互相调用的函数。真正麻烦的不是“调一个大模型出结果”这件事,而是结果如何流转、出错之后怎么回退、上下文怎么裁剪、每一步超时了怎么处理。

openrig换了个思路:把每一步都抽象成一个节点,节点之间用边连接,整条链路用配置描述。就像一条实体生产线,模型、工具、判分支是不同工序,配置是装配图纸。这样多个Agent的协作对手是从代码层面抽离出去,变成了数据流层面的问题,后续调优和排查就容易很多。

1.2 openrig的定位:不上不下,刚刚好

对比一下,市面上偏重的编排框架会带很多内置能力,学习成本和自托管的复杂度都不低;纯代码的轻量方案又很难应付频繁变化的流程。openrig的目标落在这两者之间:用一个简短的DSL描述流程,运行时负责调度,扩展能力靠插件协议补。这个定位让我在项目早期就明确了边界:不做大而全的框架,做一个足够好用的工作台底座。

下面是当初做技术选型时列的一份对比表,供方向参考:

方案灵活度上手成本维护成本适用场景
纯代码硬编码编排高低高(经常改)流程固定、小规模
重型编排框架高高中复杂系统、团队统一施策
openrig中高低低中等复杂度流程、快速迭代

选择openrig路线最核心的原因只有一个:大多数研发团队的AI业务复杂度还没到需要重型框架的程度,但已经超出了硬编码能健康维护的水平。这时候用声明式配置最划算。

1.3 配置驱动的好处

配置驱动还有一个额外红利:产品同学和研发同学可以同一份流程文档交流。大家在需求评审里说“先检索,再判断是否需要生成摘要,摘要出来了再做要点提取”,我直接把对应的YAML字段贴在后面即可,沟通成本降得很明显。对团队而言,多Agent项目的可维护性往往不是看代码写得好不好,而是看需求变化后改动是否足够小。配置驱动之后,多数改动都不碰核心代码。

2. 核心架构与关键技术选型拆解

2.1 三个基础模块:描述层、运行层、扩展层

openrig从工程结构上分成三块:描述层,负责解析YAML/JSON定义的工作流;运行层,负责按拓扑顺序执行节点并传递上下文;扩展层,负责把大模型、工具、数据库等能力注册成节点。这个分层设计参照了我做后端服务时惯用的“配置-调度-实现”三分法,好处是每一层可以独立测试,不会因为改了一个工具实现而影响整个引擎。

描述层我选择了YAML作为第一语言。YAML写嵌套结构比JSON顺手,字段之间的关系一眼能看明白。为了防止太长的配置文件变得难维护,我支持$include语法,可以按业务域把一条大型流程拆成多个文件。对于喜欢更严格类型约束的使用者,后续也补了JSON Schema校验方案。

运行层是最有趣的部分。它需要处理几个麻烦问题:多分支如何决定后续路径、多个叶子节点是否并发执行、循环结构如何防止死循环。最终我借鉴了有向无环图(DAG)的调度方式,早期版本干脆禁止了循环依赖,让排查简单很多。节点之间的数据传递通过一个context对象完成,类似HTTP请求里的request上下文,后续节点只读自己关心的字段。

扩展层的协议其实很简单,每个插件只需要声明自己是什么类型、接受什么参数、返回什么结构即可。我用TypeScript写了一套接口定义,JavaScript实现的第三方插件也能用duck typing方式接入,注意关键字段别漏就行。

2.2 技术栈清单

整个项目没有刻意追逐新东西,尽量选了我自己熟悉且社区稳健的组件:

模块选型原因
运行时Node.js + TypeScript生态丰富,适合IO密集和异步编排
前端工作台React + 轻量画布组件便于可视化调试流程
配置描述YAML + JSON Schema对人友好,可校验
存储SQLite(默认)单机可跑,后续可切换PostgreSQL
部署Docker Compose一键启动,适合内网使用

选Node的一个重要考量是:工具生态里有大量现成的npm包,比如各种文件解析、网页抓取、消息推送SDK,插件化包装成本很低。如果换成Python,很多前端工具链接入反而要绕路。Python方向我留了HTTP调用接口,跨语言调用也能做,但默认推荐还是Node。

2.3 为什么不做成无代码平台

有一段时间我认真想过,要不要加拖拽编排界面做成完整无代码平台。后来还是忍住了。原因是无代码平台真正吃功夫的地方在于“可视化配置的复杂交互”,这会把大量精力吸走,偏离项目验证“装配思路”的核心目标。现在做的可视化只定位成调试辅助:跑完流程后能回看节点状态、耗时和上下文快照。用户完成装配的主要方式仍然是写YAML配置,这也是为什么它不是面向纯业务人员的图形工具。

3. 核心实现细节:装配台上最关键的那些节点

3.1 内置节点类型:不只是调用大模型

openrig目前内置了六类节点:llm、tool、retriever、router、aggregator、http。名字直白,但实操里需要注意的细节很多。

llm节点是最常用的。除了model、prompt、temperature这些常规参数,我加了一个叫responseSchema的字段。在项目里接LLM输出结构化数据时,如果只靠提示词要求“返回JSON”,偶尔会因为格式不合法导致后续节点崩溃。用了responseSchema之后,运行时会在拿到原始输出后先做一个本地校验,不符合就直接重试,最多重试三次。这让整体稳定性提升非常明显,尤其在批量跑历史数据的时候。

tool节点的设计参考了MCP的“工具即函数”思想。一个工具就是一个带输入输出的函数声明,openrig不强求工具自己处理异常,而是统一捕获异常后按预设策略执行重试、降级或直接失败。比如调外部天气API超时,可以配置为“返回空的天气对象继续往下走”,而不是让整条流程断裂。

router节点承担分支决策。它读取context里某个字段,做字符串匹配或数值比较,输出路由结果。比如我在客服场景里用它判断用户意图是“退款”还是“咨询”,然后把不同分支指向不同节点。早期版本路由逻辑比较死,后来我允许配置一个prompt字段,让大模型返回一个标签,再映射到分支,灵活性高了很多。

// 路由节点核心逻辑,简化版 type RouterConfig = { inputField: string; prompts?: string; routes: Record<string, string>; default?: string; }; async function routeNode(ctx: Context, config: RouterConfig) { const raw = ctx.get(config.inputField); if (config.prompts) { const label = await callLLM(config.prompts, raw); ctx.set(`${config.inputField}_label`, label); return config.routes[label] ?? config.default ?? "end"; } return config.routes[raw] ?? config.default ?? "end"; }

3.2 上下文传递规则与数据裁剪

上下文是整个装配台的地基。我采用局部优先的取值规则:ctx里有一个核心对象和临时缓冲区,临时缓冲区里的字段优先级更高,处理完一条分支后自动清掉,这样同一链路里不同分支之间不容易串数据。粗看像是多此一举,实际跑复杂流程时,它可以省掉非常多“这个字段到底哪来的”之类的问题。

LLM输入长度始终是个坎。长文档摘要、多轮对话这类场景很容易把Token打满。我在llm节点里加了一个contextStrategy字段,可选truncate、mapReduce、refine。truncate是暴力截断,适合不敏感的描述性文本;mapReduce是分段总结再合并,适合长文档;refine是逐段迭代完善输出,适合要求连贯性的生成任务。这个特性不是openrig独有的,但把它做进配置项里,使用者不需要在自己的代码里重复实现,省了很多事。

3.3 重试与超时策略

多Agent应用最影响体验的其实是“偶发不可控”。外部模型API偶尔超时、偶尔返回空内容,这些都不该直接杀掉整个流程。我在配置里预设了统一的策略块:

execution: timeout: 60s retries: 2 retryDelay: 1.5s onError: continue # 可选 fail / continue / fallback

实测下来,单节点重试两次、超时60秒,在普通文本生成场景下足够覆盖绝大多数异常。重要的是不要让用户等太久后直接看到失败页面。onError: continue会让流程带一个警告标记跑完,后续人工介入兜底,对内部工具型应用比较友好。

4. 实操过程:从一个“需求拆分Agent”看完整落地

4.1 场景设定与安装启动

这里拿一个我给自己团队做的“需求拆分Agent”当案例。需求是这样的:产品经理丢一段原始需求描述,系统先判断它是否包含验收标准,如果没有就提取关键点生成问题清单;然后综合原始需求和问题清单,输出用户故事列表;最后把输出整理成Markdown格式。整个过程涉及检索判断、两次LLM调用、一次结构化输出,非常适合演示openrig的使用方式。

第一步,安装openrig。因为是个人项目分支,我直接用Docker Compose起的服务,关键就两步:

git clone <你的openrig仓库地址> openrig cd openrig docker compose up -d

启动后访问本地工作台地址,默认端口3030,能看到节点状态列表和一个简单的调试面板。首次使用需要填一个模型API的base地址和key,openrig兼容OpenAI接口格式,所以主流的模型服务商都能接。

工作台面板本身不是必需组件,如果你只把它当库嵌入,也可以直接import运行时包,代码里初始化Engine对象,加载YAML文件运行。Docker方案是为团队协作设计的统一环境,省去各人本地依赖不同的烦恼。

4.2 编写流程配置

核心流程配置比我想象的短。下面是简化后的实际配置:

id: requirement-splitter name: 需求拆分Agent version: 1.0.0 nodes: - id: has_acceptance type: llm prompt: | 判断以下需求是否已经包含明确的验收标准。 需求内容:{{input.requirement}} 只返回 yes 或 no。 responseSchema: type: string enum: ["yes", "no"] output: has_acceptance - id: ask_questions type: llm prompt: | 根据以下需求,生成不超过5个需要澄清的问题。 需求内容:{{input.requirement}} 请以 numbered list 输出。 output: questions - id: split_stories type: llm prompt: | 结合需求与澄清问题,输出用户故事列表。 需求:{{input.requirement}} 问题:{{nodes.ask_questions.output}} responseSchema: type: array items: type: object properties: title: { type: string } acceptance_criteria: { type: array, items: { type: string } } output: stories edges: - from: start to: has_acceptance - from: has_acceptance to: ask_questions condition: output == "no" - from: has_acceptance to: split_stories condition: output == "yes" - from: ask_questions to: split_stories - from: split_stories to: format_markdown type: pass

配置里比较关键的一点是output字段。它相当于给每个节点的结果起了个名字,后续节点通过nodes.<节点id>.output引用。这样整条流程像水管一样,每段管道都有清晰的接口。刚开始用的时候很容易忘了给节点加output,结果后续取不到数据,换成现在的命名方案后基本没再犯过。

format_markdown节点我没写详细配置,实际是一个tool类型的文本处理节点,接收JSON数组,渲染成Markdown列表。这种“模型输出结构化数据,工具节点做最后格式化”的组合在实际项目里非常常见,因为LLM处理长文本格式容易丢样式,传统代码处理又需要精确的数据结构。

4.3 运行结果与调试观察

启动流程后,输出面板会实时显示节点状态。我观察到几个比较有意思的行为。第一,has_acceptance节点返回no的时候,整条链路会先走ask_questions再走split_stories,而返回yes时直接跳过提问节点,这里条件路由生效。第二,因为split_stories用了responseSchema,运行时自动做了JSON解析和校验,偶尔模型返回的内容带了一些多余文字,openrig也会在本地重试一次,基本上不会把脏数据传给后面的格式化工具。

一次典型运行的时间分布大概是:两个LLM节点各占10到15秒,格式化节点不到1秒。如果哪天某个节点耗时异常飙升,工作台里能看到具体超时时间和上下文快照,排查起来比看日志直观很多。

4.4 把流程发布成一个小应用

openrig还提供了一个轻量发布接口,运行好的流程可以注册成一个HTTP端点。比如上面的需求拆分流程,只加一段配置:

api: path: /api/requirement-split method: POST input: requirement: string output: markdown

跑起来之后,对外就多了POST /api/requirement-split这个接口,入参是requirement字符串,出参是Markdown。这个能力让我在内部系统里直接复用同一套流程逻辑,不需要重新实现前端页面或机器人服务。发布接口这一层做得很轻,本质上就是在运行时外围包了一层HTTP路由,但因为编排逻辑都在YAML里,新接一个流程的成本非常低。

5. 常见问题与排查技巧实录

使用过程中遇到的问题不少,我把最有代表性的几条整理成速查表,方便大家直接对着解决。

问题原因解决思路
节点A的返回在节点B里取不到没设置output字段或字段名拼写不一致统一用nodes.<id>.output访问,并检查YAML缩进
LLM输出JSON解析失败模型返回了额外说明文字或格式不规范启用responseSchema强制重试;给prompt加“只输出JSON”约束
流程一直循环或卡住配置里出现了环状依赖,或某节点超时未配置早期强制使用DAG,避免循环;统一配置超时时间
某个工具节点异常影响整条链路外部API偶发故障未兜底使用onError: continue加警告标记,保留上下文快照
prompt里模板变量没被替换模板变量引用的字段不存在启动前做静态解析校验,跑之前检查上下文里的字段列表
节点状态正常但http接口返回空发布接口的output声明和实际节点输出不一致严格按照最终节点output字段填写api.output

第一条问题看起来很小,但我真被坑过好几次。YAML是一种对缩进非常敏感的描述格式,一个字段配错层级,解析出来的对象结构就不是你想要的,而且经常不报错。后来我在设计里加入了“启动前静态检查”:加载YAML之后先遍历所有output引用,如果发现有节点引用了不存在的输出字段,直接报错并提示可能的原因。这个检查给后续使用省了非常多时间。

除此之外,调试多节点流程还有一个我特别推荐的做法:每跑完一个节点,就把该节点的输入和输出快照固化下来,存成一个可回放的日志。这个日志的作用有点像一个自动化测试用例,以后修改prompt或者调整工具参数时,可以拿之前的case回归测试,验证结果是否有退化。在个人项目里这件事靠手动做就行,但只要涉及到团队协作,一定要让它成为流程的一部分。

最后提醒一点,千万别把所有业务逻辑都塞进一个大LLM节点。虽然大模型能处理很多事情,但这样做会让流程的可控性变差,而且单点超时会拖垮整个应用。openrig的价值恰恰在于把一个大任务拆成多个小步骤,哪一步不对就单独修哪一步。

6. 后续还能怎么扩展

说实话,openrig现在还远谈不上完美。后续我比较想补的方向有三个。第一是让router节点更聪明,支持基于语义相似度而不是简单的字符串匹配,这样在意图识别场景下能少依赖一次LLM调用。第二是增加简单的锁机制,当多个流程实例读到共享资源的时候避免并发冲突。第三是插件市场,让社区贡献的节点能力可以一键安装。这些功能我不会全堆进核心引擎里,更多的会做成官方插件。

从设计思路上说,这个项目最值得关注的不是某一个具体节点的效果,而是它提供了一个“把复杂流程当作可配置资产”的方法。很多团队做多Agent应用,最大的瓶颈不是模型能力不够,而是流程改了之后没法快速验证和落地。openrig的装配台形式,至少让“改流程”这件事在一个可控的范围里发生。如果你也在做类似的事,不妨试着先把你的业务流程画成节点和边的结构,再想哪些环节需要模型、哪些需要代码处理,这个思考过程本身就可能帮你减少一半以上的无效代码改动。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询