从 Prompt 到工作流:中级开发者如何用 AI Agent 快速搭建可落地的自动化业务助手
很多开发者第一次接触大模型时,体验都差不多:写个 Prompt,模型回一段还不错的话。
这一步很容易让人兴奋,但真正把 AI 用进业务,问题马上就来了:
- 回答偶尔很聪明,偶尔很离谱
- 一旦任务变长,Prompt 开始失控
- 需要查数据库、调接口、发通知时,单轮对话根本不够用
- 业务方真正想要的不是“一个会聊天的机器人”,而是“一个能干活的助手”
所以,中级开发者的关键跃迁,不是把 Prompt 写得更花,而是把它升级成可编排、可观测、可回退的工作流。这篇文章我会从工程落地角度讲清楚:如何把“问答式 AI”做成“自动化业务助手”。
背景与问题
为什么 Prompt 很快会碰到天花板
在 demo 阶段,大家常见的模式是这样的:
- 用户输入需求
- 把需求拼进一个 Prompt
- 调一次模型接口
- 直接把结果返回给用户
这适合做内容生成、摘要、润色,但不适合复杂业务流程。原因很直接:真实业务不是一句话生成结果,而是一连串判断、调用和校验。
比如一个“售后工单助手”,它通常要做这些事:
- 识别用户意图:退款、换货、投诉、进度查询
- 查询订单系统
- 判断是否在 SLA 期限内
- 根据规则决定下一步动作
- 生成回复话术
- 必要时转人工或发起审批
这时候,Prompt 不再是主角,工作流才是主角。
一个典型误区:把 Agent 当成“更强的 Prompt”
很多团队一上来就说“我们要做 Agent”,但最后做出来的还是:
- 一个超长 system prompt
- 一堆自然语言规则
- 一个“请你像客服主管一样思考”的角色扮演
这在小样本下看起来有效,但一进入生产环境就会出现几个明显问题:
- 稳定性差:同样输入,不同时间结果不同
- 不可审计:你很难解释模型为什么这么决策
- 不可维护:Prompt 越写越长,像一坨配置文件
- 边界模糊:该由代码控制的流程,被塞给模型猜
我踩过一个坑:把“是否允许退款”的判断也交给模型,结果模型根据“用户语气诚恳”给了退款建议。
这很像人情社会,但显然不是我们想要的系统行为。
核心原理
从工程视角看,一个可落地的 AI Agent,通常由 4 层组成:
- Prompt 层:负责表达任务、约束输出格式
- 工具层:负责连接数据库、API、搜索、消息系统
- 工作流层:负责步骤编排、状态流转、异常处理
- 治理层:负责日志、权限、成本、重试、审计
可以把它理解成一句话:
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 过期
- 消息系统限流
排查路径
- 看模型调用日志
- 看工具调用耗时
- 看工作流每一步状态
- 区分“模型失败”和“工具失败”
这个区别非常重要。
很多所谓的“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 到工作流,本质上是一次思维升级:
- 不是“怎么让模型更像人”
- 而是“怎么让系统更像一个可靠的业务执行者”
你可以把文章里的方法压缩成三句话:
- Prompt 负责理解和表达,不负责最终裁决
- 规则、状态、权限必须掌握在代码和系统里
- Agent 的价值不在会聊天,而在能稳定完成任务
如果你是中级开发者,最现实的切入方式不是追逐最复杂的自主 Agent,而是先把一个业务助手做成:
- 可运行
- 可追踪
- 可回退
- 可扩展
当你把第一个闭环场景跑通后,再逐步加入:
- 更强的工具调用
- 多步任务规划
- 多 Agent 协作
- 状态持久化和人工审核
这样做,既能真正落地,也不会把系统复杂度一下子拉爆。
一句我自己挺认同的经验是:
别让模型替你设计系统,让模型成为系统中被精心使用的一环。
这才是 AI Agent 进入生产环境时,最稳的姿势。