Files
group_fqcd_jr/AGENTS.md
T

154 lines
13 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.
# 项目级开发约束
以下规则对人工开发者和编码 Agent 均为强制约束:
1. 数据库以 `docs/00-新数据库基线设计.md` 为不可变业务基线。
2. 允许创建新表,允许在已有表中增加新字段。
3. 禁止重命名或删除已有表。
4. 禁止重命名、删除、复用已有字段,禁止改变已有字段的类型、可空性和既有业务含义。
5. 历史结构无法满足新需求时,使用新增字段、新表、兼容视图或应用双读解决。
6. 架构固定使用 MVC+S;Agent 属于 Service 层。
7. 业务 Agent 必须继承公共 `BaseAgent` 并由 `AgentFactory` 创建,不得绕过公共鉴权、记忆、模型路由、工具、合规、审计和事件流程。
8. 当前系统业务功能只针对场内基金模拟交易;场外基金运营流程独立,不得写入场内交易表。
修改数据库文档或迁移前,必须对比基线并证明没有改变任何已有表名和已有字段定义。
---
## 📖 接手先读(按此顺序,只读这些就够)
> **⭐ 第 0 步先读这个**:`docs/superpowers/handoff/2026-09-11-交接文档-客服Agent与RAG收尾.md`
> —— 客服 Agent + RAG 这条线的交接文档(**含合并完成后的第二次更新**):环境口径、交付内容与
> **可复现验证证据**、合并后修掉的 3 个真机故障、**已知问题清单(逐条标注当前状态)**、Git/PR 状态。
> **主集成分支是 `qyqy_develop`**(ZSY 的客服接入线已由 PR #7 合入,见 `docs/36`);
> 客服/RAG 那条线的个人分支是 **`NL_develop`**(个人分支 → PR 合回 `qyqy_develop`),**不要再用 `6516ccb`**。
> 它是对"当前状态"最准确的一份,读完它再读下面这些。
>
> **⚠️ 文档现状(2026-09-11 第二次修订)**:本文件原先声明"已删除 5 份编号文档",
> 那条**已作废** —— 经评审,`docs/04`/`06`/`10`/`13`/`99` **全部保留**(架构师明确要求保留:
> 删除收益为零,而保留成本同样为零)。它们的内容**未被核对过、可能过期**,
> 因此**列在下面的 D 类"不要用来判断当前进度"**里,只作历史参考。
> 被删除的只有 10 份**过程产物**,理由与清单见 `docs/superpowers/ARCHIVE-2026-09-11-文档清理归档.md`。
### A. 核心 7 份(无论接手哪条线都必读)
| 序 | 文档 | 承载的唯一权威内容 |
|---|---|---|
| 1 | `docs/00-新数据库基线设计.md` | **不可变业务基线**:表/字段业务语义的唯一来源 |
| 2 | `docs/05-接口文档.md` | **接口唯一权威**:信封/错误码/幂等/SSE、§8.3 知识库管理三端点、§8.4 四个只读工具索引与两段式白名单 |
| 3 | `docs/01-通用Agent平台开发设计.md` | MVC+S 分层约束、`BaseAgent` 执行骨架、`AgentFactory` |
| 4 | `docs/02-数据库建表设计.md` | 51 张业务表总览 + DDL + §8 幂等与 Outbox 语义 |
| 5 | `docs/03-平台端到端流程文档.md` | 一次请求从受理→Worker→审计→事件的全链路与降级矩阵 |
| 6 | `docs/08-数据库结构审计基线.md` | 三个审计工具 + `migration_state_check` 的职责;"证明未改基线"的证据出处 |
| 7 | `docs/07-测试问题修复记录.md` | **无替代**:P0-1/2/3 鉴权与 Worker 租约闭环、P1-1~P1-5(含 `api_request_receipt` 事务幂等) |
### B. 按角色补充
| 你接手的是 | 再读这些 |
|---|---|
| **客服 Agent + RAG 这条线** | `docs/14`(接入入口)→ `docs/18`(RAG 三集合路由方案)→ `docs/19`(可运行示例)→ `docs/09`(底座用法与四工具) |
| **整个底座** | 补 `docs/20`(第一版→当前的破坏性改动 + §5 四条尚未修复的偏差 + "跑验收前先停常驻 Worker") |
| **只改某个业务域** | `docs/00` → `docs/05` → `docs/02` → `docs/14` → `docs/19`;行情加 `docs/12`,前端/联调加 `docs/17` |
| **看"现在做到哪了"** | `docs/验收与审计/phase1-acceptance-report.md`(Phase 1 七条验收标准的逐条可复现证据)+ 同目录 `phase1-acceptance-criteria.md`(老师验收标准原文摘录) |
> 注:完整的过程台账(`progress.md`、各 Task 报告、审计报告)在 **`.superpowers/sdd/2026-09-10-客服Agent与RAG-qyqy版/`**,
> 但该目录**被 `.gitignore` 忽略**(属工作区过程产物)—— 因此**结论性文档已复制到 `docs/验收与审计/`** 以保证 git 可见。
> 若要查过程细节再去看 `.superpowers/`;日常接手**只需读本文档列出的这些**。
### C. 同主题的重复文档(读一份即可,避免信息冲突)
| 主题 | 唯一权威 | 重复品(仅历史参考) |
|---|---|---|
| Agent 组员接入 | **`docs/14`** | `docs/11`(旧版说明书)、`docs/15`(详细手册)、`docs/16`(入门易懂版)—— 三份已各自在开头标注"以 `14` 为准" |
| 接口说明 | **`docs/05`** | `docs/17`(易懂版,自述"不替代 05") |
### D. ⚠️ 不要用来判断"当前进度"
| 文件 | 为什么 |
|---|---|
| `TODO.md` | **自 2026-09-09 起未随 Phase 1 更新**:5 处"49 张表"(实为 51 张业务表)、T8.1 客服 Agent 整节未勾选但**已交付**、多处标"进行中"其实已完成。**当前进度一律以 `phase1-acceptance-report.md` 为准。** |
| `docs/04` / `06` / `10` / `13` / `99` | 内容**未核对过、可能过期**(自相矛盾 / 结论失效 / 清单不全)。经 2026-09-11 评审**保留**(不再删除),仅作历史参考。**不要用它们判断现状** |
### E. 环境与命令口径(易错点)
- 解释器:本机用 **`.\.venv\Scripts\python.exe`**;架构师环境用 `D:\conda\envs\jr_py313\python.exe`。
两者等价,**各用本机可用的那个**(`.venv` 被 `.gitignore` 忽略、不进仓库,不存在"需要统一"的问题)。
- 数据库现为 **90 张表**(含 `alembic_version`)= **89 张业务表** =
**场内 51 + 场外/推广 17 + 投顾 21**。
后 38 张(`offsite_*` / `promotion_*` / `advisor_*`)**不进 `docs/00` 基线**(规则 8):
场外/推广那 17 张逐表登记见 `docs/28-场外与推广域数据表登记.md`;
**投顾那 21 张的登记文档待补**(按同样口径另立一份)。
核验命令:`python tools/audit_schema.py`(若报 `unexpected` 先分清是"库里多表"还是"迁移没进来")。
- 已注册业务 Agent(**7 个**,见 `app/service/agent/implementations/` 与 `app/service/agent/`):
`FundQueryDemoAgent`、`CustomerServiceAgent`、`RiskAgent`、`PlatformProbeAgent`、
**`AdvisorAgent`**、**`OffsiteFundAgent`**、**`PromotionMaterialAgent`**。
- 已注册公共只读工具:`search_knowledge`(客服知识检索)、`check_suitability`、`query_customer_profile`(画像)、`query_fund_quote`;
**`query_knowledge` 是 `search_knowledge` 的别名**(同一 handler,为兼容一期发布配置与旧客户端保留,见 `bootstrap.py`);
其余业务线工具(风控、投顾、NL2SQL)按各自 Agent 白名单注册,全部在 `bootstrap.py` 的 `get_agent_factory()` 里。
**工具可用范围 = 代码上限 ∩ 当前 active `config_release` 的发布白名单**,缺发布配置则失败关闭。
- ⚠️ **RBAC 权限码的定义源是 `tools/seed_test_rbac.py` 的 `PERMISSIONS`**(9001-9046 号段):
那个脚本是 **DELETE 重建**语义(`DELETE FROM sys_permission WHERE id BETWEEN 9001 AND 9099`),
**没并进它的权限码重建一次就没了**,表现是"接口突然 403"而没有任何报错线索。
`tools/grant_*.py` 只补种子里缺的,且 id 必须与种子**逐条一致** ——
一致性由 `python tools/check_rbac_seed_consistency.py` 及其单测守着
(2026-09-12 曾因两套 id→code 映射并存,让 `advisor` 在种子重建后静默拿到语义错误的权限)。
另注:`sys_user` 已改为「存在则更新、不存在才插入」,故重跑种子**不会**再弄丢演示密码。
- ⚠️ **`config_release` 是环境数据,不随代码合并**:本机 active 版本 id 与架构师环境**不同**
(本机是我方发布的客服白名单;他那边还有风控的 9 条白名单)。**"白名单已发布"必须带环境限定**,换环境要重发。
发布脚本 `tools/publish_customer_service_config.py`(**同 key 的继承项必须被本次定义覆盖**,否则旧值会被子集校验 422 拦下整次发布)。
- ⚠️ **Milvus 集合 schema 也因环境而异**:本机是 `knowledge_id`/`snippet`(无 `visibility`),
架构师环境是 `doc_id`/`content`/`visibility`/`chapter`…。**检索层已改为运行时探测字段名**
(`app/core/knowledge_schema.py`)——**不要在任何地方硬编码字段名**,那会把另一套环境打挂。
- ⚠️ **Docker Desktop 不会常驻**:它没运行时 Milvus 不可用(`docker` CLI 报连不上守护进程)。
跑真机验证前先确认 Docker Desktop 在运行。
- ⚠️ **`memory_sync_outbox` 的取值必须是小写英文**(`milvus`/`neo4j`、`upsert`、
`pending`/`failed`/`processed`/`dead`)。`docs/00` §6.4.6 那一栏曾写作大写
`MILVUS`/`NEO4J`、`UPSERT` + 中文 `待处理`,**与全仓实现从未对齐,照它写会静默失效**:
消费端按 `handlers.get(target_store)` 分派、且只领 `status in {"pending","failed"}`,
大写 + 中文两个条件都不满足 ⇒ **事件任何消费者都领不到、永久滞留且不报错**
(唯一键 `(event_uuid, target_store)` 对大小写无约束,MySQL 也不报错)。
取值口径以**主干既有读取方**为准(`projection_reconciliation_service.py`、
`graph_projection_worker.py`),不是文档。详见 `docs/37-记忆投影链路实现说明.md`。
- ⚠️ **一张表只能有一个 ORM 类**:`app/model/` 下曾出现**两个类都映射 `profile_snapshots`**
(`profile.py` 与 `risk_questionnaire.py`),各自单独导入都没事,**同时导入即抛**
`InvalidRequestError: Table 'profile_snapshots' is already defined for this MetaData instance`
—— Worker 既要重建画像又要处理投顾问卷,因此**真的被打挂过**(库里 `memory_sync_outbox`
留下 `last_error='InvalidRequestError'` 的行)。2026-09-12 已修为 re-export,见 `docs/37` §6.2。
**新增模型前先搜一遍 `__tablename__` 有没有被占用。**
- 测试基线(**2026-09-12 合并主干 PR #7 + 本线记忆投影链路之后实测**):
`mypy app` → **245 个文件 0 错**;
`pytest tests`(全量)→ `2 failed, 1415 passed, 2 skipped`;
`pytest tests/integration` → `102 passed, 1 skipped`。
⚠️ **用例数会随开发增减,判断健康看"0 failed"而不是看绝对值**。
剩下那 2 个失败**都不是代码缺陷**,接手时不要"修"它们:
`tests/unit/service/test_offsite_document_recognition_adapter.py` 的 2 个用例 —— **环境相关**:
它们断言请求体里是中文原文,而 httpx 会把中文序列化成 `\uXXXX`,字节序列自然不匹配。
功能无影响;若要修,正确做法是断言 `json.loads(body)` 后的字段值(字节级断言不该用来测 JSON)。
- **mypy:`mypy app` → `Success: no issues found in 245 source files`(0 错)。**
⚠️ 曾在本机报 184 个错,**已查明是环境版本旧**,与代码质量无关 —— 复现矩阵:
| SQLAlchemy | mypy | 报错数 |
|---|---|---|
| 2.0.34(本机旧) | 1.14.1 | **173** |
| 2.0.34 | 1.20.2 | 173 |
| 2.0.52 | 1.14.1 | **6** |
| 2.0.52 | 1.20.2 | **0**(当前) |
⇒ 主因是 **SQLAlchemy 的补丁版本**(旧补丁版类型标注不完整,`BIGINT`/`DATETIME` 被判成未类型化
函数,`app/model/*.py` 每个列定义报一条)。出现"一边上百个错、另一边 0 错"时**先对版本**,
别当代码质量问题;根因是某一侧的虚拟环境没满足 `pyproject.toml` 的
`sqlalchemy>=2.0,<3` / `mypy>=1.14,<2`。
**不要装 `sqlalchemy2-stubs`** —— 那是给 SQLAlchemy **1.4** 用的,2.0 自带 `py.typed`,
装上会按 1.4 API 核对 2.0 代码、换一批新错(`mapped_column` / `DeclarativeBase` 不存在)。
`pyproject.toml` 的 `sqlalchemy>=2.0,<3` 允许范围内补丁版差异会造成量级差异;
若门禁数字要求稳定,需把 SQLAlchemy 钉到具体补丁版(属公共约定,改前先问)。
- ⚠️ **`MILVUS_LOCAL_URI` 配了就会"看着正常、查的是另一个库"**:一旦在 `.env` 里设置它,
健康检查与部分检索链路会指向本地 **Milvus Lite 文件**。团队/生产环境请**保持该变量为空**。
对应的 `milvus-lite` 属**本地开发依赖**,应放在 `pyproject.toml` 的
`optional-dependencies`,**不要进主 `dependencies`**。
- 集成测试前置(**不跑这两步,`tests/integration` 会有 13 个登录/RBAC 用例因 401 而红**,
容易被误判成代码缺陷):先 `python tools/seed_test_rbac.py`(角色/权限/演示账号),
再 `python tools/set_user_password.py`(演示口令,**非幂等**:重复执行等于重设密码)。
接口联调清单见 `docs/32-平台侧交接与联调准备.md`;主干 PR #7 合并的逐项证据见
`docs/36-PR7合并记录与权限号段修正.md`。