Agentic支付助手MultiAgent实操项目-Stage 3: Multi-Agent 编排

0 阅读12分钟

Stage 3: Multi-Agent 编排

你要学什么

单 Agent 再强,也有局限——一个模型既要懂风控、又要懂路由、还要懂跨境,专业度不够。Multi-Agent 的核心思想是分工专业化:每个 Agent 专注一个领域,通过协作完成复杂任务。

支付场景下的 Multi-Agent 分工:

用户请求 → 主控Agent (Orchestrator)
              │
     ┌────────┼────────┐
     ▼        ▼        ▼
  风控Agent  路由Agent  跨境Agent
  (专家)     (专家)    (专家)
     │        │        │
     └────────┼────────┘
              ▼
        综合决策输出

为什么要拆分:

  1. 专业度:每个 Agent 只专注一个领域,Prompt 更聚焦,效果更好
  2. 并行处理:风控和路由可以同时查,速度更快
  3. 可维护性:改风控逻辑不影响路由逻辑
  4. 扩展性:新增一个支付场景,加一个 Agent 就行

运行代码

cd /Users/salar/agentic-payment

cat > stage3_multi_agent.py << 'PYEOF'
"""
Stage 3: Multi-Agent 编排
学习:多智能体协作、角色分工、主控Agent调度
核心模式:主控Agent + 专家Agent(风控/路由/跨境)

PM思考:
- 单Agent为什么不够?因为一个模型什么都懂,但什么都不精
- Multi-Agent的关键是分工:每个Agent只做自己最擅长的
- 主控Agent负责调度和整合,专家Agent负责深度处理
"""
from zhipuai import ZhipuAI
import os
import json
import concurrent.futures

# ====== 0. 配置 ======
client = ZhipuAI(api_key=os.environ.get("ZHIPUAI_API_KEY"))
MODEL = "glm-4-flash"

# ====== 1. Mock数据(复用前两阶段) ======
MOCK_DATA = {
    "accounts": {
        "ACC001": {"holder": "何静", "balance_cny": 125680.50, "balance_usd": 3200.00, "level": "A"},
        "ACC002": {"holder": "优品超市", "balance_cny": 890000.00, "balance_usd": 0, "level": "A"},
        "ACC003": {"holder": "环球数码", "balance_cny": 45000.00, "balance_usd": 15000.00, "level": "C"},
    },
    "merchants": {
        "M001": {"name": "优品超市", "level": "A", "category": "零售", "daily_limit": 500000, "refund_rate": 0.02},
        "M002": {"name": "环球数码", "level": "C", "category": "跨境电商", "daily_limit": 200000, "refund_rate": 0.18},
        "M003": {"name": "速达贸易", "level": "D", "category": "贸易", "daily_limit": 0, "refund_rate": 0.35},
    },
    "blacklist": ["M003", "ACC999"],
    "transactions": {
        "M001": [
            {"id": "T001", "amount": 320, "time": "2026-06-22 10:15", "card": "debit", "status": "success"},
            {"id": "T002", "amount": 1580, "time": "2026-06-22 11:30", "card": "credit", "status": "success"},
        ],
        "M002": [
            {"id": "T003", "amount": 28000, "time": "2026-06-22 03:20", "card": "credit", "status": "success"},
            {"id": "T004", "amount": 27500, "time": "2026-06-22 03:25", "card": "credit", "status": "success"},
            {"id": "T005", "amount": 29800, "time": "2026-06-22 03:30", "card": "credit", "status": "success"},
        ],
    },
    "channels": {
        "alipay": {"name": "支付宝", "fee_rate": 0.006, "success_rate": 0.995, "limit": 50000},
        "wechat": {"name": "微信支付", "fee_rate": 0.006, "success_rate": 0.993, "limit": 50000},
        "unionpay": {"name": "银联快捷", "fee_rate": 0.005, "success_rate": 0.998, "limit": 200000},
        "cross_border": {"name": "跨境电汇", "fee_rate": 0.01, "success_rate": 0.99, "limit": 350000},
    },
    "exchange_rates": {
        "USD_CNY": 7.24, "EUR_CNY": 7.85, "HKD_CNY": 0.92,
    }
}

# ====== 2. 三个专家 Agent ======

def risk_agent(merchant_id: str, amount: float) -> dict:
    """
    风控专家 Agent
    专注:风险评估、黑名单检查、交易异常检测
    """
    merchant = MOCK_DATA["merchants"].get(merchant_id, {})
    in_blacklist = merchant_id in MOCK_DATA["blacklist"]
    transactions = MOCK_DATA["transactions"].get(merchant_id, [])
    
    risk_factors = []
    risk_score = 0
    
    # 黑名单检查
    if in_blacklist:
        risk_factors.append("商户在黑名单中")
        risk_score += 100
    
    # 商户等级
    level = merchant.get("level", "C")
    if level == "D":
        risk_factors.append("商户为D级(高风险)")
        risk_score += 50
    elif level == "C":
        risk_factors.append("商户为C级(较高风险)")
        risk_score += 30
    
    # 金额风险
    if amount > 100000:
        risk_factors.append(f"超大额交易{amount}元")
        risk_score += 30
    elif amount > 50000:
        risk_factors.append(f"大额交易{amount}元")
        risk_score += 15
    
    # 退款率
    refund_rate = merchant.get("refund_rate", 0)
    if refund_rate > 0.15:
        risk_factors.append(f"退款率{refund_rate*100:.1f}%,超过15%阈值")
        risk_score += 25
    
    # 交易异常检测(短时间多笔大额)
    if len(transactions) >= 3:
        recent_amounts = [t["amount"] for t in transactions[-3:]]
        if all(a > 20000 for a in recent_amounts):
            risk_factors.append("短时间内多笔大额交易,疑似拆单")
            risk_score += 20
    
    # 风险等级
    if risk_score >= 80:
        risk_level = "极高"
        action = "立即拦截,转人工审核"
    elif risk_score >= 60:
        risk_level = "高"
        action = "暂停交易,人工复核"
    elif risk_score >= 30:
        risk_level = "中"
        action = "放行但标记,事后复核"
    else:
        risk_level = "低"
        action = "正常放行"
    
    return {
        "agent": "风控专家",
        "merchant": merchant.get("name", "未知"),
        "merchant_level": level,
        "risk_score": risk_score,
        "risk_level": risk_level,
        "risk_factors": risk_factors if risk_factors else ["无风险因素"],
        "recommended_action": action
    }

def routing_agent(merchant_id: str, amount: float, is_cross_border: bool = False) -> dict:
    """
    路由专家 Agent
    专注:支付通道选择、成本优化、成功率保障
    """
    merchant = MOCK_DATA["merchants"].get(merchant_id, {})
    merchant_level = merchant.get("level", "B")
    
    channels = MOCK_DATA["channels"]
    candidates = []
    
    for key, ch in channels.items():
        # 跨境通道只在跨境场景考虑
        if key == "cross_border" and not is_cross_border:
            continue
        if key != "cross_border" and is_cross_border:
            continue
        
        # 金额超限排除
        if amount > ch["limit"]:
            continue
        
        # D级商户排除低成功率通道
        if merchant_level == "D" and ch["success_rate"] < 0.99:
            continue
        
        fee = round(amount * ch["fee_rate"], 2)
        # 综合评分:成功率*0.6 - 费率*0.4(归一化后)
        score = ch["success_rate"] * 60 - ch["fee_rate"] * 100 * 40 + merchant.get("daily_limit", 0) / 10000
        
        candidates.append({
            "channel": ch["name"],
            "fee": fee,
            "success_rate": ch["success_rate"],
            "limit": ch["limit"],
            "score": round(score, 2)
        })
    
    # 按综合评分排序
    candidates.sort(key=lambda x: x["score"], reverse=True)
    
    if not candidates:
        return {
            "agent": "路由专家",
            "recommended_channel": None,
            "reason": "没有可用通道",
            "candidates": []
        }
    
    best = candidates[0]
    return {
        "agent": "路由专家",
        "recommended_channel": best["channel"],
        "estimated_fee": best["fee"],
        "estimated_success_rate": best["success_rate"],
        "reason": f"综合考虑成功率、费率、限额,推荐{best['channel']}",
        "all_candidates": candidates
    }

def cross_border_agent(merchant_id: str, amount: float, currency: str = "USD") -> dict:
    """
    跨境支付专家 Agent
    专注:汇率计算、跨境合规、外汇额度
    """
    exchange_key = f"{currency}_CNY"
    rate = MOCK_DATA["exchange_rates"].get(exchange_key, 0)
    amount_cny = round(amount * rate, 2) if rate else 0
    
    merchant = MOCK_DATA["merchants"].get(merchant_id, {})
    merchant_level = merchant.get("level", "C")
    
    # 跨境合规检查
    compliance_issues = []
    if merchant_level == "D":
        compliance_issues.append("D级商户不支持跨境业务")
    if amount_cny > 200000:
        compliance_issues.append("单笔超过20万人民币,需额外申报")
    
    # 外汇额度检查
    daily_limit = merchant.get("daily_limit", 200000)
    limit_ok = amount_cny <= daily_limit
    if not limit_ok:
        compliance_issues.append(f"金额超过商户日限额{daily_limit}元")
    
    return {
        "agent": "跨境支付专家",
        "original_amount": amount,
        "original_currency": currency,
        "exchange_rate": rate,
        "amount_cny": amount_cny,
        "compliance_check": "通过" if not compliance_issues else "需关注",
        "compliance_issues": compliance_issues,
        "recommended_channel": "跨境电汇",
        "estimated_fee": round(amount_cny * 0.01, 2)
    }

# ====== 3. 主控 Agent (Orchestrator) ======

def orchestrator_agent(user_query: str) -> dict:
    """
    主控Agent:理解用户意图,调度专家Agent,整合结果
    """
    # 简化版:用关键词匹配判断需要哪些专家
    # 生产环境可以用LLM做意图识别
    query_lower = user_query.lower()
    
    needed_agents = []
    params = {}
    
    # 提取商户ID(简单正则)
    import re
    merchant_match = re.search(r'(M\d{3})', user_query)
    merchant_id = merchant_match.group(1) if merchant_match else "M001"
    
    # 提取金额
    amount_match = re.search(r'(\d+(?:\.\d+)?)\s*(?:万|元|块|USD|usd)', user_query)
    if amount_match:
        amount_str = amount_match.group(1)
        amount = float(amount_str)
        if "万" in user_query[amount_match.start():amount_match.end()+1]:
            amount *= 10000
    else:
        amount = 50000  # 默认金额
    
    # 判断是否跨境
    is_cross_border = "跨境" in user_query or "USD" in user_query or "usd" in user_query or "外汇" in user_query
    
    # 判断需要哪些专家
    if "风险" in user_query or "评估" in user_query or "能通过吗" in user_query or "安全" in user_query:
        needed_agents.append("risk")
    
    if "通道" in user_query or "路由" in user_query or "推荐" in user_query or "手续费" in user_query:
        needed_agents.append("routing")
    
    if is_cross_border or "跨境" in user_query or "汇率" in user_query:
        needed_agents.append("cross_border")
    
    # 如果一个都没匹配到,默认全部调用(综合判断)
    if not needed_agents:
        needed_agents = ["risk", "routing"]
        if is_cross_border:
            needed_agents.append("cross_border")
    
    # 并行调用专家 Agent
    results = {}
    
    def call_agent(agent_name):
        if agent_name == "risk":
            return "risk", risk_agent(merchant_id, amount)
        elif agent_name == "routing":
            return "routing", routing_agent(merchant_id, amount, is_cross_border)
        elif agent_name == "cross_border":
            currency = "USD" if "USD" in user_query or "美元" in user_query else "USD"
            return "cross_border", cross_border_agent(merchant_id, amount, currency)
    
    with concurrent.futures.ThreadPoolExecutor() as executor:
        futures = [executor.submit(call_agent, name) for name in needed_agents]
        for future in concurrent.futures.as_completed(futures):
            name, result = future.result()
            results[name] = result
    
    # 整合结果,生成最终回答(用LLM润色)
    results_str = json.dumps(results, ensure_ascii=False, indent=2)
    
    prompt = f"""作为支付助手主控,请根据以下专家分析结果,给用户一个清晰、专业的综合回答。

用户问题:{user_query}
商户ID:{merchant_id}
交易金额:{amount}元
是否跨境:{'是' if is_cross_border else '否'}

各专家分析结果:
{results_str}

请输出:
1. 总体结论(一句话)
2. 风险评估结果
3. 推荐方案
4. 注意事项

用中文回答,专业但易懂。
"""
    
    response = client.chat.completions.create(
        model=MODEL,
        messages=[{"role": "user", "content": prompt}],
        temperature=0.3,
    )
    
    return {
        "orchestrator_decision": {
            "merchant_id": merchant_id,
            "amount": amount,
            "is_cross_border": is_cross_border,
            "experts_called": needed_agents,
            "expert_results": results
        },
        "final_answer": response.choices[0].message.content
    }

# ====== 4. 测试 ======
if __name__ == "__main__":
    test_cases = [
        ("商户M001有一笔3万元的支付,帮我评估一下风险和推荐通道", "综合评估(风控+路由)"),
        ("商户M002要做一笔5000美元的跨境支付,帮我看看", "跨境支付全流程"),
        ("商户M003有一笔1万元支付,风险怎么样", "高风险商户"),
    ]
    
    print("=" * 60)
    print("Stage 3: Multi-Agent 编排")
    print("=" * 60)
    
    for query, case_name in test_cases:
        print(f"\n{'='*60}")
        print(f"测试案例: {case_name}")
        print(f"用户问题: {query}")
        print(f"{'='*60}\n")
        
        result = orchestrator_agent(query)
        
        print("各专家Agent分析:")
        for expert, data in result["orchestrator_decision"]["expert_results"].items():
            print(f"\n  【{data['agent']}】")
            for k, v in data.items():
                if k != "agent" and not isinstance(v, list):
                    print(f"    {k}: {v}")
        
        print(f"\n{'='*40}")
        print("📋 综合回答:")
        print(result["final_answer"])
    
    print("\n" + "=" * 60)
    print("Stage 3 完成!理解了Multi-Agent分工协作模式")
    print("=" * 60)
PYEOF

python stage3_multi_agent.py

跑完后想一想

  1. 为什么要拆成多个 Agent? 一个 Agent 不能同时做风控和路由吗?
  2. 并行 vs 串行:风控和路由同时跑,节省了多少时间?
  3. 主控 Agent 的价值:如果没有主控,直接调专家不行吗?
  4. 边界问题:什么时候该加新的专家 Agent,什么时候在现有 Agent 里加功能?

学习要点

Multi-Agent 的核心优势:

  1. 专业化分工:每个 Agent 只做自己擅长的,效果更好
  2. 并行处理:多个专家同时工作,速度更快
  3. 可维护性:改一个专家的逻辑不影响其他
  4. 可扩展性:新增业务场景 = 新增专家 Agent

常见 Multi-Agent 模式:

  • 主控 + 专家(本项目用的):一个调度,多个专家执行
  • 流水线模式:A 的输出是 B 的输入,顺序执行
  • 辩论模式:多个 Agent 各抒己见,投票或讨论得出结论
  • 群聊模式:多个 Agent 自由讨论,适合创意类任务

支付场景为什么选「主控 + 专家」:

  • 支付决策有明确的分工边界(风控、路由、清算)
  • 需要并行处理提高效率
  • 决策链路需要清晰可追溯

面试使用指南

不要说:"我做了个多 Agent 系统"

要说:"我用 Multi-Agent 架构重构了支付助手,拆成了风控专家、路由专家、跨境专家三个专业 Agent,由主控 Agent 统一调度。这样每个 Agent 更专业,还能并行处理。风控和路由同时跑,响应时间减少了接近一半。"

面试官可能追问:

  • "什么时候用单 Agent,什么时候用 Multi-Agent?"
    • "任务单一、领域聚焦用单 Agent;任务涉及多个专业领域、需要并行处理、或者逻辑复杂到一个 Prompt 装不下,就上 Multi-Agent。"
  • "Multi-Agent 之间怎么通信?"
    • "可以用消息队列、共享状态、或者主控调度。简单场景用主控调度就行,复杂场景可以用 LangGraph 这种编排框架。"

跑完之后的心得

学习到的点

核心优势(PM 视角,解决单 Agent 痛点)
  1. 专业深度更高
  • 单 Agent 一套逻辑兼顾风控 + 路由 + 跨境,规则杂糅容易遗漏;
  • 每个专家只负责一小块业务,规则可以无限细化、独立迭代,互不干扰。
  1. 并行计算提速
  • 无依赖的专家(风控 + 路由)多线程同时运行,不用串行一步步 ReAct 轮询,大幅减少 LLM 调用次数与耗时。
  1. 可插拔扩展
  • 新增对账 Agent、退款 Agent、分账 Agent,只需要新增函数 + 主控增加匹配规则,原有逻辑完全不动。
  1. 职责隔离,便于运维
  • 风控规则变更只改 risk_agent;通道费率调整只改 routing_agent,故障定位清晰。
三阶段架构横向对比(单 ReAct Agent / MCP 工具服务 / Multi-Agent)

需要优化的点

提出的 5 个优化方向(从支付 PM 视角)

  1. 意图识别 → LLM 参数抽取 ✅
  • 痛点:正则只能匹配 M001、纯数字,处理不了"环球数码 5 万美金"
  • 方案:用 LLM 做语义理解,提取商户名称、金额、币种
  • 价值:用户体验提升,不用记商户 ID
  1. 专家分布式化 ✅
  • 痛点:本地函数无法水平扩展
  • 方案:每个专家封装为 MCP 服务
  • 价值:生产级部署,支持高并发、故障隔离
  1. Agent 间通信 🚀
  • 痛点:当前是主控单次分发,专家不对话
  • 方案:AutoGen 风格的专家协商
  • 价值:动态修正风险评分,更智能
  • 例子很贴切:
    • 跨境专家:36万人民币,超限!
    • 风控专家:收到通知,追加大额跨境风险因子
    • 最终风险:175分 → 215分(更准确)
  1. 多轮对话记忆 🚀
  • 痛点:每次请求独立,无法复用参数
  • 方案:工作记忆存储上下文
  • 价值:用户体验更好,不用重复输入
  1. 异常处理机制 🚀
  • 痛点:专家调用失败怎么办?
  • 方案:重试机制 + 降级策略
  • 价值:生产环境可靠性

生产级 Multi-Agent 架构演进(从 Demo 到生产级的演进路径)

  • 当前(Demo 级)
  - 主控Agent → 专家Agent1 + 专家Agent2 + 专家Agent3(本地函数)
  • 生产级(企业级)
  主控Agent → [MCP服务1][MCP服务2][MCP服务3]
           ↓      ↓      ↓
        负载均衡  故障转移  缓存
  • 高级(AutoGen 风格)
  主控Agent ←→ 风控Agent ←→ 路由Agent ←→ 跨境Agent
   ↓           ↓           ↓           ↓
  工作记忆    消息队列    消息队列    消息队列

"多轮对话记忆功能将提升用户体验,避免重复输入商户信息"
"专家间通信能让风险评分更准确,比如跨境超限时动态调整风险因子"