Files
baiyue-logistics-chat/README.md
T

8.1 KiB
Raw Blame History

柏悦智能物流客服系统

基于 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。

回答长度控制

  • 中文回答:120350 字;英文回答: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 的口径不同,修改时注意核对报价表。