跳转到内容
123xiao | 无名键客

《从 Prompt Engineering 到 Agent Workflow:中级开发者构建可落地 AI 自动化流程的实践指南》

字数: 0 阅读时长: 1 分钟

从 Prompt Engineering 到 Agent Workflow:中级开发者构建可落地 AI 自动化流程的实践指南

很多开发者第一次接触大模型时,都是从“写一个好 Prompt”开始的:
给模型一段上下文,补几条约束,再加几个示例,效果立刻就比“裸问”强不少。

但只要你尝试把它放进真实业务,就会很快遇到几个问题:

  • 单次 Prompt 效果不错,但流程一长就不稳定
  • 模型会“看起来很懂”,但关键步骤并不可靠
  • 一旦接入外部系统,问题从“回答得好不好”升级为“能不能执行、能不能回滚、能不能审计
  • 业务方想要的不是一个聪明聊天窗口,而是一个可落地、可监控、可维护的 AI 自动化流程

这时,你会发现:
Prompt Engineering 很重要,但它只是起点;真正让 AI 产生业务价值的,是 Agent Workflow。

这篇文章我会从中级开发者的视角,把这条路径拆开讲清楚:
从提示词设计,到任务拆分、工具调用、状态管理、异常处理,最后形成一个能跑起来的自动化工作流。


背景与问题

为什么“只会写 Prompt”还不够

Prompt Engineering 解决的是:如何让模型更好地完成一次推理
而 Agent Workflow 解决的是:如何让模型在一个多步骤流程中,稳定地完成任务并与系统协作

可以把两者理解成:

  • Prompt Engineering:偏“单次交互优化”
  • Agent Workflow:偏“端到端任务编排”

比如一个“自动处理客服工单”的需求:

  1. 读取用户工单内容
  2. 判断意图与优先级
  3. 查询知识库
  4. 如果缺信息,自动生成追问
  5. 如果是退款类问题,调用订单系统核验
  6. 生成最终回复
  7. 记录处理日志和置信度

这里已经不是一个 Prompt 能优雅解决的事情了。
你需要的是:

  • 明确的步骤划分
  • 可控的工具调用
  • 中间状态存储
  • 错误兜底策略
  • 人工介入节点
  • 可观测性与审计

中级开发者最容易卡住的地方

我见过不少团队在这一步翻车,原因通常不是模型不够强,而是工程设计太“聊天化”:

  • 把整个任务塞进一个超长 Prompt
  • 期待模型自己“规划、执行、纠错、总结”
  • 没有结构化输出约束
  • 工具调用没有超时、重试、幂等设计
  • 日志只记最终答案,不记中间推理和动作
  • 没有区分“模型擅长判断”与“系统必须确定执行”的边界

所以,进入 Agent Workflow 之前,得先建立一个原则:

让模型负责智能判断,让系统负责确定性执行。


核心原理

这一部分是整篇文章的骨架。你只要把这几个原则抓住,后面的工程实现就顺了。

1. 从“生成回答”转向“完成任务”

Prompt 的核心目标通常是“输出一段尽可能好的文本”。
Agent Workflow 的核心目标则是“在给定约束下完成业务任务”。

这意味着你设计系统时,需要先问:

  • 任务的输入是什么?
  • 任务的完成标准是什么?
  • 哪些步骤必须确定性执行?
  • 哪些步骤适合交给模型判断?
  • 失败后如何补救?

2. Agent 不等于“一个会自己行动的大模型”

更准确的理解是:

Agent = 大模型 + 工具集 + 工作流状态 + 执行策略

它不是神奇黑箱,而是一个被编排的系统组件。

一个可落地的 Agent Workflow,通常至少包含:

  • Planner / Router:决定任务路径
  • Tool Executor:调用外部系统
  • Memory / State Store:保存上下文与中间结果
  • Validator:校验输出是否合法
  • Fallback / Human-in-the-loop:异常时切换兜底逻辑

3. Prompt 的角色发生了变化

在单轮问答中,Prompt 是主角。
在工作流中,Prompt 更像是某个节点的“行为配置”。

也就是说,你不该追求“一个万能 Prompt”,而应该为不同节点设计不同 Prompt:

  • 分类节点 Prompt
  • 信息抽取节点 Prompt
  • 工具选择节点 Prompt
  • 回复生成节点 Prompt
  • 审核节点 Prompt

4. 工作流优先于自由自治

很多人一上来就想做“全自动自主 Agent”,但现实里更推荐的是:

  • 先做固定流程
  • 再做条件分支
  • 再做有限自治
  • 最后才考虑开放式规划

因为开放式自治虽然酷,但也是最难测、最难控、最难审计的。


一张图看懂:从 Prompt 到 Workflow 的升级路径

flowchart LR
    A[单次 Prompt 调用] --> B[结构化输出]
    B --> C[多步骤链式处理]
    C --> D[接入工具调用]
    D --> E[引入状态管理]
    E --> F[异常重试与回滚]
    F --> G[可观测/审计/人工介入]
    G --> H[可落地 Agent Workflow]

这条路径非常符合大多数团队的演进规律。
不要跳级,真的。很多坑我都是在“试图一步到位”时踩出来的。


一个实用设计范式:把任务拆成 4 层

为了避免系统越写越乱,我通常建议用下面这个分层思路:

第 1 层:输入规范化

把原始用户输入转成结构化上下文:

  • 用户原文
  • 渠道
  • 时间
  • 历史记录
  • 业务标识
  • 附件摘要

第 2 层:语义决策

交给模型做擅长的判断,例如:

  • 意图分类
  • 紧急程度识别
  • 是否需要补充信息
  • 该走哪个流程分支

第 3 层:确定性执行

交给程序和外部系统做:

  • 查数据库
  • 调订单服务
  • 写 CRM
  • 发邮件 / 发消息
  • 记录审计日志

第 4 层:结果生成与校验

再由模型负责组织语言,但输出前必须经过:

  • 字段校验
  • 业务规则校验
  • 敏感词/越权内容校验

工作流时序示意

sequenceDiagram
    participant U as 用户
    participant API as 业务服务
    participant LLM as 大模型
    participant KB as 知识库
    participant OMS as 订单系统
    participant LOG as 日志系统

    U->>API: 提交工单
    API->>LLM: 意图分类/信息抽取
    LLM-->>API: 结构化结果
    API->>KB: 检索相关知识
    KB-->>API: 文档片段
    API->>OMS: 查询订单状态
    OMS-->>API: 订单详情
    API->>LLM: 基于上下文生成回复
    LLM-->>API: 回复草稿
    API->>LOG: 记录中间状态与结果
    API-->>U: 返回处理结果

注意这个图里有个关键点:
模型不直接操作业务系统,而是由 API 层控制调用。

这能显著提升安全性、可测性和回放能力。


核心原理落地:一个最小可用 Agent Workflow

下面我们用 Python 写一个可运行的最小示例:

场景:自动处理简化版售后工单

目标:

  1. 识别工单类型
  2. 如果是退款,查询订单
  3. 如果信息不足,要求补充
  4. 生成最终回复
  5. 输出完整处理结果

为了便于你直接运行,我这里用“模拟 LLM”方式演示工作流骨架。
如果你要接真实模型,把 mock_llm_* 函数换成实际 API 调用即可。


实战代码(可运行)

目录中的核心代码

import json
from dataclasses import dataclass, asdict
from typing import Optional, Dict, Any


@dataclass
class Ticket:
    user_id: str
    ticket_id: str
    message: str


@dataclass
class WorkflowState:
    intent: Optional[str] = None
    urgency: Optional[str] = None
    needs_more_info: bool = False
    order_id: Optional[str] = None
    order_status: Optional[str] = None
    final_reply: Optional[str] = None
    error: Optional[str] = None


# ---------- 模拟 LLM ----------
def mock_llm_classify(message: str) -> Dict[str, Any]:
    text = message.lower()
    result = {
        "intent": "other",
        "urgency": "low",
        "needs_more_info": False,
        "order_id": None
    }

    if "退款" in message or "refund" in text:
        result["intent"] = "refund"
        result["urgency"] = "medium"

    if "尽快" in message or "马上" in message:
        result["urgency"] = "high"

    # 简单提取订单号:假设格式为 ORD123
    import re
    match = re.search(r"ORD\d+", message)
    if match:
        result["order_id"] = match.group(0)

    if result["intent"] == "refund" and not result["order_id"]:
        result["needs_more_info"] = True

    return result


def mock_llm_reply(context: Dict[str, Any]) -> str:
    if context["needs_more_info"]:
        return "您好,为了帮您处理退款申请,请补充订单号(例如 ORD123)。"

    if context["intent"] == "refund":
        status = context.get("order_status", "未知")
        return f"您好,已收到您的退款请求。当前订单状态为:{status}。我们将按售后流程继续处理。"

    return "您好,已收到您的问题,我们会尽快为您处理。"


# ---------- 模拟工具 ----------
def query_order_system(order_id: str) -> Dict[str, str]:
    mock_db = {
        "ORD100": {"status": "已支付"},
        "ORD200": {"status": "已发货"},
        "ORD300": {"status": "已完成"},
    }
    return mock_db.get(order_id, {"status": "订单不存在"})


# ---------- 工作流 ----------
def process_ticket(ticket: Ticket) -> WorkflowState:
    state = WorkflowState()

    try:
        # Step 1: LLM 分类与信息抽取
        classify_result = mock_llm_classify(ticket.message)
        state.intent = classify_result["intent"]
        state.urgency = classify_result["urgency"]
        state.needs_more_info = classify_result["needs_more_info"]
        state.order_id = classify_result["order_id"]

        # Step 2: 条件分支 - 查询订单
        if state.intent == "refund" and state.order_id and not state.needs_more_info:
            order_result = query_order_system(state.order_id)
            state.order_status = order_result["status"]

        # Step 3: 生成回复
        state.final_reply = mock_llm_reply({
            "intent": state.intent,
            "urgency": state.urgency,
            "needs_more_info": state.needs_more_info,
            "order_id": state.order_id,
            "order_status": state.order_status
        })

    except Exception as e:
        state.error = str(e)
        state.final_reply = "系统正在处理中,请稍后再试或联系人工客服。"

    return state


if __name__ == "__main__":
    tickets = [
        Ticket(user_id="U1", ticket_id="T1", message="我要申请退款,订单号是 ORD200"),
        Ticket(user_id="U2", ticket_id="T2", message="我要退款,请尽快处理"),
        Ticket(user_id="U3", ticket_id="T3", message="你们这个商品怎么开发票?"),
    ]

    for t in tickets:
        result = process_ticket(t)
        print(f"\n=== Ticket {t.ticket_id} ===")
        print(json.dumps(asdict(result), ensure_ascii=False, indent=2))

运行结果你应该关注什么

不是只看“有没有回复”,而是看这些字段是否完整:

  • intent
  • urgency
  • needs_more_info
  • order_id
  • order_status
  • final_reply
  • error

这就是从“文本输出”走向“结构化状态机”的第一步。


为什么这段代码比“一个大 Prompt”更靠谱

因为它把问题拆开了:

  • 分类与抽取:交给模型
  • 订单查询:交给工具
  • 回复生成:再交给模型
  • 异常处理:交给程序

这就是工程上的边界感。

如果你把这些全塞进一个 Prompt,模型可能会:

  • 编造订单状态
  • 忘记缺少订单号时应该追问
  • 输出格式不稳定
  • 在长上下文中漏掉关键条件

进一步升级:加入状态机思维

当工作流复杂起来时,我建议你显式维护状态,而不是隐式拼 Prompt。

stateDiagram-v2
    [*] --> Received
    Received --> Classified
    Classified --> NeedMoreInfo: 信息不足
    Classified --> QueryOrder: 退款且有订单号
    Classified --> DraftReply: 普通咨询
    QueryOrder --> DraftReply
    NeedMoreInfo --> WaitingUser
    DraftReply --> Validated
    Validated --> Done
    Validated --> HumanReview: 校验失败
    HumanReview --> Done

一旦你开始用状态图思考,就会自然考虑:

  • 哪些状态可重试?
  • 哪些状态必须人工介入?
  • 哪些状态需要持久化?
  • 哪些状态变更要写审计日志?

这也是把 Demo 变成生产系统的关键一步。


Prompt Engineering 在 Workflow 里该怎么写

不是不要 Prompt,而是要节点化设计

例 1:分类节点 Prompt

你是售后工单分类助手。
请根据用户输入识别以下字段,并只输出 JSON:
- intent: refund / invoice / logistics / other
- urgency: low / medium / high
- needs_more_info: true / false
- order_id: 字符串或 null

规则:
1. 如果用户表达退款诉求,则 intent=refund
2. 如果退款但缺少订单号,则 needs_more_info=true
3. 不要输出解释,不要输出 markdown

例 2:回复生成节点 Prompt

你是客服回复助手。
请根据提供的结构化上下文,生成简洁、礼貌、可直接发送给用户的中文回复。

要求:
1. 不得编造订单信息
2. 如果信息不足,优先要求补充
3. 语气自然,不要过度承诺

中级开发者该有的一个习惯

每个 Prompt 只做一件事。

如果一个 Prompt 同时承担:

  • 分类
  • 检索判断
  • 工具选择
  • 回复生成
  • 合规审查

那它大概率会变成你未来最难维护的一坨配置。


常见坑与排查

这一节非常重要。很多系统不是设计错了,而是排查手段太弱。

坑 1:输出不稳定,JSON 经常解析失败

现象

模型有时输出 JSON,有时前面加解释,有时字段拼错。

原因

  • Prompt 约束不够强
  • 没有做 schema 校验
  • 把“自然语言回复”与“机器可解析结果”混在一起

排查与修复

  • 强制模型只输出 JSON
  • 对输出做 json.loads + schema 校验
  • 如果失败,走一次自动修复或重试
  • 最好把“结构化决策”和“自然语言生成”拆成两个调用

示例校验代码:

def validate_classify_result(data: dict) -> bool:
    required_keys = ["intent", "urgency", "needs_more_info", "order_id"]
    for key in required_keys:
        if key not in data:
            return False
    if data["intent"] not in ["refund", "invoice", "logistics", "other"]:
        return False
    if data["urgency"] not in ["low", "medium", "high"]:
        return False
    if not isinstance(data["needs_more_info"], bool):
        return False
    return True

坑 2:模型“看起来会”,但工具调用很乱

现象

模型选错工具、漏传参数、重复调用。

原因

  • 工具定义不清晰
  • 没有限制调用条件
  • 没有幂等设计
  • 没有调用日志

建议

  • 工具接口一定要小而清晰
  • 参数必须有类型和默认值
  • 每次调用都记录 tool_name / input / output / duration / status
  • 对高风险动作加确认门槛

坑 3:上下文越来越长,效果越来越差

现象

流程多跑几轮后,模型开始遗漏条件、答非所问、成本暴涨。

原因

  • 把所有历史对话都原样塞进去
  • 没有摘要机制
  • 没有按任务阶段裁剪上下文

建议

保留三类上下文即可:

  • 当前任务必需字段
  • 最近相关交互
  • 关键事实摘要

而不是“能塞多少塞多少”。


坑 4:异常处理只做了 try/except

现象

出了问题时只能返回“系统繁忙”。

更好的做法

区分异常类型:

  • 模型调用超时
  • 工具调用失败
  • 输出校验失败
  • 业务规则冲突
  • 权限不足

不同异常走不同兜底策略,而不是一把抓。


坑 5:把 Prompt 调优当成唯一优化手段

这是我自己早期最容易犯的错。
效果不好时,总想着“再改几句 Prompt”。

但很多时候真正的问题是:

  • 流程拆分不合理
  • 工具返回信息不足
  • 状态字段设计不完整
  • 校验规则缺失

Prompt 优化有用,但不能替代流程设计。


安全/性能最佳实践

AI 工作流一进业务系统,安全和性能就不是附属品,而是主线。

1. 最小权限原则

不要让模型直接拥有数据库写权限、支付权限、退款权限。

正确做法:

  • 模型提出建议
  • 服务端校验
  • 工具层按白名单执行
  • 高风险操作必须人工确认

2. 防 Prompt Injection

尤其是接入网页、邮件、知识库、用户上传文档时,要警惕外部内容污染提示词。

建议:

  • 将用户内容与系统指令物理隔离
  • 对外部文本做清洗和截断
  • 不允许外部文本覆盖系统角色定义
  • 工具调用必须经过服务端授权判定

3. 输出校验先于执行

模型输出任何用于执行的内容前,都必须经过程序校验:

  • 参数类型
  • 枚举值
  • ID 格式
  • 权限范围
  • 业务规则

4. 超时、重试、熔断要补齐

典型建议:

  • LLM 调用设置超时
  • 工具调用做指数退避重试
  • 外部依赖失败时熔断
  • 给每个任务分配 trace id

5. 成本控制要前置

Agent Workflow 比单次问答更容易“悄悄烧钱”。

建议监控这些指标:

  • 单任务 token 消耗
  • 平均工具调用次数
  • 每个节点延迟
  • 重试率
  • 人工接管率
  • 任务成功率

6. 可观测性比“聪明”更重要

线上系统最怕的不是偶发错误,而是“出错了但你不知道为什么”。

建议每次任务都记录:

  • 输入摘要
  • 节点执行顺序
  • 每个节点的模型输入/输出摘要
  • 工具调用结果
  • 状态变更
  • 最终决策来源

一个更贴近生产的工程建议

如果你准备把这类工作流真正上线,我推荐按下面的最小架构来搭:

flowchart TD
    A[客户端/业务入口] --> B[Workflow Orchestrator]
    B --> C[Prompt Node: 分类]
    B --> D[Tool Node: 查询订单]
    B --> E[Prompt Node: 回复生成]
    B --> F[Validator Node]
    B --> G[Human Review Node]
    B --> H[State Store]
    B --> I[Logs & Metrics]

这个架构的好处

  • 节点职责清晰
  • 每一步都能单测
  • 方便局部替换模型或工具
  • 利于灰度发布
  • 出了问题能快速定位

对于中级开发者来说,这类“简单但清楚”的架构,比追求花哨框架更重要。


一个逐步落地的实施顺序

如果你现在手上就有业务需求,不妨按这个顺序推进:

第 1 步:先做结构化输出

不要先追求自治。
先让模型稳定输出 JSON。

第 2 步:拆出独立节点

把分类、抽取、回复生成拆开。

第 3 步:接入一个确定性工具

比如查订单、查库存、查知识库。
先把“模型判断 + 系统执行”的闭环跑通。

第 4 步:加状态持久化

至少要能记录:

  • 当前任务在哪一步
  • 中间结果是什么
  • 失败在哪里

第 5 步:补校验、重试、日志

这是从 Demo 到可用系统的分水岭。

第 6 步:最后再考虑更复杂的 Agent 能力

例如:

  • 动态规划
  • 多 Agent 协作
  • 长时记忆
  • 自动反思与重试

这些都可以做,但不要抢跑。


总结

从 Prompt Engineering 到 Agent Workflow,本质上不是“提示词升级”,而是一次工程思维升级

  • 从一次回答,转向完整任务
  • 从文本生成,转向状态驱动
  • 从模型主导,转向模型与系统协作
  • 从“能演示”,转向“能落地”

如果你是中级开发者,我最建议你记住这 5 条:

  1. Prompt 不是全部,工作流设计更关键
  2. 让模型负责判断,让系统负责执行
  3. 每个节点只做一件事
  4. 结构化输出、状态管理、校验机制必须有
  5. 先做固定流程,再逐步提高自治程度

边界条件也很明确:

  • 如果业务需要强审计、强合规、强确定性,优先 workflow,不要放任模型自由发挥
  • 如果任务高度开放、探索性强,可以适度增加 Agent 自治,但必须保留监控和回滚能力
  • 如果团队还没有稳定的日志、测试、配置管理基础,不建议一上来做复杂多 Agent 系统

一句话收尾:

能上线的 AI,不是最会说的那个,而是最能被控制、被验证、被维护的那个。

如果你先把一个小而稳的 Agent Workflow 跑起来,再逐步扩展能力,这条路会比你反复打磨“万能 Prompt”走得更快。


分享到:

上一篇
《Spring Boot 3 中基于 JWT 与 Spring Security 6 的前后端分离认证鉴权实战》
下一篇
《Web逆向实战:基于浏览器抓包与 JavaScript 动态调试定位前端签名算法的完整方法》