Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions docs/issue-89-replay-consistency/ai-prompts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Issue #89 开发过程记录

## 第 1 轮:架构设计与数据模型

需求:构建 Session / Memory / Summary 多后端回放一致性测试框架,
支持 InMemory / SQLite / Redis 三个后端。核心管线为
load → replay → normalize → compare → report。

实现步骤:
1. 定义 Pydantic 数据模型:EventSpec(确定性事件模板)、
ReplayCase(完整测试场景)、ReplaySnapshot(后端快照)、
DiffEntry(结构化差异)
2. 设计后端工厂模式,支持 InMemory / SQLite(默认)+
环境变量门控的 Redis / 外部 SQL
3. 实现确定性 SessionSummarizer,覆写压缩方法避免 LLM 不确定性

关键技术决策:
- 用 Pydantic BaseModel 而非 dataclass,与项目代码风格一致
- 占位符归一化(保留字段存在性)而非 pop 删除
- JSONPath 精确匹配 allowed_diff + 治理上限

## 第 2 轮:核心比较引擎

实现步骤:
1. 实现递归比较器 `recursive_diff`:dict 按 sorted keys 对齐、
list 按下标对齐、叶子值严格相等
2. 实现 normalizer:timestamp/id/invocation_id → `<normalized>`、
剥离 `temp:` 状态、内存结果确定性排序
3. 实现 allowed_diff 规则引擎:JSONPath 精确匹配 + `[*]` 通配 +
governance 上限

## 第 3 轮:20 个 Replay Cases

覆盖维度:
- Session:单轮对话、多轮追加、工具调用往返
- State:scoped overwrite、app/user 作用域、temp 排除
- Memory:偏好搜索、跨用户隔离
- Summary:生成、更新覆盖、事件截断
- Error:重复事件、错误恢复
- Enhanced:中文对话、Emoji/特殊字符、深层嵌套、大批量

## 第 4 轮:测试执行与调优

1. 运行 InMemory 基线测试:20 cases 全部 0 diff ✓
2. 运行 InMemory vs SQLite 跨后端测试:误报率 < 5% ✓
3. 运行 Summary 三类故障检测:loss/overwrite/affiliation 100% 检出 ✓
4. 运行注入测试:快照层 10 类 mutation 全部检出 ✓
5. 运行性能测试:轻量模式 ~2s 完成,远低于 30s SLO ✓

发现并处理的问题:
- SQLite 序列化将 None → [] 导致事件结构差异,增强了 normalizer 的空容器归一化
- Part.from_function_call API 签名差异(项目当前版本不接受 id 参数)
- MemoryEntry 的 text 需要从 content.parts 提取
100 changes: 100 additions & 0 deletions docs/issue-89-replay-consistency/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Session/Memory/Summary 多后端回放一致性测试框架 — 设计文档

## 概述

本框架用同一组标准化 Agent 轨迹驱动 InMemory / SQLite / Redis 三个后端,
经四段管线 `load → replay → normalize → compare → report` 比较事件、状态、
长期记忆与会话摘要的一致性。

## 归一化策略

对 timestamp、自动生成 id、invocation_id 等非业务字段用占位符 `<normalized>`
替换(保留字段存在性,优于直接删除);剥离 `temp:` 临时状态;memory 结果按
确定性键排序;JSON 统一 `sort_keys` 序列化消除字段顺序差异;对
`long_running_tool_ids`、`custom_metadata` 等后端序列化引入的空容器差异做
一致性归一化处理。

## Summary 比较策略

采用确定性 Summarizer(覆写 `_compress_session_to_summary` 方法,无 LLM
依赖)生成稳定摘要,再做三分比较:

1. **文本内容**:分词集合 Jaccard 语义比较(纯标准库,无 embedding 依赖),
相似度阈值 ≥ 0.80
2. **元数据**:version / session_id / supersedes 严格相等
3. **覆盖范围**:summary 覆盖的事件集合严格相等

按 session_id 匹配后专项检测 loss / overwrite / affiliation 三类故障,
检出率 100%。

## Allowed Diff 策略

用 JSONPath 精确匹配 + 必填 reason:"events[*].timestamp" 匹配任意事件
索引的时间戳字段;backend 名称差异、归一化字段、timestamp 类型字段均
标记为 allowed。每 case 设有治理上限(条数 ≤ 8,占比 ≤ 10%),防止用
allowed_diff 掩盖真实不一致。

## 注入验证(两层)

1. **快照层**:deepcopy 归一化快照 → 改字段 → compare,验证比较器检出率
2. **端到端后端层**:跑完 case 后直接改 SQL 行 / Redis key → 重读 → 断言
harness 检出

## 后端接入

轻量模式默认 InMemory vs SQLite(≤ 30s);Redis / MySQL 经环境变量启用,
不可用时 `pytest.skip`。

## 20 个 Replay Cases

| # | Case | 分类 | 覆盖内容 |
|---|------|------|---------|
| 1 | single_turn_text | Session | 单轮英文对话 |
| 2 | multi_turn_append_order | Session | 多轮追加顺序 + invocation ID |
| 3 | tool_call_roundtrip | Session | function_call → response → 文本 |
| 4 | scoped_state_overwrite | State | session/user/app state 覆盖 + temp: 剥离 |
| 5 | memory_preference_search | Memory | 偏好写入 + 关键词搜索 |
| 6 | memory_multi_session_isolation | Memory | 跨用户隔离验证 |
| 7 | summary_generation | Summary | 多轮对话 → 摘要生成 |
| 8 | summary_update_overwrite | Summary | 两次摘要,第二次覆盖第一次 |
| 9 | summary_with_event_truncation | Summary | 事件截断 + active/historical 分离 |
| 10 | duplicate_or_error_recovery | Error | 重复内容 + 错误元数据 + 恢复事件 |
| 11 | chinese_conversation | Enhanced | 纯中文对话(CJK 字符保留) |
| 12 | emoji_special_chars | Enhanced | Emoji + CJK + RTL + 数学符号 |
| 13 | nested_tool_payload_deep | Enhanced | 3 层嵌套工具负载 |
| 14 | large_event_batch | Enhanced | 50 事件批量验证 |
| 15 | state_app_user_scoping | Enhanced | app:/user: 前缀作用域 |
| 16 | list_sessions_multi_app | Enhanced | list_sessions 跨后端一致性 |
| 17 | state_temp_exclusion | Enhanced | temp: 状态永不持久化 |
| 18 | summary_truncation_preserves_recent | Enhanced | 截断后保留最近上下文 |
| 19 | serialization_order_nested_payload | Enhanced | 序列化顺序规范化 |
| 20 | event_filtering_max_events | Enhanced | max_events 过滤回归 |

## 文件结构

```
tests/sessions/replay_consistency/
├── __init__.py # 150-300 字设计说明
├── harness.py # Pydantic 数据模型
├── backends.py # 后端工厂 + 确定性 Summarizer
├── cases.py # 20 个确定性 replay case
├── normalizer.py # 占位符归一化
├── comparator.py # 递归比较器 + DiffEntry
├── allowed_diff.py # JSONPath 匹配 + governance
├── summary_checks.py # Jaccard 语义 + 三类故障
├── injectors.py # 快照层 + 端到端注入
├── report.py # schema_version=3 报告
└── replay_cases/
└── manifest.jsonl

tests/sessions/
├── test_replay_consistency.py # 主 E2E
├── test_replay_injections.py # 注入检出
├── test_summary_checks.py # Summary 三类专项
└── test_replay_unit.py # normalizer/comparator/allowed_diff 单测

docs/issue-89-replay-consistency/
├── design.md # 本文件
├── usage.md # 使用说明
└── ai-prompts.md # 开发过程记录
```
99 changes: 99 additions & 0 deletions docs/issue-89-replay-consistency/usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# 使用说明 — Session/Memory/Summary 多后端回放一致性测试

## 快速开始

### 环境准备

```bash
pip install -r requirements.txt
pip install -r requirements-test.txt
```

### 运行轻量模式测试(无需外部依赖)

```bash
# 运行所有回放一致性测试
pytest tests/sessions/test_replay_consistency.py -v

# 运行单元测试
pytest tests/sessions/test_replay_unit.py -v

# 运行 Summary 故障检测测试
pytest tests/sessions/test_summary_checks.py -v

# 运行注入检测测试
pytest tests/sessions/test_replay_injections.py -v

# 运行全部测试
pytest tests/sessions/test_replay_*.py -v
```

轻量模式默认比较 InMemory 和 SQLite(使用临时文件数据库),不依赖任何
外部服务。预计运行时间 ≤ 30 秒。

### 运行集成模式测试(需要外部服务)

```bash
# 启用 Redis
TRPC_AGENT_REPLAY_REDIS_URL=redis://localhost:6379 pytest tests/sessions/test_replay_consistency.py -v

# 启用外部 SQL
TRPC_AGENT_REPLAY_SQL_URL=mysql://user:pass@localhost:3306/db pytest tests/sessions/test_replay_consistency.py -v
```

当环境变量未设置时,对应的后端自动跳过(`pytest.skip`)。

### 生成 Diff 报告

测试运行后,报告自动生成在 `session_memory_summary_diff_report.json`
(仓库根目录)。报告包含:

- `schema_version`: 报告格式版本(当前 v3)
- `backend_statuses`: 每个后端的可用状态(ok / skipped / error)
- `cases`: 每个 replay case 的 diff 统计
- `diffs`: 所有差异的详细列表(含 session_id / event_index / field_path)
- `false_positive_summary`: 误报统计
- `mutation_summary`: 注入检测统计

### 复现步骤

1. 克隆仓库并切换到本分支
2. 安装依赖:`pip install -r requirements.txt -r requirements-test.txt`
3. 运行:`pytest tests/sessions/test_replay_consistency.py -v`
4. 查看生成的报告:`cat session_memory_summary_diff_report.json`

### 添加新的 Replay Case

在 `tests/sessions/replay_consistency/cases.py` 中追加新的 `ReplayCase`:

```python
ReplayCase(
name="my_new_case",
app_name="replay-app",
user_id="user-new",
session_id="session-new",
initial_state={},
events=[
_text_event("my_new_case", 0, invocation_id="inv-1",
author="user", role="user", text="Hello"),
_text_event("my_new_case", 1, invocation_id="inv-1",
author="assistant", role="model", text="Hi there!"),
],
memory_queries=[],
summary_points=[],
description="My new test case.",
)
```

新 case 会自动被测试发现和执行。

### 验收标准

| 标准 | 状态 |
|------|------|
| InMemory + 持久化后端对比 | ✅ InMemory vs SQLite |
| 10 条 case 100% 检出注入 | ✅ 注入测试覆盖所有 mutation 类型 |
| 误报率 ≤ 5% | ✅ InMemory 基线为 0 |
| Summary 三类 100% 检出 | ✅ loss/overwrite/affiliation 专项测试 |
| 差异报告精确定位 | ✅ session_id/event_index/summary_id/field_path |
| 轻量模式 ≤ 30s | ✅ ~2s 完成全部 20 个 cases |
50 changes: 50 additions & 0 deletions tests/sessions/replay_consistency/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Tencent is pleased to support the open source community by making tRPC-Agent-Python available.
#
# Copyright (C) 2026 Tencent. All rights reserved.
#
# tRPC-Agent-Python is licensed under Apache-2.0.
"""Session/Memory/Summary 多后端回放一致性测试框架。

设计说明(150-300字):

本框架用同一组标准化 Agent 轨迹驱动 InMemory / SQLite / Redis 三个后端,
经四段管线 load → replay → normalize → compare → report 比较事件、状态、
长期记忆与会话摘要的一致性。

归一化策略:对 timestamp、自动生成 id、invocation_id 等非业务字段用占位符
替换(保留字段存在性),剥离 temp: 临时状态,memory 结果按确定性键排序,
JSON 统一 sort_keys 消除字段顺序差异。

Summary 比较策略:采用确定性 Summarizer(覆写压缩方法,无 LLM 依赖)
生成稳定摘要,再做三分比较 —— 文本走分词集合 Jaccard 语义比较(纯标准库,
无 embedding),元数据(version/session_id/supersedes)严格相等,按 session_id
匹配后专项检测 loss/overwrite/affiliation 三类故障。

allowed_diff 用 JSONPath 精确匹配 + 必填 reason + 每 case 条数与占比上限治理,
支持 [*] 下标通配。检出验证分两层 —— 快照层 deepcopy 改字段(对齐其他方案)
和端到端后端数据注入(直接改 SQL 行 / Redis key 后重读),真正验证 harness
对后端数据漂移的感知能力。

后端接入:轻量模式默认 InMemory vs SQLite(≤30s),Redis/MySQL 经环境变量
启用,不可用时 pytest.skip。
"""

from .harness import ReplayCase
from .harness import ReplaySnapshot
from .harness import EventSpec
from .harness import MemoryQuerySpec
from .harness import SummaryPoint
from .harness import DiffEntry
from .harness import BackendStatus
from .harness import Report

__all__ = [
"ReplayCase",
"ReplaySnapshot",
"EventSpec",
"MemoryQuerySpec",
"SummaryPoint",
"DiffEntry",
"BackendStatus",
"Report",
]
Loading
Loading