## 为什么做这一步 权威文档 74 份此前**只在本机**,评审者 clone 分支后看不到任何设计文档;而仓库里那两份同名目录 是 **2026-09-16 之前的过期副本,连文件名都是旧的**(无体系编号)。本次按「**权威覆盖过期**」入库。 ## 入库内容 | 目录 | 文件数 | 体积 | 说明 | |---|---|---|---| | `客服agent/` | 24 | 0.77 MB | `D2.1`~`D2.6` 对外交付四件套 + 演示脚本/答辩报告 + `_build` 构建工具 | | `开发文档/` | 50 | 2.16 MB | `D1.x` 索引与决策、`D3.x` 方案、`D4.x` 清除与重构留痕、`D5.x` 业务流程、`D6.x` 业务事实基座、`D7.x` 交付物、`D8.x` 规范 | **旧的过期副本整体移除**(`客服Agent执行Todolist.md` → `D2.1-客服Agent执行Todolist.md` 之类 的改名 + 新增 `D2.5`/`D2.6`),入库后目录内容与权威副本**逐文件一致(零差异,已复核)**。 ## 入库前的安全扫描(必须留痕) - 扫描规则:`sk-` 类密钥 / `Bearer` 长串 / `password=`、`api_key=` 赋值 / 会话中出现过的两把明文 key 片段。 - 结论:**真实密钥只出现在 `.env`**(已被 `.gitignore` 命中,未入库);`.env.example` 与 `config/risk.env.example` 只有**空占位**。 - 文档内唯一命中是 `D3.1` 里一处**截断的示例 JWT**(`Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...`), 末尾带省略号,是接口文档的示意值,**不是可用凭据**。
419 lines
46 KiB
Markdown
419 lines
46 KiB
Markdown
# 客服 Agent 重构报告(清除了什么、缺什么、按什么顺序装回去)
|
||
|
||
> **体系编号**:`D4.1` · 域:四、清除与重建留痕 · 编号体系见 `D1.1` §4.0
|
||
|
||
> **编号**:CS-REFACTOR-2026-010
|
||
> **日期**:2026-09-16
|
||
> **性质**:**重构执行报告(v5.1 级修订)** —— 取代 `D3.4-客服Agent重构Todolist.md` v5.0 的 §5 执行看板 / §6 关键路径 / §7 完工判据 / §8 风险登记四节
|
||
> **依据**:`客服重建启动前落点复核与建议-2026-09-16.md`(CS-REFACTOR-2026-009,落点逐项取证)+ `D4.3-客服模块清除执行报告-2026-09-16.md`(CS-PURGE-2026-008,清除事实)
|
||
> **不变的部分**:v5.0 的方法论、批次结构、19 项决策、§1 底座接触面总表、§0 规范依据(N-1~N-14)**全部继续有效**,本报告不推翻它们
|
||
> **取证方法**:逐文件 Glob / Grep / Read,**不采信任何文档表述**
|
||
> **能力边界**:⚠️ 本机无 pytest/ruff/mypy 依赖、无 venv → **本报告所有结论均为「文件系统级 + 读码级」,未经运行时验证**
|
||
|
||
---
|
||
|
||
## 0. 一句话结论
|
||
|
||
**v5.0 的方法论是对的,但它写于模块清除之前,20 项任务的落点已不存在。**
|
||
|
||
**另有一项连带影响需要登记**:`customer_service` 是**唯一**接受 `visitor` 角色的 Agent,因此游客侧的「对话入口」随它一起消失。**但这不是另一条链路,也不需要新建任何「游客 Agent」**——游客与客户**共用同一个 `customer_service` Agent**(见 §2),重建它即自然恢复。
|
||
|
||
---
|
||
|
||
## 1. 复核结论总览
|
||
|
||
对 v5.0 的 46 项逐条取证,结果分三类:
|
||
|
||
| 类别 | 数量 | 处置 |
|
||
|---|---|---|
|
||
| ✅ **落点仍有效,可直接开工** | **26 项** | 照 v5.0 执行 |
|
||
| ⚠️ **落点已消失,落点须改写为「新建」** | **20 项** | 见 §4,本报告已逐项给出新落点 |
|
||
| 🟢 **已完成**(由形态A代为完成) | **1 项**(C-07) | 勾掉 |
|
||
| ⚠️ **v5.0 未写清的既有要求** | **4 项(P-6~P-9)** | 见 §2、§5。**不是新增能力,是「重建客服时必须一并做全」的既有属性** |
|
||
|
||
---
|
||
|
||
## 2. 游客侧对话入口:随客服 Agent 一起消失(**同一件事,非新链路**)
|
||
|
||
> ⚠️ **本节曾在 2026-09-16 被误定性为「游客链路已被切断」的独立重大缺陷。经用户纠正 + 复核 `app/static/portal/README.md:30-33`,该定性**错误**,已按下文更正。**
|
||
>
|
||
> **更正要点**:**游客不需要独立的 Agent**。游客侧只做两件事——① 看公开产品信息(**不经 Agent**,走 `GET /api/v1/products` / P001 → `public_product_service`);② 用客服浮窗问公开信息(**就是 `customer_service` 这一个 Agent**)。因此不存在「游客链路」这个模块,也不存在「游客 Agent」这个待建项。
|
||
|
||
### 2.1 原始设计(仓库原文,权威)
|
||
|
||
`app/static/portal/README.md:30-33`:
|
||
|
||
> 客服浮窗由公开首页、基金产品页和客户工作台统一挂载。访客使用 `/api/v1/visitor-tokens` 获取短期令牌(角色 `visitor`,权限只有 `agent:run` + `knowledge:query`),因此**必须**走 `query_knowledge` 这个工具名;登录客户走 `search_knowledge`。两者都要出现在发布配置的 `agent_tools/customer_service:<intent>` 白名单里,缺哪一条,对应人群就一问即失败。
|
||
|
||
**关键结构**:
|
||
|
||
| 项 | 游客 | 登录客户 |
|
||
|---|---|---|
|
||
| 入口 | 访客令牌(`/api/v1/visitor-tokens`) | 登录 JWT |
|
||
| 角色 | `roles=("visitor",)` | `roles=("customer",)` |
|
||
| **Agent** | **同一个 `customer_service`** | **同一个 `customer_service`** |
|
||
| 工具名 | `query_knowledge` | `search_knowledge` |
|
||
| 白名单 key | **同一条** `agent_tools/customer_service:<intent>` | **同一条** |
|
||
| 挂载点 | `app-shell.js`(公开首页/产品页/客户工作台统一挂载) | 同左 |
|
||
|
||
**结论**:游客与客户**共用一个 Agent、一条白名单 key**,靠**令牌角色 + 工具名**区分。这就是 v5.0 的 **D-04**(档位由 `context.roles` 推导)在设计上的由来。
|
||
|
||
### 2.1b 代码里的第二重印证:游客侧**意图范围更窄**
|
||
|
||
原 `customer_service.py:49-50`(`_cs_purge_backup/...`)把意图分成两档——这正是「游客只要公开信息对话」的直接体现:
|
||
|
||
```python
|
||
BUSINESS_INTENTS = (INTENT_FAQ, INTENT_PRODUCT, INTENT_POLICY, INTENT_SUITABILITY)
|
||
# 公开问答 产品信息 政策解释 适当性裁决
|
||
VISITOR_INTENTS = (INTENT_FAQ, INTENT_PRODUCT, INTENT_POLICY, INTENT_CHITCHAT, INTENT_TRANSFER)
|
||
# 公开问答 产品信息 政策解释 闲聊 转人工
|
||
```
|
||
|
||
| | 客户侧 | 游客侧 |
|
||
|---|---|---|
|
||
| 公开问答 `faq` | ✅ | ✅ |
|
||
| 产品信息 `product_inquiry` | ✅ | ✅ |
|
||
| 政策解释 `policy_explain` | ✅ | ✅ |
|
||
| **适当性裁决 `suitability_check`** | ✅ | ❌ **刻意不给**(需登录、需持仓/测评,且属投资建议边界) |
|
||
| 闲聊 / 转人工 | ✅ | ✅ |
|
||
|
||
**⇒ 因此不需要「为游客建 Agent」**:范围收窄是通过 **`VISITOR_INTENTS` 白名单(同一 Agent 内的意图子集)** 实现的,不是通过独立的 Agent 实现的。重建时这个子集**必须原样保留**——它同时是 MVP 第 1 步「游客无投资建议类内容」的落点之一(与 C-09 配合)。
|
||
|
||
### 2.2 现状(清除后的连带影响,3 处)
|
||
|
||
| # | 事实 | 取证 | 性质 |
|
||
|---|---|---|---|
|
||
| 1 | 已注册 7 个 Agent **无一接受 `visitor`**(`fund_query_demo`/`risk`/`offsite_fund`/`promotion_material`/`platform_probe`/`advisor`/`financial_nl2sql`);`customer_service` 是唯一 `allowed_roles=("visitor","customer")` 者 | `implementations/*.py` + `bootstrap.py:423-452`;原定义见 `_cs_purge_backup/.../customer_service.py:193-206` | **客服被删的必然结果**,非独立缺陷 |
|
||
| 2 | active `config_release`(id=254)9 条 `agent_tools` 中 **4 条是 `customer_service:*` 死条目**,**`query_knowledge`(游客线工具名)一条都没有** | `docs/evidence/release-state.json`(2026-09-11 快照,中证据) | 同上(白名单 key 是 `customer_service:*`) |
|
||
| 3 | `tools/publish_{customer_service_config,profile_tool_whitelist,chitchat_prompt}.py` **三个发布脚本已删** | `tools/` 现存仅 `publish_{advisor_demo,financial_nl2sql,risk_agent}_config.py` | **重建客服的组成部分**(重发配置需要脚本) |
|
||
|
||
### 2.3 无需做的事 / 必须做对的事
|
||
|
||
| | 内容 |
|
||
|---|---|
|
||
| ❌ **无需做** | **不新建「游客 Agent」**、不加 `visitor_id`(决 7)、不为游客单开白名单 key、不新开接口路径。游客的权限域是**凭证不是路径** |
|
||
| ✅ **必须做对** | ① 重建的 `customer_service` 的 `AgentDefinition.allowed_roles` **必须含 `"visitor"`**(照抄原定义:`("visitor","customer")`);② `allowed_tools` **必须同时含 `search_knowledge` 与 `query_knowledge`**;③ 重发的 `agent_tools/customer_service:<intent>` 白名单**两个工具名都要在**(缺 `query_knowledge` → 游客一问即失败);④ 客服浮窗随前端重建时重新挂到 `app-shell.js` |
|
||
|
||
### 2.4 对 MVP 的影响(更正版)
|
||
|
||
| 游客能力 | 现状 |
|
||
|---|---|
|
||
| 浏览公开产品信息(`guest/home`、`guest/products`、`guest/product-detail`) | ✅ **可用**,且**不经 Agent**、不受客服删除影响 |
|
||
| 取访客令牌 | ✅ **可用** |
|
||
| **用客服浮窗对话** | 🔴 不可用(因为 `customer_service` 被删 + 浮窗已删)→ **随客服重建自然恢复** |
|
||
|
||
> **更正**:本报告初稿写「MVP 第 1 步(游客)演示不了」。**更准确的说法是**:第 1 步若只要求「浏览公开产品且无投资建议类内容」→ **现在就能演示**;若要求「游客能向客服提问」→ 需等客服重建 + 浮窗重新挂载。**这属于客服模块自身的工作量,不构成额外前置项。**
|
||
|
||
### 2.5(已清除)
|
||
|
||
> 本节原为「能否不对访客做鉴权」的讨论结论,**已按用户要求清除**,以待「访客与角色分离」的新设计重写。
|
||
> 原文归档:`开发文档/归档-访客鉴权与访客分层讨论-已清除-2026-09-16.md`。
|
||
> 新的鉴权方案建议:`开发文档/D3.3-访客与角色分离的鉴权方案建议-2026-09-16.md`。
|
||
|
||
### 2.6 附:7 个已注册 Agent 的角色矩阵(取证留档)
|
||
|
||
| Agent | `allowed_roles` | 是否接受访客 |
|
||
|---|---|---|
|
||
| `fund_query_demo` | customer, advisor, operator, admin | ❌ |
|
||
| `risk` | risk_operator, admin | ❌ |
|
||
| `offsite_fund` | operator, risk_operator, admin | ❌ |
|
||
| `promotion_material` | operator, admin, super_admin | ❌ |
|
||
| `platform_probe` | admin | ❌ |
|
||
| `advisor` | customer, advisor, operator, admin | ❌ |
|
||
| `financial_nl2sql` | advisor, operator, admin, super_admin | ❌ |
|
||
| ~~`customer_service`~~ | **visitor, customer** | 🗑️ **唯一一个**(重建时必须原样恢复) |
|
||
|
||
取证:`app/service/agent/implementations/*.py` 的 `AgentDefinition.allowed_roles`;`app/service/agent/bootstrap.py:423-452` 的 7 处 `factory.register(...)`;原客服定义见 `_cs_purge_backup/app/service/agent/implementations/customer_service.py:193-206`。
|
||
|
||
**这张表的用途**:它说明「游客的对话能力」**不属于任何独立模块**,而属于 `customer_service` 这一个 Agent 的 `allowed_roles` 与发布白名单。**重建时漏掉 `"visitor"`,游客对话就会静默失效(症状为「请登录」)**——这是 P-9 的全部理由。
|
||
|
||
### 2.7(已清除)
|
||
|
||
> 本节原为「把访客做到服务层」的落点取证与三项新发现,**已按用户要求清除**,以待「访客与角色分离」的新设计重写。
|
||
> 原文归档:`开发文档/归档-访客鉴权与访客分层讨论-已清除-2026-09-16.md`。
|
||
> 新的鉴权方案建议:`开发文档/D3.3-访客与角色分离的鉴权方案建议-2026-09-16.md`。
|
||
|
||
|
||
---
|
||
|
||
## 3. 执行纪律(本报告新增,6 条)
|
||
|
||
这 6 条是我对本次重构的判断,落为纪律。**第 3、5 条与 v5.0 的技术判断不同,请优先看。**
|
||
|
||
### 纪律 1 · 先修订落点,再开工
|
||
|
||
v5.0 有 20 项落点指向已删除的文件。**不修订就开工,最坏情况是有人照着 v5.0 从 git 历史恢复旧实现**——那会把 6 个已核实的缺陷(详见 MODE 记忆与 CS-REFACTOR-2026-006)原样带回来,其中 `_visibility_filter` 的静默降级和恒置 `transfer_required=True` 都是**不报错的**缺陷。
|
||
|
||
> ⚠️ **`F-01` 尤其危险**:v5.0 写的是「修改 `_load_mysql_prior_context()` L98-116 增加主体过滤」。该方法已被清除,而**备份里的那份正是缺陷本体**(只按 `session_id` 取历史、跨主体可读)。正确动作是「**在新受理链路里重新实现含主体过滤的版本**」,不是「把备份里的搬回来再加过滤」。
|
||
|
||
### 纪律 2 · 做 D 批次之前,先把依赖装上、跑通 A-02
|
||
|
||
**这是最被低估的风险。** 现在全项目的验证能力只有「610 个 `.py` 语法 0 错误 + 悬空 import 0 条」。**语法正确 ≠ 能跑。**
|
||
|
||
而 `D-02` 的失败模式恰恰是**静默降级**:过滤器表达式改错后 → 缺 `visibility` 的集合被 Milvus 报错 → 被 `except` 吞成 `failures` → `degraded=True` → **客服一律转人工且不报错**。**这种失败,静态扫描 100% 抓不到。**
|
||
|
||
v5.0 每一项都有「验证」列,但 `pytest`/`ruff`/`mypy` 现在全部跑不了 → **那些验证列目前是空头支票**。不补依赖,§7 完工判据第 7 条(门禁干净)无法验收。
|
||
|
||
### 纪律 3 · 顺序:先重建客服,再删投顾 ← 与用户原计划不同
|
||
|
||
用户原计划是「先删投顾,再重建客服」。**建议调整为「先重建客服,投顾留到最后」**,三条理由:
|
||
|
||
1. **投顾是当前唯一完好的业务线**,是重建客服时改动 6 个底座文件的**对照组**。任何回归,必须拿另一个 Agent 做 A/B 才能判断「是我改坏的」还是「本来就坏的」。
|
||
2. **两个模块同时处于「拆了没装回」的状态时,演示一失败,你连它属于哪一层都判断不出来。**
|
||
3. 投顾前端 2045 行,`客服与投顾模块重构前代码清理建议-2026-09-15.md` §四.7 **本就写着「建议单独立项」**——它不该和客服塞在同一批。
|
||
|
||
> 若因答辩时间必须并行:**至少保证投顾的 9 步主干在客服重建期间是绿的**,不要动它。
|
||
|
||
### 纪律 4 · 重建路径选「丙」:备份当参考实现,不当基线代码
|
||
|
||
| 方案 | 好处 | 代价 | 结论 |
|
||
|---|---|---|---|
|
||
| 甲 · 全量恢复 `_cs_purge_backup/` 再改 | 起步快 | 把 6 个已核实缺陷原样带回 | ❌ |
|
||
| 乙 · 完全从零写 | 干净 | 丢掉 748 行里大量「**为什么这么写**」的决策注释——**那是资产**;且要重新踩坑 | ❌ |
|
||
| **丙 · 先做 D 批次护栏 → 再写业务层,写时对照备份** | 护栏先有、注释可搬、**缺陷不回流** | 需人工甄别「哪些注释该搬」 | ✅ **建议** |
|
||
|
||
**具体做法**:每搬一段代码前问一句——「这段是不是那 6 个已核实缺陷之一?」(`_visibility_filter` 无差别应用 / 恒置 `transfer_required=True` / 访客随机 id 写进 `customer_id` / 工单 `priority` 无赋值 / `_references()` 死代码 / 访客侧「不得提供投资建议」零实现)。
|
||
|
||
### 纪律 5 · 立刻做两件「不依赖任何代码」的事
|
||
|
||
| 动作 | 为什么现在就能做 |
|
||
|---|---|
|
||
| **`F-06` 产品证据链 dry-run** | 🔴 **当前唯一硬阻断点**(方案里无产品 → 演示第⑥⑦步跑不出 `candidate_count > 0`)。脚本 `tools/sync_nanfang_official_product_governance.py` **已确认存在**,dry-run 不依赖任何重建。**越早跑越有余量走降级阶梯** |
|
||
| **`B-01` + `B-02` 语料修正** | 纯数据层(`knowledge/` 源文件),**不碰代码、不依赖会签**。语料里 308 块自称「南方科技」、66 块含 `400-XXX-XXXX` —— 这些是**客户可见**的错,且重建后要重灌库,**必须先修再灌**(串行约束 S-1) |
|
||
|
||
### 纪律 6 · 本报告不新增任务编号,用前置项 P-6~P-9 承载
|
||
|
||
v5.0 有「附录A 编号映射」的纪律(编号变动会让既有引用失效)。因此新增事项**不插进 A~F 批次**,而是作为**全局前提 P-6~P-9**(承接 v5.0 的 P-1~P-5),在 §5 定义。
|
||
|
||
---
|
||
|
||
## 4. 修订后的执行清单(v5.1)
|
||
|
||
### 4.1 批次 A · 冻结与准备(10 项)
|
||
|
||
| ID | 任务 | v5.0 落点 | **v5.1 落点 / 状态** | 变更说明 |
|
||
|---|---|---|---|---|
|
||
| **A-01** | 安全用例集基线复现 | 对**现存客服 Agent**跑 RT-001~010 | ⚠️ **改为「只登记预期、不跑实测」** | **被测对象已不存在**,无从执行。改为:以 `docs/客服Agent一期_合规红队与业务评测集_v1.md` 为准登记「预期结果」,把**实测**整体后移到 `C-06` 之后(重建出 Agent 才有对象) |
|
||
| **A-02** | 门禁数字基线 | 仓库根 | ✅ **不变**,但**必须先装依赖**(纪律 2) | 5 条命令:`ruff` / `mypy app` / `pytest tests/unit tests/contract` / `pytest tests/integration` / `tools/audit_schema.py`(预期 89 表 = 零 DDL 对照) |
|
||
| **A-03** | 配置与环境快照 | `customer_service:*` 的 `agent_tools` 白名单全文 | ⚠️ **快照对象改写**:① 现存 9 条 `agent_tools`(含 4 条 `customer_service:*` 死条目);② **确认 `query_knowledge` 是否在白名单**(当前证据:不在);③ **确认投顾线靠哪条 key 在跑**;④ 三集合字段名与行数;⑤ embedding 模型名 + 维度(预期 1024) | 客服 `agent_type` 已注销 → 其白名单当前**不存在**。快照改为「记录现状 + 标出缺口」,为 `P-7` 重发配置提供对照 |
|
||
| **A-04** | 业务一致性三查基线 | 预期 `15936583816` 代码 **2 处**(`customer_service_rules.py:35` + `governance.py:46`) | ✅ 保留,**但预期值校正为「代码 1 处」**:仅 `app/service/agent/governance.py:47` | 另一处随模块删除。**若不校正,基线数字对不上会被误判成「改坏了」** |
|
||
| **A-05** | 演示前检查清单固化 | `docs/44-演示流程.md` 同款 | ✅ 不变 | 产物是文档;执行在 F-04 |
|
||
| **A-06** | 分支与 PR 策略 | `qyqy_develop` → `cs-refactor` | ✅ 不变(⚠️ 本机 git 写入受沙箱限制,需用户在正常终端执行) | 底座会签项单独一个 PR |
|
||
| **A-07** | 🔴 访客链路可用性实测 | 访客令牌 → `agent-runs` → `query_knowledge` | ⚠️ **改为「实测前置条件 + 待执行标记」**:① 本项的四条观测点(白名单是否含 `query_knowledge`、是否抛 `ForbiddenAgentError`、`context.user_id` 形态、日志能否区分「配置错」与「正常引导登录」)**全部保留**;② 但**实测时机后移**——`customer_service` 重建前无对象可测(游客对话 100% 失败,且原因是「Agent 未注册」,测不出别的);③ 本项在 A 批次内的产出改为:**确认 §2.2 的 3 处现状 + 把四条观测点登记为 P-7/P-9 的验收条目** | 观测点保留、时机后移。同时记录「哪些日志能区分『配置错』与『正常引导登录』」(承接 E-02) |
|
||
| **A-08** | 前端约束与配合点复核 | `docs/41-...前端开发约束_v1.md`、`widget.js`、`app-shell.js` | ⚠️ **改写**:前两个文件**已删除**;核对面收窄为 `app-shell.js` + `guest/**` 页面 + **`visitor-token.js`**;新增产出「**重建前端时须重新产出一份约束文档**」 | 结论可以是「0 项」,但必须是**核对后**的 0 项 |
|
||
| **A-09** | 🔴 《可改文件白名单》落文 | 第②类列了 `customer_service.py` / `customer_service_rules.py` / `session_memory_service` / 转人工三件套 / `customer-service-widget/**` | ⚠️ **改写**:第②类**清空** → 前 4 项已删除,**全部并入第①类「纯新增」**;转人工三件套**移入第④类「禁止修改」**(形态A有意保留的共享资产);第④类**新增** `docs/customer-service-routing-legacy-keywords.md`(重建验收基线,不得删) | 不清理会让白名单指向不存在的文件 |
|
||
| **A-10** | 🔴 底座会签申请单落文 | §1.1 的 6 处 + §1.2 的 1 处条件触发 | ✅ **不变**(6 处落点已逐处确认仍在) | ⚠️ **新增第 4 项受影响 Agent**:`_mask_mobile` 白名单分支(见 P-8) |
|
||
|
||
### 4.2 批次 B · 语料与话术(5 项,串行)
|
||
|
||
| ID | 任务 | v5.0 落点 | **v5.1 落点 / 状态** | 变更说明 |
|
||
|---|---|---|---|---|
|
||
| **B-01** | 🔴 语料入库门禁(四查) | `tools/build_knowledge_chunks.py` | ✅ **不变** | 四条件:占位符 / 品牌白名单(`南方财富`+`南方基金`+`nanfangwm.com`)/ 禁止类目 / **每块 `visibility` 必须显式声明**(承接 D-07 降级) |
|
||
| **B-02** | 🔴 语料一次性修正 | `knowledge/` 5 源文件 + `SOURCES` | ✅ **不变** | 品牌名 / 热线 `400-826-9518` / 服务时间 / HNW 第二章下线 / 新增 `docs/43`+`docs/45` 入 SOURCES |
|
||
| **B-03** | `P1_REPLY` 界面指代修正 | `customer_service_rules.py` + `test_customer_service_rules.py:154` | ⚠️ **改为「新建时即写对」**:两文件均已删除 → 本项并入「重建 `customer_service_rules.py`」的动作,DoD 增加「话术里的页面名必须存在于 `app-shell.js` 链接表」的**新断言** | 从「改」变「写」 |
|
||
| **B-04** | 🔴 重跑灌库 + 四查验收 | `build_knowledge_chunks.py` → `load_knowledge_milvus.py` | ✅ **不变**(六条验收标准全部保留) | ⚠️ 跑前**先停 Worker** |
|
||
| **B-05** | 热线与服务时间单点化 | 五组落点(①真源 `customer_service_rules.py`、②`governance.py`、③数据层种子、④3 个测试、⑤前端) | ⚠️ **重排**:<br>① **「真源」已随模块删除 → 改为「重建时先建真源」**(`CONTACT_PHONE`/`CONTACT_HOURS`);<br>② **`governance.py` 部分提前为 `P-8`**(独立前置,清 `CUSTOMER_SERVICE_HOTLINE` + `_mask_mobile` 分支);<br>③ 数据层 `tools/seed_compliance_baseline.py:99,101` **不变**;<br>④⑤ 3 个测试 + 前端文案**随重建一并写对** | ⚠️ **串行约束 S-2 改写**:原为「B-05 热线定值 → B-02 填值」。现真源未建 → S-2 改为「**P-8 清旧副本 → B-02 填值 → 重建时建真源**」 |
|
||
|
||
### 4.3 批次 C · 规则重构与安全(9 项)
|
||
|
||
| ID | 任务 | v5.0 落点 | **v5.1 落点 / 状态** | 变更说明 |
|
||
|---|---|---|---|---|
|
||
| **C-01** | 拆输入/输出两套常量 | `customer_service_rules.py:47-62` | ⚠️ **改为「新建该文件时即按两套常量写」**:<br>① `ZERO_TOLERANCE_WORDS` **保持原名与 11 条内容**(输入侧零容忍集,名字准确);<br>② 新增 `INPUT_PROMISE_WORDS`(8 条字面承诺词)+ `INPUT_PROMISE_PATTERNS`(句式),由 `hits_zero_tolerance()` 一并消费 | 语义校正(v5.0 §9 校正 5)**继续有效**;只是从「改名/改内容」变成「一开始就写对」 |
|
||
| **C-02** | 输入侧改句式判定 | `customer_service_rules.py` | ⚠️ **改为「新建时即写对」**:三条句式规则(收益率问法限定时 / `安全`+承诺词 / 保留 `YIELD_TRAP_PATTERNS` 四条不动) | 同上 |
|
||
| **C-03** | 🔴 输入侧补安全缺口 | `customer_service_rules.py` | ⚠️ **改为「从留痕文档取回词表后新建」**:<br>① 移植提示词注入 **8 条**(插在 P0 之后、合规拦截之前);<br>② 补「推荐」「收益最高」「纠纷」;<br>③ 补裸词账户问法;<br>④ `_CHITCHAT_MESSAGES`/`_CHITCHAT_PHRASES` 一并重建(原在 `customer_service_routing.py`,该文件已删)<br>🔴 **词表唯一副本 = `group_fqcd_jr/docs/customer-service-routing-legacy-keywords.md` §二,禁止凭记忆重写** | **这是本批次最高风险项**。不取回 → 红队 RT-009/010 必然失败 |
|
||
| **C-04** | 🔴 输出侧补豁免线索〔需会签〕 | `app/core/compliance_context.py:43-48` | ✅ **落点完全有效**(已取证:`NEGATION_CUES` 确不含 `不保本`/`不保收益`/`非保本`) | 补多字短否定式 + 指标名白名单(同句生效);**禁止加裸「不」** |
|
||
| **C-05** | 🔴 输出侧收益比较类规则〔数据层〕 | DB `agent_negative_word` 种子 | ✅ **不变**(零代码)。三条硬约束照旧 | 跑 `test_compliance_seed_mysql.py` 对账 |
|
||
| **C-06** | 🔴 双向验证(放行闸门) | 红队集 + `test_customer_service_rules.py` + `test_compliance_context.py` | ⚠️ **改写**:前两个测试**已删除** → 随重建一并写。**四方向用例一个都不能少**:<br>A 放开(3 条可答)/ B 收紧(5 条诱导性问法仍拒答)/ C 访客(问「推荐一只基金」不产生推介)/ **D 反例守卫**(`本产品不保本` 不判违规;`严禁承诺保本。但这只基金保本` **仍判违规**) | + **A-01 的红队实测在此落地** |
|
||
| **C-07** | 删除一期确定性路由 + 兼容桩 | `customer_service_routing.py`、`customer_service_agent.py` | 🟢 **已完成**(由形态A执行) | `customer_service_routing.py` 在 `_cs_delete_list.txt` 内;`customer_service_agent.py` 已无踪迹。**词表已留痕**(`docs/customer-service-routing-legacy-keywords.md`) |
|
||
| **C-09** | 🔴 访客侧推介边界 | `customer_service_rules.py`、`customer_service.py`、`compliance_context.py` | ⚠️ **改写**:前两个文件已删 → 业务侧「**新建时即实现**」;`compliance_context.py` 部分**归入 C-04 的会签**。<br>🔴 **同时补上 v5.0 漏掉的一环**:访客线**前提是 P-9**(Agent 必须接受 visitor)——**P-9 不做,本项无从验证** | 输入侧识别访客推介句式 + 输出侧按 `subject_type` 分化;客户侧行为不变(回归验证) |
|
||
| **C-10** | `source_references` 定向落地或降级 | 〔甲〕`tool_executor.py` + `governance.py`;〔乙〕`docs/` + 护栏断言 | ✅ **不变**(v5.0 §10 已定稿:请底座方提供,否则降级乙) | ⚠️ **串行约束 S-8 保留**:禁止在知识出口启用 `_references()`,否则整个 run 失败 |
|
||
|
||
### 4.4 批次 D · 架构护栏(7 项)—— **整批落点有效,未受清除影响**
|
||
|
||
> ✅ 已取证:`knowledge_search_service.py` 的 `_visibility_filter`(L138)、`include_internal: bool = False`(L162)、`expression` 应用点(L202)、`_hit_from_row`(L488)**全部原样在位**;`knowledge_tool.py:25` 的 `del context` **仍在**,`search()` 调用**确无 `tiers` 参数**。
|
||
|
||
| ID | 任务 | 落点 | 状态 |
|
||
|---|---|---|---|
|
||
| **D-01** | 检索签名 fail-closed〔需会签〕 | `knowledge_search_service.py:138-163` | ✅ 不变(`include_internal` → `tiers: frozenset[str]`;**不改 `knowledge_contracts.py`**) |
|
||
| **D-02** | 🔴 按集合逐个拼过滤表达式〔需会签〕 | 同上 L138-154 / L202-217 / L488 | ✅ 不变(`filter=expression if schema.has("visibility") else None`;修 L153 不符的注释;`_hit_from_row` 缺字段**不再回落 public**;补独立计数) |
|
||
| **D-03** | over-fetch + 独立指标 | 同上 | ✅ 不变 |
|
||
| **D-04** | 档位判定纯函数 + handler 内取身份〔需会签〕 | `knowledge_tool.py:25` | ✅ 不变(删 `del context`;`context.roles` 推导档位;`visitor → {public}`)**← 与 P-9 直接相关** |
|
||
| **D-05** | 缓存键含档位 | 检索/embedding 缓存 | ✅ 不变 |
|
||
| **D-06** | 访客主体口径统一〔需会签〕 | `agent_run_application_service.py` + `agent_persistence_service.py:93` | ✅ 不变(访客 `customer_id = NULL`,零 DDL) |
|
||
| **D-07** | API 入库路径档位(已降级) | 不碰 `milvus_knowledge_writer.py`;落点为 B-01 第四查 + `docs/` 登记 | ✅ 不变 |
|
||
|
||
### 4.5 批次 E · 业务闭环与出口(8 项)
|
||
|
||
| ID | 任务 | v5.0 落点 | **v5.1 落点 / 状态** | 变更说明 |
|
||
|---|---|---|---|---|
|
||
| **E-01** | 🔴 工单建单 + priority + reason_code〔需会签〕 | `agent_persistence_service.py:82-153`(底座,✅有效)+ `customer_service.py:685-700` + `customer_service_rules.py` | ⚠️ **底座侧不变**;业务侧两文件**已删除** → 「新建时即实现」。<br>🔴 **白名单必须限定 `run.agent_type == "customer_service"`**(否则改到风控/投顾) | 底座 `complete_run` 未被触碰,取证通过 |
|
||
| **E-02** | `auth_required` 运行时意图 | `customer_service.py` | ⚠️ 改为「新建时即实现」 | 复用既有出口 `_guide_to_login(reason)`;按 reason 区分日志 |
|
||
| **E-03** | 工单可见方落文 | `docs`(无需新增端点/权限码) | ✅ 不变 | — |
|
||
| **E-04** | 适当性「不匹配告知 + 主动确认」 | `customer_service.py` `_answer_suitability` else 分支 | ⚠️ 改为「新建时即实现」 | 留痕落 `conversation_message.tool_calls` + `interaction_audit`(零 DDL) |
|
||
| **E-05** | `_topic_of` 文本反解收口 | `customer_service.py` 出口结构 | ⚠️ 改为「新建时即实现」:出口**显式声明主题** | 该机制已咬 5 次 |
|
||
| **E-06** | 长答案截断提示 | `customer_service.py:339` | ⚠️ 改为「新建时即实现」(`POL-AST-009` 长 2828 字符且高频命中) | 加提示或走「整节 + 可追问」 |
|
||
| **E-07** | ⏸ `registered` 档位内容标注 | `build_knowledge_chunks.py` 的 `SOURCES` | ⏸ **保持挂起**(决 10) | 唯一候选已因合规下线 |
|
||
| **E-08** | 🔴 三条红线客服侧对照验证 | `customer_service.py`、`customer_service_rules.py`、`docs/` 留痕 | ⚠️ 落点改为「新建的产物 + 留痕」 | 缺口按「**补拦截**」而非「补话术」处理 |
|
||
|
||
> 📌 **E-02/E-04/E-05/E-06/E-08 同文件**——它们现在不是「改同一个文件」,而是「**写同一个文件**」。**建议合并为一次成型**(写 `customer_service.py` 时一次把 5 项都做进去),而不是分 5 次改。
|
||
|
||
### 4.6 批次 F · 演示与收口(7 项)
|
||
|
||
| ID | 任务 | v5.0 落点 | **v5.1 落点 / 状态** | 变更说明 |
|
||
|---|---|---|---|---|
|
||
| **F-01** | MySQL 回退路径主体隔离〔需会签〕 | `agent_run_application_service._load_mysql_prior_context()` L98-116 | 🔴 **改写**:该方法**已被形态A删除**。→ 「**在新受理链路里重新实现**含主体过滤的版本」,主体判据**必须调用 `app/core/memory_scope.py`**(N-9)。<br>🔴 **禁止从 `_cs_purge_backup/` 恢复旧实现**——那份**正是缺陷本体**(只按 `session_id` 取、跨主体可读) | **本报告最需要提醒的一项** |
|
||
| **F-02** | 访客专项端到端验证 | 手工清单 + 脚本 | ✅ 保留,**前置 = P-7 + P-9**(两者缺一,五条全跑不了) | 越权 / 限流 / 会话隔离 / 登录后上下文继承 / **推荐类请求不产生投资建议** |
|
||
| **F-03** | 🔴 MVP 九步演示脚本与彩排 | `docs/44-演示流程.md` 同款 | ✅ 不变 | ⚠️ 第 1 步「游客浏览无投资建议类内容」**若只到「浏览」则不依赖客服**;**若含「向客服提问」则依赖 P-7 + P-9 + 浮窗重新挂载** |
|
||
| **F-04** | 演示前五项自检 + 账号速查 | `tools/seed_*.py` | ✅ 保留;⚠️ `tools/seed_demo_data.py` 已移除客服两步,**账号速查表可能缺客服账号** → 需确认 | 用现有 seed 生成客户/访客/管理员三类账号 |
|
||
| **F-05** | 文档回写 v2.3 | `D3.1-客服Agent需求开发文档与设计方案.html` + `docs/**` | ✅ 保留,**新增 5 项必写内容**(见下) | — |
|
||
| **F-06** | 🔴 产品证据链(决 13 · 甲) | `tools/sync_nanfang_official_product_governance.py` | ✅ **不变,且脚本已确认存在** → **立即可跑 dry-run**(纪律 5) | 唯一硬阻断点 |
|
||
| **F-07** | `D7.1-需求文档.html` 24 个失效锚点 | `D7.1-需求文档.html` | ✅ 不变(只改 `href`,不动正文 `id`) | — |
|
||
|
||
**F-05 新增的 5 项必写内容**(v5.0 成文时尚不存在):
|
||
|
||
1. **形态A清除的完整记录**(34 删 / 17 改 + 备份位置 `_cs_purge_backup/`)——否则下一轮读文档的人会以为这个模块「从未有过」;
|
||
2. 🔴 **安全能力当前为零**:提示词注入拦截 8 条 / 凭据披露短语 / 安全关键词路由 + 零容忍词 11 条,**在生效路径上不存在**;词表唯一副本在 `docs/customer-service-routing-legacy-keywords.md`;
|
||
3. 🔴 **游客链路已断**(本报告 §2):无 Agent 接受 `visitor` 角色 + `query_knowledge` 不在发布白名单 + 三个发布脚本已删;
|
||
4. **`governance.py` 热线副本的处置结果**(P-8)与 `PROFILE_CANDIDATE_AGENT_TYPES` 的当前值(空集合,重建后填回);
|
||
5. **重建验收基线两份文档不得被清理**:`docs/客服Agent一期_合规红队与业务评测集_v1.md`、`docs/customer-service-routing-legacy-keywords.md`。
|
||
|
||
---
|
||
|
||
## 5. 需补写清的事项 P-6 ~ P-9(承接 v5.0 的 P-1~P-5)
|
||
|
||
> **定性说明(2026-09-16 更正)**:这 4 项**不是新增能力、也不是独立模块**,而是「**重建 `customer_service` 时必须一并做全的既有属性**」。它们此前散落在 v5.0 的 A-03 / E-02 / F-04 里没说清,或随模块删除而失去载体(P-6/P-7 的脚本被删)。
|
||
> **特别地:不存在「游客 Agent」这个待建项**——游客与客户共用同一个 `customer_service`,见 §2。
|
||
|
||
| # | 事项 | 落点 | DoD | 何时做 | 风险 |
|
||
|---|---|---|---|---|---|
|
||
| **P-6** | **重建客服发布脚本** | `tools/` 新增(模板:`tools/publish_risk_agent_config.py`,其文件头注明「与 `publish_customer_service_config.py` 同源」) | ① 发布 `customer_service:<intent>` 工具白名单;② 发布画像工具白名单;③ 发布闲聊提示词。**三个被删脚本的功能必须补回**,且遵从 `AGENTS.md:163` 的「同 key 继承项必须被本次定义覆盖」(该教训原本就挂在被删的 `publish_customer_service_config.py` 上) | **批次 A 之后、D 之前** | 中(缺它则 active release 无法重发) |
|
||
| **P-7** | **重发 `config_release`** | 环境数据(`config_release` / `platform_config_item`) | 新版配置须:① 移除 4 条 `customer_service:*` 死条目(或随重建替换);② 🔴 **同一条 `customer_service:<intent>` 白名单里必须同时含 `search_knowledge`(客户)与 `query_knowledge`(游客)**——这是 `README.md:30-33` 的明文要求,也是 N-7 的真实原因;③ 确认投顾线 key(`advisor:*` 在 9 条快照里一条都没有,需实测澄清);④ 发布后 A-03 快照对比 | **A-07 之前** | 中高(缺 `query_knowledge` → **游客一问即失败**;且失败表现为「请登录」) |
|
||
| **P-8** | **清除 `governance.py` 的热线孤岛**(原 B-05 ②,提前) | `app/service/agent/governance.py:47` + `_mask_mobile` 分支 L335-342 | 删 `CUSTOMER_SERVICE_HOTLINE = "15936583816"`;`_mask_mobile` 内联为 `return "[手机号已脱敏]"`。<br>**理由(取证)**:`customer_service_rules.CONTACT_PHONE` 已删 → 此常量现为**全仓唯一热线副本**,且分支只对旧号码生效。若重建话术改用新号 `400-826-9518`:① 分支成**不可达死代码**(新号不匹配 `1[3-9]\d{9}`);② **旧号码若出现在输出里会被原样放行、不脱敏** | **重建之前(独立小改动,可立即做)** | 低(无单测断言该常量;脱敏口径只可能更严) |
|
||
| **P-9** | 🔴 **重建的 `customer_service` 必须保持 `allowed_roles=("visitor","customer")`**(**保持原定义,非新增**) | 新建的 `app/service/agent/implementations/customer_service.py` 的 `AgentDefinition` | 照抄原定义(`_cs_purge_backup/.../customer_service.py:193-206`):① `agent_type="customer_service"`;② **`allowed_roles=("visitor","customer")`**;③ `allowed_portals=("api",)`;④ `allowed_tools` 含 `search_knowledge` + `query_knowledge` + `check_suitability` + `query_customer_profile`;⑤ `supported_intents` 六项齐全(faq / product_inquiry / policy_explain / suitability_check / chitchat / transfer_human);⑥ `recalls_customer_memory=False`(客服不隐式召回长期画像——原值,重建须保留);⑦ **`VISITOR_INTENTS` 子集原样保留**(FAQ/PRODUCT/POLICY/CHITCHAT/TRANSFER,**不含 `suitability_check`**,见 §2.1b) | **随客服 Agent 重建时生效** | 🔴 **高**——漏 `"visitor"`,游客对话**静默失效**(症状为「请登录」,与设计如此无法区分) |
|
||
|
||
> **P-9 的取证依据**:`_cs_purge_backup/app/service/agent/implementations/customer_service.py:193-206`(原 `AgentDefinition` 全文)+ 现存 7 个 Agent 的 `allowed_roles` 逐一无 `visitor`(§2.6 矩阵)。
|
||
|
||
---
|
||
|
||
## 6. 关键路径与串行约束(v5.1 修订)
|
||
|
||
### 6.1 关键路径
|
||
|
||
```
|
||
[会签窗口:A-10 提交 → 底座方受理 §1.1 的 6 处] ──┐
|
||
↓
|
||
P-8 → A-02 → A-07 → D-01 → D-02 → D-04 → D-06 → E-01 → E-03 → F-03 → 演示跑通
|
||
↑
|
||
B-01 → B-02 → B-05 → B-04 ───────────────┤
|
||
│
|
||
C-01 → C-03 → C-09 → C-06 ───────────────┘
|
||
|
||
独立支线(不阻塞主干,但阻塞「游客能对话」这条演示要求):
|
||
P-6 → P-7 → A-07(访客实测)
|
||
P-9(随客服 Agent 重建一并生效;F-02 / F-03 第 1 步若含对话则依赖它)
|
||
```
|
||
|
||
**最长链仍为 9 步**(`A-02 → A-07 → D-01 → D-02 → D-04 → D-06 → E-01 → E-03 → F-03`)。
|
||
**新增**:`P-8` 是**可立即独立完成**的小改动(建议今天就做);`P-6/P-7` 是**重发配置所需的脚本 + 配置本身**(客服重建的组成部分,不是额外模块)。
|
||
|
||
### 6.2 串行约束修订(v5.0 九条 → 保留 8 条 + 改写 1 条 + 新增 1 条)
|
||
|
||
| # | 约束 | 变化 |
|
||
|---|---|---|
|
||
| S-1 | B-01 门禁 → B-02 改语料 → B-04 重跑 | ✅ 不变 |
|
||
| S-2 | ~~B-05 热线定值 → B-02 填值~~ | ⚠️ **改写**:**P-8 清旧副本 → B-02 填值 → 重建时建真源**(真源 `customer_service_rules.py` 已删) |
|
||
| S-3 | ~~C-06 验证 → C-07 删除~~ | 🟢 **已消解**:C-07 由形态A完成、词表已留痕。**新约束**:**C-03 必须先取回留痕词表再写**(`docs/customer-service-routing-legacy-keywords.md` §二) |
|
||
| S-4 | D-02 修过滤表达式 → 任何集合加 `visibility`/改字段名 | ✅ 不变 |
|
||
| S-5 | D-01 fail-closed → D-04 传身份 | ✅ 不变 |
|
||
| S-6 | D-03 over-fetch 与 D-01/D-02 同批 | ✅ 不变 |
|
||
| S-7 | 集合 schema 变更后必须重启 API + Worker | ✅ 不变(`get_knowledge_search_service()` 是 `@lru_cache` 单例) |
|
||
| S-8 | 禁止在知识出口启用 `_references()` | ✅ 不变 |
|
||
| S-9 | 🔴 底座 6 处未获会签前不得动手 | ✅ 不变 |
|
||
| **S-10** | 🆕 **`allowed_roles` 未含 `"visitor"` 或白名单未含 `query_knowledge` → 不得宣称「游客线可演示」** | **新增**(游客对话的两处开关,对应 P-7 + P-9) |
|
||
|
||
---
|
||
|
||
## 7. 整体完工判据(v5.1 修订)
|
||
|
||
v5.0 的 10 条**全部保留**,其中 4 条需要调整或补充:
|
||
|
||
| # | 判据 | v5.1 调整 |
|
||
|---|---|---|
|
||
| 1 | 业务一致性三查全绿 | ✅ 不变(0 个 `XXX` / 0 个「南方科技」/ 0 个 `nanfangtech` / 0 个 `400-XXX-XXXX`;`app/ tests/ tools/ knowledge/` 内无 `15936583816`) |
|
||
| 2 | 安全只增不减 | ✅ 不变(红队 RT-001~010 与 A-01 登记基线逐条对比,**无一条变松**) |
|
||
| 3 | 规则三个方向都对 | ✅ 不变 |
|
||
| 4 | 护栏生效 | ✅ 不变(访客检索 `registered` 返回空、客户正常;公开问题召回量与基线一致;混合 schema 与字段名差异两类替身测试通过) |
|
||
| 5 | 访客主体一致 | ✅ 不变(`conversation_message.customer_id IS NULL` = 访客) |
|
||
| 6 | 工单分类正确 | ✅ 不变(**其它 Agent 的建单行为未变**) |
|
||
| 7 | 门禁干净 | ⚠️ **补充**:`ruff` / `mypy` / `pytest` **必须先装依赖才能验**(纪律 2);判据是 **0 failed**(绝对用例数会随重建增减) |
|
||
| 8 | 演示跑通 | ⚠️ **改为条件式**:第 1 步若只要求「浏览公开产品且无投资建议类内容」→ **不依赖客服重建即可演示**;若要求「游客能向客服提问」→ 需 **P-7 + P-9 已完成**,另需客服浮窗重新挂载 |
|
||
| 9 | 红线可举证 | ✅ 不变 |
|
||
| 10 | 🔴 底座纪律达标 | ✅ 不变,**补 1 项证据**:`P-8` 的热线副本已清除(或明确登记为已知未修) |
|
||
| **11** | 🆕 **游客对话已通**(原写「游客链路已恢复」) | **新增**:① `customer_service.Allowed_roles` 含 `"visitor"`(P-9);② `agent_tools/customer_service:<intent>` 白名单含 `query_knowledge`(P-7);③ 访客令牌 → `agent-runs` 返回 202(非 `ForbiddenAgentError`);④ 访客问「我的持仓」被拒、问公开知识可答;⑤ 访客检索只命中 `public` 档 |
|
||
|
||
---
|
||
|
||
## 8. 风险登记(v5.1,重点盯防 8 项)
|
||
|
||
| 排名 | 项 | 最高风险点 | 对策 |
|
||
|---|---|---|---|
|
||
| **1** | 🔴 **P-9** 恢复 `allowed_roles` 里的 `"visitor"` | 重建时照抄现存 7 个 Agent 的写法(都只有 `customer`)→ 漏掉 `"visitor"` → 游客对话**静默失效**,症状为「请登录」,与设计如此**无法区分** | 把 `allowed_roles` 与 `allowed_tools` **原文照抄原定义**写进 A-09 验收清单;`F-02` 第一条即验 |
|
||
| 2 | **D-02** 过滤器按集合逐个拼 | 改错 → 客服**全员转人工且不报错** | 先补两类替身测试再改实现;上线后验「公开问题召回量不变」;保留「缺字段跳过」独立计数 |
|
||
| **3** | 🆕 **C-03** 安全词表取回 | 凭记忆重写 → **词条缩水**,且缩水不报错 | **必须从 `docs/customer-service-routing-legacy-keywords.md` §二 逐条抄**;C-06 与 A-01 基线对比 |
|
||
| 4 | **C-06** 四向验证 | 收窄输入侧放过诱导性问法;访客方向漏;C-04 补宽放过真实承诺 | 四方向用例都必须有;反例守卫(`严禁承诺保本。但这只基金保本` 仍判违规) |
|
||
| 5 | **E-01** 建单白名单 | 不限定 `agent_type` → **改动其它 Agent 的建单行为** | DoD 明确「仅对 `agent_type=="customer_service"` 生效」;把风控/投顾既有建单用例纳入回归 |
|
||
| 6 | 🆕 **P-7** 重发配置(两个工具名都要在) | 只发 `search_knowledge` → **客户可问、游客一问即失败**;且失败表现为「请登录」 | 发布后立刻跑 `A-03` 快照对比;`E-02` 按 reason 区分日志 |
|
||
| 7 | **D-01 / D-04** 档位隔离 | 业务层自行过滤 → 形成第二套实现,其中一套必漏 | **明确否决**在客服模块内新建检索实现(N-2/N-4/N-6) |
|
||
| 8 | **B-04 / F-06** 两次连外部服务 | 环境问题伪装成功能缺陷 | 先跑 A-03/A-05 快照与自检;B-04 前停 Worker;F-06 先 dry-run |
|
||
|
||
---
|
||
|
||
## 9. 已拍板(7 项 · 2026-09-17 全部按建议采纳)
|
||
|
||
| # | 事项 | 我的建议 | 备选 |
|
||
|---|---|---|---|
|
||
| **1** | 是否采用本报告作为 v5.1 执行依据(取代 v5.0 的 §5/§6/§7/§8) | **采用**(20 项落点非改不可) | 直接在 v5.0 上边做边改 |
|
||
| **2** | 是否**先装依赖**跑 A-02 | **装**(否则验证列全悬空、判据 7 无法验收) | 继续只在语法层验证 |
|
||
| **3** | 顺序:客服 ↔ 投顾 | **先重建客服、投顾留到最后**(纪律 3) | 按原计划先删投顾 |
|
||
| **4** | `P-8`(`governance.py` 热线孤岛) | **立即做**(独立小改动,越晚越容易漏)——⚠️ **但见下方澄清** | 随 B-05 一起做 |
|
||
| **5** | 重建路径 | **丙**(先做 D 批次护栏 → 再写业务层,**搬注释不搬逻辑**) | 甲(全量恢复)/ 乙(从零) |
|
||
| **6** | 🆕 **访客权威单点化**(`security.py:92-97` ↔ `runtime.py:179-184` 两份逐字副本) | **并入「访客与角色分离」方案一并定**——见 `开发文档/D3.3-访客与角色分离的鉴权方案建议-2026-09-16.md`(原取证见 `归档-访客鉴权与访客分层讨论-已清除-2026-09-16.md`)。两文件均**不在 A-09 白名单**,改动须单独授权;不做则**访客权限无法实时收紧**、受理与执行身份可能不一致 | 保持两份副本,靠注释 + code review 守 |
|
||
| **7** | 🆕 **`registered` 档测试块**(证据见归档文件 §2.7-五) | **随 C-06 一并造**——否则 617 块全 `public`,「三档已接通」**无法证伪** | 不造,验收只看公开问答召回量不变 |
|
||
|
||
> ✅ **决策记录(2026-09-17)**:用户答复「**按照你的建议**」→ 上表 7 项**全部按「我的建议」列采纳**,不再单选。
|
||
>
|
||
> ⚠️ **关于第 4 项(`P-8`)「立即做」的准确含义**:`P-8` 与 Todolist §1.1 第 4 项(`B-05` 底座侧)**是同一处改动**,
|
||
> 而该文件(`app/service/agent/governance.py`)**属于组 1、须会签**。因此「立即做」的准确动作是
|
||
> **「立即提案」**——把 `P-8` 单独写进会签申请单先提交,而**不是**直接改 `governance.py`。
|
||
> 这是本次唯一需要修正的措辞:`P-8` 在流程上**不独立于会签门**(P-5 / S-9)。
|
||
>
|
||
> 其中第 6 项(访客权威单点化)已从「待授权」升级为**可执行任务**,并**主动扩张了底座接触面**:
|
||
> 它要触碰的文件(`app/core/security.py`、`app/worker/runtime.py`、`app/api/dependencies/auth.py`、`app/service/agent/base.py`)
|
||
> **其中 3 个原本在 Todolist §1.5「零改动清单」内**。因此 Todo 侧已把它独立为**批次 G(组 2 会签)**,
|
||
> 与 §1.1 的组 1 分开提案、分开 PR。详见《客服 Agent 重构 Todolist》v5.1 §1.6 与批次 G。
|
||
>
|
||
> **另外一件事需要你确认**:`A-03` 需要**连库实测**当前 active `config_release`(本报告 §2.2 的数据来自 `docs/evidence/release-state.json` 的 **2026-09-11 快照**,属中证据)。若你能提供本机 MySQL/Milvus/Redis 可连的环境,我可以在下一步把 A-03 一次跑完,把 §2.2 的「读码级证据」升级为「运行时强证据」,同时查清「投顾线当前靠哪条配置在跑」。
|
||
|
||
---
|
||
|
||
## 10. 本次复核的能力边界(诚实声明)
|
||
|
||
| 已做(强/中证据) | 未做 |
|
||
|---|---|
|
||
| **文件系统级取证**:46 项落点是否仍存在 → **强证据** | **未运行任何代码**(无依赖、无 venv) |
|
||
| **角色矩阵取证**:7 个 Agent 的 `allowed_roles` 逐一无 `visitor` → **强证据** | 未连 Milvus / MySQL / Redis 核实数据侧 |
|
||
| **原始设计取证**:`static/portal/README.md:30-33` 明文写「游客走 `query_knowledge`、客户走 `search_knowledge`、**同一条 `customer_service:<intent>` 白名单**」→ **强证据**(仓库原文,非我推断) | 未验证运行时行为(如访客请求实际错误码、白名单实际内容) |
|
||
| **配置快照取证**:`release-state.json` 9 条 `agent_tools` → **中证据**(2026-09-11 快照,非实时) | 未验证「投顾当前靠哪条配置在跑」(A-03 待测) |
|
||
| **代码级核对**:`NEGATION_CUES` / `del context` / `_visibility_filter` / `CUSTOMER_SERVICE_HOTLINE` 逐字确认 → **强证据** | — |
|
||
|
||
**结论**:§1(落点)、§2.1(原始设计)、§2.6(角色矩阵)、§5 P-8/P-9(取证)是**强证据**;§2.2(配置快照)与「某项判断是否仍成立」是**中证据**。§9 表末的 A-03 连库实测通过后,中证据可升级为强证据。**(原 §2.5 / §2.7 已按用户要求清除并归档,其取证同样为强证据,见归档文件。)**
|
||
|
||
---
|
||
|
||
## 11. 勘误记录(本报告自身)
|
||
|
||
| 日期 | 位置 | 原表述 | 更正后 | 原因 |
|
||
|---|---|---|---|---|
|
||
| 2026-09-16 | §0、§2、§5、§7-11、§8-1/6、§4 的 A-07/F-02/F-03 | 定性为「**游客链路已被切断**」,并称「本次重构 = 重建客服 + 修复被切断的游客链路」;P-9 列为「新增前置项」 | 定性为「**游客侧对话入口随客服 Agent 一起消失**(同一件事,非新链路)」;明确「**游客不需要独立 Agent,与客户共用同一个 `customer_service`**」;P-9 改为「**保持原定义**」 | **用户纠正**:游客只需「公开产品信息 + 一个客服对话」。复核 `static/portal/README.md:30-33` 证实:两者共用同一条 `customer_service:<intent>` 白名单 key,靠令牌角色 + 工具名区分 |
|
||
| 2026-09-17 | §9「待你拍板」→「已拍板」;新增批次 G 指引;§4 清单增列 G 组 | 仍以「待拍板 7 项」表述 | 改为「已拍板(7 项全部按建议采纳)」;访客权威单点化升为**可执行任务**,并**显式披露底座接触面扩张**(其 4 个文件中 3 个原在 Todolist §1.5「零改动清单」内) | 用户 2026-09-17 答复「按照你的建议」,并要求落进需求文档 / Todolist / 本报告三份文件;需求文档同步升 v2.3、Todolist 升 v5.1 |
|