工作流和 Agent 能混着用吗?外层由代码控制、内层交给模型,边界怎么划?
简化版
能,而且生产系统大多是混着用的。常见做法是分两层:外层由代码控制,负责开跑前的校验、每一轮的成败判定、要不要再来一轮、最多几轮、交付哪一版;内层交给模型,在一轮之内自己决定调哪些工具、按什么顺序、什么时候够了。划边界的原则是:判定规则能写成代码、结果要被验收、动作有副作用或要跨轮次保存的,归代码;必须看到中间结果才能决定下一步的,归模型。模型在内层再自由,也要被代码设的上限兜住,而且成败以代码查到的事实为准,不采信模型的总结。
详细版
两种嵌套方向:
Workflow 套 Agent:固定流程的某一步里放一个受限 Agent
外层:校验 → [Agent 在一轮内自由调工具] → 代码验收 → 不过就带着问题再来一轮
Agent 调 Workflow:固定链路整体包装成一个节点或工具
外层:模型判断要不要再查 → 每次「查」都走同一条固定检索链路
| 决策 | 归谁 | 原因 |
|---|---|---|
| 开跑前配置、模板、数据是否齐全 | 代码 | 规则确定,开跑前就能判断 |
| 一轮之内调哪个工具、调几次 | 模型 | 取决于刚查到的结果,写不成固定顺序 |
| 这一轮的产出算不算合格 | 代码(或受代码约束的模型评审) | 需要可复现、可展示的判据 |
| 要不要再来一轮、最多几轮 | 代码 | 关系到成本上限 |
| 轮数用完交付哪一版 | 代码 | 同一份数据算几次结果都应一致 |
| 金额、时长等汇总数字 | 代码 | 模型报的数字不可信 |
要点:外层循环的触发条件是业务规则,框架不知道;内层的「模型调工具 → 执行 → 回传」是标准动作,交给框架即可;内层必须有轮数、调用次数上限;每一轮结束回到数据库核对产物。
完整版教学
一、为什么纯 Workflow 和纯 Agent 都不够
把行程规划写成纯 Workflow,要预先写死「先查哪个景点、再查哪家酒店、再选哪趟车」。但一个城市有几十个候选点位,排到第几天该去哪、交通赶不赶得上,取决于前面已经排了什么,分支多到写不完。
反过来做成纯 Agent,把所有事情都交给模型:它可能排完三天就说「规划完成」,可能把总花费算错,也可能一轮一轮重排停不下来。这些问题都不是再多写几句提示词能根治的,因为「这一版合不合格」「还要不要再来」需要确定、可复现的判断。
纯 Workflow 的问题:探索性的步骤写不出来
纯 Agent 的问题: 验收、上限、交付这些「管理动作」没人负责
混合: 管理动作交给代码,探索动作交给模型
所以混合不是折中,而是把两类性质不同的决策放到各自擅长的地方。整体选型的思路见「AI Agent 与 Workflow 有什么区别?应该如何选型?」。
二、两种嵌套方向
混合有两个方向,适用场景不同:
方向 A:Workflow 套 Agent(外层确定、内层探索)
┌───────────── 代码控制的外层循环 ─────────────┐
│ 前置校验 → 清理旧结果 → 第 N 轮: │
│ ┌── 模型控制的内层 ──┐ │
│ │ 调工具 ↔ 看结果 │ → 代码验收 → 过? │
│ └────────────────────┘ 否 ↺ 下一轮 │
└──────────────────────────────────────────────┘
方向 B:Agent 调 Workflow(外层探索、内层确定)
模型判断「资料够不够」→ 不够就再查 →
每次「查」= 改写 → 双路召回 → 融合 → 重排(固定链路,一步不少)
方向 A 适合「产出复杂、需要验收」的任务,比如排行程、排课表;方向 B 适合「单步动作已经很成熟,但要不要重复做需要判断」的任务,比如检索到的资料不够时换个说法再查。两种方向的共同点是:确定的那一层都由代码写死,不确定的那一层交给模型,而且模型的那一层被代码设了上限。内层由模型驱动的循环本身怎么设计,见「ReAct 与 Plan-and-Execute 有什么区别?Agent 如何选择规划方式?」。
三、划边界的四个问题
拿不准某个决策归谁时,依次问四个问题,任一回答「是」就倾向交给代码:
| 问题 | 例子 | 归代码的理由 |
|---|---|---|
| 判定规则能写成代码吗? | 总花费是否超预算、某点位是否排在闭馆日 | 代码判定可复现、可展示、可统计 |
| 结果要被验收或统计吗? | 这一版通过了没有、第几版通过的 | 验收标准不能随模型的心情变化 |
| 动作有副作用吗? | 删除上一次的结果、写入最终版本 | 出错代价高,需要确定的前置条件 |
| 要跨轮次保存吗? | 每一版的明细、上一版的问题清单 | 模型的上下文会丢,数据库不会 |
剩下的,比如「这一天先排哪个景点」「要不要再查一次酒店详情」,才交给模型。这四个问题的共同指向是:凡是事后要有人对结果负责的判断,都不能只存在于模型的一次输出里。
记忆钩子:模型负责「怎么做」,代码负责「做得算不算数、还要不要做」。
四、外层循环为什么要自己写
框架可以托管「模型调工具 → 执行 → 回传」这一圈,但托管不了外层,原因是外层的每个动作都依赖业务知识:
for (round = 1; round <= maxRound; round++)
prompt = 基础 Prompt
if (round > 1) prompt += 上一版的问题清单 + 改进要求 // 业务:问题怎么描述
内层:一次带工具的模型调用(框架托管几十轮往返)
count = 查库:这一版写了几条明细
if (count == 0) 本轮失败 // 业务:什么叫没干活
checks = 代码校验这一版 // 业务:校验规则
if (checks 满足通过条件) break // 业务:通过阈值
保存这一版的问题,供下一轮使用 // 业务:跨轮次记忆
交付:通过的那一版;都没通过就按规则挑一版 // 业务:交付规则
每一行注释里的「业务」,框架都不知道:什么算合格、合格线在哪、问题怎么转述给下一轮、交付哪一版。把这些写进提示词让模型自己判断,判断就变得不可复现;写成代码,每一次决策都能在数据库里查到依据。
五、内层交给模型时,代码还要守住什么
内层交出去的是「顺序和选择」,不是「无限的次数」和「最终解释权」。代码至少守住三件事:
| 守住什么 | 做法 | 不守的后果 |
|---|---|---|
| 次数上限 | 单轮往返轮数、工具调用总次数、同一工具同参数连续次数各设上限 | 模型原地打转,一轮烧掉大量 token |
| 事实核对 | 内层结束后回到数据库数产物,数不到就判失败 | 模型说「已完成」,库里一条都没有 |
| 数字重算 | 汇总金额、时长由代码按明细重算 | 页面显示的是模型报的错误数字 |
内层用框架托管时,业务代码看不到它转了多少圈,上限要交给框架参数去拦,计数放在监听器或工具方法里,具体做法见「框架托管的工具循环是黑盒,怎么记录每一步、怎么限制轮数?」。手写还是托管的取舍见「手写 Function Calling 循环和框架托管有什么区别?怎么选?」。
六、用数字算一笔成本账
双层循环最大的风险是成本相乘。下面用一组假设的上限做示意:
外层最多 3 轮,内层每轮最多 40 次模型往返
最坏情况:3 × 40 = 120 次模型调用
若平均每次往返输入 6000 token、输出 300 token:
最坏输入量 = 120 × 6000 = 720,000 token
若首轮就通过、内层实际用了 25 次往返:25 × 6000 = 150,000 token
两者相差约 4.8 倍
这组数字说明两件事:第一,外层和内层的上限要一起定,只看其中一层会低估最坏成本;第二,外层通过条件定得越严,平均轮数越多,成本随之上升。所以通过线不能拍脑袋定,要结合历史运行统计「第几轮通过」的分布来调。
七、两种控制方式可以并存,用开关切换
同一份资源上,固定链路和 Agent 两种控制方式完全可以同时存在,由一个配置项决定走哪条:
agent 开关 = 关 → 直线链路:改写 → 检索 → 生成(几步由配置决定)
agent 开关 = 开 → 状态图: 检索 → 模型评判 → 不够就改写重查 → 生成 → 校验
└─ 其中「检索」这一步,内部调用的就是上面那条直线链路
这样做的好处是:直线链路一行不用改,被整体复用为状态图里的一个节点;两种模式对同一批问题都能跑,效果差距可以用同一套评测集量化,而不是凭感觉说「Agent 更好」。代价是多了一种运行路径要维护和观测。
八、常见误区与追问
- 误区:用了 Agent 就不需要外层流程。 验收、上限、交付这些管理动作没人负责,Agent 会在不合格的产出上宣布完成。
- 误区:外层循环也交给框架最省事。 外层的触发条件和通过线是业务规则,框架不知道,交给模型判断又不可复现。
- 误区:内层交给模型,就不用设上限。 内层是成本最大的地方,外层上限乘内层上限才是最坏调用次数。
- 误区:模型说完成了,这一轮就算完成。 成败要回到数据库数产物,汇总数字由代码重算。
- 误区:固定链路升级成 Agent 要推倒重写。 固定链路可以整体作为 Agent 的一个节点复用,用开关切换两种模式。
- 追问:怎么判断外层通过线定得合不合理? 统计历次运行「第几轮通过」的分布:大量运行跑满轮数仍不通过,说明线太严或内层能力不够;几乎全部首轮通过,说明外层没起作用。
- 追问:一轮没做完和一轮做完了但不合格,处理一样吗? 不一样。前者应保留已完成部分、让模型接着补;后者才进入「带着问题再来一轮」,两者的判定条件和产物都不同。
九、加强记忆
混合编排记「两层三守」:两层是外层代码、内层模型。外层负责前置校验、每轮验收、要不要再来、最多几轮、交付哪一版,因为这些依赖业务规则,必须可复现;内层负责一轮之内调什么工具、按什么顺序,因为它要看中间结果临场决定。划边界问四个问题:规则能不能写成代码、结果要不要验收、动作有没有副作用、要不要跨轮次保存。三守是内层交出去后代码仍要守住的:次数上限、回库核对、数字重算。嵌套可以是 Workflow 套 Agent,也可以是 Agent 调 Workflow,固定链路能整体复用成 Agent 的一个节点,用开关切换、用评测集比较。
项目实战落地
项目里怎么做的
《AI Agent旅游行程智能规划平台》的行程编排就是「外层代码、内层框架托管」的双层循环,入口 executeTripPlanning 分四段:
- 前置校验:模型配置能取到、模型能构建、向模型发一次最短的请求探活,编排和反思两个模板都存在、启用、内容非空,启用中的工具能取到,候选池能预热且至少有可排的点位与酒店;
- 清旧数据、建运行记录:行程单置为「规划中」,删掉上一次的明细、校验和教训,建一条运行记录,候选池快照写进输入快照;
- 外层反思循环:轮次上限取自系统参数
TRIP_MAX_REFLECT_ROUND(兜底 3);每一轮是一次带工具的模型调用,内部几十轮「模型决定调工具 → 执行 → 回传」由 LangChain4j 托管;一轮结束先反查库里的明细条数,为 0 判失败,再由代码重算每天的花费、游玩时长和车程,跑 14 条硬校验,没过就把问题转成教训,拼进下一轮的 Prompt; - 收尾:交付版轮次、实际轮数、代码汇总的总花费写回行程单。
一版之内还有「续排」:模型这次只排了一部分天数时,复用同一个轮次号把剩下的天补完。它和反思循环是两回事,前者处理「没排完」,后者处理「排完了没过校验」。
《AI Agentic RAG高级企业知识库平台》是另一个方向:问答应用上有 agent_enabled 开关,关着走直线检索链路,几步由检索策略表的开关决定;打开走 LangGraph 状态图,转几轮由模型每一轮的判断决定。状态图的检索节点内部调用的就是那条直线链路,一行不用改。
为什么这样取舍
- 外层自己写:反思的触发条件是代码判出来的问题,框架不知道 14 条规则是什么;轮次上限、教训落库、多版本留存、最终版挑选都是业务动作,必须由代码控制节奏。
- 内层交给框架:单轮的「模型调工具 → 执行 → 回传」是标准动作,自己写反而容易漏掉并行调用、错误回传这些细节,业务代码只在工具方法和模型监听器上补步骤记录、计数和护栏。
- 知识库保留两种模式:同一个知识库、同一套检索策略两种模式都能跑,效果差异交给评测给出数字。
面试官还会追问
- 前置校验为什么必须在删除上一次的结果之前全部做完?探活一次能拦下哪些问题?
- 三道护栏在内层把一轮拦下来之后,这一版已经写进库的明细怎么处理?
- 反思模板的末尾为什么固定要求「没有被点名的部分尽量保持不变」?
学完《AI Agent旅游行程智能规划平台》,上面这些追问你都会迎刃而解。