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

《从 Prompt 到工作流:中级开发者如何用 AI Agent 快速搭建可落地的自动化业务助手》

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

从 Prompt 到工作流:中级开发者如何用 AI Agent 快速搭建可落地的自动化业务助手

很多开发者第一次接触大模型时,体验都差不多:写个 Prompt,模型回一段还不错的话
这一步很容易让人兴奋,但真正把 AI 用进业务,问题马上就来了:

  • 回答偶尔很聪明,偶尔很离谱
  • 一旦任务变长,Prompt 开始失控
  • 需要查数据库、调接口、发通知时,单轮对话根本不够用
  • 业务方真正想要的不是“一个会聊天的机器人”,而是“一个能干活的助手”

所以,中级开发者的关键跃迁,不是把 Prompt 写得更花,而是把它升级成可编排、可观测、可回退的工作流。这篇文章我会从工程落地角度讲清楚:如何把“问答式 AI”做成“自动化业务助手”。


背景与问题

为什么 Prompt 很快会碰到天花板

在 demo 阶段,大家常见的模式是这样的:

  1. 用户输入需求
  2. 把需求拼进一个 Prompt
  3. 调一次模型接口
  4. 直接把结果返回给用户

这适合做内容生成、摘要、润色,但不适合复杂业务流程。原因很直接:真实业务不是一句话生成结果,而是一连串判断、调用和校验

比如一个“售后工单助手”,它通常要做这些事:

  • 识别用户意图:退款、换货、投诉、进度查询
  • 查询订单系统
  • 判断是否在 SLA 期限内
  • 根据规则决定下一步动作
  • 生成回复话术
  • 必要时转人工或发起审批

这时候,Prompt 不再是主角,工作流才是主角

一个典型误区:把 Agent 当成“更强的 Prompt”

很多团队一上来就说“我们要做 Agent”,但最后做出来的还是:

  • 一个超长 system prompt
  • 一堆自然语言规则
  • 一个“请你像客服主管一样思考”的角色扮演

这在小样本下看起来有效,但一进入生产环境就会出现几个明显问题:

  1. 稳定性差:同样输入,不同时间结果不同
  2. 不可审计:你很难解释模型为什么这么决策
  3. 不可维护:Prompt 越写越长,像一坨配置文件
  4. 边界模糊:该由代码控制的流程,被塞给模型猜

我踩过一个坑:把“是否允许退款”的判断也交给模型,结果模型根据“用户语气诚恳”给了退款建议。
这很像人情社会,但显然不是我们想要的系统行为。


核心原理

从工程视角看,一个可落地的 AI Agent,通常由 4 层组成:

  1. Prompt 层:负责表达任务、约束输出格式
  2. 工具层:负责连接数据库、API、搜索、消息系统
  3. 工作流层:负责步骤编排、状态流转、异常处理
  4. 治理层:负责日志、权限、成本、重试、审计

可以把它理解成一句话:

Prompt 负责“想”,工具负责“做”,工作流负责“按规矩做”。

Agent 的最小闭环

一个简单但实用的 Agent 闭环是:

flowchart TD
    A[用户请求] --> B[意图识别]
    B --> C[任务拆分]
    C --> D[调用工具/API]
    D --> E[结果校验]
    E --> F[生成最终回复]
    E -->|校验失败| G[回退/重试/转人工]

这张图里最容易被忽略的是“结果校验”。
很多人觉得模型生成完就结束了,但真正稳定的系统一定要做:

  • 输出格式校验
  • 字段完整性校验
  • 业务规则校验
  • 高风险场景人工兜底

Prompt、Tool、Workflow 各自负责什么

1. Prompt:负责约束,不负责兜底

Prompt 最适合做这些事:

  • 定义角色和目标
  • 给出结构化输出要求
  • 指导模型如何总结、分类、改写
  • 提醒模型在信息不足时返回澄清问题

但 Prompt 不适合负责:

  • 金额计算
  • 权限判断
  • SLA 校验
  • 风险决策
  • 真正的状态持久化

2. Tool:让模型接上外部世界

没有工具的 Agent,本质上还是个“会说话的文本生成器”。
有了工具之后,它才能:

  • 查询订单
  • 查知识库
  • 创建工单
  • 发送企业微信/邮件
  • 更新 CRM 状态

3. Workflow:把不确定模型装进确定性流程

工作流的价值在于:把“模型自由发挥”限制在可控边界内
例如:

  • 先查订单,再判断,再生成回复
  • 查询失败时自动重试
  • 高风险结果必须人工确认
  • 超时则直接降级到模板回复

一个实用的职责划分原则

我建议中级开发者记住这条线:

  • 规则明确的,交给代码
  • 语义模糊的,交给模型
  • 高风险动作,必须校验
  • 关键状态,必须落库

这条原则能帮你少走很多弯路。


一个可落地的业务场景:售后工单自动化助手

我们选一个中等复杂度的例子:
用户发来一句话,系统自动识别诉求,查询订单,判断是否可退款,并生成建议回复。

流程拆解

sequenceDiagram
    participant U as 用户
    participant S as 工作流服务
    participant M as LLM
    participant O as 订单系统
    participant N as 通知系统

    U->>S: 提交售后请求
    S->>M: 识别意图并提取订单号
    M-->>S: 意图=退款, 订单号=12345
    S->>O: 查询订单信息
    O-->>S: 已签收 3 天, 金额 299
    S->>S: 根据规则判断是否可退款
    S->>M: 生成面向用户的回复
    M-->>S: 退款建议话术
    S->>N: 写入工单/发通知
    S-->>U: 返回结果

这里有个重要细节:
“是否可退款”由规则引擎判断,不由模型判断。
模型只负责两件事:

  • 理解用户话语
  • 生成自然语言回复

这就是“模型做擅长的事,代码守住边界”。


实战代码(可运行)

下面用 Python 做一个最小可运行版本。
为了让示例真正能跑,我把“模型调用”做成了可替换接口:你可以接真实 LLM,也可以先用本地 mock。

项目目标

实现一个简单 Agent:

  • 输入:用户售后文本
  • 输出:识别意图、查询订单、规则判断、生成回复
  • 具备:结构化输出、异常处理、可扩展工具接口

目录结构

agent_demo/
├── app.py
├── requirements.txt
└── README.md

安装依赖

flask==3.0.3
pydantic==2.7.1

完整代码

from flask import Flask, request, jsonify
from pydantic import BaseModel, ValidationError
from typing import Literal, Optional
import re

app = Flask(__name__)

# ----------------------------
# 1. 数据模型
# ----------------------------
class IntentResult(BaseModel):
    intent: Literal["refund", "exchange", "complaint", "query"]
    order_id: str
    user_message: str

class OrderInfo(BaseModel):
    order_id: str
    status: str
    days_since_signed: int
    amount: float

class WorkflowResult(BaseModel):
    success: bool
    intent: str
    order_id: str
    decision: str
    reply: str


# ----------------------------
# 2. Mock LLM:真实项目里可替换为 OpenAI/Claude/自建模型
# ----------------------------
def call_llm_for_intent(user_text: str) -> dict:
    """
    这里用简单规则模拟 LLM 的结构化输出。
    真实接入时,应要求模型输出 JSON,并做 schema 校验。
    """
    order_match = re.search(r"\b\d{5}\b", user_text)
    order_id = order_match.group(0) if order_match else "00000"

    text = user_text.lower()
    if "退款" in user_text or "refund" in text:
        intent = "refund"
    elif "换货" in user_text or "exchange" in text:
        intent = "exchange"
    elif "投诉" in user_text or "complaint" in text:
        intent = "complaint"
    else:
        intent = "query"

    return {
        "intent": intent,
        "order_id": order_id,
        "user_message": user_text
    }


def call_llm_for_reply(intent: str, order_info: OrderInfo, decision: str, user_text: str) -> str:
    """
    模拟 LLM 生成自然语言回复。
    """
    if decision == "approved_refund":
        return (
            f"您好,已为您确认订单 {order_info.order_id} 符合退款条件。"
            f"该订单金额为 {order_info.amount} 元,我们将为您发起退款流程,请您留意后续通知。"
        )
    elif decision == "manual_review":
        return (
            f"您好,关于订单 {order_info.order_id}{intent}申请,我们已为您提交人工审核。"
            f"由于当前订单状态为“{order_info.status}”,需要进一步核实,请耐心等待。"
        )
    else:
        return (
            f"您好,订单 {order_info.order_id} 当前暂不满足直接处理条件。"
            f"若您愿意,我可以继续帮您补充售后信息并转交人工客服。"
        )


# ----------------------------
# 3. 工具层:查订单
# ----------------------------
def get_order_info(order_id: str) -> Optional[OrderInfo]:
    """
    模拟订单系统查询。
    """
    fake_db = {
        "12345": {"order_id": "12345", "status": "signed", "days_since_signed": 3, "amount": 299.0},
        "23456": {"order_id": "23456", "status": "signed", "days_since_signed": 10, "amount": 599.0},
        "34567": {"order_id": "34567", "status": "shipped", "days_since_signed": 0, "amount": 129.0},
    }
    data = fake_db.get(order_id)
    return OrderInfo(**data) if data else None


# ----------------------------
# 4. 规则层:确定性判断
# ----------------------------
def evaluate_policy(intent: str, order_info: OrderInfo) -> str:
    """
    规则示例:
    - 退款:签收 7 天内可直接退款
    - 其他情况:人工审核
    """
    if intent == "refund":
        if order_info.status == "signed" and order_info.days_since_signed <= 7:
            return "approved_refund"
        return "manual_review"

    if intent in ["exchange", "complaint"]:
        return "manual_review"

    return "need_more_info"


# ----------------------------
# 5. 工作流编排
# ----------------------------
def run_workflow(user_text: str) -> WorkflowResult:
    llm_result_raw = call_llm_for_intent(user_text)

    try:
        intent_result = IntentResult(**llm_result_raw)
    except ValidationError as e:
        raise ValueError(f"LLM 输出不合法: {e}")

    order_info = get_order_info(intent_result.order_id)
    if not order_info:
        return WorkflowResult(
            success=False,
            intent=intent_result.intent,
            order_id=intent_result.order_id,
            decision="order_not_found",
            reply="未找到对应订单,请核对订单号后重试。"
        )

    decision = evaluate_policy(intent_result.intent, order_info)
    reply = call_llm_for_reply(intent_result.intent, order_info, decision, user_text)

    return WorkflowResult(
        success=True,
        intent=intent_result.intent,
        order_id=intent_result.order_id,
        decision=decision,
        reply=reply
    )


# ----------------------------
# 6. HTTP 接口
# ----------------------------
@app.route("/assist", methods=["POST"])
def assist():
    data = request.get_json(silent=True) or {}
    user_text = data.get("message", "").strip()

    if not user_text:
        return jsonify({"error": "message 不能为空"}), 400

    try:
        result = run_workflow(user_text)
        return jsonify(result.model_dump())
    except Exception as e:
        return jsonify({"error": str(e)}), 500


if __name__ == "__main__":
    app.run(debug=True, port=8000)

启动方式

pip install -r requirements.txt
python app.py

测试请求

curl -X POST http://127.0.0.1:8000/assist \
  -H "Content-Type: application/json" \
  -d '{"message":"我想申请退款,订单号 12345,商品不太合适"}'

预期返回

{
  "success": true,
  "intent": "refund",
  "order_id": "12345",
  "decision": "approved_refund",
  "reply": "您好,已为您确认订单 12345 符合退款条件。该订单金额为 299.0 元,我们将为您发起退款流程,请您留意后续通知。"
}

为什么这段代码比“一个大 Prompt”更适合上线

这套实现虽然简单,但已经具备可落地系统的雏形:

  • 意图识别:交给模型或 mock
  • 结构化约束:用 Pydantic 校验
  • 工具调用:查询订单系统
  • 业务决策:用代码规则判断
  • 自然语言生成:交给模型
  • 接口封装:可被前端、机器人、工作台调用

也就是说,它不是“模型自己决定一切”,而是一个模型参与的工作流系统


进阶:把工作流拆成状态机更稳

当业务越来越复杂时,我建议不要继续堆 if else,而是引入状态机思路。
这样能更清晰地区分:

  • 等待用户补充信息
  • 查询中
  • 已命中规则
  • 需人工审核
  • 已完成
  • 执行失败
stateDiagram-v2
    [*] --> Received
    Received --> Parsed: 提取意图和订单号
    Parsed --> QueryOrder: 订单号有效
    Parsed --> NeedInfo: 缺少关键字段
    QueryOrder --> PolicyChecked: 查询成功
    QueryOrder --> Failed: 查询失败
    PolicyChecked --> AutoApproved: 命中自动处理规则
    PolicyChecked --> ManualReview: 需要人工审核
    AutoApproved --> Completed
    ManualReview --> Completed
    NeedInfo --> Completed
    Failed --> Completed

这个设计特别适合接入:

  • Redis / DB 持久化状态
  • 异步队列
  • 人工审核节点
  • 审计日志

常见坑与排查

下面这些问题,我几乎在每个 Agent 项目里都见过。

1. 模型输出不稳定,JSON 经常解析失败

现象
你要求模型返回 JSON,它却夹带解释文本、Markdown 代码块,甚至少字段。

原因

  • Prompt 只说“请输出 JSON”,但没有给 schema
  • 没做输出校验
  • 把一次生成结果直接当真

排查方式

  • 打印原始模型输出
  • 记录失败样本
  • 用 schema 校验统计失败率

建议

  • 给出明确字段示例
  • 让模型只返回 JSON
  • 失败时自动重试一次
  • 重试后仍失败,降级为人工或规则逻辑

示例约束方式:

请严格输出 JSON,不要包含任何解释文字:
{
  "intent": "refund|exchange|complaint|query",
  "order_id": "string",
  "user_message": "string"
}

2. 把业务规则写进 Prompt,后期维护爆炸

现象
Prompt 里混着几十条“退款场景 A/B/C”“投诉升级规则”“VIP 特殊处理”。

结果

  • Prompt 越来越长
  • 新人接手困难
  • 改一条规则影响全局
  • 无法做版本审计

正确做法

  • Prompt 负责理解文本
  • 规则放代码、配置中心或规则引擎
  • 风险决策必须可追踪

3. 工具调用成功率低,但你误以为是模型不行

现象
用户说“Agent 不稳定”,你先怀疑 Prompt,其实真正失败的是:

  • 订单接口超时
  • 知识库索引延迟
  • 鉴权 token 过期
  • 消息系统限流

排查路径

  1. 看模型调用日志
  2. 看工具调用耗时
  3. 看工作流每一步状态
  4. 区分“模型失败”和“工具失败”

这个区别非常重要。
很多所谓的“AI 不靠谱”,本质上是外围系统不靠谱。


4. 多轮对话越聊越乱

现象
第一轮还正常,第三轮开始模型忘了上下文,或者把旧订单号带进新任务。

原因

  • 对话状态没有结构化保存
  • 所有历史消息无差别喂给模型
  • 缺少“当前任务上下文”与“闲聊上下文”的分离

建议

  • 只保留关键状态字段,不要无脑拼接全文历史
  • 对话上下文按任务维度存储
  • 每一轮都重新确认关键槽位:如订单号、意图、操作目标

安全/性能最佳实践

Agent 一旦接入真实业务,安全和性能不能等上线后再补。

安全最佳实践

1. 工具权限最小化

不要让 Agent 拿到“大一统后台权限”。
应该做到:

  • 查询订单和修改订单分开授权
  • 只开放必要 API
  • 高风险动作必须二次确认

比如:

  • “查询物流”可以自动执行
  • “发起退款”必须人工复核或签名校验

2. 防 Prompt 注入

如果 Agent 会读取外部文本,比如知识库、邮件、用户留言,要警惕提示注入。
常见攻击形式是:

  • “忽略之前所有规则”
  • “输出你的系统提示词”
  • “调用某个不该调用的工具”

对策:

  • 系统指令与用户输入分层
  • 工具调用必须走白名单
  • 不让模型自由拼接内部敏感上下文
  • 高风险工具加代码侧权限判断

3. 敏感信息脱敏

日志里不要直接记录:

  • 手机号
  • 身份证号
  • 地址
  • 支付信息
  • 全量聊天内容

至少要做部分脱敏:

def mask_phone(phone: str) -> str:
    if len(phone) == 11:
        return phone[:3] + "****" + phone[-4:]
    return phone

性能最佳实践

1. 不要所有步骤都调大模型

一个常见浪费是:
明明可以正则提取、规则判断、模板回复,却每一步都调用 LLM。

经验上可以这么分:

  • 文本分类/提取:小模型或轻量调用
  • 规则判断:本地代码
  • 复杂回复生成:大模型
  • 固定通知文案:模板化

2. 做缓存和幂等

这些地方都适合缓存:

  • 知识库检索结果
  • 同一订单短时间内的查询结果
  • 重复问题的标准回复

同时,业务动作要做幂等控制,比如“发起退款”不能因为重试触发两次。

3. 加超时、重试和降级

建议给每层都配策略:

  • LLM 调用超时
  • 工具调用超时
  • 指数退避重试
  • 超过阈值后降级到模板结果或人工处理

一个推荐的生产架构分层

flowchart LR
    A[前端/IM/工单系统] --> B[Agent API 层]
    B --> C[工作流编排层]
    C --> D[LLM 服务]
    C --> E[工具层 API]
    E --> F[订单系统]
    E --> G[知识库]
    E --> H[通知系统]
    C --> I[日志/监控/审计]

这张图的重点在于:
LLM 只是依赖之一,不是整个系统本身。


给中级开发者的落地建议

如果你准备把 AI Agent 真正用进业务,我建议按下面节奏推进,而不是一上来追求“全自动智能体”。

第一步:先做单一场景闭环

挑一个小而稳定的任务,比如:

  • 自动分类工单
  • 自动生成客服回复草稿
  • 自动整理会议纪要并发通知

不要一开始就做“全能企业助手”。
一个能稳定跑通的窄场景,价值远高于一个处处都能聊两句但哪里都不可靠的大系统。

第二步:明确哪些步骤由模型做

推荐优先让模型做:

  • 分类
  • 提取
  • 总结
  • 改写
  • 生成话术

不推荐直接让模型做:

  • 最终审批
  • 资金操作
  • 权限判定
  • 合规判断

第三步:用工作流把关键步骤显式化

至少把这些节点显式设计出来:

  • 输入校验
  • 模型理解
  • 工具调用
  • 规则判断
  • 回复生成
  • 审计记录
  • 异常回退

只要这些节点是清楚的,系统就不会太难维护。

第四步:先追求可观测,再追求更智能

很多团队一味调 Prompt,但没有日志、没有 trace、没有错误分类。
结果是系统出问题时,只能靠猜。

建议最低限度记录:

  • 输入摘要
  • Prompt 模板版本
  • 模型响应原文
  • 工具调用结果
  • 工作流节点耗时
  • 最终决策来源

总结

从 Prompt 到工作流,本质上是一次思维升级:

  • 不是“怎么让模型更像人”
  • 而是“怎么让系统更像一个可靠的业务执行者”

你可以把文章里的方法压缩成三句话:

  1. Prompt 负责理解和表达,不负责最终裁决
  2. 规则、状态、权限必须掌握在代码和系统里
  3. Agent 的价值不在会聊天,而在能稳定完成任务

如果你是中级开发者,最现实的切入方式不是追逐最复杂的自主 Agent,而是先把一个业务助手做成:

  • 可运行
  • 可追踪
  • 可回退
  • 可扩展

当你把第一个闭环场景跑通后,再逐步加入:

  • 更强的工具调用
  • 多步任务规划
  • 多 Agent 协作
  • 状态持久化和人工审核

这样做,既能真正落地,也不会把系统复杂度一下子拉爆。

一句我自己挺认同的经验是:
别让模型替你设计系统,让模型成为系统中被精心使用的一环。

这才是 AI Agent 进入生产环境时,最稳的姿势。


分享到:

上一篇
《Java开发踩坑实战:排查并修复 Spring 事务失效的 8 个高频场景》
下一篇
《Web3 中级实战:从零搭建基于智能合约的钱包登录与链上身份认证系统-345》