fix(memory): Resolve multi-turn dialogue defects and address existing test bugs
- Fixed three key defects in multi-turn dialogue handling: improved RAG to utilize merged messages, enhanced intent classification with recent memory, and ensured proper merging of chat and consultation memories. - Closed five existing test bugs unrelated to the current round, including parameter adjustments and assertion corrections in various test files. - Finalized adjustments in the F-β selection process to align race condition handling in test cases, ensuring all relevant tests pass successfully. This update enhances the coherence of multi-turn dialogues and improves the reliability of the testing framework.
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
# 2026-09-14 · 多轮对话三缺陷修复 + 既存测试 bug 收口 + F-β 选A
|
||||
|
||||
> 起因:用户「看看其他 agent 里的构造有没有类似缺陷」。结论:三缺陷是**节点函数设计选择**,
|
||||
> 非 LangGraph 架构问题;全仓唯一历史感知路径是交易续轮(`_trade_context_window` / `_resolve_advisor_tool`)。
|
||||
|
||||
## 做了什么
|
||||
|
||||
| 项 | 职责 | 改动要点 |
|
||||
| --- | --- | --- |
|
||||
| 缺陷① RAG 只用当前消息 | `visitor_service` / `customer_service` | 各加 `_merged_items` / `_merged_memory_text` / `_rag_query`,从已加载 state 合并 consult+chitchat(按 ts 排序),不再回 Redis 重取 |
|
||||
| 缺陷② 意图分类只用当前消息 | `customer_prompts` 等 | INTENT prompt 注入 `{memory}`(合并后的近期记忆) |
|
||||
| 缺陷③ 闲聊+咨询记忆割裂从不合并 | 同上 + `tool_service` / `kb_tools` | RAG query 拼接近期原文(解「那申购呢」省略指代)· `run_tool` 给 `search_knowledge` 转发 `context_window`(原死管道:tool_node 注入但 run_tool 丢弃)· `search_knowledge` 加 `context_window` 参数拼接进 query |
|
||||
| 既存测试 bug 收口(5 处) | `tests/` + `trade_action_service.py` | 均非本轮三缺陷引入,git stash 取证为 pristine HEAD 即失败(见下) |
|
||||
| F-β 选A 收口 | `tests/test_convert_accept.py` | 竞态用例抢占者份额 100→50 对齐 L1 四元组 |
|
||||
|
||||
**问数线 `analyst_agent` 排除**:单发 NL→SQL,无 RAG/意图/记忆,不适用本修法。
|
||||
|
||||
## 5 处既存测试 bug(非本轮引入 · 已取证)
|
||||
|
||||
| # | 文件 | 问题 | 修法 |
|
||||
|---|---|---|---|
|
||||
| 1 | `tests/test_wave3_customer_service.py` | `fake_suit` 缺 `user_message` 形参 | 补形参 |
|
||||
| 2 | `tests/test_kb_tools.py` | `test_query_injected` 断言缺 `_context_window` | 断言补 `"_context_window": ""` |
|
||||
| 3 | `tests/test_wave2_prompts.py` | `VALID_INTENTS` 断言 13,实际 14(含 `trade_action`) | 13→14 |
|
||||
| 4 | `app/service/trade_action_service.py` | **真 bug**:`looks_like_trade_execute` 把「我上个月申购了啥」误判 trade_action | `_TRADE_QUERY_MARKERS` 补过去问法(`了啥`/`过啥`/`了什么`/`过什么`/`了哪些`/`过哪些`) |
|
||||
| 5 | `tests/test_chat.py` | `test_chat` mock 返 4 元组,现 `run_customer_chat` 返 5(+`pending_trade`) | 补 `None` |
|
||||
|
||||
## F-β 选A 收口
|
||||
|
||||
`test_convert_confirm.py` 的 28 条其实已在 `f856ab4`(客户 C3→C4)修掉;仅剩
|
||||
`test_convert_accept.py::test_accept_uk_idem_race_falls_back_to_idempotent` 1 条:
|
||||
L1 同键异体→409 后,竞态用例「抢占者」份额 100≠50 四元组不匹配误走冲突 → 抢占者份额改 50。
|
||||
|
||||
## 验证
|
||||
|
||||
```text
|
||||
pytest tests/test_trade_gateway.py tests/test_trade_flow_service.py \
|
||||
tests/test_trade_action_service.py tests/test_convert_accept.py \
|
||||
tests/test_convert_confirm.py -q # 99 passed / 0 failed
|
||||
```
|
||||
|
||||
## 仍开放
|
||||
|
||||
- **git**:`merger` 工作区大量未 commit(本轮三缺陷修复 + 测试 bug 收口 + F-β 收口,由用户确认提交)。
|
||||
- `docs/memory/MEMORY.md` §0 已落账本轮;`docs/memory/TODO.md` §进行中 已勾选;`docs/memory/ITERATION.md` 已补 09-13/09-14 行。
|
||||
|
||||
## 记忆同步
|
||||
|
||||
- `MEMORY.md` §0 交接清单(多轮三缺陷 + 5 测试 bug + F-β)
|
||||
- `TODO.md` §进行中
|
||||
- `ITERATION.md` 表(09-13 补录 + 09-14 三行)
|
||||
- 本日志
|
||||
@@ -39,3 +39,9 @@
|
||||
| 2026-09-12 | **问数原句模板 + 常用问法 UI**:`match_phrases` · `/template-prompts` · 标星/最近 · 种子问句 | 模板误命中口径 | template_service · web · seed |
|
||||
| 2026-09-12 | **Core 模拟数据 enriched**:净值 **CSI300+beta**(`seed_nav_history.py`)· **08 跨月交易** · 持仓/阈值 DEMO 行保留 | 问数/趋势/demo 更贴近大盘 | `scripts/core/07` `08` · reset.ps1 |
|
||||
| 2026-09-10 | **TODO 日终清单 + 优化 TODO 补全**:硬伤盘点落账 · 2026-09-10 已完成/进行中/将要做 | 用户要求 | TODO · ITERATION |
|
||||
| 2026-09-13 | **客户线交易端到端第四轮(CT-001)**:真浏览器 + 真 HTTP + 真库对账 · P0 鉴权 `infer_roles` 忽略 body.roles · P1 适当性 `simulate_self_service_blocked` · L1 幂等 409 · CT6 赎回 T+2 份额 · **Part A 135 PASS/0 FAIL/1 SKIP/25 INFO · Part B 41 PASS/0 FAIL/0 SKIP/7 INFO(退出码 0)** | 客户线交易 E2E 收口 | MEMORY / TODO / gateway / convert / trade_action |
|
||||
| 2026-09-13 | **端到端第五轮覆盖缺口补测(三条线全补 · 一线一包)**:[AN-003](tests/2026-09-13-analyst-e2e/TEST-LOG-2026-09-13-AN-003.md) 问数 **137/6** · [AD-001](tests/2026-09-13-advisor-e2e/TEST-LOG-2026-09-13-AD-001.md) 理财师 **31/4**(AD8 接缝取证)· [RK-001](tests/2026-09-13-risk-e2e/TEST-LOG-2026-09-13-RK-001.md) 风控 **23/0** · [XC-001](tests/2026-09-13-crosscut-e2e/TEST-LOG-2026-09-13-XC-001.md) 横切 **33/0/1** | 「还有哪块没有端到端跑过」 | MEMORY / TODO / tests 四包 / scripts/dev |
|
||||
| 2026-09-13 | **前端设计栈补强 + 测试基建收口**:Inter 字体 + Radix Colors · 6 个 `test_sprint2_*.py` 陈旧登录契约(`/api/v1`)迁移 `advisor_test_utils.login_token` | 前端一致 + 陈旧契约修复轮 | web / tests |
|
||||
| 2026-09-14 | **多轮对话三缺陷修复(visitor/customer/advisor · 6 文件 +90/−20)**:RAG 只用当前消息 · 意图分类只用当前消息 · 闲聊+咨询记忆割裂从不合并 → visitor/customer 各加 `_merged_*` 合并 consult+chitchat、RAG query 拼近期原文、INTENT 注入 `{memory}`、`run_tool` 转发 `context_window`、`search_knowledge` 接 `context_window`(问数线 `analyst_agent` 单发 NL→SQL 排除) | 用户「看看其他 agent 有没有类似缺陷」 | MEMORY / TODO / visitor / customer / advisor service |
|
||||
| 2026-09-14 | **既存测试 bug 收口(5 处 · 非本轮引入,git stash 取证 pristine HEAD 即失败)**:`fake_suit` 缺 `user_message` · `test_query_injected` 断言缺 `_context_window` · `VALID_INTENTS` 14 非 13 · `looks_like_trade_execute` 把「申购了啥」误判 trade_action(真 bug:补 `_TRADE_QUERY_MARKERS` 过去问法)· `test_chat` mock 返 4 元组现返 5 | 复跑取证 | tests / trade_action_service |
|
||||
| 2026-09-14 | **F-β 选A 收口**:`test_convert_confirm.py` 28 条已在 `f856ab4`(客户 C3→C4)修掉;剩 `test_convert_accept.py::test_accept_uk_idem_race_falls_back_to_idempotent` 竞态用例抢占者份额 100→50 对齐 L1 四元组 → **五文件 99 passed / 0 failed** | L1 同键异体→409 语义 | tests / convert_service |
|
||||
|
||||
@@ -153,6 +153,7 @@
|
||||
**本轮复跑实测 49 passed / 28 failed,与 Phase 0 完全一致** ⇒ **证明是既有问题,不是本轮引入**。这正好实证了上一轮 `MEMORY.md` 里「⚠️ 复跑新发现(需拍板)」的第 ② 条:**「P1 修复可能收窄了业务能力」**。
|
||||
→ **只报告不修**。**更新 2026-09-13:此项已查清** —— 业务上 R4 的"需揭示 → 自助端阻断"**方向正确**(L0 矩阵刻意设三态 + 答辩稿「C3×R4 揭示 block」+ 前端 `canSelfServe:false`),**28 条失败是测试前提过期而非接口缺陷**。完整依据、建议与反向禁忌见 **§8.2**。
|
||||
→ **✅ 已修(2026-09-13 修复轮 · 选 A)**:`test_convert_confirm.py` 客户档位 C3→C4(`C4×R4=allowed`),四文件 **77 passed / 0 failed**;**零业务代码改动**,未补 `forced_full_transfer`(遵守反向禁忌)。
|
||||
→ **✅ 收尾(2026-09-14)**:另 1 条 `test_convert_accept.py::test_accept_uk_idem_race_falls_back_to_idempotent`(L1 同键异体→409 后,竞态用例「抢占者」份额 100≠50 四元组不匹配)→ 抢占者份额改 50 对齐;交易**五文件(含 convert_accept)99 passed / 0 failed**。
|
||||
|
||||
#### **F-γ —— `core_share_lot` 的基线读数与快照不符(**本轮更正口径**)
|
||||
|
||||
@@ -290,6 +291,7 @@
|
||||
> **规则固化(答辩口径同此)**:`C3×R4` / `C4×R5` = **匹配但需揭示** ⇒ **自助渠道阻断并引导线下**,这是**设计意图,不是缺陷**。
|
||||
> **禁止项**:不得给阻断响应体补 `forced_full_transfer` 让测试变绿(=让"阻断"伪装成"受理成功")。选项 B(做揭示书 Modal)**已否决**,若日后要做属独立需求轮。
|
||||
> **✅ 已执行(2026-09-13 修复轮)**:`test_convert_confirm.py` 客户档位 **C3→C4**(`C4×R4=allowed`),四文件 **77 passed / 0 failed**;`CUST_LOW`(C1) 阻断用例保留作 T+1 复核对照;阻断响应体仍**不含** `forced_full_transfer`。
|
||||
> **✅ 收尾(2026-09-14)**:`test_convert_accept.py::test_accept_uk_idem_race_falls_back_to_idempotent` 抢占者份额 100→50 对齐 L1 四元组 → 交易**五文件 99 passed / 0 failed**。
|
||||
|
||||
以下为支撑该结论的原始取证(存档备答辩追问):
|
||||
|
||||
|
||||
@@ -166,3 +166,4 @@
|
||||
| --- | --- | --- | --- |
|
||||
| v1.0 | 2026-09-13 | 本轮补测 | 首次建立:GX1–GX5 + Part B 四支 + Part C 门槛 + 测试基建台账 + Phase 6 还原校验 |
|
||||
| v1.1 | 2026-09-13 | 修复轮 | F-β 选 A 已执行(C3→C4,77/0)· F-ε 已修(模块属性访问 + skip subprocess,零污染)· 6 个 sprint2 陈旧登录契约已迁移 · P0 mock 登录门禁「本轮不动,只记录」 |
|
||||
| v1.2 | 2026-09-14 | 修复跟进 | **F-β 收尾**:`test_convert_accept.py::test_accept_uk_idem_race_falls_back_to_idempotent`(L1 同键异体→409 后竞态用例「抢占者」份额 100≠50 四元组不匹配)→ 抢占者份额改 50,交易**五文件(含 convert_accept)99 passed / 0 failed** · 顺带收口 5 处既存单测 bug(`fake_suit` 缺 `user_message` · `test_query_injected` 缺 `_context_window` · `VALID_INTENTS` 14 非 13 · `looks_like_trade_execute` 把「申购了啥」误判 trade_action · `test_chat` mock 4→5 元组),均 git stash 取证为 pristine HEAD 即失败 |
|
||||
|
||||
@@ -492,8 +492,9 @@ def test_accept_uk_idem_race_falls_back_to_idempotent(sqlite_engine, monkeypatch
|
||||
模拟方式:第一次 `get_by_client_request_id` 返回 None 的同时,让「抢先者」把行插进去
|
||||
—— 这正是 T-13 真库实测过的交错窗口。若无兜底,未映射的 IntegrityError 会直穿 503。
|
||||
|
||||
本笔申请量取 50:抢占者已占 100,可用余额恰为 150 − 100 = 50,
|
||||
这样既穿过了 ⑤ 占用校验,又必然在 ⑥ 撞上 `uk_idem`(否则测不到兜底分支)。
|
||||
本笔申请量取 50,抢占者同取 50:二者四元组一致 → 撞 `uk_idem` 后走
|
||||
「同键同体 → 幂等让路」分支(L1 起同键异体改走 409 冲突,已由
|
||||
`test_accept_same_client_request_id_different_body_raises_conflict` 单独覆盖)。
|
||||
"""
|
||||
_seed(sqlite_engine)
|
||||
winner = {}
|
||||
@@ -501,7 +502,7 @@ def test_accept_uk_idem_race_falls_back_to_idempotent(sqlite_engine, monkeypatch
|
||||
def _race(engine):
|
||||
winner["gid"] = "CNV-WINNER"
|
||||
ConvertRequestRepository(engine=engine).insert(
|
||||
"CNV-WINNER", CUST, PROD_OUT, PROD_IN, Decimal("100"),
|
||||
"CNV-WINNER", CUST, PROD_OUT, PROD_IN, Decimal("50"),
|
||||
client_request_id="REQ-AC-RACE",
|
||||
requested_at=datetime.combine(TODAY, datetime.min.time()),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user