AgentScope 2.0 学习笔记:一分为多——意图路由与多 Agent 协作(把客服拆成一队)
系列:AgentScope 2.0 学习笔记 · 第 008 篇
难度:进阶
适合谁:客服 Agent 已经能跑、但觉得「一个 Agent 干所有活」不对劲的开发者
前置:建议先读 007《双剑合璧——工具 + RAG,做一个会查会答的客服》
阅读收获:用意图路由把「单兵客服」拆成一队,各司其职
运行环境:WSL Ubuntu-24.04 + Python 3.11+ + AgentScope 2.x
你见过这种「一根筋」的客服机器人吗?用户说「我用了你们家产品过敏了,要退货」,它张口就来「那换这款试试吧,这款更温和」——人家来退货,它还在推销。反过来,用户说「我想买瓶面霜」,它先问「您是要退货吗」。
这不是模型笨,是让一个人干了两个人的活。真实公司里,销售和售后是两个部门、两套话术、两种考核——你见过让同一个柜员既卖货又处理投诉的吗?有,但一定两个都干不好。这一篇,就是把「全包型单兵客服」拆成一队:一个路由器负责判断来的是谁,导购 Agent 和售后 Agent 各接各的活。
一、007 的客服,其实是个「全包型单兵」
007 篇,我们拼出了一个会查会答的客服。但它有个隐藏的问题:它什么都得会。
用户来咨询买什么 → 它答;用户来投诉过敏要退货 → 它也得答。可「导购」和「售后」是两种截然不同的活儿:
- 导购要热情:了解需求、推荐产品、促成下单;
- 售后要克制:先安抚、再处理、复杂转人工。
让一个 Agent 既热情推销、又冷静安抚,就像让一个人既当销售又当客服——顾此失彼。用户说「过敏了」,它可能还热情推荐「那换这款试试」;用户说「想买」,它可能先问「您是要退货吗」。
这就是「全包型单兵」的天花板。破局的办法,就是一分为多。
二、一分为多:让专业的 Agent 干专业的活
思路很朴素:与其让一个 Agent 什么都懂,不如让每个 Agent 只懂一件事。
- 导购 Agent「小帮」:只管推荐、报价、搭配;
- 售后 Agent「小护」:只管安抚、退换、转人工。
每个 Agent 的人设(system_prompt)都聚焦一件事,回答自然更专业、更不容易串味。
这个思路,本质上就是把真实公司的组织架构搬进代码:销售部一个 Agent、售后部一个 Agent,各有一套「岗位说明书」(人设卡),各守各的红线。你在真实业务里怎么分工,Agent 就怎么分工——这是多 Agent 设计最容易上手的入口:先从组织架构图里抄。 销售和售后是两大部门,所以你第一步就拆出这两个。
但拆成多个 Agent 后,冒出个新问题:用户进线,先找谁? 这就是「意图路由」要解决的——先判断用户意图,再派给对的 Agent。
三、意图路由:规则快,LLM 兜底
意图路由的核心,是一个「分流器」:读用户的话,判断是「导购」还是「售后」,然后派单。
怎么判断?两层:
第一层:规则(关键词)。 用户的话里有「多少钱」「推荐」「买」→ 导购;有「过敏」「退货」「投诉」→ 售后。关键词命中就完事,快、省成本(不调模型)。
第二层:LLM(兜底)。 规则判断不了的模糊话(「我最近皮肤状态不太好」),交给模型判断意图。
为什么「规则优先、LLM 兜底」?因为规则零成本、零延迟、可解释;LLM 灵活、能理解语义。两者结合,既快又准——这正是系列里反复出现的「LLM 决策 + 规则校验」双层架构,在路由场景的落地。
这里要特别强调「规则优先」的顺序——别图省事每次都上 LLM。路由是用户每句话都要经过的,如果每句都调一次模型,就是「用大炮打蚊子」:又慢又贵。真实客服一天几万次进线,规则层能挡掉七八成,省下的就是真金白银。成本意识,要从架构的第一笔开始算。
顺带说一句:路由的「意图」不止导购/售后两类。真实系统里,你可以再分出「查订单」「转人工」「闲聊」等更多意图,每加一类就加一个专职 Agent 和一组关键词。路由的骨架不变,变的只是意图清单和对应的 Agent 列表——这就是多 Agent 架构的扩展方式。
四、规则层的经典坑:否定句
规则路由看着简单,其实有个坑,专门坑新手:否定句。
用户说「我没有过敏,就是想买个洗面奶」——里面明明有「过敏」这个售后关键词,但用户其实是想买(导购)。
如果不处理,规则会傻乎乎地把「想买洗面奶」的用户丢给售后。所以规则层要加否定词过滤:关键词前面如果紧跟着「不、没有、没、无」这类否定词,就跳过这个关键词。
这个坑我在学习里真踩过,写代码时专门做了修复。你在写自己的路由时,一定记得加这一层——关键词匹配的朴素方案,永远过不了否定句这一关。
不过也要认清规则的边界:否定词过滤只管「关键词前紧邻的否定词」,管不了更复杂的转折和反问。比如「这款控油但不紧绷」里的「控油」是肯定语义、「但」只是转折;再比如「我不太清楚自己是不是过敏」这种绕弯子的话。这些复杂情况,正是交给 LLM 兜底层去处理的——规则管「快而准的常见情况」,LLM 管「规则的盲区」。
五、运行环境
- 系统:WSL Ubuntu-24.04
- Python:3.11+,虚拟环境
venv - 框架:AgentScope 2.x
- API Key:
DEEPSEEK_API_KEY(对话;本篇不涉及 Embedding,无需百炼)
wsl -d Ubuntu-24.04
cd /2026_Study/agentscope/
source venv/bin/activate
export DEEPSEEK_API_KEY="sk-xxxx"
python 008_multi_agent.py
六、完整代码(单文件自包含)
保存为 008_multi_agent.py。一个路由器 + 两个专职 Agent,对三类问题分别派单。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
AgentScope 2.0 · 一分为多——意图路由与多 Agent 协作
导购 Agent「小帮」+ 售后 Agent「小护」+ 双层意图路由(规则关键词 + LLM 兜底)。
运行环境:WSL Ubuntu-24.04 + Python 3.11+ + AgentScope 2.x
运行命令:python 008_multi_agent.py
"""
import os
import gc
import asyncio
import warnings
warnings.filterwarnings("ignore", category=DeprecationWarning)
from agentscope.agent import Agent
from agentscope.message import Msg
from agentscope.model import DeepSeekChatModel
from agentscope.credential import DeepSeekCredential
# 1. 两个专职 Agent 的人设
SALES_PROMPT = """
你叫【小帮】,是「敏辰」的专业护肤导购。
回答流程:先了解肤质需求 → 匹配产品 → 说明价格成分 → 关联推荐 → 红线检查。
⚠️ 红线:不承诺疗效、不贬低其他品牌。
""".strip()
AFTERSALES_PROMPT = """
你叫【小护】,是「敏辰」的售后客服。
回答流程:先安抚 → 了解情况 → 分级处理 → 确认满意 → 复杂转人工。
⚠️ 红线:不推卸责任、不过度承诺、人身伤害必须转人工。
""".strip()
# 2. 规则层:意图关键词 + 否定词过滤
SALES_KEYWORDS = ["推荐", "适合", "价格", "多少钱", "成分", "搭配", "买", "哪款", "肤质"]
AFTERSALES_KEYWORDS = ["过敏", "退货", "退款", "物流", "快递", "投诉", "换货", "破损", "质量"]
NEGATION_WORDS = ["不", "没有", "没", "无", "并非", "不会"]
def rule_route(question: str):
"""规则层:关键词命中判断意图。返回 'sales' / 'after_sales' / None。
关键技巧:否定句过滤——「我没有过敏想买产品」里的「过敏」被「没有」否定,
不应判为售后。关键词前紧邻否定词则跳过,继续找下一个。
"""
for k in AFTERSALES_KEYWORDS:
idx = question.find(k)
while idx != -1:
prefix = question[max(0, idx - 3):idx]
if not any(n in prefix for n in NEGATION_WORDS):
return "after_sales"
idx = question.find(k, idx + len(k))
for k in SALES_KEYWORDS:
if k in question:
return "sales"
return None # 规则判断不了,交给 LLM 层
# 3. LLM 层:兜底意图判断
async def llm_route(model, question: str) -> str:
"""LLM 层:规则无法判断时,用模型判断意图。"""
router = Agent(
name="意图路由",
model=model,
system_prompt=(
"你是意图路由器。判断用户问题属于「导购」(咨询产品/推荐/价格)"
"还是「售后」(退换/过敏/物流/投诉)。只回复 sales 或 after_sales。"
),
)
msg = Msg(name="user", content=[{"type": "text", "text": question}], role="user")
response = await router.reply(msg)
result = extract_text(response).strip().lower()
return "after_sales" if "after" in result or "售后" in result else "sales"
# 4. 模型构建 + 收尾
def build_model():
"""对话模型(DeepSeek,stream=False 干净退出)。"""
return DeepSeekChatModel(
credential=DeepSeekCredential(api_key=os.environ["DEEPSEEK_API_KEY"]),
model="deepseek-v4-flash",
stream=False,
)
def extract_text(response) -> str:
for block in response.content:
if hasattr(block, "type") and block.type == "text":
return block.text
return ""
async def cleanup(model):
"""收尾:逐个 await self.client.close()(干净退出三件套)。"""
closed = 0
client = getattr(model, "client", None)
if client is not None:
try:
await client.close()
closed += 1
except Exception:
pass
if closed:
print(f"\n🧹 已关闭 {closed} 个 HTTP 连接池")
# 5. 意图路由 + 多 Agent 协作演示
async def demo(model) -> None:
sales_agent = Agent(name="小帮", model=model, system_prompt=SALES_PROMPT)
after_agent = Agent(name="小护", model=model, system_prompt=AFTERSALES_PROMPT)
test_questions = [
"你们家的美白精华多少钱?", # 导购:规则命中「多少钱」
"我用了产品过敏了,想退货", # 售后:规则命中「过敏/退货」
"我最近皮肤状态不太好", # 模糊:规则无法判断 → LLM 兜底
]
print("=" * 60)
print(" 一分为多:意图路由 + 多 Agent 协作")
print("=" * 60)
for q in test_questions:
print(f"\n{'─' * 60}")
print(f" 👤 用户:{q}")
intent = rule_route(q)
route_source = "规则层"
if intent is None:
intent = await llm_route(model, q)
route_source = "LLM 层"
target = sales_agent if intent == "sales" else after_agent
target_name = "小帮(导购)" if intent == "sales" else "小护(售后)"
print(f" 🧭 路由:{route_source} → {target_name}")
msg = Msg(name="user", content=[{"type": "text", "text": q}], role="user")
response = await target.reply(msg)
print(f" 💬 回答:{extract_text(response)}")
print(f"\n{'=' * 60}")
print(" ✅ 意图路由 + 多 Agent 协作演示完成")
print("=" * 60)
def check_keys():
need = ["DEEPSEEK_API_KEY"]
missing = [k for k in need if not os.environ.get(k)]
if missing:
print("❌ 缺少环境变量:" + ", ".join(missing))
return False
return True
async def main():
if not check_keys():
return
model = build_model()
try:
await demo(model)
finally:
await cleanup(model)
del model
gc.collect()
if __name__ == "__main__":
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
warnings.simplefilter("ignore", FutureWarning)
warnings.simplefilter("ignore", PendingDeprecationWarning)
asyncio.run(main())
七、代码拆解:三个关键点
① 人设决定分工。 两个 Agent 的 system_prompt 截然不同——小帮「先了解肤质→匹配→报价→关联」,小护「先安抚→了解→分级→转人工」。同一个模型,人设不同,就是两个不同的「岗位」。这印证了 003 篇那句话:换一张人设卡,就是换一个人。
② 规则优先、LLM 兜底。 rule_route() 先跑关键词,命中就直接返回,一次模型都不调;只有返回 None 时,才进 llm_route() 调模型。这个顺序不能反——如果每次都让 LLM 判断,路由就成了「用大炮打蚊子」,又慢又贵。
③ 否定词过滤是规则的「安全带」。 rule_route() 里那段 while 循环,专门处理「关键词前有否定词」的情况。没有它,规则路由在真实对话里会频繁翻车——因为「我没有过敏」和「我过敏了」只差一个字,意图却完全相反。
八、运行结果(真实输出)
下面是本文件在 WSL Ubuntu-24.04 里的真实运行结果。三类问题,三种路由路径,全部踩在点子上。
Case 1(导购) —— 规则层命中「多少钱」,零成本路由到小帮:
👤 用户:你们家的美白精华多少钱?
🧭 路由:规则层 → 小帮(导购)
💬 回答:在给您介绍价格之前,我需要先了解您的皮肤状况……请问您目前的肤质是
偏干、偏油还是混合型?这样我可以为您推荐最适合的美白精华方案。
Case 2(售后) —— 规则层命中「过敏/退货」,路由到小护:
👤 用户:我用了产品过敏了,想退货
🧭 路由:规则层 → 小护(售后)
💬 回答:非常理解您现在的心情,请先不要着急,我们一定会帮您妥善处理……
如果是严重的过敏反应,尤其是涉及面部、呼吸等,请务必立即就医。
Case 3(模糊) —— 规则层判断不了,LLM 层兜底,路由到小帮:
👤 用户:我最近皮肤状态不太好
🧭 路由:LLM 层 → 小帮(导购)
💬 回答:皮肤状态不好是很多原因的……方便具体说说吗?您是暗沉发黄、局部泛红、
还是干起皮、偏油闭口?
最后一行 🧹 已关闭 1 个 HTTP 连接池,程序干净退出、零收尾报错。
三个回答,三种味道,正是「一分为多」的成果:
- 路由正确:前两问走「规则层」(零成本、零延迟),第三问走「LLM 层」(规则盲区,模型兜底)——两层分工清晰。
- 人设鲜明:导购「小帮」先问肤质、不盲目推贵的;售后「小护」先安抚、了解情况、严重过敏提醒就医。同一个模型,两张人设卡,就是两个截然不同的「岗位」——这正是 003 篇「换人设卡就是换人」的实战印证。
- 红线守住:售后那句「严重过敏请务必立即就医」,正是售后人设里「人身伤害必须转人工」这条红线的体现。
九、总结与下一步
这一篇,你把「单兵客服」拆成了「一支队伍」:
单兵(007)= 一个 Agent 全包,顾此失彼
一分为多(本篇)= 路由器 + 导购 + 售后,各司其职
核心就一句:让专业的 Agent 干专业的活,用意图路由做分流。 路由的秘诀是「规则快、LLM 兜底」,再加一层「否定词过滤」保平安。
但「分工」只是多 Agent 协作的第一课。分工之后,还有更进阶的玩法:让多个 Agent 对同一个问题各自作答、再投票汇总,用「多数人的智慧」压住单个模型的随机性。下一篇《AgentScope 2.0 学习笔记:集思广益——多 Agent 投票与结果汇总》,我们把这套「对答案」的机制搭起来。敬请期待。
十、写在最后:你的业务有几条线,就该有几个 Agent
这一篇讲的是「一分为多」,但它背后其实是一个更值钱的问题:怎么判断该拆成几个 Agent?
我的经验是八个字:跟着业务走,别跟着技术走。 你打开自家客服的组织架构图——有几个工种,就拆几个 Agent。销售一条线、售后一条线、订单一条线,就拆三个;再加上意图路由当「前台」,一个人力兜底当「总机转人工」,一套客服系统就成型了。拆多了维护累,拆少了串味,「业务里怎么分工,代码里就怎么分」是最不容易出错的尺度。
这也正是我做 Agent 落地项目时的习惯:先画组织架构,再写代码。如果你正在设计自己的客服/业务系统,卡在「该拆几个 Agent、路由怎么设计」这类问题上,欢迎来评论区讨论。下一篇「多 Agent 投票」见。
本文为「AgentScope 2.0 学习笔记」系列第 008 篇,代码已通过 py_compile 语法校验,运行环境见第五节。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)