Files
inquiry_robot/prompt/06-Python智能体.md
T
jillion886andCursor 83b907c4b8 修复空运锁舱签名、报价编号落库和查价 Token 失效重试。
锁舱按签名原字节出站,查价要带上 TMS 真实 quoteId/optionNo;收到 2001 换令牌再查,避免工单建了却提示查询失败。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-16 18:19:44 +08:00

117 lines
8.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 提示词: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。