8.1 KiB
柏悦智能物流客服系统
基于 Flask + DeepSeek 的中欧/英国跨境物流智能客服,支持多轮对话、运费试算、运单查询、历史会话管理。
功能
- 多轮上下文:能理解"那空运呢""这个多少钱"这类追问
- 知识库优先:优先使用本地知识库回答,减少模型编造
- 追问兜底:当前问题没命中关键词时,用上一句提问去检索
- 运费试算:识别重量/尺寸/渠道,按报价表阶梯单价计算并展示完整明细
- 运单号查询:发送 8~20 位运单号,返回时间轴轨迹卡片
- 历史会话:会话保存到 SQLite,支持打开、搜索、删除
- 交互:停止生成、复制(纯文本)、重答、多行输入、回到底部
- 中英文切换:右上角「中/EN」按钮或快捷键
Ctrl + L - 异常处理:接口超时、Key 失效、中断时给出明确提示与重试
- 深色模式:右上角切换,偏好本地保存
- 响应式:窄屏下侧栏自动变为抽屉
报价数据
运费试算使用 炜易达 VIP 报价表(2026-10-15 9:00 执行版) 中的真实数据,覆盖 8 条渠道:
| 渠道 | 21KG+ | 101KG+ | 500KG+ | 2000KG+ | 参考时效 |
|---|---|---|---|---|---|
| 欧洲海运快线 | 12.3 | 10.3 | 10.2 | 10.0 | 开船后 38-45 天 |
| 义乌中欧铁路 | 15.5 | 13.5 | — | 13.5(1000+) | 发车后 36-40 天 |
| 中欧卡航 | 19.4 | 17.4 | 17.4 | 17.3 | 发车后 16-20 天 |
| 欧洲空运 | 39 | 36 | — | 37(71+) | 10-13 天 |
| 英国海运 | 12.8 | 10.9 | — | — | 约 40 天 |
| 英国铁路 | 18.0 | 16.2 | — | — | 25-30 天 |
| 中英卡航 | 21.4 | 19.4 | — | — | 20-22 天 |
| 英国空运 | 48(22+) | 42 | — | 44(51+) | 8-10 天 |
计费规则(均来自报价表):
- 运费 = 阶梯单价 × 计费重量,按重量落入的档位取价
- 计费重量 = 实重与材积重取大,材积 = 长×宽×高(cm) ÷ 6000(1CBM = 167KG)
- 最低起收档 21KG(英国空运 22KG)
- 铁路、卡航单件计费重不足 13KG 加收超轻费 5 元/kg;空运单件不足 12KG 按 12KG 计费
- 偏远地址 +7 元/kg,单票最低 250 元
数据定义在 am.py 的 RATES 字典中,修改价格只需改这里,不需要动计算逻辑。
扩展数据(rates_extra.py)
从同一份报价表提取、目前已支持:
- 海外仓卡派按方计费:0.5CBM+ / 1CBM+ 两档,按提问里的国家自动匹配仓点分组
- 产品附加费:47 类产品(手机 5 元/kg、电池 1-2 元/kg、玩具 2 元/kg 等)
- DPD 岛屿偏远判断:12 个国家的岛屿/偏远邮编,命中即提示 35 欧/件
用法示例:
| 提问 | 效果 |
|---|---|
| 2方 海运 到德国海外仓多少钱 | 按 1CBM+ 档计算并列出德国仓点分组 |
| 寄手机到德国要加什么费用 | 给出 5 元/kg 附加费 |
| 邮编07000 算偏远吗 | 命中意大利撒丁岛,提示 35 欧/件 |
⚠️ 报价表含商业价格与联系人信息,公开发布前请脱敏或替换为示例数据。
运行环境
- Python 3.9 及以上
- Windows / macOS / Linux
快速启动(Windows)
双击 start.bat 即可,脚本会自动检查端口占用、API Key 和依赖。
安装与启动
1. 安装依赖
pip install -r requirements.txt
2. 配置 API Key
Key 不写进代码,通过系统环境变量提供。
Windows PowerShell:
setx DEEPSEEK_API_KEY "sk-你的key"
设置后需要重新打开终端才生效,可用下面的命令确认:
echo $env:DEEPSEEK_API_KEY
Linux / macOS:
export DEEPSEEK_API_KEY="sk-你的key"
Key 申请地址:https://platform.deepseek.com/api_keys
也可以参考项目根目录的 .env.example(不含真实 Key 的配置示例文件),其中列出了需要设置的环境变量及说明。
3. 启动服务
cd E:\wuliu_chat
python am.py
4. 打开页面
浏览器访问 http://127.0.0.1:5000
测试
单元测试
pytest -v
共 52 个用例,覆盖运费意图解析、运单号识别、阶梯价计算、卡派按方、附加费识别、岛屿邮编、卡片转换等。
测试在临时目录中运行,不会改动 history.db。
回答长度控制
- 中文回答:120
350 字;英文回答:120700 字 - 超过上限时由代码自动压缩一次(保留全部数字),仍超标再压一次
- 提示词之外的第二道保险,确保字数可控
场景测试
测试记录.md 记录了 29 个业务场景的输入、预期结果、首次结果、修复动作与复测结果,
其中 26 项通过或已修复,3 项列为未解决/待验证(超长输入、并发提问、无答案兜底)。
目录结构
wuliu_chat/
├── am.py 主程序(路由、知识库、对话逻辑、运单查询、运费试算)
├── rates_extra.py 报价表扩展数据(卡派按方、附加费、岛屿邮编)
├── test_am.py 单元测试(52 个用例)
├── start.bat Windows 一键启动脚本
├── requirements.txt 依赖清单
├── README.md 说明文档
├── knowledge.md 知识资料(由 knowledge_list 导出)
├── 测试记录.md 场景测试记录(29 个场景)
├── .gitignore Git 忽略规则
├── .env.example 配置示例(不含 Key)
├── history.db SQLite 数据库(会话与消息)
├── static/ 静态资源
│ └── fa.jpg 网站图标
├── templates/
│ └── index.html 前端页面
└── .vscode/
└── launch.json VS Code 调试配置
kefu.py、SQLite.py为早期遗留文件,不影响运行,可安全删除。
主要接口
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | / |
聊天页面 |
| GET | /stream_chat?msg=&conv_id= |
流式对话(SSE) |
| POST | /api/new_chat |
新建会话,返回 {"id": 会话id} |
| GET | /api/get_history |
会话列表 |
| GET | /api/get_messages?conv_id= |
单个会话的全部消息 |
| DELETE | /api/del_history?id= |
删除会话(级联删消息) |
| DELETE | /api/clear_all |
清空全部历史 |
流式事件格式
/stream_chat 返回 Server-Sent Events,每帧是一条 JSON:
data: {"t": "正文片段"} 正常回答片段
data: {"e": "错误提示"} 出错时
data: {"card": {...}} 运单轨迹卡片
data: {"d": 1} 正常结束
数据库
SQLite,文件为 history.db,首次启动自动创建。
chat_history:会话(id、title、ctime、utime)messages:消息(id、conv_id、role、content、ctime)
运单卡片以 __CARD__{json} 形式保存在 messages.content,前端识别后重新渲染。
常见问题
页面报"服务未配置 API Key"
环境变量没读到。确认变量名是 DEEPSEEK_API_KEY,并重新打开终端后再启动。
页面报"API Key 无效或已过期"
Key 已作废,去平台重新生成一把,再用 setx 覆盖。
端口 5000 被占用
netstat -ano | findstr ":5000 "
taskkill /PID <进程号> /F
确认只有一行 LISTENING,避免两个进程同时抢端口。
改了 am.py 没生效
Flask 启动时加载代码,改后端必须重启服务;只改 index.html 则刷新页面即可。
说明
- 运单轨迹当前为模拟数据(按单号固定生成),接真实接口只需修改
am.py中的build_track()。 - 服务使用 Waitress 多线程启动(8 个线程),适合内网小范围使用;面向公网仍需反向代理与 HTTPS。
- 默认只监听
127.0.0.1,如需局域网访问,把am.py中的HOST改为"0.0.0.0"。 - 运费计费方式为阶梯单价 × 计费重量,价格与时效来自炜易达报价表。报价更新时只需修改
RATES;知识库文案在knowledge_list中,两者建议同步更新。 - 材积按 ÷6000 计算(1CBM = 167KG),与部分渠道 ÷5000 的口径不同,修改时注意核对报价表。