柏悦智能物流客服系统
基于 Flask + DeepSeek 的中欧/英国跨境物流智能客服,支持多轮对话、运费试算、运单查询、历史会话管理。
功能
- 多轮上下文:能理解"那空运呢""这个多少钱"这类追问
- 知识库优先:优先使用本地知识库回答,减少模型编造
- 追问兜底:当前问题没命中关键词时,用上一句提问去检索
- 运费试算:识别重量/尺寸/渠道,按报价表阶梯单价计算并展示完整明细
- 运单号查询:发送 8~20 位运单号,返回轨迹卡片;先展示最近节点,可展开全部轨迹
- 历史会话:会话保存到 SQLite,支持打开、搜索、删除
- 交互:停止生成、复制(纯文本)、重答、多行输入、回到底部
- 中英文切换:右上角「中/EN」按钮或快捷键
Ctrl + L - 异常处理:接口超时、Key 失效、中断时给出明确提示与重试
- 深色模式:右上角切换,偏好本地保存
- 响应式:窄屏下侧栏自动变为抽屉
- 附件预览:发送前查看文件名或缩略图,可移除待发送附件
- 客服工作台:默认展示会话列表与全高聊天区,统计收在「数据概览」,服务资料按需展开;支持会话搜索、多行回复和切换会话保留草稿,常用回复先填入再发送
- 手机客服接待:会话列表与聊天整屏切换,返回列表保留草稿;回复框固定在聊天底部,可从列表或聊天打开服务资料
报价数据
代码标注的报价来源为 炜易达 VIP 报价表(2026-10-15 9:00 执行版),覆盖 8 条渠道。以下逐渠道列出代码中的重量档位;按计费重量选择不大于该重量的最高档位:
| 渠道 | 重量档位 → 基础单价(元/kg) | 参考时效 |
|---|---|---|
| 欧洲海运快线 | 21KG+ → 12.3;101KG+ → 10.3;500KG+ → 10.2;2000KG+ → 10.0 | 开船后 38-45 天 |
| 义乌中欧铁路 | 21KG+ → 15.5;100KG+ → 13.5;1000KG+ → 13.5 | 发车后 36-40 天 |
| 中欧卡航 | 21KG+ → 19.4;101KG+ → 17.4;500KG+ → 17.4;2000KG+ → 17.3;4000KG+ → 17.3 | 发车后 16-20 天 |
| 欧洲空运 | 21KG+ → 39;45KG+ → 38;71KG+ → 37;101KG+ → 36 | 10-13 天 |
| 英国海运 | 21KG+ → 12.8;101KG+ → 10.9;301KG+ → 10.9 | 约 40 天 |
| 英国铁路 | 21KG+ → 18.0;101KG+ → 16.2;301KG+ → 16.2 | 25-30 天 |
| 中英卡航 | 21KG+ → 21.4;101KG+ → 19.4;301KG+ → 19.4 | 20-22 天 |
| 英国空运 | 22KG+ → 48;51KG+ → 44;101KG+ → 42 | 8-10 天 |
计费规则(均来自报价表):
- 运费 = 阶梯单价 × 计费重量,按重量落入的档位取价
- 计费重量 = 实重与材积重取大,材积 = 长×宽×高(cm) ÷ 6000(1CBM = 167KG)
- 最低起收档 21KG(英国空运 22KG)
- 铁路、卡航单件计费重不足 13KG 加收超轻费 5 元/kg;空运单件不足 12KG 按 12KG 计费
- 偏远地址 +7 元/kg,单票最低 250 元
基础报价维护在 logistics_chat/pricing.py 的 RATES 字典中;修改价格时同步知识资料,避免调整计算逻辑。
calc_freight() 的 total 当前仅为基础运费。超轻费、偏远费及产品附加费以说明或单独结果展示,尚未统一计入 total;上述业务规则不能理解为已全部自动结算。
扩展数据(logistics_chat/rates_extra.py)
从同一份报价表提取、目前已支持:
- 海外仓卡派按方计费:0.5CBM+ / 1CBM+ 两档,按提问里的国家自动匹配仓点分组
- 产品附加费:47 类产品(手机 5 元/kg、电池 1-2 元/kg、玩具 2 元/kg 等)
- DPD 岛屿偏远判断:12 个国家的岛屿/偏远邮编,命中即提示 35 欧/件
用法示例:
| 提问 | 效果 |
|---|---|
| 2方 海运 到德国海外仓多少钱 | 按 1CBM+ 档计算并列出德国仓点分组 |
| 寄手机到德国要加什么费用 | 给出 5 元/kg 附加费 |
| 邮编07000 算偏远吗 | 命中意大利撒丁岛,提示 35 欧/件 |
⚠️ 报价表含商业价格与联系人信息,公开发布前请脱敏或替换为示例数据。
运行环境与启动
项目使用 Python、Flask、SQLite 和 DeepSeek API,无需前端构建。建议使用独立虚拟环境;所有应用启动命令均在项目根目录执行。
Windows
py -3 -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
Copy-Item .env.example .env # 仅首次运行;已有 .env 时跳过
# 编辑 .env,填写 DEEPSEEK_API_KEY 后再启动
.\.venv\Scripts\python.exe am.py
上述命令固定使用项目 .venv,不受其他已激活环境影响。可选的一键入口 start.bat 按已激活的 VIRTUAL_ENV、项目 .venv、py -3、PATH 中的 python 顺序逐个验证 Python 3。使用它之前先退出不需要的旧环境,或检查诊断结果是否指向预期解释器。启动.bat 调用同一入口:
.\start.bat
仅检查并打印选中的解释器,不安装依赖或启动服务:
.\start.bat --check-python
浏览器访问 http://127.0.0.1:5000;客服登录页为 http://127.0.0.1:5000/admin。
Linux / macOS
python3 -m venv .venv
.venv/bin/python -m pip install -r requirements.txt
cp .env.example .env # 仅首次运行;已有 .env 时跳过
# 编辑 .env,填写 DEEPSEEK_API_KEY 后再启动
.venv/bin/python am.py
配置说明
- 本地密钥放在项目根目录的
.env中:首次运行复制.env.example,填写DEEPSEEK_API_KEY=你的密钥。.env已被 Git 忽略,示例文件中的值应保持为空。 - 应用启动时自动读取根目录
.env,不依赖终端的当前目录;am.py、Windows 启动脚本和 WSGI 应用工厂均使用同一配置入口。修改密钥后须重启服务。 - 配置优先级为:显式传给
create_app的配置 > 进程环境变量 >.env> 空值。环境变量即使为空也会覆盖.env;本地填写后仍提示未配置时,请检查并移除旧的空环境变量。 - 部署环境仍可注入
DEEPSEEK_API_KEY。PowerShell 的$env:DEEPSEEK_API_KEY只影响当前终端及其子进程,双击启动脚本可直接使用.env,无需配置系统环境变量。 - 密钥申请或更换可在 DeepSeek 平台 完成;不要把密钥写入脚本、提交到 Git,或打印到日志。
- 当前服务监听
0.0.0.0:5000,局域网设备可能访问。仅供本机开发时,将logistics_chat/config.py中默认的HOST设置为"127.0.0.1"。 - 历史启动脚本曾提交明文密钥。当前文件已移除该配置,但 Git 历史仍保留旧内容,应在平台作废旧密钥,并在
.env或部署环境中配置新密钥。
Jenkins 灰度部署
现有灰度环境按 Jenkins 部署指南 发布:构建和测试镜像,SSH 发送至专用 Compose 服务,再配置独立域名的 HTTPS 和全站访问密码。密钥由 Jenkins 注入,数据和证书保存在独立宿主机目录;启动失败时恢复之前的应用和站点。面向客户开放前仍需补齐鉴权和数据隔离。另附 Linux 单机参考。
目录结构
baiyue-logistics-chat/
├── am.py CLI/WSGI 兼容入口
├── Jenkinsfile 灰度构建、测试及受管发布
├── Dockerfile 应用、测试及证书运维镜像
├── .dockerignore 显式限制构建上下文
├── rates_extra.py 扩展数据旧导入路径的兼容转发
├── logistics_chat/
│ ├── __init__.py create_app 应用工厂
│ ├── config.py .env、环境变量与绝对路径配置
│ ├── db.py SQLite、历史持久化、备份
│ ├── knowledge.py 知识与关键词检索
│ ├── pricing.py 运费解析与计算
│ ├── rates_extra.py 扩展报价唯一数据源
│ ├── tracking.py 模拟轨迹与卡片协议
│ ├── ai_client.py 模型 HTTP、压缩和追问
│ ├── chat_service.py 问答与 SSE 编排
│ ├── history_service.py 会话 CRUD
│ ├── human_service.py 人工会话
│ ├── business_service.py 登录、统计、订单、留言、导出
│ ├── file_service.py 附件与清理
│ ├── hooks.py 限流和缓存头
│ ├── runtime.py 服务启动
│ └── routes/ HTTP Blueprints
├── templates/ 客户、登录、客服工作台 HTML
├── static/
│ ├── css/ theme.css 共享主题 + chat/admin/admin_login 页面样式
│ ├── js/ 对应经典页面脚本
│ └── fa.jpg
├── scripts/ 人工会话检查/关闭 CLI
├── deploy/ 灰度 Compose、发布/回退、证书及单机参考
├── tests/ 规则、存储、HTTP/SSE、启动与维护回归
├── docs/ architecture.md、knowledge.md
├── examples/requests/ 人工接口示例
├── uploads/.gitkeep 本地附件不纳入版本控制
├── start.bat 可选 Windows 启动检查
├── 启动.bat 兼容入口,调用 start.bat
├── requirements.txt
├── .env.example
├── .gitignore
├── AGENTS.md
└── README.md
运行后还会生成 history.db、logs/ 和 backups/,这些运行数据以及虚拟环境均由 Git 忽略。已经上传的本地文件在本次目录整理中保留。
测试
先安装项目依赖,再使用同一个项目虚拟环境运行:
.\.venv\Scripts\python.exe -m pytest -v
也可执行 .\.venv\Scripts\python.exe -m unittest discover -s tests -v。Linux/macOS 将解释器替换为 .venv/bin/python。纯规则和维护 CLI 本身仅依赖标准库,整套业务测试还需要 Flask 等项目依赖。
安装 Node.js 后,Python 测试还会运行 tests/frontend/admin.test.cjs,验证发送失败保留草稿、重复点击、切换会话、输入法及手机抽屉;也可单独执行 node --test tests/frontend/admin.test.cjs,无需安装 npm 依赖。未安装 Node.js 时该项明确跳过。
覆盖范围包括配置文件加载与优先级、运费边界与单位换算、知识检索、运单识别、旧 SQLite schema 迁移、事务回滚、附件、人工状态、历史、CSV、SSE 压缩/追问/异常/取消,以及 Windows 解释器选择。Windows 启动测试在其他系统上跳过,三个页面的外置资源也由接口测试检查。
模型请求使用模拟响应,数据库和附件使用临时目录;这不代表完成真实 DeepSeek 或真实物流接口联调。旧文档提及的原始 52 个业务测试和 29 个场景记录未随仓库提供,当前结果以实际测试输出为准。
维护与调试
在根目录执行:
py -3 scripts/inspect_human_sessions.py
py -3 scripts/close_active_sessions.py
两个脚本默认定位到仓库根目录的 history.db,不依赖调用时的工作目录,也可用 --db "D:\data\history.db" 指定已有数据库。检查脚本只读;关闭脚本将所有 active 人工会话标记为 closed,保留 waiting 会话和聊天消息。数据库不存在时会报错退出,不会误建空库。
人工客服接口请求示例见 examples/requests/README.md。知识资料见 docs/knowledge.md,实际知识内容维护在 logistics_chat/knowledge.py 的 knowledge_list 中。
模块定位、文件归属和后续拆分规范见 docs/architecture.md。该文档与当前已拆分的目录保持同步。
主要接口
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | / |
客户聊天页面 |
| GET / POST | /stream_chat |
SSE 流式对话;POST JSON 接收 msg、conv_id |
| POST | /api/new_chat |
新建会话并保存欢迎语,返回 id、welcome |
| GET | /api/get_history |
会话列表 |
| GET | /api/get_messages?conv_id= |
单个会话的消息 |
| DELETE | /api/del_history?id= |
删除单个会话和消息 |
| DELETE | /api/clear_all |
清空聊天历史 |
| POST | /api/upload |
上传图片、PDF、TXT、CSV,最大 10MB |
| POST | /api/human/request |
申请人工客服 |
| POST | /api/human/accept |
客服接待 |
| POST | /api/human/send |
发送人工消息 |
| GET | /api/human/poll |
轮询人工消息 |
| POST | /api/human/close |
关闭人工会话 |
| POST | /api/human/transfer |
将接待中的会话转回排队列表 |
| POST | /api/human/note |
保存会话备注 |
| POST | /api/human/recall |
按 mid 撤回客服消息 |
| POST | /api/rate |
提交人工服务评价(sid、1–5 的 stars、comment) |
| GET | /api/agents/list |
客服列表,不包含密码 |
| GET | /api/search_chat?q= |
搜索历史消息,最多返回 50 条 |
| GET | /admin |
客服登录页面 |
| POST | /admin/login |
校验客服账号密码 |
| GET | /admin/dashboard |
客服工作台 |
| GET | /api/export_chat |
导出 Excel;?format=csv 保留 CSV 导出 |
人工客服每天 8:00–22:00 接受新申请,由服务端校验时间;其余时间可提交离线留言。客服工作台保留白色界面,转接后由其他客服在排队列表主动接入。页面的铃铛按钮可开启浏览器通知,页面切到后台时提示排队或新消息;实际通知能力取决于浏览器权限。
SSE 每帧为一条 JSON,主要事件包括:
data: {"t": "正文片段"}
data: {"e": "错误提示"}
data: {"card": {...}}
data: {"replace": "精简后的全文"}
data: {"suggest": ["相关追问"]}
data: {"d": 1}
数据库与运行数据
首次启动会自动创建 chat_history、messages、feedback、human_sessions、human_messages、ratings、agents、orders 和 tickets 九张表,并兼容已有数据,不预置账号或演示数据。本地入口使用根目录 history.db,Linux 部署工厂另存至专用数据目录。客服账号需由管理员在实际环境中配置。会话备注保存在 human_sessions.note;消息撤回事件通过 human_messages.recalled_id 指向原消息,轮询客户端按编号更新。运单卡片以 __CARD__{json} 形式保存,前端据此恢复展示。
普通导入 am.py 或 logistics_chat 不创建运行数据。显式调用 create_app() 或访问 WSGI 兼容入口 am.app 时,默认初始化 SQLite、创建正常运行日志、备份数据库并清理修改时间超过 7 天的上传文件。测试须事先配置临时数据库、附件、日志和备份目录;INITIALIZE_RUNTIME=False 可禁止自动初始化,此时调用方负责准备 schema。历史会话的自动清理仍已关闭。
当前限制
- 运单轨迹为模拟数据,尚未接入真实物流查询接口。
- 不预置客服账号;应用内客服登录仍沿用旧账号校验逻辑。公网自用验收可按部署指南使用全站访问密码进入工作台。
- 后台登录尚未建立完整会话鉴权,管理页面和接口缺少登录校验。
- 运费数字由 Python 计算后交给模型组织说明;附加费是否适用仍需结合具体货物和地址核对。
- 回答提示词目标为 120–350 字;代码压缩阈值为中文 800、英文 1400 字符,超过阈值最多尝试压缩两次。
- 服务优先使用 Waitress 的 8 个线程;缺少 Waitress 时会回退到 Flask 开发服务器。公网部署前还需完善鉴权及部署配置。