这个项目其实是从广东省智慧统计平台的智能问数开始的。

做完智能问数以后,我对 Dify 已经比较熟悉了,也开始想:如果不只是做一个问答助手,而是让一个总控 Agent 去调用多个专业 Agent,最后再回来汇总和判断,Dify 能不能完成?

我没有一上来就选 LangGraph。那时候连这套调用方式能不能达到预期都不确定,先用熟悉的 Dify 验证最快。

结果证明,这条路是能走通的。Dify 版做完后,我给用户实际试用过,效果也不错。但项目做得越完整,它的限制就越明显。后来我又用 LangGraph 做了第二版,不是因为第一版没做成,而是第一版让我知道了自己真正想要的是什么。

Dify 版先把想法跑通

Dify 版已经有一套完整的多 Agent 流程。用户提出问题后,先做意图判断,再分发给不同的专业 Agent,最后把结果交给总控汇总。

我还单独做了 Tool Gateway,把模型和数据库隔开。模型不能直接查库,也不能自己写 SQL,只能调用事先定义好的接口。涉及导入、修改等操作时,也不是 Agent 说一句“已经完成”就算结束,而是先生成候选,用户确认后再真正写入。

这些做完以后,它就不只是一个聊天页面了。路由、子 Agent、工具调用、数据读写和最终回复都能跑起来,拿给用户试用也确实有效果。

问题出在流程继续变复杂以后。

Dify 的工作流需要提前画好。总控根据用户问题判断走哪条路,本质上还是不断增加条件分支。Agent 少的时候还比较清楚,Agent 和意图多了以后,就开始变成一堆 if。这不是我想要的总控,它更像一个披着 Agent 外壳的流程路由器。

我真正想做的是闭环。总控把任务交给子 Agent 后,应该还能看到结果,判断资料够不够、有没有遗漏、不同 Agent 之间有没有冲突。如果结果不好,它可以修改要求再调用一次,或者补派另一个 Agent,然后才给用户最后的回答。

Dify 版能做到“分出去”,但很难自然地“再回来”。对话的连续性也有类似问题。用户第二轮说“继续分析刚才的数据”,我希望系统保留的不只是几句聊天记录,还包括上一轮找过哪些资料、调用过哪些 Agent、哪些问题还没解决。

这两个问题最终让我决定再做一版。

为什么选择 LangGraph

当时我没有把市面上的 Agent 框架全部研究一遍再打分。LangGraph 已经比较成熟,而且它就是用状态和有向图来组织任务,循环、分支和状态回流都比较符合我的设想,所以就选了。

第二版也不是把 Dify 画布翻译成 Python。真正保留下来的,是数据库、MinIO 文件库、MCP 工具这些事实源,以及已经验证过的业务目标。Agent 怎样组织、任务怎样运行、前后端怎样交互,基本都重新做了。

我想减少代码对业务流程的硬编码,让模型真正参与任务理解、资料选择和结果判断。但这里有一条线不能越过:让 Agent 更自由,不等于把系统权限全交给它。

查询事实要经过固定的数据能力,外部工具要经过白名单,写入和删除仍然要做参数检查和人工确认。说到底,就是先把 AI 圈在一个受控环境里,再让它在这个范围内尽量发挥。

1.0 做到了哪里

LangGraph 1.0 先解决的是最小闭环。

它已经支持连续对话和多轮工具调用。内部数据走 DataCenter,外部 MCP 工具走 ToolCenter;写入仍然要经过候选、确认和执行。PostgreSQL、MinIO、Redis 分别保存运行状态、文件和短期缓存,前端通过 SSE 接收运行事件。测试除了普通单元测试,还有针对 Agent 行为的行为级测试,检查它到底调用了什么工具、有没有遗漏数据、最后的回答有没有越界。

但 1.0 没有一步做到我最初设想的完整架构。当时生产主图还是:

START -> native_agent -> END

真正的模型循环、工具调用和功能 Agent 调度,大部分还集中在 NativeAgentRunner 里。换句话说,LangGraph 在这一版里先承担了状态入口,真正的有向成环多 Agent 还没有完全落到生产主路径。

这不是做不下去,而是第一版先把范围收住。数据、工具、确认、存储、前后台和测试基线都跑通以后,下一步该改什么也看得更清楚了:总控太大,Python 分支偏多,各个功能 Agent 还没有真正独立运行。

2.0:调度真正放回图里,但不是用我原计划的方式

2.0 已经在 7 月底发布,旧版本正式停用,新架构接管了生产入口。

它最终长成的样子是:一张 14 个固定节点的薄 StateGraph。主控 supervisor 以有界多轮 ReAct 理解任务——观察、行动、再观察,最多 8 轮,轮次和工具预算都是硬约束;需要专业判断时,用 Send 把任务并行派给 7 个业务节点,每个节点有自己独立的 Prompt、输出 Schema 和能力白名单;DataCenter 和 ToolCenter 从 1.0 的“服务层”变成了图里的确定性节点,不调模型,只做固定 SQL、参数校验、白名单执行;数据导入走“生成候选 → 挂起等人工确认 → 正式写入”三段链,写入完成必须带回真实保存编号。

START -> supervisor(多轮 ReAct)
           |-- 附件 -> preprocessing 先结构化
           |-- 数据导入 -> 候选 -> 人工确认(挂起/恢复)-> 正式写入
           |-- Send 并行派发 -> 7 个业务节点 -> 回到 supervisor 汇总
           |-- data_center / tool_center 确定性执行
         -> END

但这里要坦白一件事:2.0 不是按我原来的设想做出来的。

原来的计划是一个重得多的方案——给每次任务建立冻结的契约对象,主控先出计划、执行节点干活、复核节点打分,状态变更全部走带权限校验的变更单,计划的每次修改都留版本。我把这套方案做了出来,然后在评审时亲手把它砍了。

砍掉的原因很实际:这套机制想防的问题(状态被改乱、步骤被偷偷重排),在“单主控 + 确定性中台”的结构里本来就不会发生;而它带来的成本是真实的——每加一种交互就要多定义一类契约,调试时要在计划版本和变更单之间来回跳,验证的复杂度也翻了几倍。模型在 ReAct 循环里本来就能根据新观察动态决定下一步,用契约把每一步钉死,等于把模型的灵活性收缴了,换成一堆我自以为更安全的 Python。

砍掉的方案没有上线过一天。现在回头看,这是 2.0 最重要的设计决定:调度职责真正从 Runner 收进了图里,但靠的是更薄的图和更清楚的边界,而不是更重的契约。1.0 时代那个几千行的 Runner 随之退役,连同旧版本的路由和测试基建一起删干净了。

2.1:解决运行期才看得见的问题

2.1 没有改图结构,加的都是真正跑起来之后才暴露的能力。

联网搜索与证据分层。主控和研判类节点需要最新公开信息,但网页内容和收费数据的可信度完全不同。现在搜索走自托管的 SearXNG,经 ToolCenter 白名单按需调用,只读、有超时与重试限制;回答里网页证据和既有数据分层标注来源、时点和置信度,不混为一谈。搜索服务只跑在内网,不引入新的 API Key。

模型治理收口。不同厂商的 OpenAI-compatible 接口各有脾气——返回格式、思考流的切法都不一样。这些差异全部收口到模型 adapter 层,图和业务节点只消费规范化后的结果,代码里不允许出现厂商特判。在此之上,9 个模型调用点支持按节点绑定不同模型,管理后台可以查看和切换,运行中的任务按启动时的快照执行,切换只影响新任务——不会出现一个任务跑到一半换了模型。

主控思考流公开。前台现在能实时看到脱敏后的主控思考、派工和工具调用过程,刷新后历史投影和实时流一致。边界划得很清楚:思考事件进前台前过滤密钥、连接串、SQL、Prompt 和工具参数;业务节点的思考、原始运行状态不进前台。让用户看见系统在做什么,和把系统内部全部摊开,是两回事。

首轮背景预读。以前主控第一轮要自己去查用户画像和待办事项,白白烧掉工具预算。现在首轮进入循环前,代码层直接读好画像摘要和短期事项拼进系统提示——不走工具调用、不计预算;数据为空就如实说明,后续轮次也不重复注入。

这四件事的共同点:它们都不是“新功能”,而是把模型差异、证据可信度、过程可见性这些运行期真实问题工程化。Agent 系统做到后面会发现,真正花时间的从来不是让 Demo 跑起来,而是这些。

MAF,我接下来想看看什么

最近我也在关注 Microsoft Agent Framework,也就是 MAF。现在还没有时间做完整研究,更没有在项目里验证,所以这部分只能算下一步准备做的实验。

我比较感兴趣的是它的 Agent Harness。以前我理解 Harness,更多是给 AI Coding 准备项目上下文、规范和验证流程。MAF 里的 Harness 更接近 Agent 自己的运行外壳:它帮 Agent 记住计划和 Todo,保存每一步执行记录,在上下文快满时做压缩,工具调用前做审批,任务没完成时继续循环,也可以把子任务交给后台 Agent。

这些恰好都是超长任务最麻烦的地方。长任务不能只是把上下文窗口开大。Agent 跑了几十步以后,哪些已经完成、哪些失败了、当前拿到了什么产物、下一步还能不能执行,都要放在模型之外保存。否则上下文一断,前面做过的工作很容易丢掉。

MAF 不是简单地补上 LangGraph 没有的功能。LangGraph 本身就有持久化、恢复、Human-in-the-loop 和长任务运行能力,只是它比较底层,很多东西需要自己组合。MAF Harness 的方向更像是把计划、Todo、上下文压缩、工具审批和运行记录先装成一套可以直接使用的外壳。

我暂时不会因此停掉手里的项目再重写第三遍——2.1 刚发布,正是观察真实运行的时候。比较实际的做法,是拿一个独立小场景试试 MAF:让它跑一段真正的长任务,中途暂停和恢复,再看上下文压缩、工具审批和后台 Agent 到底能省掉多少自研工作。

有一点不会因为换框架而改变。MAF 官方文档也明确提醒,Shell 的 deny-list 不是安全边界。将来即使使用 Harness,数据写入、删除、权限检查和人工确认,仍然要放在自己的受控执行层里。

参考资料

两个框架怎么选

做完两版以后,我不觉得 LangGraph 一定比 Dify 好。

流程比较确定时,例如分类、检索、生成固定格式的回答,Dify 仍然更省事。它搭得快,画布也直观,用来验证想法很合适。没有必要为了一个固定工作流,自己维护一整套运行时。

如果总控需要根据中间结果不断调整计划,子 Agent 可能被重复调用,任务还要暂停、恢复、限制预算,这时候才值得使用 LangGraph 这类可编程框架。

所以从 Dify 到 LangGraph,不是一次框架替换,而是项目目标变了。Dify 帮我确认多 Agent 这条路能走通;LangGraph 让我继续往更自由的调度和更完整的工程底座走。1.0 先把系统跑稳,2.0 把调度真正放回图里——顺手还砍掉了自己过度设计的第一稿;2.1 则把运行期暴露的问题一个个收口。

这两个项目做下来,我最关心的也不再是“用了几个 Agent”。我更在意的是:哪些判断应该交给模型,哪些流程必须由代码兜住,以及怎样让 Agent 有足够的自由,但始终不能越过系统边界。