【第三部分:第一个 Agent 应用】12. 使用状态机开发一个可控 Agent

简介: 本文围绕“使用状态机开发一个可控 Agent”展开,以需求分析 Agent 为例,介绍 State、Node、Edge、Conditional Edge、Checkpoint、Interrupt、Resume 与 Human-in-the-Loop 等核心机制,并通过 LangGraph 代码说明如何把自由 Agent Loop 收束为可分支、可循环、可暂停、可恢复的业务流程。核心思想是:状态机负责确定性的流程控制,Agent 负责节点内部的理解、推理、RAG 与 Tool 调用,实现“确定性的骨架 + 概率性的智能”。

上一篇介绍主流 Agent Framework 时,我们已经看到,简单 Agent 的核心仍然是 Model → Action → Observation → Model。模型根据当前上下文判断下一步,调用 Tool 获取结果,再结合新的 Observation 继续决策。

这种自由循环非常适合搜索、研究和 Coding 等开放式任务,但进入需求分析、合同审核、审批、财务操作等企业场景后,系统关注的问题开始发生变化:Agent 当前执行到哪里?哪些步骤不能跳过?一个节点最多允许重复多少次?服务重启以后怎样继续?什么时候必须让人确认?

例如一个需求分析 Agent,需要完成需求提取、完整性检查、生成、质量评价、修改和人工确认。如果全部继续写在一个巨大的 while + if + else 中,代码很快就会变得难以维护。

因此,复杂业务 Agent 更适合采用一个基本原则:状态机负责控制任务如何流转,Agent 负责解决节点内部无法完全写死的智能问题。

这一篇就围绕一个“需求分析 Agent”,看看如何通过 State、Node、Edge、Checkpoint、Interrupt 和 Human-in-the-Loop,真正开发一个更加可控的 Agent。


一、为什么生产系统不能只依赖自由 Agent Loop

上一篇手写的最小 Agent,本质上是:

while not finished:
    response = model(messages)
    if response.tool_calls:
        result = execute_tool(response.tool_calls)
        messages.append(result)
    else:
        finished = True

image.gif

这种方式最大的优势是灵活:模型可以决定调用哪个 Tool、是否继续检索以及什么时候结束。问题也恰恰来自这种自由度。

维度 自由 Agent Loop 状态机 Agent
下一步由谁决定 主要由模型动态判断 State 和 Edge 限定
执行路径 开放、动态 显式、可预测
循环方式 Agent 自主继续 条件和次数明确
中断恢复 需要额外实现 天然适合 Checkpoint
人工审批 需要嵌入 Loop 可以成为正式 Node
适合场景 Coding、研究、搜索 需求、合同、审批、业务流程

现实中的生产 Agent 通常并不是二选一,而是:外层使用确定性的 State Machine 控制业务边界,节点内部继续使用 LLM / Agent 处理不确定问题。


二、先定义 State:把任务状态从 Messages 中拿出来

LangGraph 当前的核心抽象就是 StateNodeEdge:State 表示应用当前快照,Node 读取并更新 State,Edge 根据当前状态决定下一步执行什么。对于需求分析 Agent,我们先定义真正影响流程的数据:

from typing import TypedDict
class RequirementState(TypedDict):
    raw_requirement: str
    extracted_data: dict
    missing_fields: list[str]
    requirements: list[dict]
    quality_score: int
    revision_count: int
    human_decision: str

image.gif

这些字段分别回答:

State 字段 表达的状态
raw_requirement 用户原始需求
extracted_data 已提取的结构化信息
missing_fields 仍然缺少什么
requirements 当前生成的需求条目
quality_score 当前质量得分
revision_count 已经自动修改多少次
human_decision 人工最终决策

相比上一篇只维护:messages = [...]。这里最大的变化不是多定义了几个变量,而是:任务的业务状态被显式建模了。即使模型上下文重新组装,系统仍然知道任务执行到了哪里。


三、Node:让 LLM 只负责节点内部的智能问题

有了 State 以后,把需求分析过程拆成几个职责明确的 Node:

extract
check
generate
review
revise
human_approval

image.gif

每个 Node 接收当前 State,只返回自己产生的更新。LangGraph 的 Node 本质上就是这种读取当前 State、执行计算并返回部分 State Update 的函数。例如需求提取节点:

def extract_requirement(state: RequirementState):
    extracted = llm_extract(
        state["raw_requirement"]
    )
    return {
        "extracted_data": extracted
    }

image.gif

llm_extract() 代表我们封装好的模型调用,这里不展开具体 Provider API。完整性检查则更适合优先使用确定性代码:

REQUIRED_FIELDS = [
    "role",
    "goal",
    "input",
    "output"
]
def check_completeness(state: RequirementState):
    data = state["extracted_data"]
    missing = [
        field
        for field in REQUIRED_FIELDS
        if not data.get(field)
    ]
    return {
        "missing_fields": missing
    }

image.gif

这里已经体现出一个非常重要的分工:

LLM 负责从自然语言中理解:用户角色是什么?目标是什么?输入输出是什么?

程序 负责检查:这些必填字段到底有没有。

生产 Agent 不应该为了体现“智能”而把这种确定性判断也重新交给模型。


四、Conditional Edge:模型可以判断,但程序决定怎样跳转

状态机真正开始发挥价值,是在条件分支出现以后。检查完需求完整性,我们需要决定:

  • 信息完整 → 继续生成;
  • 信息不足 → 请求用户补充。

可以定义一个非常简单的路由函数:

def route_completeness(state: RequirementState):
    if state["missing_fields"]:
        return "ask_user"
    return "generate"

image.gif

再把它注册成 Conditional Edge:

builder.add_conditional_edges(
    "check",
    route_completeness,
    {
        "ask_user": "ask_user",
        "generate": "generate"
    }
)

image.gif

LangGraph 当前的 add_conditional_edges() 就是根据一个 routing function 的结果动态选择下一节点。这与自由 Agent Loop 有一个非常重要的区别。

自由 Agent 可能是:“模型,你觉得下一步该怎么办?”

状态机则变成:“系统只允许你在这几个合法路径中继续。”

因此:模型可以参与判断,但最终允许走哪些路径,由程序定义。


五、需求生成和质量检查:把智能留在真正需要智能的地方

需求生成显然更适合 LLM:

def generate_requirement(state: RequirementState):
    requirements = llm_generate(
        state["extracted_data"]
    )
    return {
        "requirements": requirements
    }

image.gif

质量评价也可以使用模型,例如从完整性、清晰度、可测试性和一致性几个维度打分:

def review_quality(state: RequirementState):
    score = llm_review(
        state["requirements"]
    )
    return {
        "quality_score": score
    }

image.gif

但“评分以后怎么办”,不应该继续让模型自由决定。例如:

MAX_REVISIONS = 3
def route_quality(state: RequirementState):
    if state["quality_score"] >= 80:
        return "human_approval"
    if state["revision_count"] >= MAX_REVISIONS:
        return "human_approval"
    return "revise"

image.gif

这样即使模型连续给出:72 → 76 → 79 → 79。系统也不会无限进入:“再优化一次看看。”第三次仍未达标,就转人工。这体现了状态机 Agent 最核心的工程思想:模型决定怎么修改,Runtime 决定最多允许修改多少次。


六、把这些 Node 真正组装成一个 State Graph

有了 State、Node 和路由函数以后,整个 Agent 的核心结构已经可以写出来:

from langgraph.graph import (
    StateGraph,
    START,
    END
)
builder = StateGraph(RequirementState)
builder.add_node(
    "extract",
    extract_requirement
)
builder.add_node(
    "check",
    check_completeness
)
builder.add_node(
    "ask_user",
    ask_user
)
builder.add_node(
    "generate",
    generate_requirement
)
builder.add_node(
    "review",
    review_quality
)
builder.add_node(
    "revise",
    revise_requirement
)
builder.add_node(
    "human_approval",
    human_approval
)
builder.add_edge(
    START,
    "extract"
)
builder.add_edge(
    "extract",
    "check"
)

image.gif

再把几个关键分支接起来:

builder.add_conditional_edges(
    "check",
    route_completeness,
    {
        "ask_user": "ask_user",
        "generate": "generate"
    }
)
builder.add_edge(
    "generate",
    "review"
)
builder.add_conditional_edges(
    "review",
    route_quality,
    {
        "revise": "revise",
        "human_approval": "human_approval"
    }
)
builder.add_edge(
    "revise",
    "review"
)
builder.add_edge(
    "human_approval",
    END
)

image.gif

到这里,文章标题里的“使用状态机开发 Agent”已经真正落到了代码上。状态图不再只是架构图,而是直接成为应用运行逻辑。LangGraph 的 StateGraph 也正是按照“定义 State → 添加 Nodes → 添加 Edges → compile”的方式构建和运行 Graph。

image.gif


七、状态机不是固定 Workflow:Node 内部依然可以运行 Agent

使用 StateGraph 并不意味着 Agent 被退化成普通 BPM Workflow。例如 generate Node 内部完全可以继续执行:

读取当前 State
+ RAG 检索业务知识
+ 调用需求模板 Skill
+ LLM 生成

image.gif

甚至可以封装一个局部 Agent:

def generate_requirement(state):
    result = requirement_agent.run(
        goal="生成结构化需求",
        context=state["extracted_data"],
        tools=[
            search_knowledge,
            load_requirement_template
        ]
    )
    return {
        "requirements": result
    }

image.gif

于是整体架构实际变成:外层 State Machine,内层 Agent Loop。

状态机决定:什么时候生成;生成以后去哪;是否允许重新生成;最多重新生成多少次。

Agent 决定:需要查什么资料;调什么 Tool;如何组织最终内容。

这就是“确定性的骨架 + 概率性的智能”。


八、Checkpoint:让 Agent 不再依赖一次请求活到底

到目前为止,这个 Graph 仍然可以一次跑完。但真实需求分析通常会遇到:用户几个小时以后才补充需求;审批人第二天才确认;服务升级;Worker 重启;长任务执行失败。这时就需要 Checkpoint。

LangGraph 支持在 compile Graph 时加入 checkpointer,用于保存 Graph State;官方当前示例使用 InMemorySaver 做内存级 Checkpoint,同时也明确指出生产环境应该使用 PostgreSQL、SQLite 等持久化 Saver。示例:

from langgraph.checkpoint.memory \
    import InMemorySaver
checkpointer = InMemorySaver()
graph = builder.compile(
    checkpointer=checkpointer
)

image.gif

调用任务时给它一个稳定的 thread_id

config = {
    "configurable": {
        "thread_id": "requirement-1001"
    }
}
graph.invoke(
    initial_state,
    config=config
)

image.gif

于是:requirement-1001。不再只是一次 HTTP Request,而是一个可以继续恢复的任务实例。生产环境里,InMemorySaver 只是示例,真正部署时应换成持久化 Checkpointer。


九、Interrupt + Resume:真正实现“等待用户”和“人工审批”

需求不完整时:请求用户补充。人工审核时:等待 Approve / Edit / Reject。这两类场景都不应该通过线程等待。LangGraph 当前提供 interrupt() 暂停节点执行,并依赖 Checkpointer 保存当前状态;后续可以通过 Command(resume=...) 使用相同 thread_id 恢复。例如人工审核节点:

from langgraph.types import interrupt
def human_approval(state: RequirementState):
    decision = interrupt({
        "type": "requirement_approval",
        "requirements": state["requirements"],
        "quality_score": state["quality_score"]
    })
    return {
        "human_decision": decision
    }

image.gif

执行到这里后,Graph 会暂停。审批人稍后提交:APPROVE。系统再恢复:

from langgraph.types import Command
graph.invoke(
    Command(resume="APPROVE"),
    config=config
)

image.gif

如果审批结果是:EDIT。或者:REJECT。则可以继续通过 Conditional Edge 路由到 Review 或 Revise。需要特别注意:LangGraph 当前的 interrupt 在恢复时会从当前 Node 开头重新执行,因此 interrupt 之前的副作用应该保持幂等,或者拆到独立 Node 中。这个细节很重要,因为它直接关系到:邮件会不会发两次?数据库会不会插入两条记录?审批任务会不会重复创建?


十、Checkpoint、Interrupt、Resume 最好理解成一个生命周期

这一部分不需要再用长数据流表示,可以压缩成三个阶段:

阶段 作用
Checkpoint 保存当前 State
Interrupt 暂停任务并释放执行资源
Resume 恢复 State 后继续执行

典型场景包括:等待用户补充需求;等待审批;等待外部系统回调;服务升级与任务恢复;长任务阶段性执行。 image.gif


十一、State 和 Memory 不要混在一起

引入 Checkpoint 后,很容易把 State 与后续文章要介绍的 Memory 混淆。

State Memory
范围 当前任务 跨任务、跨会话
解决的问题 运行到哪里 长期应该记住什么
示例 当前评分 85 用户偏好敏捷模板
生命周期 Task 生命周期 可以长期保存
主要用途 Checkpoint / Resume 个性化、经验复用

例如:

quality_score = 85
revision_count = 2
human_decision = ""

image.gif

属于 Task State。而:

用户习惯输出三级需求
用户偏好使用敏捷模板

image.gif

属于长期 Memory。因此:Checkpoint 解决任务恢复,不等于 Agent 已经拥有长期记忆。


十二、自由 Harness 和 State Graph 是两条不同路线

不是所有 Agent 都应该画成状态图。Coding、研究、搜索任务往往路径非常开放,Agent 可能不断读取代码、运行测试、观察错误、重新修改。Pi 这类 minimal harness 更强调保持自由 Loop,再通过扩展能力增强;DeepSeek Harness 则通过 Session/Event Log 记录模型消息、Tool Call 和 Tool Result,使自由 Agent 同样能够恢复和重放。可以简单比较:

State Graph Harness / Event Log
核心 预定义合法路径 记录实际执行轨迹
自由度 相对较低 相对较高
适合 业务流程 Coding、研究、探索
状态 显式业务 State Session / Event
恢复 Checkpoint Session Replay / Resume
共同点 持久化、恢复、可观测 持久化、恢复、可观测

因此关键不是:LangGraph 还是 Harness?而是:任务本身是流程型,还是探索型?


十三、企业 Agent 更值得采用“两层控制”

综合前面的代码和设计,一个企业 Agent 更实用的结构通常是两层。

确定性控制层 智能执行层
State LLM
Node / Edge RAG
Checkpoint Tool Calling
Approval Reasoning
Timeout / Retry 内容分析
Permission / Recovery 内容生成

外层 Workflow 负责:哪些路径合法、什么时候暂停、什么时候结束。

内层 Agent 负责:节点内部具体怎样解决问题。

image.gif

确定性的骨架 + 概率性的智能


十四、什么时候值得引入状态机

状态机也不是越多越好。

任务特征 推荐方式
搜索、研究、Coding Agent Loop
简单 Tool Calling Agent Loop
简单 RAG 问答 Agent Loop / Workflow
明确业务步骤 State Machine
多分支、多循环 State Machine
人工审批 State Machine
长任务与恢复 Durable State Runtime
复杂企业任务 State Machine + Agent

如果任务只有:查询天气 → 计算 → 回答。完全没有必要引入 StateGraph。

但当出现:分支 + 循环 + Checkpoint + Human Approval + Resume。状态机的价值就开始明显。


十五、小结

上一篇解决的是:主流框架替我们封装了哪些 Agent 能力?这一篇进一步通过实际代码回答:怎样用状态机开发一个真正可控的 Agent?整个需求分析 Agent 最终可以概括成四层:

第一层:State 保存任务当前事实。

第二层:Node 让程序和 LLM 分别完成适合自己的工作。

第三层:Edge 把模型的不确定判断转换成明确的流程路由。

第四层:Checkpoint / Interrupt / Resume 让任务能够暂停、等待人工并可靠恢复。

最终形成的不是纯 Workflow,也不是完全自由的 Agent:状态机负责控制任务如何流转,Agent 负责解决节点内部无法完全写死的智能问题。也就是:确定性的骨架 + 概率性的智能。

对于需求分析 Agent 来说,模型可以理解自然语言、生成需求和评价质量;状态机则决定什么时候补充信息、什么时候修改、最多修改几次以及什么时候必须交给人确认。这也是 Agent 从 Demo 走向真正业务系统的一次重要变化:生产级 Agent 的目标不是拥有无限自主权,而是在明确、可恢复、可审计的边界内完成任务。


上一篇回顾:

https://developer.aliyun.com/article/1758517?spm=a2c6h.13148508.setting.16.2a9a4f0eKImYFP

下一篇将进入为 Agent 增加记忆能力

因为 State Machine 解决的是:这一次任务执行到哪里。而 Memory 要解决的是:跨会话、跨任务以后,Agent 应该记住什么,以及什么时候应该忘掉什么。

相关文章
|
3天前
|
人工智能 自然语言处理 安全
阿里云AI数智鉴密:AI 生成内容如何拿到一张"防篡改的身份证"
隐形水印 + C2PA签名:让AI生成内容“持证上岗”。
1105 0
|
12天前
|
人工智能 自然语言处理 安全
阿里云千问办公、Qoder Teams、Qoder CN区别与选择指南:模型能力、适用场景与最新活动参考
本文聚焦阿里云2026年推出的三款自研AI办公产品,清晰拆解千问办公、Qoder Teams、Qoder CN的差异化定位与能力边界:千问办公主打职场全场景提效,支持自然语言指令一键完成PPT生成、数据分析等高频办公任务;Qoder Teams面向程序员团队,深度整合AI代码生成、团队协同与企业知识库能力;Qoder CN则专为金融、政务等强合规场景打造,实现数据不出境与VPC私有化部署。文章同步给出分场景选型指南与最新活动定价,帮助不同类型的企业按需组合产品,实现业务岗、研发岗与强合规场景的AI能力全覆盖。
3697 3
阿里云千问办公、Qoder Teams、Qoder CN区别与选择指南:模型能力、适用场景与最新活动参考
|
24天前
|
人工智能 缓存 前端开发
DeepSeek Harness 首发实测 + 入门教程,夯爆了!梁神我错了
DeepSeek Harness + DeepSeek V4 Pro 项目实战保姆级教程!手把手带你从零安装开源 AI 编程工具,开发架构图、知识讲解网站、3D 网页游戏、全栈 AI 应用 4 个项目,覆盖运行模式选择、插件安装与开发,看看能不能对标 Claude。
13494 93
DeepSeek Harness 首发实测 + 入门教程,夯爆了!梁神我错了
|
17天前
|
Web App开发 人工智能 API
16 个超火的 DeepSeek Harness 插件,大肥鱼已经落后 N 个版本了。。。
DeepSeek Harness 精选插件推荐合集,从图片识别、浏览器操控、多 Agent 协作到手机远程控制,一口气带你看完 DSH 社区热门的十几个插件,覆盖技能扩展、UI 界面增强、整活玩法三大类,让你的鲸鱼变得更强。
1963 5
|
3天前
|
人工智能 运维 BI
阿里云千问办公QwenWork深度解析:基于Qwen3.8,六大核心能力重构企业全自动化工作流与计费选型指南
传统AI办公工具大多停留在对话问答、文档摘要、简单文案生成层面,只能完成单点碎片化任务,无法自主拆解复杂业务流程,很难串联多工具、多文档、外部业务系统完成端到端完整工作交付。很多企业在落地AI办公的时候,需要组合多款不同工具,来回切换界面,手动复制粘贴中间结果,智能化改造落地门槛居高不下。千问办公QwenWork是整合多款智能体产品能力打造的一体化企业办公智能体平台,底层基座依托Qwen3.8大模型,打通桌面端Agent、云端Agent、企业协同Agent三种运行形态,不再局限简单问答,接收业务目标之后自主拆解任务步骤,调用各类工具,处理文档、表格、浏览器自动化、数据查询,直接输出可交付的办公
950 0
|
13天前
|
人工智能 Linux iOS开发
Ollama使用教程:Ollama官网下载、Ollama本地部署大模型(2026最新)
Ollama 是一款免费开源的本地大模型运行工具,支持在 Windows/macOS/Linux 上离线运行 Qwen、DeepSeek、Llama 等主流开源模型,数据不出本机、隐私安全。提供 OpenAI 兼容 API,命令行一键拉取/运行/管理模型,无需联网,无调用限制,是开发者与 AI 爱好者部署本地 AI 助手的理想选择。(239 字)
|
9天前
|
人工智能 并行计算 数据可视化
秋叶ComfyUI-AKI最新整合包|完整部署教程+核心指令手册
秋叶ComfyUI-AKI一键整合包,国内适配最优、稳定性最强的商用/学习级版本:全封装虚拟环境、预装90%常用节点、内置绘世启动器与成熟工作流,免配置、零依赖、解压即用,完美兼顾新手入门与专业批量生产需求。(239字)
|
10天前
|
人工智能 监控 测试技术
Qwen3.8-Flash 来了,100万上下文、Agent、Coding 都加强了
8月26日,通义千问发布Qwen3.8-Flash-Next:125B参数、每Token仅激活6B,原生支持26万Token、可扩展至100万上下文;Coding、Agent与工具调用能力显著增强,面向真实软件工程任务,推动大模型从“回答问题”迈向“完成工作”。

热门文章

最新文章