221 lines
8.1 KiB
Markdown
221 lines
8.1 KiB
Markdown
# 柏悦智能物流客服系统
|
||
|
||
基于 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 的口径不同,修改时注意核对报价表。
|
||
|