锁舱按签名原字节出站,查价要带上 TMS 真实 quoteId/optionNo;收到 2001 换令牌再查,避免工单建了却提示查询失败。 Co-authored-by: Cursor <cursoragent@cursor.com>
117 lines
8.5 KiB
Markdown
117 lines
8.5 KiB
Markdown
# 提示词:Python 智能体 / Worker 端
|
||
|
||
你负责与现网询价智能体 **功能等价** 的运行时。代码全新组织,不要做成单文件上帝模块。**每个模块、Handler、Graph 节点、队列/槽代码必须有详细中文注释**(文件头 + 函数职责/副作用/线程;见 `05` 注释节)。无注释不验收。
|
||
|
||
## 必须提供的进程
|
||
|
||
只允许下面这些自研进程(详见 `01` 目标进程):
|
||
|
||
1. **一个 HTTP**:合并现 8810/8809/8811。询价入口、H5、主账唤醒、企微应用回调。请求线程只验签、入 inbox/任务表、返回;禁止同步 LibreOffice / 等完整 LLM。
|
||
2. **一个 Worker**:消费 LLM、识别、报价(Excel→PDF)、微盛存档。队列 + **按任务认领**(处理中/超时回收)+ 重试。并发:LibreOffice **1 槽**,其余 HTTP 类 **2~3 槽**。Windows 服务只起一份;**禁止** Redis 全局 Worker 单例锁(现网 `wecom:worker:singleton`)。
|
||
3. **AIBOT 客户端**:连本机桥(测试 8913)。有 `response_url` 当场回;没有则 POST 桥 `/send-group`(空运群发文件后马上说话)。激活发空运模板走 `/send-file`。桥进程可仍是 Node `.cjs`。
|
||
4. 企微 inbox 消费与 outbox 外发:放在 HTTP 进程的快循环,或 Worker 里与 soffice **隔离的槽**,不得和转 PDF 串行互堵。
|
||
|
||
不要:独立 `bridge_api` / `wecom_robot` 端口;五个 NSSM Worker。群机器人 8812 默认不做。
|
||
|
||
## 禁止卡顿(必须多线程 / 多槽)
|
||
|
||
实现验收:**企微回调与 H5 在慢活进行时仍能秒回**;一份 PDF 不能拖死收消息、查价、出站。
|
||
|
||
1. **请求线程只做短事**:验签、去重、入 inbox/任务表、202/明文成功码、返回。禁止在回调 / H5 / 唤醒 HTTP 里同步 `invoke` 完整 Graph、等 LLM、跑 soffice。
|
||
2. **尽量用多线程**:HTTP 用线程池(uvicorn workers 或等价);Worker **进程内**用线程槽并行——HTTP 类(模型、识别、主账、存档、outbox)**至少 2~3 槽**;inbox 消费与 outbox 外发独立线程,不得和转 PDF 排成一条队列。
|
||
3. **唯一例外**:LibreOffice **1 个槽**(本机两台 soffice 会互抢卡死)。这不是「整进程单线程」。
|
||
4. **I/O 等待要让出**:调企微、主账、TMS 经主账、对象存储、模型 HTTP 用超时 + 线程池;禁止无限阻塞、禁止热循环 `sleep` 轮询把槽占满。Graph `resume` 放在 Worker 槽里跑,不要占回调线程。
|
||
5. **禁止用一把进程大锁串行所有工单**。防重入只锁「同一条任务 / 同一个 thread_id 的写冲突」,不同工单必须能同时跑。
|
||
6. **共享状态要线程安全**:checkpoint、认领、outbox 用 DB 事务或按条锁,不要无锁改全局 dict。
|
||
7. Windows 上线程优先于再开多个 Python 进程(已规定只有一个 Worker 进程)。需要并行就加槽/线程,不要再拆 NSSM。
|
||
|
||
卡住整机的写法一律视为缺陷,不是「先跑通再优化」。
|
||
|
||
## 模块边界
|
||
|
||
```
|
||
NormalizedInbound
|
||
→ 冻结只读状态快照
|
||
→ 允许的意图集合
|
||
→ DeepSeek A 唯一 route_decision
|
||
→ 确定性 RouteDecision
|
||
→ 唯一 handler
|
||
→ 执行到下一个 wait 边界
|
||
→ 持久化 version + 幂等键
|
||
```
|
||
|
||
## 模块划分(源码,不是进程)
|
||
|
||
进程可以只有 HTTP + Worker;**包必须按功能切开**。现网把编排、企微业务、识别堆进超大文件,新项目禁止复现。
|
||
|
||
建议包(名称可改,边界不能混):
|
||
|
||
| 包 | 只做什么 | 不准塞什么 |
|
||
|---|---|---|
|
||
| `channel/wecom` | 验签、XML/JSON 解析、`sender_id`、inbox 写入 | Graph、LLM、建单 |
|
||
| `channel/h5` | 表单上传、clarification 页 | 六态、TMS |
|
||
| `channel/outbox` | 企微发送、限流、重试 | 抽字段 |
|
||
| `routing` | DeepSeek A、RouteDecision | 副作用(建单/发消息) |
|
||
| `handlers/` | 每类意图一个模块:会话、补问、建单、TMS 展示、协同、采用/调价、成交、异常 | 一个文件处理全部卡片 |
|
||
| `graph/` | 图定义、节点各文件、pause/resume、wake | 企微 HTTP、soffice |
|
||
| `llm/` | 网关、队列、各 mode schema | 业务六态 |
|
||
| `jobs/` | 认领循环、识别槽、LibreOffice 槽、Archive 槽 | 回调路由 |
|
||
| `ledger/` | 主账 HTTP 客户端 | 直连 TMS、直连 MySQL |
|
||
| `storage/` | 对象存储 | 工单状态 |
|
||
| `config/` | 只读环境变量 | 业务 if/else |
|
||
|
||
规则:
|
||
|
||
- 新功能先归包再写代码。入口(`main`、FastAPI `app`、Worker 启动)只组装依赖,不写业务分支。
|
||
- Graph **节点分文件**(或按阶段分子包),禁止单文件堆全部节点。
|
||
- Handler **一类卡片/一类意图一个文件**;公共校验放 `schema/` 或 `policy/`,不要复制粘贴进每个 handler。
|
||
- 跨包只通过明确接口(函数/协议/事件),禁止互相 import 私有内部函数绕过边界。
|
||
- 单文件超过约 400~600 行且已含两类职责:拆,不要继续往下加。
|
||
|
||
堆在一起的上帝模块一律视为缺陷,与卡顿同样不可验收。缺详细中文注释同样不可验收。
|
||
|
||
## 身份
|
||
|
||
入站已带企微 userid(`sender_id`)。禁止在收到聊天后再请求企微换 ID。无 `sender_id` 不进 Graph。工单 owner、会话、卡片权限、outbox `touser` 只用该字段。角色查主账,不查企微。H5 不从 URL 读 userid。细则见 `09`。
|
||
|
||
## 状态
|
||
|
||
| 状态 | 存储 |
|
||
|---|---|
|
||
| 活动工单、打断栈 | conversation session |
|
||
| 图节点、业务上下文 | Graph checkpoint |
|
||
| 等待点 waitVersion | suspend 账 |
|
||
| 补问/卡片绑定 | clarification,TTL(H5 30 分钟) |
|
||
| 正式六态 | 只调用主账 API |
|
||
| LLM/识别/报价任务 | 各自任务表 + 认领状态(谁在做、超时释放),不是进程级单例 |
|
||
|
||
运行账 PostgreSQL:`PG_HOST`/`PG_DATABASE`/`PG_DSN` 走环境变量(测试 `ytd_test_runtime`,正式 `ytd_runtime`,见 `13`)。开发期间 inbox/outbox/checkpoint/任务表缺结构:**直接加迁移并执行到当前 `PG_DATABASE`**,不要问能不能加表。禁止动现网 `ytd_runtime`。
|
||
|
||
**LangGraph / Redis 何时用、怎么用:见 `14-LangGraph与Redis.md`。** 图:一单一个 `thread_id`,checkpoint 在 PG,停点中断,Worker 结构化 wake。Redis:`REDIS_URL`(测试 `/1` + `ytd:test:`),只做 Stream 或限流/token;checkpoint 与六态不准进 Redis。
|
||
|
||
## LLM
|
||
|
||
实现队列(Redis Stream 或 DB 任务表)、按条认领、重试、熔断、审计。不要全局 `SET NX` 单例。模式至少:
|
||
|
||
- `ordered_actions`(DeepSeek A,strict tool)
|
||
- 文本字段抽取(DeepSeek B)
|
||
- 附件文本 / 图片读取(千问)
|
||
- 群报价抽取
|
||
- 报价工作表映射
|
||
|
||
环境变量名与 `03-配置与密钥清单.md` 对齐;测试/正式取值见 `13`(`YTD_ENV`、域名、端口、库)。禁止把 `ai.ytd-scm.com` 写死在代码里。
|
||
|
||
## 字段
|
||
|
||
实现与 `inquiry-required-fields-v1.json` 等价的条件激活(JSON 放在新仓库)。SEA 箱型箱量保持单原始字段 + B 模型投影 `containerItems`。协同点击用不可变询价原话重跑 B,去重补问。
|
||
|
||
## 出站
|
||
|
||
- 主账:登录或 Token,所有写操作要幂等键(message_id / request_id / quoteVersion)。
|
||
- 企微:只通过 outbox;失败重试;过期出站丢弃不计业务成功。询价与正式同一 Agent `1000010`;运维 `1000014`,测试**告警**正文加 `[test]`,销售询价确认不加 `[test]`、不加「询价卡片」标题。建单确认按空运询价必填 8 项:起运地、目的地、品名、件数、重量、体积、包装类型、报价日期(另加工程单号、运输方式),末行「工单已创建,正在查询TMS报价。」销售侧标签不得写成毛重、重量(KG)、体积(CBM)。缺项补问先回显已识别,再列待补充并给示例(重量如 100KGS、体积如 2CBM、包装如托盘),待补充行不加圆点;报价日期销售说了写入已识别,没说默认当天,不进待补充。起运地/目的地说了地名即采集完成,不得因对不上三字码再补问。每次出询价确认都新开一单再查 TMS,相同需求不复用旧工单号。包装等字段销售说什么就记什么,禁止白名单把散货等原文挡回去再补问。
|
||
- 对象存储:S3 兼容;生成文件后登记主账附件再发企微。
|
||
|
||
## 测试
|
||
|
||
每个业务线:完整字段不补问;缺一项只补该项;TMS 有价/无价/1003/技术失败;重复回调不建第二单。禁止依赖真实密钥才能跑单测(TMS 用 fixture 模式)。**不要**按企微 userid 在 Redis 里伪造 TMS。
|