Skip to content

面试官问:做一个工具,到底选 Workflow 还是 Agent?

本文基于“高德一面:Workflow 和 Agent 的差异”与《AI Handbook》中的 Harness 文章整理。核心问题是:任务应该由代码编排,还是交给模型动态决策?下文进一步把 Workflow、Agent、Harness、LangGraph 和 ReAct Agent 放到同一张架构图里说明。

先给面试答案

如果需求的步骤、输入输出和异常分支可以提前描述,我会优先使用 Workflow;只有当任务需要根据中间结果动态拆解、选择工具、重试或改变计划时,才引入 Agent。生产系统通常不是二选一,而是“确定性 Workflow 包住受控的 Agent 节点”:代码负责边界、状态、权限、预算和副作用,模型负责不确定的判断。

判断标准不是“Agent 更先进”,而是下面这个问题:

下一步行动能不能在系统设计时写成稳定的规则?

  • 能写成规则:Workflow。
  • 规则只能覆盖大多数情况,剩下的情况需要理解语义并临场决定:在局部节点使用 Agent。
  • 连工具集合、步骤数量和完成条件都高度不确定:使用 Agent,但必须放进可靠的 Harness 中。

这句话既回答了“选哪个”,也说明了为什么不能把所有功能都做成一个自由循环的 Agent。

一、先把五个词分开

很多面试争论来自概念混用。它们处在不同抽象层,不是五种互斥产品。

概念解决的问题谁决定下一步典型形态
Tool完成一个可调用动作调用方search()create_ticket()
Workflow把已知步骤可靠地编排起来代码、规则、状态机DAG、状态机、流水线
Agent在不确定环境中决定做什么模型策略(受约束)规划 → 调工具 → 观察 → 再规划
Harness让 Agent 能安全、可观测、可恢复地运行运行时和平台上下文、循环、权限、预算、审批、日志
LangGraph用图来实现状态、节点和转移图定义 + 节点逻辑Workflow 图或 Agent 循环图
ReAct Agent一种让模型交替推理和行动的策略模型输出 actionThought/Action/Observation 循环

可以用下面的分层来记忆:

text
业务目标

   ├── Workflow:固定的流程和分支
   │       └── Agent 节点:在局部做动态决策

   └── Agent:模型驱动的循环策略
           └── Harness:把循环变成可运行的生产系统

LangGraph 是承载上面两类编排的图运行时;ReAct 是 Agent 节点常用的决策模式。

因此,“LangGraph 还是 ReAct Agent”不是同一层的选择:前者偏运行时和编排,后者偏行为模式。可以用 LangGraph 实现一个 ReAct Agent,也可以在 LangGraph 中只写确定性的 Workflow。

二、Workflow 和 Agent 的共同基础

Workflow 和 Agent 不是两个完全无关的世界。参考文章把两者的共同点归纳为五类,这些共同点也解释了为什么它们经常被组合在同一个系统中:

  1. 都是目标导向的系统。 都需要明确输入、可衡量的输出,以及围绕目标组织起来的一组动作。
  2. 都要和外部工具或系统交互。 Workflow 节点可能调用邮件、数据库和微服务;Agent 可能调用搜索、代码执行和 API。
  3. 都可以把 LLM 作为执行单元。 Workflow 可以让 LLM 做分类、抽取或摘要;Agent 则通常让 LLM 参与下一步决策。
  4. 都需要状态管理和错误处理。 运行到哪一步、拿到了什么中间结果、失败后怎么重试和超时,不能因为用了模型就消失。
  5. 都应该模块化和可复用。 Workflow 把步骤封装成节点,Agent 把能力封装成有契约的工具。

共同点意味着:选择 Workflow 并不等于拒绝 LLM,选择 Agent 也不等于放弃代码编排。真正的分野在于谁拥有控制流的决策权。

三、五个核心差异:谁决定下一步

差异维度WorkflowAgent
决策权开发者在设计阶段预先定义路径和分支模型在运行时根据目标、上下文和工具结果决定下一步
灵活性与可预测性行为透明、稳定、易测试、易审计适应开放任务,但同一问题可能走不同轨迹
面对不确定性的能力适合已知问题,重点是自动执行适合未知问题,需要探索、试错和根据反馈调整
成本结构前期设计成本高,运行成本通常较低工具和提示设计成本高,运行时 token、延迟和评测成本更高
失败模式节点报错、超时、格式错误,通常容易定位选错工具、参数错误、误判完成、重复循环,必须分析轨迹

可以把它概括成一句话:Workflow 是在设计时穷举路径,Agent 是在运行时生成路径。

四、Workflow:把确定性放在代码里

Workflow 的特点是:节点、数据结构、转移条件和失败策略大体已知。模型可以参与某个节点(例如抽取字段或生成文案),但不能随意改变整个系统的控制流。

以“报销单审核”为例,流程可以写成:

text
上传发票
  → OCR/字段抽取
  → 金额与发票真伪校验
  → 查询员工额度
  → 规则判定
      ├─ 通过 → 写入财务系统
      ├─ 超额 → 转人工审批
      └─ 材料缺失 → 返回补充清单

伪代码如下,重点是状态和转移由程序掌握:

python
def reimbursement_workflow(request):
    state = extract_fields(request)
    state = validate_invoice(state)

    if state.errors:
        return ask_for_supplement(state.errors)

    state.limit = query_employee_limit(state.employee_id)
    decision = apply_policy(state)  # 可审计、可复现的规则

    if decision == "approve":
        return write_finance_system(state)
    if decision == "manual_review":
        return create_approval_task(state)
    return reject(state)

Workflow 的优势

  • 可预测:同样的输入经过同样的分支,方便回归测试。
  • 可审计:每个节点、条件和副作用都有明确责任人。
  • 低延迟、低成本:不必让模型为已知步骤反复思考。
  • 易于控制风险:权限、事务、重试和幂等可以写在代码里。

Workflow 也不是“完全不用模型”

常见的 Workflow 节点仍然会调用模型,例如把邮件分类成 退款/投诉/咨询,或从合同中抽取结构化字段。关键区别在于:模型只负责一个有边界的函数,输出经过 schema 校验后才会进入下一步;它不拥有任意调用工具和修改流程的权力。

五、Agent:把不确定性放进闭环

Agent 适合目标明确、路径未知的任务。例如“调查这次线上故障并给出修复建议”:需要先查监控,再看发布记录,可能还要查询日志、对比配置;不同证据会改变下一步。

最小 Agent 循环可以表示为:

python
state = init_state(user_goal)
for turn in range(MAX_TURNS):
    decision = model.decide(
        goal=state.goal,
        context=state.context,
        tools=allowed_tools,
    )

    if decision.type == "final":
        return decision.answer

    tool_result = run_tool_safely(decision.tool, decision.arguments)
    state = state.observe(tool_result)

return stop_with_budget_exceeded(state)

模型在每一轮回答的不是最终答案,而是“现在要不要调用哪个工具、参数是什么、拿到结果后是否继续”。这带来灵活性,也带来新的失败模式:工具选错、参数错误、循环不止、证据不足仍然下结论、成本失控。

什么时候值得引入 Agent

下面的信号越多,越适合使用 Agent:

  1. 用户目标是自然语言,无法枚举成少量意图。
  2. 需要在多个工具或数据源之间路由,且路由取决于中间结果。
  3. 任务需要多跳检索、比较、反思或动态重试。
  4. 工具的调用顺序不是固定的,失败后可能换策略。
  5. 能接受一定的延迟和非确定性,并且有评测和人工兜底。

如果只是“接收表单 → 调三个 API → 返回结果”,Agent 往往是过度设计。

六、Harness:Agent 外面那层真正的工程

运行一个模型循环并不等于交付一个 Agent。Harness 可以理解为 Agent 的运行时外壳,负责把模型的“想法”放在一个可控的执行环境中。它至少应包含以下能力:

《AI Handbook》给出的一个很有用的心智模型是:

text
Model → Harness → Environment

模型提供推理和工具调用意图,环境提供文件、进程、网络、数据库等真实资源,Harness 则负责把观察、行动、反馈和完成验证组织成一个闭环。它更像“底盘”,而不是模型本身:模型是引擎,Harness 负责转向、制动、权限和恢复。

Harness 的三层定位

  • 模型层(Model):提供语言理解、推理和工具调用能力。
  • Harness 层:服务于单个 Agent,负责观察、行动、反馈、验证、权限和记忆。
  • Agent Runtime 层:服务于多个会话或多个 Agent,负责调度、租户隔离、持久化、治理和控制面。

Harness 和 Agent Runtime 是互补关系:前者关注“一个 Agent 这一次任务执行得是否可靠”,后者关注“很多 Agent 如何被系统化地运营”。

Harness 的六项核心职责

职责具体问题典型实现
观察 Observation模型应该看到哪些环境事实?文件状态、进程输出、用户输入、API 响应结构化
行动 Action模型提出的动作如何安全执行?工具审批、沙箱隔离、并发调度、副作用记录
反馈 Feedback执行结果如何回到下一轮上下文?结果回写、错误重试、摘要压缩、中间状态
完成验证 Completion如何确认任务真的完成,而不是模型说完成?Schema 校验、测试、证据包、审计结果
权限 PermissionAgent 能操作到什么边界?Allowlist/Blocklist、文件/网络/进程控制、人工审批
记忆 Memory中断后能否继续,跨会话能否保持状态?Session 持久化、长期记忆、Checkpoint、Lineage

这六项职责是区分“只提供调用封装的框架”和“真正的 Harness”的关键。后者还要把状态、环境、安全和恢复纳入同一个执行闭环。

把这些职责落到工程实现,至少要有下面这份清单:

Harness 能力要回答的工程问题最小实现
上下文管理本轮应该看到哪些信息?历史裁剪、摘要、检索结果、状态 schema
工具边界模型能调用什么?白名单、JSON Schema、超时、权限注入
执行循环何时继续、何时停止?最大轮数、最大 token/费用、终止条件
状态与记忆中断后能否恢复?checkpoint、任务状态、短期/长期记忆分层
安全与审批哪些动作必须由人确认?读写权限、敏感操作审批、租户隔离
可靠性工具失败怎么办?重试、退避、幂等键、补偿动作
可观测性出错时能否解释?trace、模型输入输出、工具参数和结果、成本
评测升级模型后是否变好?轨迹评测、任务成功率、工具准确率、回放

从框架到 Harness:状态和环境都要持久化

普通框架往往围绕一次请求、一次调用或一张图组织;Harness 面向的是可能运行数小时、数天甚至跨会话的任务。因此抽象会发生变化:

维度传统框架Harness
核心抽象Chain、Graph、PipelineEpisode、Session、Task
状态模型单次请求或进程内状态持久化会话、Checkpoint、Session Lineage
执行环境调用方进程内Sandbox、Gateway、定时调度和隔离工作区
失败处理抛异常、简单 RetryResume、Rollback、补偿和人工接管
安全模型Prompt Guardrail权限审批、能力隔离、文件/网络/进程审计
可观测性Print 或普通日志结构化 Trace、证据包和审计追踪

这也是 Harness 和普通 Agent 框架的边界:框架让 Agent“能跑起来”,Harness 让 Agent“在真实环境里长期、可恢复、可追责地运行”。

Always-on Agent 的安全边界

当 Agent 从一次性问答变成长时间运行的任务,消息、记忆、技能、调度器和 Shell 可能被折叠到同一个权限边界里,安全风险会明显扩大:

  • Prompt Injection:外部消息、文档或工具结果可能诱导 Agent 改变原始目标。
  • Confused Deputy:Agent 持有合法权限,却被欺骗去执行用户本不想做的操作。
  • 权限逃逸:只在 Shell 层做拦截不够,还要覆盖文件、网络和进程级能力。
  • Checkpoint 泄露:快照和会话记忆可能包含密钥、个人信息或业务数据。

因此高风险工具应采用最小权限、显式审批、作用域隔离和完整审计;“模型说要执行”不能直接等同于“系统允许执行”。

这也是“先做 Harness,再谈 Agent 能力”的原因:没有预算、权限、停止条件和日志的 Agent,演示时很聪明,生产中却难以负责。

七、LangGraph 和 ReAct Agent 到底是什么关系

1. ReAct 是一种 Agent 行为模式

ReAct(Reasoning + Acting)的核心是交替进行推理和行动:

text
用户目标
  → 推理:当前缺什么信息?
  → 行动:调用 search_logs(query=...)
  → 观察:得到日志片段
  → 推理:是否足够?下一步查发布记录还是结束?
  → 行动/最终回答

早期实现常把 Thought / Action / Observation 写在提示词文本中;现在的模型通常通过 tool calling 返回结构化 action,但控制思想没有改变。ReAct 规定的是“模型如何在循环中做决定”,并没有规定 checkpoint、权限、部署或业务状态如何实现。

2. LangGraph 是状态图运行时

LangGraph 把应用表示成:

  • State:任务目标、消息、工具结果、错误、预算等共享状态。
  • Node:一个可执行步骤,可以是普通函数、模型调用或工具调用。
  • Edge:节点之间的固定或条件转移。
  • Checkpointer:保存状态,使任务可以暂停、恢复、重放。
  • Interrupt/Human-in-the-loop:在敏感节点前暂停等待人工决定。

固定流程可以写成 A → B → C,动态 Agent 则写成 model → tool → model 的条件循环:模型返回工具调用就走工具节点,返回最终答案就结束,超过预算则转失败节点。同一个图运行时同时覆盖 Workflow 和 Agent。

3. 两者的对应关系

维度LangGraphReAct Agent
抽象层编排/运行时决策/提示与循环策略
关注点状态、节点、边、恢复、并发、人工介入推理、行动、观察、再推理
是否必须用模型不必须,纯代码图也可以必须依赖模型做下一步决策
是否能表达固定流程不擅长,容易引入不必要的非确定性
是否能表达动态工具调用能,通过条件边和循环是,正是它的典型用途
生产能力可接入 Harness 能力需要外部 Harness 才可靠

因此可以有三种常见组合:

  1. LangGraph + 普通节点:规则审批、数据同步等纯 Workflow。
  2. LangGraph + ReAct 节点:图负责状态和安全边界,ReAct 负责局部动态决策。
  3. 其他运行时 + ReAct:只要能实现工具循环,也可以不用 LangGraph;但要自己补齐持久化、观测和人工介入。

面试中不要说“LangGraph 就是 ReAct”,更准确的说法是:LangGraph 可以承载 ReAct,ReAct 是 LangGraph 中一种常见的 Agent 节点逻辑。

八、一个可落地的混合架构

继续以“故障排查工具”为例。第一版不需要让 Agent 控制全部系统:

text
接收问题
  → 鉴权与租户校验(代码)
  → 结构化故障对象(模型,schema 约束)
  → 选择排查策略(Workflow 条件分支)
      ├─ 已知错误码 → 查知识库 → 生成说明
      ├─ 线上异常 → 进入 ReAct 排查节点
      └─ 高风险操作 → 人工审批
  → 输出带证据的报告(代码校验 + 模型生成)

ReAct 节点只开放只读工具:query_metricssearch_logsget_deployments。它不能直接执行回滚;如果模型提出回滚建议,图会转到 human_approval,审批通过后才调用独立的 rollback_service

一个简化的 LangGraph 风格伪代码如下:

python
class State(TypedDict):
    question: str
    incident: dict
    evidence: list[dict]
    next_step: str
    budget: int

def classify(state):
    incident = extract_incident(state["question"])
    return {"incident": incident}

def route(state):
    if state["incident"].get("error_code"):
        return "known_error"
    if state["incident"].get("service"):
        return "react_investigator"
    return "ask_clarification"

def react_investigator(state):
    # 在 Harness 的工具白名单、轮数和预算内运行 ReAct
    return run_react_loop(state, tools=[query_metrics, search_logs, get_deployments])

graph.add_node("classify", classify)
graph.add_node("known_error", known_error_lookup)
graph.add_node("react_investigator", react_investigator)
graph.add_node("ask_clarification", ask_clarification)
graph.add_conditional_edges("classify", route)

这里的关键不是某个 API 名称,而是职责分离:

  • 外层图决定哪些路径存在、哪些工具可见、何时需要人。
  • Agent 节点决定当前路径下的下一次查询。
  • 工具服务负责真正的权限、参数校验和副作用控制。

九、选择方案的决策表

问题WorkflowAgent推荐判断
步骤是否固定?固定步骤动态步骤固定就选 Workflow
工具数量是否有限且已知?可能动态扩展工具越稳定越偏 Workflow
是否允许重复尝试?代码定义重试模型选择策略失败策略已知就写代码
是否有资金、写库、发消息等副作用?容易加审批需要额外约束高风险动作放在 Workflow/Harness
延迟和成本是否严格?更容易保证可能多轮调用SLA 严格时优先 Workflow
评测能否写成固定断言?容易需要轨迹评测可断言就优先 Workflow
用户问题是否开放?需要大量规则更适合开放目标才引入 Agent

实践中可以采用“确定性优先,动态性局部化”的渐进路线:

  1. 先用 Workflow 验证业务价值,建立输入、输出、错误和评测集。
  2. 找出最难用规则覆盖的一个节点,把它封装成 Agent,并限制工具集合。
  3. 为 Agent 加上 Harness:预算、停止条件、权限、checkpoint、trace 和人工审批。
  4. 用真实轨迹比较 Agent 与规则版本,再决定是否扩大自治范围。

十、常见误区

误区 1:模型参与了,就叫 Agent

抽取、分类、改写等单次 LLM 调用仍然可以是 Workflow 节点。是否是 Agent,关键看模型是否拥有“选择下一步行动”的控制权。

误区 2:用了 LangGraph,就自动获得 Agent 能力

LangGraph 只是编排和状态运行时。图里全是固定节点时,它就是 Workflow;只有模型根据状态选择工具或路径时,才体现 Agent 行为。

误区 3:ReAct 的 Thought 必须暴露给用户

ReAct 是内部决策轨迹。对外应返回可验证的结论、引用和执行摘要,不要把未经审查的内部推理当作产品输出。生产日志也应遵循隐私和最小记录原则。

误区 4:给 Agent 越多工具越强

工具越多,选择空间、错误参数和权限风险越大。应按任务动态暴露最小工具集,并为每个工具定义 schema、超时、幂等和错误契约。

误区 5:只评估最终答案

Agent 可能“碰巧答对”,但走了危险或昂贵的路径。除了答案正确率,还要评估工具选择、参数正确率、轨迹长度、成本、延迟、拒答和越权率。

十一、面试时可以这样完整回答

我会先把需求拆成确定性部分和不确定性部分。确定的步骤、校验、权限和有副作用的操作用 Workflow 或状态图实现;只有需要根据中间结果动态拆解、选择工具和恢复策略的部分才使用 Agent。Agent 本身采用受控的 ReAct 循环,外面用 Harness 管理上下文、工具白名单、预算、最大轮数、checkpoint、日志和人工审批。LangGraph 是承载这套状态图和循环的运行时,它既能实现普通 Workflow,也能承载 ReAct Agent;ReAct 是决策模式,不是 LangGraph 的同义词。上线前我会用固定用例和真实轨迹评测成功率、工具调用正确率、延迟、成本与越权风险,再逐步扩大 Agent 的自治范围。

这段回答体现了四个层次:业务决策、编排方式、Agent 策略和生产运行时。面试官继续追问时,可以分别展开,而不会陷入“框架选型就是架构设计”的误区。

参考资料

本文使用“Workflow、Agent、Harness”的工程化视角整理上述资料;示例代码为说明职责边界的伪代码,具体 LangGraph API 应以项目所使用的版本文档为准。

最后更新时间: