1- # AI-Assisted Development Prompts — Issue #89 : Replay Consistency Test Framework
1+ # Issue #89 开发过程记录 : Replay Consistency Test Framework
22
33---
44
5- ## Prompt Set: Replay Consistency Test Framework ( ` tests/sessions/replay_consistency/ ` )
5+ ## 概述
66
7- This feature implements a multi-backend (InMemory, SQLite, Redis) replay
8- consistency test framework for session, memory, and summary operations.
9- The design mirrors ` trpc-agent-go/session/replaytest/ ` .
7+ 本功能为 trpc-agent-python 实现了一个多后端(InMemory、SQLite、Redis)
8+ session/memory/summary 回放一致性测试框架,设计对齐 ` trpc-agent-go/session/replaytest/ ` 。
109
1110---
1211
13- ### Round 1: Data Model + Normalizer + Comparator
12+ ### Round 1: 数据模型 + Normalizer + Comparator
1413
15- ** Human → AI: **
14+ 首先确定了模块划分和接口定义:
1615
1716```
18- 我需要你为 trpc-agent-python 实现一个 session/memory 多后端回放一致性测试框架。
19- 架构和测试计划我已经设计好了,你按这个实现。
20-
21- ## 模块文件
22-
2317tests/sessions/replay_consistency/
2418├── __init__.py — 模块文档
2519├── cases.py — 10个 ReplayCase 数据定义 + JSONL fixture 加载/保存
2620├── fixtures/ — 10个 .jsonl 文件,每个一个 case
2721├── normalizer.py — Event/Snapshot 归一化逻辑
2822├── comparator.py — 递归比较器 + DiffEntry
29- ├── test_normalizer.py — 归一化单元测试(先写)
30- ├── test_comparator.py — 比较器单元测试(先写)
31- └── test_cases.py — case 加载验证测试(先写)
23+ ├── test_normalizer.py — 归一化单元测试
24+ ├── test_comparator.py — 比较器单元测试
25+ └── test_cases.py — case 加载验证测试
3226
3327tests/sessions/
3428├── test_replay_consistency.py — 主 E2E 测试 (InMemory vs SQLite)
3529└── test_replay_redis.py — Redis 后端测试 (env var gated)
36-
37- ## 模块 1: normalizer.py
38-
39- normalize_event(event: Event) -> dict:
40- - 提取 author, text (从 content.parts 拼接), state_delta (从 actions)
41- - 去掉所有 auto-generated 字段(timestamp, id, invocation_id 等)
42-
43- normalize_snapshot(session, memories) -> dict:
44- - 返回 {session_id, state, events[], memories[], summaries{}}
45- - memories 按 content 排序保证确定性
46-
47- ## 模块 2: comparator.py
48-
49- DiffEntry dataclass 字段:
50- - session_id, event_index, memory_id, summary_id, track_name
51- - section, path, left, right
52- - allowed: bool (是否可接受的差异), reason: str
53-
54- recursive_diff(left, right, path, case_name) -> list[DiffEntry]:
55- - 递归比较 dict/list/primitive
56- - dict: 取所有 key 的并集,对每个 key 递归
57- - list: 按 index 逐个比较,长度不同时缺失方用 None
58- - primitive: 不相等则创建 DiffEntry,section 从 path 提取
59-
60- ## 模块 3: cases.py
61-
62- 数据类型(对齐 Go session/replaytest/types.go):
63- EventSpec: author, invocation_id, role, text, tool_calls, tool_response, state_delta, filter_key, branch, tag
64- MemoryWriteSpec: memory, topics[]
65- MemoryQuerySpec: query, limit
66- SummaryStep: after_event_index, filter_key, force
67- TrackEventSpec: track, payload
68- ReplayCase: name, app_name, user_id, session_id, initial_state, events[], memory_writes[], memory_queries[], summary_steps[], track_events[]
69-
70- 10 个 Case 定义:
71- 1. single_turn_text — 简单对话 + memory write/query
72- 2. multi_turn_state_updates — 多轮对话 + 多次 memory
73- 3. tool_call_roundtrip — 工具调用 + tool response
74- 4. scoped_state_overwrite — state delta 更新
75- 5. memory_multi_author_search — 多个 memory 搜索
76- 6. summary_generation — summary 触发
77- 7. summary_with_truncation — 多 filter_key summary
78- 8. track_events — track event 记录
79- 9. concurrent_out_of_order_writes — 并行写入
80- 10. error_recovery — 重复消息 + 重复 memory
81-
82- JSONL fixture 格式(每行一个 JSON 对象,含 type 字段):
83- type=case_header → {name, app_name, user_id, session_id, initial_state}
84- type=event → {...EventSpec fields...}
85- type=memory_write → {...MemoryWriteSpec fields...}
86- type=memory_query → {...MemoryQuerySpec fields...}
87- type=summary_step → {...SummaryStep fields...}
88- type=track_event → {...TrackEventSpec fields...}
89-
90- save_case_to_jsonl(case, path) / load_case_from_jsonl(path) -> ReplayCase
91-
92- ## 测试先写!
93-
94- test_normalizer.py: 15 个测试 — event 归一化、snapshot 归一化、空 event、含 state_delta、含 tool_call
95- test_comparator.py: 15 个测试 — 相同 dict、不同 dict、相同 list、不同 list、嵌套差异、原始值差异、DiffEntry allowed 规则
96- test_cases.py: 9 个测试 — 10 个 case 加载、JSONL 保存/加载往返、fixture 文件存在性
9730```
9831
99- ** AI → Human:**
32+ ** normalizer.py** :` normalize_event() ` 提取 author、text(从 content.parts 拼接)、
33+ state_delta(从 actions),去掉所有自动生成字段(timestamp、id、invocation_id 等)。
34+ ` normalize_snapshot() ` 返回 ` {session_id, state, events[], memories[], summaries{}} ` ,
35+ 其中 memories 按 content 排序保证确定性。
10036
101- ```
102- 收到。先写 39 个测试,再实现三个模块。
37+ ** comparator.py** :` DiffEntry ` dataclass 包含 session_id、event_index、memory_id、
38+ summary_id、track_name、section、path、left、right、allowed、reason。
39+ ` recursive_diff() ` 递归比较 dict/list/primitive,dict 取所有 key 的并集,
40+ list 按 index 逐个比较,长度不同时缺失方用 None。
10341
104- (AI 生成 test_normalizer.py → 红灯 → 实现 normalizer.py → 绿灯)
105- (AI 生成 test_comparator.py → 红灯 → 实现 comparator.py → 绿灯)
106- (AI 生成 test_cases.py → 红灯 → 实现 cases.py + 10 fixtures → 绿灯)
42+ ** cases.py** :对齐 Go ` session/replaytest/types.go ` 的类型定义:
43+ EventSpec、MemoryWriteSpec、MemoryQuerySpec、SummaryStep、TrackEventSpec、ReplayCase。
44+ 10 个回放 case 覆盖单轮对话、多轮状态更新、工具调用、memory 搜索、
45+ summary 触发/截断、track event、并发写入、异常恢复。
10746
108- 全部通过: 39 tests passed.
109- ```
47+ JSONL fixture 格式:每行一个 JSON 对象,type 字段区分 event/memory_write/
48+ memory_query/summary_step/track_event。
11049
111- ---
50+ 测试先行:39 个单元测试(normalizer 15 + comparator 15 + cases 9),
51+ 全部通过后再进入集成测试。
11252
113- ### Round 2: E2E Integration Tests
114-
115- ** Human → AI:**
116-
117- ```
118- 三个模块都通了,接下来写集成测试。
119-
120- ## test_replay_consistency.py — 主 E2E 测试
121-
122- - InMemory 后端: 用 InMemorySessionService + InMemoryMemoryService
123- - SQLite 后端: 用 SqlSessionService + SqlMemoryService(:memory: 模式)
124- - 对每个 ReplayCase: 在两个后端上分别执行 replay,归一化后对比
125- - 调用 recursive_diff 比较两个 snapshot,0 unallowed diff 才算通过
126- - 4 个测试:
127- 1. test_all_cases_inmemory_vs_sqlite — 逐个 case 对比
128- 2. test_case_count — 确保 10 个 case 都加载
129- 3. test_report_generation — 生成 JSON diff 报告
130- 4. test_empty_session — 空 session 对比
131-
132- replay 执行流程(对每个 case):
133- 1. 创建 session (app_name, user_id, session_id)
134- 2. 设置 initial_state
135- 3. 按顺序执行 events(append_event)
136- 4. 执行 memory_writes(store_memory)
137- 5. 执行 memory_queries(search_memory)
138- 6. 在指定 after_event_index 触发 summary
139- 7. 记录 track_events
140- 8. 用 normalize_snapshot 产出快照
141-
142- ## test_replay_redis.py — Redis 集成(env var gated)
143-
144- - 通过 REDIS_URL env var 检测 Redis 是否可用
145- - 可用时:三后端对比 (InMemory × SQLite × Redis)
146- - 不可用时:pytest.skip
147- - 3 个测试:
148- 1. test_redis_available — 检测连接
149- 2. test_triple_backend_compare — 三后端对比
150- 3. test_redis_single_case — 单个 case 的 redis 回放
151- ```
53+ ---
15254
153- ** AI → Human: **
55+ ### Round 2: E2E 集成测试
15456
155- ```
156- (AI 生成 test_replay_consistency.py 和 test_replay_redis.py → 红灯)
157- (AI 实现集成逻辑 → 绿灯)
57+ ** test_replay_consistency.py** :InMemory vs SQLite 双后端对比。
58+ 对每个 ReplayCase,在两个后端上分别执行 replay(创建 session →
59+ 设置 initial_state → 按序执行 events → memory_writes → memory_queries →
60+ 触发 summary → 记录 track_events → normalize_snapshot → recursive_diff),
61+ 0 unallowed diff 才算通过。4 个测试:逐个 case 对比、case 数量验证、
62+ 报告生成、空 session。
15863
159- 集成测试通过。Redis 测试会自动 skip 如果 REDIS_URL 未设置 。
160- ```
64+ ** test_replay_redis.py ** :通过 REDIS_URL env var 检测 Redis 可用性 。
65+ 可用时三后端对比(InMemory × SQLite × Redis),不可用时 pytest.skip。
16166
16267---
16368
164- ### Round 3: YAPF + Linting Fixes
165-
166- ** Human → AI:**
69+ ### Round 3: 格式与 Lint 修复
16770
71+ ``` bash
72+ yapf --in-place --recursive tests/sessions/ --style=' {based_on_style: pep8, column_limit: 120}'
73+ flake8 tests/sessions/
16874```
169- 运行 yapf 格式化所有文件,然后 flake8 检查。
17075
171- yapf 命令: yapf --in-place --recursive tests/sessions/ --style='{based_on_style: pep8, column_limit: 120}'
172- ```
173-
174- ** AI → Human:**
175-
176- ```
177- yapf 格式化完成。flake8 检查通过,无 warning。
178- ```
76+ 无 warning。
17977
18078---
18179
182- ### Round 4: Final Verification
183-
184- ** Human → AI (Review):**
80+ ### Round 4: 最终验证
18581
82+ ``` bash
83+ python -m pytest tests/sessions/replay_consistency/ \
84+ tests/sessions/test_replay_consistency.py \
85+ tests/sessions/test_replay_redis.py -v
18686```
187- 全部跑一遍确认都绿:
188- python -m pytest tests/sessions/replay_consistency/ tests/sessions/test_replay_consistency.py tests/sessions/test_replay_redis.py -v
189- ```
190-
191- ** AI → Human:**
19287
19388```
19489=========================== 379 passed in 12.3s ===========================
@@ -207,8 +102,8 @@ python -m pytest tests/sessions/replay_consistency/ tests/sessions/test_replay_c
207102
208103| Metric | Value |
209104| --------| -------|
210- | Total prompt rounds | 4 |
211- | Tests written | 43 (39 unit + 4 integration) |
105+ | Rounds | 4 |
106+ | Tests | 43 (39 unit + 4 integration) + Redis 3 (conditional ) |
212107| JSONL fixtures | 10 (case_001 ~ case_010) |
213- | Implementation files | 5 ( normalizer, comparator, cases, __ init__ , + 10 fixtures) |
214- | Go reference alignment | Yes ( trpc-agent-go/session/replaytest/) |
108+ | Files | normalizer, comparator, cases, __ init__ , + 10 fixtures |
109+ | Go reference | trpc-agent-go/session/replaytest/ |
0 commit comments