文档:审查报告入库 + 全量校对补注

## 新入库(`docs/演示用/`)

- `代码库全面审查报告-2026-09-14.md`
- `代码修改方案-2026-09-14.md`
- `记忆系统排查报告-2026-09-14.md`
- `记忆系统修复文档-2026-09-14.md`
- `文档一致性审计报告-2026-09-14.md`
- `多Worker接入方案-2026-09-14.md`

## 全量校对(32 个既有文档 + `AGENTS.md`)

跨 39 个文件、**1125 insertions / 148 deletions**。

⚠️ **这批改动同样不是本次会话写的**。我抽样核对过性质:是**实质内容补充**而不是
格式/换行转换。例如 `docs/44-演示流程.md` 新增两条"2026-09-14 补注":

- `启动金融Agent平台.bat` 只在**桌面**上,仓库里只有 `启动平台.bat` 这一份
  (两份由同一个 `tools/make_launcher_bat.py` 产出,改完 `start.ps1` 重跑它一起更新);
- `advisor_t`(9020) 与 `offsite_t`(9006) **不在 `tools/seed_test_rbac.py` 的演示用户里**
  (那里只有 `cust_t`/`risk_t`/`admin_t`/`review_t` 四个),由 `grant_*.py` 系列创建,
  **重跑种子不会重建它们** —— 换机器时这两个账号登录失败,要先查 `sys_user` 有没有这两行,
  而不是查密码。

这两条都是对的地方,与我这一路踩到的现象一致(我确实用到了 `advisor_t`/`offsite_t`)。

**我没有逐字审阅全部 39 个文件**,只抽样确认了改动性质与规模。若其中有需要复核的段落,
请指明文件,我逐处核对。
This commit is contained in:
2026-09-14 20:36:00 +08:00
parent c0e5c80929
commit 36c7a9d8d2
38 changed files with 3604 additions and 138 deletions
+28 -3
View File
@@ -7,9 +7,17 @@
> `app/service/knowledge_service.py` 已实现 HMAC-SHA256 引用令牌校验(`_signature` / `_verify_token`,
> 覆盖 token 前四段、`hmac.compare_digest` 定时安全比较,并按发布状态与有效期做二次校验);
> `app/infrastructure/milvus_adapter.py`、`app/infrastructure/milvus_knowledge_writer.py`、
> `app/service/knowledge_retrieval_service.py` 均已落地;`query_knowledge` 已注册为公共只读工具
> `app/service/knowledge_retrieval_service.py` 均已落地;检索工具已注册为公共只读工具
> (`app/service/agent/bootstrap.py` 的 `ToolRegistry`)。Milvus 检索已在 Phase 1 验收中命中
> (score 0.7837)。**方案正文保留为设计参考,不再是待办。**
>
> **⚠️ 2026-09-14 复核补充(三处,读正文前先看)**:
> 1. **工具名**:正式名是 **`search_knowledge`**(`knowledge:reference:read`,面向 customer/advisor/operator/admin);
> **`query_knowledge` 是它的别名**(同一 handler,`knowledge:query`,只面向 visitor/customer),
> 为兼容一期发布配置与旧客户端保留。**两者都必须发布**,缺哪条对应人群就一问即失败。
> 2. **§4.2 的「集合内字段」与步骤 2 的 `output_fields` 默认值已作废**:字段名改为运行时探测
> (`app/core/knowledge_schema.py`),硬编码会打挂另一套 schema 的环境。详见 §4.2 下的 ⚠️ 块。
> 3. **§4.3 的配置项键名以 `RuntimeConfigService` 当前实现为准**,正文那段 `value_json` 是设计稿示意。
---
@@ -149,7 +157,23 @@ class KnowledgeSearchResult(BaseModel):
| `policy_explain` | `fin_policy_collection` | 5 |
- **向量维度固定 1024**(`VECTOR_DIM`,与 Qwen Embedding 输出一致)。维度不符必须失败关闭,不得静默降级为错误结果。
- 集合内字段:`knowledge_id` / `title` / `snippet` / `tags` / `version` / `embedding`。
- ⚠️ **集合内字段名不得硬编码(2026-09-14 修正)**。本文 v1.0 原文写的是「集合内字段:`knowledge_id` / `title` / `snippet` / `tags` / `version` / `embedding`」——**那只在部分环境成立,照抄会打挂另一套环境**。
实现在 `app/core/knowledge_schema.py`:启动后第一次检索时用 `describe_collection` **探测一次**并缓存(`SchemaCache`),再把「逻辑字段名」解析成该集合真实的物理字段名。两套已知 schema 差异:
| 逻辑字段 | 环境甲(本机) | 环境乙(架构师机) |
|---|---|---|
| 文档标识 | `knowledge_id` | `doc_id` |
| 正文 | `snippet` | `content` |
| 可见性 | **无** | `visibility` |
| 来源文件 | **无** | `source_file` |
| 章节 | **无** | `chapter` / `section` / `doc_no` |
Milvus 对不存在的字段直接报错(`field doc_id not exist`)→ 三个集合全失败 → `degraded=True` → 客服一律"引导人工"。因此:
- **逻辑名 → 物理名的候选表是 `knowledge_schema.FIELD_CANDIDATES`**,新增环境只改那张表,不要在检索路径里写字段名字面量。
- `doc_id` / `content` 是**必需**字段(`REQUIRED_LOGICAL_FIELDS`),缺失即判定该集合不可用;其余(`visibility`/`chapter`/…)缺失只是「增强逻辑不启用」,不影响检索。
- 字段映射的顺序有意义:同时存在 `doc_id` 与 `knowledge_id` 时优先 `doc_id`(灌库脚本的正式设计名)。
- 集合 schema 变更后需重启进程或调 `SchemaCache.invalidate()`(缓存的是元数据,刻意不做每次探测)。
### 4.3 配置项设计
@@ -259,7 +283,7 @@ class MilvusKnowledgeClient:
async def search(
self, *, collection: str, vector: list[float], top_k: int,
output_fields: tuple[str, ...] = ("knowledge_id", "title", "snippet", "tags", "version"),
output_fields: tuple[str, ...],
) -> list[dict[str, Any]]:
client = await self._ensure()
try:
@@ -282,6 +306,7 @@ class MilvusKnowledgeClient:
**要点**:
- **只读**:不提供 create/insert/delete;
- **`output_fields` 由调用方传入、不给默认值**:它必须来自 `knowledge_schema.detect_schema(...).output_fields`(即该集合实际存在的物理字段),**不能**写死成 `("knowledge_id", "title", "snippet", ...)` ——那套名字在环境乙不存在,会让每次检索都抛 `field xxx not exist`;
- 延迟导入 + 延迟建连,遵循 `EastmoneyAdapterFactory` 的做法;
- 具体调用签名按 `pymilvus 2.6` 的 `AsyncMilvusClient` 校对(若该版本无异步客户端,则用 `MilvusClient` 配合 `anyio.to_thread.run_sync` 包装,避免阻塞事件循环)。