跳到主内容
文章阅读

从会聊天到能办事:基于 Pi 构建可控的电商客服 Agent

2026/10/073 次阅读7 分钟

用户问“北京还有黑色降噪耳机吗”,客服需要查商品和地区库存;用户说“帮我买两件”,系统又必须核对价格、确认意愿并扣减库存。再往后,退款、投诉、转人工都会涉及真实业务状态。一个回答自然的聊天机器人,距离能够承担这些工作,还有一段工程上的距离。

Pi Customer Service Agent 就是围绕这段距离构建的项目。它基于 Pi Agent Core,用 React 提供客户页面与坐席工作台,通过 Fastify 提供接口,用 MySQL 保存业务数据和会话。目标是让自然语言成为业务入口,同时让身份、确认、事务与审计保持明确的执行边界。

一、把模型放在合适的位置

项目将后端分成 Agent API 和 Commerce API。前者负责身份、会话、模型运行、流式输出和人工交接,后者负责商品、库存、订单、退款及工单规则。


flowchart LR
    Customer[客户页面] --> AgentAPI[Agent API]
    Desk[坐席工作台] --> AgentAPI
    WeChat[微信桥接] --> AgentAPI
    AgentAPI --> Runtime[Pi Agent Core]
    Runtime --> Tools[受控业务工具]
    Tools --> Commerce[Commerce API]
    Tools --> Knowledge[知识库网关]
    Commerce --> MySQL[(MySQL)]
    Knowledge --> MySQL
    AgentAPI --> MySQL

模型根据用户意图选择工具,例如 search_products、get_inventory、get_order 和 handoff_to_human,再将结果组织成回复。它没有数据库连接、终端或文件操作能力,也没有直接提交订单和审批退款的工具。

例如,模型可以提出购买某个 SKU,但商品是否可售、单价是多少、用户能否购买,都由服务端判断。即使自然语言理解出现偏差,最终执行仍需要经过业务校验。工具列表还支持按名称选择子集,系统提示词随实际启用的工具生成,减少提示规则与运行能力不一致的问题。

二、先生成草稿,再由用户确认

“帮我看看这款耳机”与“我要买这款耳机”可能只差几个字。涉及订单时,让模型自行判断用户是否最终同意,会把语言理解的不确定性带进交易。

项目采用两段式流程。模型先调用 create_order_draft,服务端根据 SKU、地区和数量查询商品目录,重新计算价格并检查库存。草稿随后呈现在聊天记录里的确认卡片中,客户点击按钮,才会进入正式提交接口。

提交时,库存会在 MySQL 事务中再次校验和扣减。幂等键用来识别同一次提交:客户连续点击、请求重发时,系统应返回同一张订单,而不是重复下单。草稿关联信息也能从会话记录恢复,刷新页面不会把待确认卡片变成一条失去作用的聊天消息。

退款沿用相同原则。create_refund_draft 只创建草稿,客户确认后才生成待审核申请。退款资格由服务端按订单下单时间判断,模型不能自行承诺审批结果或到账时间。

这里还有一个需要继续完善的业务细节:当前资格窗口按下单时间计算,而政策文档可能按签收时间描述。项目尚未接入物流签收数据,后续必须统一计算依据与政策表述。这也说明,知识回答正确与业务规则正确需要分别验证。

三、状态机让售后流程有据可查

退款不能只用一个“成功”字段表达。申请已提交、人工审核通过、支付渠道完成退款,是不同的业务事实。

项目把退款拆成待审核、已移交人工、审核通过、退款成功、驳回、执行失败和已撤销等状态。领域层约束允许的流转,例如待审核不能直接跳到退款成功。审核通过后仍可能执行失败,因此批准与到账需要分开记录。

客户可以撤销尚未开始打款的申请;进入审核通过状态后,撤回就涉及支付侧协调,当前客服接口会拒绝并说明原因。数据库唯一索引限制同一订单的活动退款申请,状态更新则检查预期旧状态,避免并发操作相互覆盖。

这些约束让模型得到明确的业务结果:能做什么、为什么被拒绝、下一步是什么。需要说明的是,目前退款状态可通过内部接口推进,坐席工作台还没有退款审批界面,也没有接入真实支付执行链路。

四、转人工是一段完整的会话交接

在聊天里回复“已为您转人工”,并不意味着真的有人接手。项目把交接落实到工单、坐席和客户会话之间。

客户提出转人工后,系统创建绑定来源会话的工单。工单处于待认领状态时,模型仍可以继续回答;坐席认领后,新进入的客户消息保留在原会话中,由人工处理。坐席可以读取完整上下文,回复也写回同一份记录。关闭工单后,模型恢复处理,并能看到人工此前给出的答复。

工单经历 open → assigned → closed 三个状态。两位坐席同时认领时,数据库按当前状态执行条件更新,只有一位成功。一个会话的未关闭工单也受到唯一索引约束,重复转人工会复用原工单。

客户界面通过轮询显示人工接入和回复,坐席工作台按状态展示队列,并定时刷新会话。对当前演示规模而言,这比增加独立推送基础设施更容易调试。未来若并发量和即时性要求提高,可以再升级消息推送机制。

五、身份和审计不交给模型决定

工具上下文里的 userId 来自服务端解析的 JWT,而不是模型参数。客户查询订单或退款申请时,业务接口按这个身份检查归属。即使对话中出现另一个订单号,也不能因此访问其他人的数据。

项目还分别记录模型工具审计和坐席操作审计。前者保存调用参数、结果、状态和耗时;后者记录认领、回复、关闭及被拒绝的原因。

坐席写操作先记录执行意图,再执行动作,最后记录结果。如果意图无法落库,动作不会开始。这便于区分未执行、执行成功和中途失败,也让重复认领等被拒绝的尝试留下线索。审计记录能够帮助排查问题,但它本身不等于已经实现跨服务事务。

当前坐席使用共享访问认证,工号也不是经过独立账号验证的身份。审计能记录工单归属与操作过程,正式商用仍需要补齐坐席账号、角色权限和可靠的身份绑定。

六、知识检索与多渠道接入

配送、保修和退款政策存放在 Markdown 文档中,入库时切分到 MySQL,模型通过 search_knowledge_base 获取内容和来源,再组织答案。

当前检索使用 LIKE,适合少量固定政策,不能称为已经实现语义向量检索。这样选择的原因是便于复现:业务数据与知识内容共用 MySQL,开发者可以直接检查检索结果。随着文档规模增长,KnowledgeGateway 接口允许替换底层实现,但向量检索、文档版本和生效时间仍需要另行建设。

微信接入则复用了同一套 Agent 与业务工具。桥接进程负责收发文字,渠道身份、消息去重、会话映射和同步游标保存在数据库中。人工回复进入待发送队列,发送失败可以重试。

微信没有网页确认按钮,因此订单与退款草稿会生成五分钟有效的一次性确认码,用户回复对应指令后才执行。交互方式改变了,确认边界仍然保留。当前渠道范围是私聊文字消息,群聊和媒体消息尚未实现。

七、Linux 部署只手动填写一个应用 Key

本地能运行之后,项目补充了 Docker 多阶段构建和单机 Compose 部署。构建阶段编译依赖包与前端,运行阶段执行编译后的服务;数据库迁移和知识入库作为一次性任务,完成后再启动依赖它们的服务。

Caddy 提供客户站与坐席站的 HTTPS 入口,MySQL 和 Commerce API 留在内部网络。Linux 管理员手动填写模型服务密钥 LLM_API_KEY,以及两个域名和客户站来源地址;使用其他模型服务时,再调整模型名称与接口地址。


CUSTOMER_SITE_ADDRESS=customer.example.com
DESK_SITE_ADDRESS=desk.example.com
CUSTOMER_ORIGIN=https://customer.example.com
LLM_API_KEY=你的模型服务密钥

在仓库根目录执行部署脚本:


bash packages/customer-service-agent/deploy/deploy.sh

脚本首次运行会生成数据库密码、JWT 密钥、服务间 token 和预览登录密码,保存到权限为 600 的 .env.secrets。浏览器进入坐席站时使用自动生成的预览账号密码,内部 token 由 Caddy 在转发请求时注入,不需要坐席填写或保存。

部署配置还包含健康检查、日志轮转、非 root Node 容器和只读文件系统。密钥文件与数据库备份需要一起妥善保管,不能随意删除后重新生成。当前已经提供部署文件与操作说明,具体服务器上的 DNS、证书申请和运行状态仍需实际验证。

八、验证行为,也说明当前边界

测试重点放在确定性的业务规则:价格校验、订单归属、确认门禁、退款状态流转、重复认领,以及人工接管后不再调用模型。核心测试使用替身和内存实现,避免依赖真实模型调用。

另外,真实 MySQL 并发测试检查事务和唯一约束;模型评测通过独立会话记录实际工具调用,判断场景要求的工具是否出现。前者验证业务执行,后者验证工具选择,目前还不能替代回答质量、成本与长对话稳定性的综合评测。

项目已经串起查询、草稿、确认、人工交接和部署流程,但定位仍是受密码保护的演示与内测。会话锁目前在单进程内,跨实例协调、外部请求超时与重试、真实用户认证和集中监控都需要继续完善,也没有可据此宣称的线上规模或效率提升数据。

做完这套系统,我更看重的是每个动作背后的责任划分:模型理解意图,工具提供有限能力,服务端落实身份和业务约束,客户确认关键操作,人工处理需要判断与协调的问题。这样,Agent 的能力才能沿着可验证的业务流程逐步扩展。

项目源码:zzx666-code/pi,客服模块位于 packages/customer-service-agent。启动与部署步骤见 README 和 部署文档。 项目已经上线,访问service.zhangzhaoxue.asia进行体验吧 人工客服端: service.zhangzhaoxue.asia