云计算百科
云计算领域专业知识百科平台

AI Agent 自动跟进客户:从需求到落地的完整架构

上一波《RAG 还是微调?用一个真实的多语言客服场景说清楚》里,我把客服知识库的技术选型聊完了,这篇往前走一步:知识库之外,客户跟进这个环节本身能不能交给 Agent 自动跑?本文基于一个真实的跨境客户管理场景做架构推演:业务是外贸团队跟进 WhatsApp 上的询盘客户,样本是近三个月约 4000 条会话的跟进节奏统计,看哪些环节适合自动化、哪些必须留在人手里。全文只谈架构与工程取舍,不涉及任何具体产品,所有 API 端点统一用 https://api.example.com/… 占位。

一、需求约束:自动跟进到底要解决什么

先把业务痛点翻译成工程语言,需求边界比想象中窄:

  • 响应时效:外贸询盘的黄金回复窗口是 30 分钟内。时差是最大敌人,凌晨两点的询盘等到早上九点回复,客户多半已被同行截走。
  • 跟进节奏:成交前平均需要 5~8 次触达。人肉跟容易漏——不是不想跟,是客户多了记不住谁该跟了。
  • 语言门槛:同一个销售要面对英语、西语、阿拉伯语客户,回复质量随语言波动很大。
  • 合规红线:绝对不能变成骚扰。客户明确拒绝后必须停止触达,频率要有硬性上限。

注意最后一条,它决定了整个架构的形态:这不是一个「生成内容」问题,而是一个「在严格约束下做决策」的问题。这直接排除了「一个大 prompt 解决一切」的做法。

二、整体架构:Agent 拆成四层

我在做的一个跨境客户管理产品里验证过的拆法(也是本文唯一的经验来源声明),是把 Agent 按职责切成四层,每层可以独立测试、独立降级:

┌─────────────────────────────────────────────┐
│ 触发层 Scheduler:定时扫描 + 事件驱动 │
├─────────────────────────────────────────────┤
│ 决策层 Agent Core:判断该不该跟、跟什么 │
├─────────────────────────────────────────────┤
│ 执行层 Tool Executor:发消息/查资料/记账 │
├─────────────────────────────────────────────┤
│ 护栏层 Guardrail:频率/黑名单/人工审批 │
└─────────────────────────────────────────────┘

四层的职责边界:

  • 触发层:两种触发源——定时扫描(每天早八点跑一遍「哪些客户到了该跟进的节点」)和事件驱动(客户回复、询盘到来)。事件驱动走 Webhook,关于消息通道同步的重复投递问题,我在《Webhook 幂等设计:AI 消息通道同步的三类重复问题(附代码)》里踩过完整的坑,这里不展开,只强调一句:没有幂等键的事件驱动 Agent,迟早会重复跟进同一个客户两次。
  • 决策层:LLM 参与的核心位置。输入是客户画像 + 最近会话摘要 + 跟进历史,输出是一个结构化决策:{action, channel, draft, urgency}。注意输出的是「决策 + 草稿」,不是「直接发送」。
  • 执行层:纯工具调用,收发消息、查订单状态、写跟进日志。这层没有任何智能,就是普通的 API 编排。
  • 护栏层:频率上限、拒绝名单、敏感词、金额超限转人工。这层是纯规则代码,一行 LLM 都不碰。

会话数据怎么存、怎么支撑百万级消息量,我在《AI 客服系统怎么做会话数据模型?支撑百万级会话的表设计》里给过完整的表结构,决策层读的「跟进历史」就是那套模型里的 follow_up_log,两篇可以对照着看。

三、决策层的三个设计取舍

取舍一:LLM 判断「该不该」,规则判断「能不能」。

最常见的错误设计是让 LLM 直接输出「发/不发」。实测下来(4000 条会话回放,统计三个版本的误判率):让 LLM 全权决策,客户已拒绝后仍被触达的比例约 2.1%;改成「规则先算出合法动作集 → LLM 只能在集合内选择」,这个数字降到 0,代价是约 8% 的本可以触达的场景被保守跳过。对 ToB 业务,这个代价完全值得——丢一个骚扰投诉比丢一次跟进机会贵得多。

取舍二:草稿生成与发送解耦。

LLM 生成的跟进草稿不直接进消息通道,而是先进「待发队列」,按 urgency 分级:高紧迫(客户刚回复)自动发,中紧迫进人工确认队列,低紧迫攒到次日批量审。这个设计让销售保留最终控制权,也让 Agent 的错误有拦截窗口。

取舍三:会话摘要而不是全量上下文。

不要把三个月聊天记录塞进 prompt。每次会话结束后异步生成一段结构化摘要(客户意向、卡点、承诺事项),决策时只喂摘要 + 最近 3 条消息。上下文 token 降了 90% 以上,决策质量实测没有下降——因为跟进决策依赖的是「历史脉络」,不是「逐字记录」。

四、方案对比:三种自动化深度怎么选

方案自动化程度主要限制更适合先试的场景
纯提醒(Agent 只算「该跟谁」) 不省人力,只省记性;依赖销售自觉执行 团队小、客单价高、话术敏感
草稿+人工确认(Agent 写、人发) 确认队列可能积压;高峰期人工吞吐跟不上 大多数外贸团队的起步形态
全自动发送(带护栏) 护栏漏一条就是事故;需要完整回滚与审计能力 标准化程度高的售后/物流类跟进

三种方案不是递进关系,是按业务风险分流:话术越标准化、越远离承诺类内容,越可以往后选。首单报价、合同条款这类内容,写到全自动也别信——LLM 编造一个交付周期,销售是会用信誉买单的。

五、状态机:跟进流程不能让 LLM 自由发挥

决策层内部,客户跟进状态用显式状态机管理,LLM 只负责状态内的动作生成,不负责状态迁移:

NEW → CONTACTED → QUOTED → NEGOTIATING → (WON | LOST)
│ │ │
└── FOLLOWUP_WAITING ←──┘

└── CHURNED(客户明确拒绝,全渠道静默)

  • 状态迁移只由代码触发:收到客户回复、报价发出、超时未回——这些事件的判定可以是 LLM 辅助的(比如从消息里识别「客户已拒绝」),但迁移动作本身是确定性代码。
  • 每个状态有独立的 prompt 模板和护栏配置:NEGOTIATING 状态的草稿必须经过人工确认(金额敏感),FOLLOWUP_WAITING 的触达频率是 72 小时一次,CHURNED 状态下任何触达动作直接被护栏拦截。
  • CHURNED 是吸收态:进去了就出不来,除非客户主动发消息。这一条是合规的生命线。

实测里 LLM 在这个架构里的真实角色是三件事:意图分类(这条回复算什么事件)、草稿生成、摘要更新。都不是「自由决策」。

六、核心代码:状态机 + 护栏的骨架

下面是可以直接改造复用的最小骨架(Python 3.10+,无第三方依赖):

# followup_agent.py — 跟进 Agent 状态机 + 护栏骨架
import json
import time
import urllib.request
from dataclasses import dataclass
from enum import Enum

API_BASE = "https://api.example.com/v1"

class State(str, Enum):
NEW = "NEW"
CONTACTED = "CONTACTED"
QUOTED = "QUOTED"
NEGOTIATING = "NEGOTIATING"
FOLLOWUP_WAITING = "FOLLOWUP_WAITING"
CHURNED = "CHURNED"
WON = "WON"
LOST = "LOST"

# 每个状态的触达约束:min_gap_hours=最小间隔, require_review=是否必须人工确认
GUARDRAILS = {
State.NEW: {"min_gap_hours": 24, "require_review": True},
State.CONTACTED: {"min_gap_hours": 72, "require_review": False},
State.FOLLOWUP_WAITING: {"min_gap_hours": 72, "require_review": False},
State.QUOTED: {"min_gap_hours": 48, "require_review": True},
State.NEGOTIATING: {"min_gap_hours": 72, "require_review": True},
State.CHURNED: None, # 吸收态:任何触达直接拦截
State.WON: None,
State.LOST: None,
}

@dataclass
class Customer:
cid: str
state: State
last_touched_at: float = 0.0
rejected: bool = False

def _post(path: str, payload: dict) > dict:
req = urllib.request.Request(
f"{API_BASE}/{path}",
data=json.dumps(payload).encode("utf-8"),
headers={"Content-Type": "application/json"},
method="POST",
)
with urllib.request.urlopen(req, timeout=10) as resp:
return json.loads(resp.read().decode("utf-8"))

def llm_decide(customer: Customer, summary: str) > dict:
"""调用 LLM 生成决策。注意:只让它做「集合内选择」,不让它决定发不发。"""
allowed = ["draft_followup", "wait", "escalate"]
if customer.state is State.CHURNED:
allowed = [] # 护栏前置:吸收态连选择集都不给
prompt = (
f"客户状态: {customer.state.value}\\n跟进摘要: {summary}\\n"
f"可选动作: {allowed}\\n"
'只输出 JSON: {"action": "…", "draft": "…", "urgency": "high|mid|low"}'
)
# 此处替换为实际的 LLM 调用;示例返回固定结构
return {"action": "draft_followup", "draft": "…", "urgency": "mid"}

def try_followup(c: Customer, summary: str, now: float | None = None) > str:
now = now or time.time()
rail = GUARDRAILS[c.state]
if rail is None:
return "BLOCKED_STATE"
if c.rejected:
return "BLOCKED_REJECTED"
if now c.last_touched_at < rail["min_gap_hours"] * 3600:
return "BLOCKED_FREQUENCY"

decision = llm_decide(c, summary)
if decision["action"] != "draft_followup":
return f"SKIP_{decision['action'].upper()}"

if rail["require_review"] or decision["urgency"] == "low":
_post("followup/queue", {"cid": c.cid, "draft": decision["draft"],
"review": True})
return "QUEUED_FOR_REVIEW"

_post("messages/send", {"cid": c.cid, "body": decision["draft"]})
c.last_touched_at = now
_post("followup/log", {"cid": c.cid, "state": c.state.value,
"action": "auto_sent"})
return "SENT"

这段骨架刻意保持简单,但它涵盖了三个最容易做错的点:护栏前置(LLM 拿到的选择集本身已经被过滤)、吸收态拦截、触达后写日志。生产环境还要补:状态迁移事件的持久化、草稿队列的过期清理、以及决策的审计留痕。

七、踩坑实录:三个真实翻车点

坑一:时区导致触发层重复扫描。 定时任务按 UTC 部署、业务按当地时间看,结果某天凌晨触发层连跑两轮,同一批客户各收到一条跟进。修复很简单——触发层加幂等键(customer_id + date + slot),但这正是《Webhook 幂等设计》里那个教训的变体:分布式系统里,「只跑一次」永远要靠显式去重,不能靠调度器自觉。

坑二:LLM 把「客户已拒绝」翻译成了「需要换个话题再试」。 回放数据里发现几条会话,客户说了 “not interested”,Agent 下周又发了一条新角度的跟进。根因是意图分类的 prompt 里没有把拒绝信号列为吸收态触发器。修复:拒绝信号改成关键词 + LLM 双通道检测,任一命中即迁移到 CHURNED。这个坑让我把「LLM 判断」从迁移路径上彻底摘掉了。

坑三:摘要更新延迟导致决策失真。 摘要是异步生成的,高峰期延迟到 20 分钟。期间决策层拿到的是旧摘要,给一个刚下过单的客户又发了一封催单邮件。修复:摘要带版本号,决策时校验版本,低于最新版本就降级为「纯提醒」模式。教训是异步链路里的每一环都要回答「数据旧了怎么办」。

八、FAQ

Q1:这个架构必须用大参数模型吗? 不需要。意图分类和摘要生成用 7B 级别的小模型就够,草稿生成可以按语言路由到不同模型。整个架构里 LLM 是被约束在窄任务里的,模型能力要求比想象中低。

Q2:Agent 误发消息了怎么办? 消息通道层面做不了撤回(对方已收到),所以重点是事前拦截而不是事后补救:护栏层 + 人工确认队列 + 影子模式(先跑两周只记日志不发送,复盘误判率再放开)。

Q3:状态机会不会太僵硬,接不住灵活的销售流程? 状态机管的是「客户所处阶段」这个客观事实,不是销售动作本身。销售完全可以自由沟通,Agent 只在跟进触达时参考状态。实践中更常见的问题是状态定义太细——6~8 个状态足够,超过 10 个说明你把销售动作混进状态里了。

Q4:怎么评估 Agent 上线后的效果? 三个指标:响应时效(首响时间中位数)、跟进覆盖率(该跟未跟的客户占比)、事故数(护栏拦截外的异常触达)。前两个是收益,第三个是风险底线,缺一不可。

Q5:多语言草稿的质量怎么保证? 按语言维护独立的 prompt 模板和few-shot样例,高紧迫草稿走人工确认。非英语小语种建议先只在「提醒模式」下运行,积累到足够的人工修订样本再放开。


上一波《Webhook 幂等设计:AI 消息通道同步的三类重复问题(附代码)》解决的是这个架构里事件驱动的数据一致性,《RAG 还是微调?用一个真实的多语言客服场景说清楚》解决的是知识库选型,加上这篇的决策层,Agent 跟进的三大件就齐了。

这个系列还剩最后 2 篇,会一篇篇更完,关注不迷路。

你们团队的客户跟进现在是纯人工还是已经有自动化了?卡在哪一步,评论区聊聊。

专注 AI 工程化实践与出海外贸技术

赞(0)
未经允许不得转载:网硕互联帮助中心 » AI Agent 自动跟进客户:从需求到落地的完整架构
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!