# 柏悦智能物流客服系统 基于 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. 安装依赖 ```bash pip install -r requirements.txt ``` ### 2. 配置 API Key Key 不写进代码,通过系统环境变量提供。 Windows PowerShell: ```powershell setx DEEPSEEK_API_KEY "sk-你的key" ``` 设置后需要**重新打开终端**才生效,可用下面的命令确认: ```powershell echo $env:DEEPSEEK_API_KEY ``` Linux / macOS: ```bash export DEEPSEEK_API_KEY="sk-你的key" ``` Key 申请地址:https://platform.deepseek.com/api_keys 也可以参考项目根目录的 **.env.example**(不含真实 Key 的配置示例文件),其中列出了需要设置的环境变量及说明。 ### 3. 启动服务 ```bash cd E:\wuliu_chat python am.py ``` ### 4. 打开页面 浏览器访问 http://127.0.0.1:5000 ## 测试 ### 单元测试 ```bash pytest -v ``` 共 **52 个用例**,覆盖运费意图解析、运单号识别、阶梯价计算、卡派按方、附加费识别、岛屿邮编、卡片转换等。 测试在临时目录中运行,不会改动 `history.db`。 ### 回答长度控制 - 中文回答:120~350 字;英文回答:120~700 字 - 超过上限时由代码自动压缩一次(保留全部数字),仍超标再压一次 - 提示词之外的第二道保险,确保字数可控 ### 场景测试 `测试记录.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 被占用** ```powershell 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 的口径不同,修改时注意核对报价表。