用 Solon AI ReActAgent 构建可控的售后工单处理链路

2026-07-13 28 预计阅读时间: 1 分钟
来源: oschina.net AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:10 分钟

当售后工单达到日均 5,000+,继续依靠人工扩编很难追上业务增速。真正需要解决的并不是“让大模型替客服聊天”,而是把查询订单、判断问题、选择规则、执行操作和记录依据串成一条可审计的处理链路。Solon AI ReActAgent 适合承担其中的编排角色:模型负责推理和选择工具,业务系统继续掌握数据与最终执行权。

ReActAgent 在工单系统中的位置

ReAct 的核心循环可以概括为:分析当前工单,选择一个工具,读取工具结果,再决定下一步动作。处理“包裹疑似丢失”时,Agent 不应该仅凭用户描述直接退款,而应依次获取订单、物流轨迹和售后规则,然后输出处理建议或发起受控操作。

一条典型链路如下:

  1. 工单系统提交工单编号、用户诉求和必要上下文。
  2. Agent 调用订单查询工具,确认订单归属、金额和商品状态。
  3. Agent 调用物流工具,读取最后轨迹及停滞时间。
  4. Agent 查询当前生效的售后规则,而不是依赖模型记忆。
  5. 低风险、规则明确的工单自动执行;高金额或信息冲突的工单转人工。
  6. 系统保存工具调用、规则版本、结论和执行结果。

这种分工能直接对应三类问题:自动首响缩短等待时间,统一工具和规则减少处理差异,常见问题自动化则释放人工客服。

工具设计决定系统上限

不要向 Agent 暴露一个无边界的 executeSql 或“任意调用内部接口”工具。更稳妥的做法是按业务意图定义窄工具,例如:

  • getOrder:读取订单快照,不修改数据。
  • getLogistics:读取物流轨迹和异常标记。
  • getAfterSalesPolicy:按场景和订单条件返回规则及版本。
  • createCompensationRequest:创建补偿申请,但不直接打款。
  • transferToHuman:转人工并附带已经收集的证据。

工具参数应采用强类型结构,并在服务端再次校验租户、用户、订单归属、金额上限和幂等键。模型传入的参数只能视为“不可信输入”。

同时,不要让模型自由决定所有业务结果。可以把工单分成三层:

风险级别 示例 建议处理方式
查询进度、补发说明、规则问答 Agent 自动回复
小额优惠券、标准补偿申请 Agent 建议,规则引擎校验后执行
大额退款、账号争议、证据冲突 强制转人工

一个可以改造的最小项目

下面是一个可复制运行的 Java 示例,用来演示 ReAct 工单循环、工具白名单和人工兜底。由于来源摘要没有给出具体 Solon AI 版本及完整 API,示例将 Agent 接口做成了最小适配层;接入项目时,可以把 TicketAgent 的实现替换为当前版本的 Solon AI ReActAgent,保留工具和风控边界。

创建项目:

mkdir react-ticket-demo && cd react-ticket-demo
mkdir -p src/main/java/demo
cat > pom.xml <<'EOF'
<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>
  <groupId>demo</groupId>
  <artifactId>react-ticket-demo</artifactId>
  <version>1.0.0</version>
  <properties>
    <maven.compiler.release>17</maven.compiler.release>
  </properties>
</project>
EOF

将下面代码放入 src/main/java/demo/Main.java。示例中的规则是演示假设:物流停滞至少 72 小时才进入补偿评估,订单金额超过 200 元必须转人工;生产环境应改为调用规则中心。

package demo;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class Main {
    record Ticket(String id, String orderId, String request) {}
    record Order(String id, long amountFen, String ownerId) {}
    record Logistics(int stalledHours, String lastStatus) {}
    record Decision(String action, String reason, List<String> evidence) {}

    interface TicketAgent {
        Decision handle(Ticket ticket);
    }

    static final class ControlledReactAgent implements TicketAgent {
        private final List<String> auditLog = new ArrayList<>();

        @Override
        public Decision handle(Ticket ticket) {
            Order order = getOrder(ticket.orderId());
            auditLog.add("getOrder:" + order.id());

            Logistics logistics = getLogistics(ticket.orderId());
            auditLog.add("getLogistics:" + logistics.lastStatus());

            Map<String, String> policy = getPolicy("suspected_lost_package");
            auditLog.add("getPolicy:" + policy.get("version"));

            List<String> evidence = List.copyOf(auditLog);
            if (order.amountFen() > 20_000) {
                return new Decision("TRANSFER_TO_HUMAN", "订单金额超过自动处理上限", evidence);
            }
            if (logistics.stalledHours() >= 72) {
                return new Decision("CREATE_COMPENSATION_REQUEST", "物流停滞达到规则阈值", evidence);
            }
            return new Decision("REPLY_AND_WAIT", "尚未达到异常处理阈值", evidence);
        }

        private Order getOrder(String orderId) {
            return new Order(orderId, 12_900, "user-42");
        }

        private Logistics getLogistics(String orderId) {
            return new Logistics(96, "运输中,轨迹长时间未更新");
        }

        private Map<String, String> getPolicy(String scene) {
            return Map.of("scene", scene, "version", "2025-01", "stalledHours", "72");
        }
    }

    public static void main(String[] args) {
        Ticket ticket = new Ticket("T-1001", "O-9001", "包裹四天没有更新,是否丢件?");
        Decision result = new ControlledReactAgent().handle(ticket);
        System.out.println(result);
    }
}

运行:

mvn -q compile
java -cp target/classes demo.Main

接入真正的 Solon AI ReActAgent 时,可以这样改造:把 getOrdergetLogisticsgetPolicy 注册为 Agent 工具;把工单内容传入 ReActAgent;再把 Agent 的最终动作映射到 Decision。具体类名、注解和构建方法应以项目所使用的 Solon AI 版本为准,避免直接套用其他版本的 API。

提示词也应明确限制权限。下面是一份可直接改造的系统提示词:

你是电商售后工单处理 Agent。

目标:根据订单事实、物流事实和当前规则生成处理决定。

约束:
1. 不得根据模型记忆解释售后政策,必须调用规则查询工具。
2. 不得编造订单、物流或赔付信息。
3. 大额退款、身份不一致、工具结果冲突时必须转人工。
4. 写操作前必须说明依据,并携带 ticket_id 作为幂等键。
5. 最终只输出 action、reason、evidence 和 customer_reply。
6. 工具超时或返回不完整时,不得自动赔付。

上线前要补齐的工程控制

Agent 能运行不代表它已经可以值守。生产上线至少要检查以下事项:

  • 幂等性:补偿、退款、补发等写操作必须使用工单编号或业务请求号去重。
  • 权限隔离:查询工具与写工具使用不同凭据,Agent 不持有无限制后台权限。
  • 规则版本化:每次结论记录规则版本,便于投诉复盘和历史重放。
  • 完整审计:保存输入摘要、工具名称、参数、结果摘要、模型输出和最终执行人。
  • 超时降级:模型、物流或订单服务不可用时,转入人工队列,而不是反复调用。
  • 敏感信息治理:进入模型前脱敏手机号、地址和身份证信息,并设置日志保留周期。
  • 离线评测:用历史工单建立测试集,分别统计分类准确率、错误执行率、转人工率和平均工具调用次数。

建议从只读场景开始:先让 Agent 查询信息并生成客服建议,经过一段时间的人工对照后,再逐步开放低金额、可撤销、可幂等的写操作。真正值得追求的指标不是“自动化率越高越好”,而是在错误执行率受控的前提下,稳定缩短首响和处理时间。


相关推荐