OpenClaw Reflection 是一个增强 OpenClaw 原生记忆系统的插件。它通过 memory_gate 和 write_guardian 两个核心组件,智能地筛选对话中的重要信息并写入对应的记忆文件(MEMORY.md, USER.md, SOUL.md, IDENTITY.md, TOOLS.md)。
当前系统存在一些限制,需要通过本需求文档规划的功能来解决。
| 需求编号 | 需求名称 | 优先级 | 状态 |
|---|---|---|---|
| REQ-001 | 完整对话持久化存储 | P0 | 待实现 |
| REQ-002 | memory_gate 行为可观测 | P0 | 待实现 |
| REQ-003 | write_guardian 行为审计与回滚 | P0 | 待实现 |
| REQ-004 | 标注数据集生成 | P1 | 待实现 |
主存储:SQLite —— 支持高效查询、事务和索引
<workspaceDir>/.openclaw-reflection/
├── reflection.db # SQLite 主数据库
├── reflection.db-wal # WAL 模式日志文件
├── reflection.db-shm # WAL 模式共享内存
│
└── exports/ # 可选的 JSONL 导出目录
├── conversations-2026-03-11.jsonl
├── dataset-sft-v1.jsonl
└── ...
| 维度 | JSONL | SQLite |
|---|---|---|
| 查询速度 | 需全文件扫描 | 索引加速,毫秒级 |
| 关联查询 | 无法实现 | JOIN 轻松关联多表 |
| 更新记录 | 几乎不可能 | UPDATE 原子操作 |
| 聚合统计 | 遍历全部数据 | 秒级 COUNT/GROUP BY |
| 事务安全 | 无 | ACID 保证 |
| 回滚支持 | 需手动实现 | 内置事务回滚 |
| 数据完整性 | 弱 | 外键约束 |
interface SQLiteStorageConfig {
enabled: boolean; // 默认: true
dbPath: string; // 默认: ".openclaw-reflection/reflection.db"
walMode: boolean; // 默认: true (提升并发写入性能)
autoVacuum: boolean; // 默认: true (自动清理碎片)
busyTimeoutMs: number; // 默认: 5000 (等待锁的超时时间)
// 性能调优
cacheSizePages: number; // 默认: -2000 (2MB 缓存)
mmapSizeMB: number; // 默认: 256 (内存映射大小)
// 数据保留策略
retentionDays?: number; // 可选: 自动清理 N 天前的数据
// JSONL 导出(可选)
jsonlExport: {
enabled: boolean;
schedule?: string; // Cron 表达式,如 "0 0 * * *" 每天导出
retentionDays: number; // 导出文件保留天数
};
}- 目前只有
FileLogger记录的日志(reflection-YYYY-MM-DD.log) SessionBufferManager使用CircularBuffer,消息会被驱逐- 调试用的
debug.json只保存最新一条消息
将所有对话完整存储到 SQLite,为后续的数据分析和模型训练提供原始数据。
-- 会话表:记录会话元数据
CREATE TABLE sessions (
id TEXT PRIMARY KEY, -- sessionKey (如 channel:xxx:yyy)
channel_id TEXT NOT NULL, -- 频道/渠道标识
conversation_target TEXT, -- 对话目标(用户/群组)
account_id TEXT, -- 账号标识
created_at INTEGER NOT NULL, -- 创建时间戳(毫秒)
last_activity_at INTEGER NOT NULL, -- 最后活动时间
metadata TEXT, -- JSON 格式的额外元数据
INDEX idx_channel (channel_id),
INDEX idx_last_activity (last_activity_at)
);
-- 对话回合表:记录每一轮对话
CREATE TABLE conversation_turns (
id TEXT PRIMARY KEY, -- ULID
session_id TEXT NOT NULL, -- 外键关联 sessions
turn_number INTEGER NOT NULL, -- 会话内递增序号
-- 用户消息
user_message_id TEXT, -- 原始消息 ID
user_message_content TEXT NOT NULL, -- 消息内容
user_from TEXT, -- 发送者标识
user_metadata TEXT, -- JSON 格式元数据
-- Agent 回复
agent_message_id TEXT, -- 原始消息 ID
agent_message_content TEXT NOT NULL, -- 消息内容
agent_metadata TEXT, -- JSON 格式元数据
created_at INTEGER NOT NULL,
UNIQUE(session_id, turn_number), -- 确保回合号唯一
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE,
INDEX idx_session_turn (session_id, turn_number),
INDEX idx_created_at (created_at)
);
-- 对话上下文窗口表:存储每个回合的上下文(用于重现场景)
CREATE TABLE conversation_contexts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
turn_id TEXT NOT NULL, -- 外键关联 conversation_turns
role TEXT NOT NULL CHECK(role IN ('user', 'agent')),
message TEXT NOT NULL,
timestamp INTEGER NOT NULL,
sequence_number INTEGER NOT NULL, -- 上下文中的顺序
FOREIGN KEY (turn_id) REFERENCES conversation_turns(id) ON DELETE CASCADE,
INDEX idx_turn_sequence (turn_id, sequence_number)
);interface SessionRecord {
id: string; // sessionKey
channelId: string;
conversationTarget?: string;
accountId?: string;
createdAt: number;
lastActivityAt: number;
metadata?: Record<string, unknown>;
}
interface ConversationTurn {
id: string; // ULID
sessionId: string;
turnNumber: number;
userMessage: {
id?: string;
content: string;
from?: string;
metadata?: Record<string, unknown>;
};
agentReply: {
id?: string;
content: string;
metadata?: Record<string, unknown>;
};
createdAt: number;
}
interface ConversationContextItem {
id: number;
turnId: string;
role: "user" | "agent";
message: string;
timestamp: number;
sequenceNumber: number;
}class ConversationStorage {
// 实时写入会话(UPSERT 更新最后活动时间)
async upsertSession(session: SessionRecord): Promise<void>;
// 实时写入对话回合
async insertTurn(turn: ConversationTurn): Promise<void>;
// 批量写入上下文(使用事务批量插入)
async insertContextBatch(
turnId: string,
context: Array<{ role: "user" | "agent"; message: string; timestamp: number }>
): Promise<void>;
// 查询某会话的所有回合
async getTurnsBySession(
sessionId: string,
options?: { limit?: number; before?: number }
): Promise<ConversationTurn[]>;
// 查询某回合的完整上下文
async getContextByTurn(turnId: string): Promise<ConversationContextItem[]>;
}memory_gate的决策只通过FileLogger记录- 无法直观查看某个会话中
memory_gate的完整决策历史 - 无法关联决策与原始对话上下文
提供完整的 memory_gate 行为追踪,通过 SQLite 支持高效查询。
-- memory_gate 决策记录表
CREATE TABLE memory_gate_decisions (
id TEXT PRIMARY KEY, -- ULID
turn_id TEXT NOT NULL, -- 外键关联 conversation_turns
session_id TEXT NOT NULL, -- 冗余存储,便于查询
-- 决策结果
decision TEXT NOT NULL CHECK(decision IN (
'NO_WRITE', 'UPDATE_MEMORY', 'UPDATE_USER',
'UPDATE_SOUL', 'UPDATE_IDENTITY', 'UPDATE_TOOLS'
)),
reason TEXT NOT NULL,
candidate_fact TEXT, -- 候选事实(非 NO_WRITE 时必填)
-- LLM 调用信息
llm_model TEXT NOT NULL,
llm_latency_ms INTEGER,
prompt_tokens INTEGER,
completion_tokens INTEGER,
raw_response TEXT, -- 原始 LLM 响应(调试用)
created_at INTEGER NOT NULL,
FOREIGN KEY (turn_id) REFERENCES conversation_turns(id) ON DELETE CASCADE,
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE,
INDEX idx_session_time (session_id, created_at),
INDEX idx_decision (decision),
INDEX idx_turn (turn_id)
);
-- memory_gate 决策统计表(物化视图或定期更新)
CREATE TABLE memory_gate_stats (
date TEXT PRIMARY KEY, -- YYYY-MM-DD
session_count INTEGER,
total_decisions INTEGER,
no_write_count INTEGER,
update_memory_count INTEGER,
update_user_count INTEGER,
update_soul_count INTEGER,
update_identity_count INTEGER,
update_tools_count INTEGER,
avg_latency_ms INTEGER,
updated_at INTEGER
);interface MemoryGateDecisionRecord {
id: string;
turnId: string;
sessionId: string;
// 决策结果
decision: MemoryDecision;
reason: string;
candidateFact?: string;
// LLM 调用信息
llmCall: {
model: string;
latencyMs: number;
promptTokens?: number;
completionTokens?: number;
rawResponse?: string;
};
createdAt: number;
}
// 查询结果增强版(关联对话内容)
interface MemoryGateDecisionWithContext {
decision: MemoryGateDecisionRecord;
turn: ConversationTurn; // 对应对话回合
context: ConversationContextItem[]; // 完整上下文
}/reflection gate [subcommand] [options]
Subcommands:
list [sessionKey] 列出决策历史
show <decision-id> 显示单次决策详情(含完整上下文)
stats 显示决策统计
export 导出决策数据
Options for list:
--limit, -l 返回最近的 N 条(默认: 10)
--since, -s 从某个时间开始(ISO 8601)
--until, -u 到某个时间结束
--decision, -d 按决策类型过滤
--session, -S 按会话过滤
--with-context, -c 包含完整上下文
Options for stats:
--daily 按天统计
--weekly 按周统计
--by-session 按会话统计
Examples:
/reflection gate list # 最近决策
/reflection gate list channel:xxx:yyy -l 20 # 指定会话
/reflection gate show <decision-id> -c # 查看详情+上下文
/reflection gate stats --daily # 每日统计
-- 查询某会话最近的 memory_gate 决策(含对话内容)
SELECT
d.id,
d.decision,
d.reason,
d.candidate_fact,
d.llm_latency_ms,
t.user_message_content,
t.agent_message_content,
d.created_at
FROM memory_gate_decisions d
JOIN conversation_turns t ON d.turn_id = t.id
WHERE d.session_id = ?
ORDER BY d.created_at DESC
LIMIT 10;
-- 按决策类型统计最近 7 天
SELECT
decision,
COUNT(*) as count,
AVG(llm_latency_ms) as avg_latency
FROM memory_gate_decisions
WHERE created_at > ?
GROUP BY decision;
-- 查询候选事实包含特定关键词的决策
SELECT * FROM memory_gate_decisions
WHERE candidate_fact LIKE '%preference%'
ORDER BY created_at DESC;write_guardian有WriteGuardianAuditLog记录写入决策- 但无法查看具体的文件变更内容(diff)
- 无法撤销已执行的写入
- 无法修改历史决策行为
提供完整的文件变更追踪、审计和回滚能力。
-- 文件快照表:存储每次写入前的文件状态
CREATE TABLE file_snapshots (
id TEXT PRIMARY KEY, -- ULID
file_name TEXT NOT NULL CHECK(file_name IN (
'MEMORY.md', 'USER.md', 'SOUL.md', 'IDENTITY.md', 'TOOLS.md'
)),
content TEXT NOT NULL, -- 文件完整内容
content_hash TEXT NOT NULL, -- SHA256 哈希,用于去重
created_at INTEGER NOT NULL,
INDEX idx_file_time (file_name, created_at),
INDEX idx_hash (content_hash)
);
-- write_guardian 审计记录表(增强版)
CREATE TABLE write_guardian_audit (
id TEXT PRIMARY KEY, -- ULID
decision_id TEXT NOT NULL, -- 外键关联 memory_gate_decisions
turn_id TEXT NOT NULL, -- 外键关联 conversation_turns
session_id TEXT NOT NULL,
-- 决策信息
target_file TEXT NOT NULL,
candidate_fact TEXT NOT NULL,
-- 文件变更
before_snapshot_id TEXT, -- 变更前快照(nullable 表示新建)
after_snapshot_id TEXT, -- 变更后快照
diff TEXT, -- unified diff 格式
lines_added INTEGER,
lines_removed INTEGER,
lines_modified INTEGER,
-- 执行结果
status TEXT NOT NULL CHECK(status IN (
'written', 'refused', 'failed', 'skipped', 'rolled_back'
)),
reason TEXT, -- 拒绝/失败原因
-- LLM 调用信息
llm_model TEXT,
llm_latency_ms INTEGER,
agent_steps INTEGER,
created_at INTEGER NOT NULL,
FOREIGN KEY (decision_id) REFERENCES memory_gate_decisions(id) ON DELETE CASCADE,
FOREIGN KEY (turn_id) REFERENCES conversation_turns(id) ON DELETE CASCADE,
FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE,
FOREIGN KEY (before_snapshot_id) REFERENCES file_snapshots(id),
FOREIGN KEY (after_snapshot_id) REFERENCES file_snapshots(id),
INDEX idx_session_time (session_id, created_at),
INDEX idx_status (status),
INDEX idx_target_file (target_file)
);
-- 回滚记录表
CREATE TABLE rollbacks (
id TEXT PRIMARY KEY,
audit_id TEXT NOT NULL UNIQUE, -- 关联的审计记录
rolled_back_at INTEGER NOT NULL,
rolled_back_by TEXT NOT NULL, -- 用户 ID 或 "system"
reason TEXT NOT NULL,
-- 回滚操作详情
restored_snapshot_id TEXT NOT NULL, -- 恢复到的快照
previous_status TEXT NOT NULL, -- 回滚前的状态
FOREIGN KEY (audit_id) REFERENCES write_guardian_audit(id),
FOREIGN KEY (restored_snapshot_id) REFERENCES file_snapshots(id),
INDEX idx_rolled_back_at (rolled_back_at)
);
-- 行为校正记录表(用于收集训练数据)
CREATE TABLE behavior_corrections (
id TEXT PRIMARY KEY,
audit_id TEXT NOT NULL,
-- 原始决策
original_status TEXT NOT NULL,
original_candidate_fact TEXT,
original_target_file TEXT,
-- 校正后
corrected_status TEXT NOT NULL,
corrected_candidate_fact TEXT,
corrected_target_file TEXT,
correction_reason TEXT NOT NULL,
-- 是否应用到未来相似场景
apply_to_future BOOLEAN DEFAULT false,
correction_pattern TEXT, -- 提取的校正模式(用于自动应用)
corrected_by TEXT NOT NULL,
corrected_at INTEGER NOT NULL,
FOREIGN KEY (audit_id) REFERENCES write_guardian_audit(id),
INDEX idx_apply_to_future (apply_to_future)
);interface FileSnapshot {
id: string;
fileName: "MEMORY.md" | "USER.md" | "SOUL.md" | "IDENTITY.md" | "TOOLS.md";
content: string;
contentHash: string;
createdAt: number;
}
interface WriteGuardianAuditRecord {
id: string;
decisionId: string;
turnId: string;
sessionId: string;
targetFile: string;
candidateFact: string;
// 文件变更
fileChange: {
beforeSnapshotId?: string;
afterSnapshotId: string;
diff: string;
lineChanges: {
added: number;
removed: number;
modified: number;
};
};
// 执行结果
status: "written" | "refused" | "failed" | "skipped" | "rolled_back";
reason?: string;
// LLM 调用
llmCall?: {
model: string;
latencyMs: number;
agentSteps: number;
};
createdAt: number;
}
interface RollbackRecord {
id: string;
auditId: string;
rolledBackAt: number;
rolledBackBy: string;
reason: string;
restoredSnapshotId: string;
previousStatus: string;
}
interface BehaviorCorrection {
id: string;
auditId: string;
originalStatus: string;
originalCandidateFact?: string;
originalTargetFile?: string;
correctedStatus: string;
correctedCandidateFact?: string;
correctedTargetFile?: string;
correctionReason: string;
applyToFuture: boolean;
correctionPattern?: string;
correctedBy: string;
correctedAt: number;
}class WriteGuardianRollback {
private db: Database;
private fileUtils: FileUtils;
/**
* 回滚指定的 write_guardian 操作
*/
async rollback(
auditId: string,
options: {
reason: string;
requestedBy: string;
createBackup?: boolean;
}
): Promise<RollbackResult> {
return this.db.transaction(async (trx) => {
// 1. 获取审计记录
const audit = await trx
.select('*')
.from('write_guardian_audit')
.where('id', auditId)
.first();
if (!audit) {
throw new Error(`Audit record ${auditId} not found`);
}
if (audit.status === 'rolled_back') {
throw new Error('This operation has already been rolled back');
}
if (audit.status !== 'written') {
throw new Error(`Cannot rollback operation with status: ${audit.status}`);
}
// 2. 获取恢复目标快照
const targetSnapshot = audit.before_snapshot_id
? await trx.select('*').from('file_snapshots').where('id', audit.before_snapshot_id).first()
: null; // 表示文件不存在(新建后回滚 = 删除)
// 3. 可选:创建当前状态的备份
if (options.createBackup !== false) {
const currentContent = await this.fileUtils.readFile(audit.target_file);
await this.createBackup(audit.target_file, currentContent);
}
// 4. 执行回滚
if (targetSnapshot) {
await this.fileUtils.writeFile(audit.target_file, targetSnapshot.content);
} else {
await this.fileUtils.deleteFile(audit.target_file);
}
// 5. 更新审计记录状态
await trx
.update('write_guardian_audit')
.set({ status: 'rolled_back' })
.where('id', auditId);
// 6. 记录回滚操作
const rollbackId = ulid();
await trx.insert('rollbacks').values({
id: rollbackId,
audit_id: auditId,
rolled_back_at: Date.now(),
rolled_back_by: options.requestedBy,
reason: options.reason,
restored_snapshot_id: audit.before_snapshot_id,
previous_status: audit.status
});
return {
rollbackId,
success: true,
restoredTo: targetSnapshot?.created_at || null
};
});
}
/**
* 批量回滚某个会话的所有写入操作
*/
async rollbackSession(
sessionKey: string,
options: {
reason: string;
requestedBy: string;
since?: number;
until?: number;
}
): Promise<BatchRollbackResult>;
/**
* 使用新的 candidate fact 重新执行某次操作
*/
async retry(
auditId: string,
newCandidateFact?: string,
newTargetFile?: string
): Promise<WriteResult>;
}/reflection guardian [subcommand] [options]
Subcommands:
list [sessionKey] 列出 write_guardian 操作历史
show <audit-id> 显示单次操作的详细信息(含 diff)
diff <audit-id> 显示文件变更的 diff
rollback <audit-id> 回滚到变更前的状态
history <file-name> 查看某文件的所有变更历史
correct <audit-id> 标记需要校正,并提供正确决策
export [options] 导出审计数据
Options for list:
--limit, -l 限制结果数量(默认: 10)
--file, -f 按目标文件过滤(MEMORY.md, USER.md, 等)
--status, -s 按状态过滤(written, refused, rolled_back)
--since 时间范围开始
--until 时间范围结束
--with-diff, -d 显示 diff 摘要
Options for history:
--file, -f 文件名(必需)
--limit, -l 限制历史版本数量
Options for correct:
--status 校正后的状态(written 或 refused)
--fact 校正后的 candidate fact
--file 校正后的目标文件
--reason 校正原因
--apply-to-future 是否应用到未来相似场景
Options for export:
--format 导出格式: json | csv | markdown
--output, -o 输出文件路径
--include-content 包含完整文件内容
Examples:
/reflection guardian list # 最近操作
/reflection guardian list -f USER.md -s written # USER.md 的成功写入
/reflection guardian show <audit-id> # 查看详情
/reflection guardian diff <audit-id> # 查看 diff
/reflection guardian rollback <audit-id> # 回滚操作
/reflection guardian history -f MEMORY.md -l 20 # 查看文件历史
/reflection guardian correct <audit-id> --status refused --reason "不应写入"
通过 Reflection 插件校正决策的过程,实际上是在产生高质量的监督微调数据(SFT data)。这些数据可以用于:
- 微调 memory_gate 模型
- 微调 write_guardian 模型
- 构建领域特定的记忆系统评估基准
自动收集和生成高质量的标注数据集,存储在 SQLite 中便于查询和导出。
-- 数据集样本表
CREATE TABLE dataset_samples (
id TEXT PRIMARY KEY,
sample_type TEXT NOT NULL CHECK(sample_type IN (
'memory_gate_positive', -- memory_gate 正确决策(最终被写入)
'memory_gate_negative', -- memory_gate 错误决策(被 guardian 拒绝或校正)
'write_guardian_decision', -- write_guardian 决策样本
'annotated_turn' -- 完整标注回合
)),
-- 关联信息
turn_id TEXT,
decision_id TEXT,
audit_id TEXT,
session_id TEXT NOT NULL,
-- 样本内容(JSON 格式,根据类型存储不同结构)
content TEXT NOT NULL,
-- 质量评分(自动计算)
quality_score REAL, -- 0-1 之间的质量分数
quality_factors TEXT, -- JSON 格式评分因子详情
-- 标注状态
annotation_status TEXT DEFAULT 'pending' CHECK(annotation_status IN (
'pending', 'approved', 'rejected', 'needs_review', 'auto_approved'
)),
annotated_by TEXT,
annotated_at INTEGER,
annotation_notes TEXT,
-- 隐私检查
privacy_checked BOOLEAN DEFAULT false,
contains_pii BOOLEAN DEFAULT false,
pii_redacted_content TEXT, -- 脱敏后的内容
-- 样本来源
collection_strategy TEXT, -- 收集策略(如 auto_positive, manual_correction)
created_at INTEGER NOT NULL,
FOREIGN KEY (turn_id) REFERENCES conversation_turns(id),
FOREIGN KEY (decision_id) REFERENCES memory_gate_decisions(id),
FOREIGN KEY (audit_id) REFERENCES write_guardian_audit(id),
FOREIGN KEY (session_id) REFERENCES sessions(id),
INDEX idx_type_status (sample_type, annotation_status),
INDEX idx_quality (quality_score),
INDEX idx_session (session_id)
);
-- 数据集导出记录表
CREATE TABLE dataset_exports (
id TEXT PRIMARY KEY,
export_name TEXT NOT NULL,
export_format TEXT NOT NULL CHECK(export_format IN (
'alpaca', 'sharegpt', 'openai', 'raw', 'csv'
)),
-- 导出参数
sample_types TEXT NOT NULL, -- JSON 数组,如 ["memory_gate_positive", "annotated_turn"]
filters TEXT, -- JSON 格式的过滤条件
-- 统计信息
total_samples INTEGER,
approved_samples INTEGER,
rejected_samples INTEGER,
avg_quality_score REAL,
-- 文件信息
file_path TEXT NOT NULL,
file_size_bytes INTEGER,
checksum TEXT,
exported_by TEXT,
exported_at INTEGER NOT NULL,
INDEX idx_exported_at (exported_at)
);
-- 人工审核队列视图
CREATE VIEW pending_review_samples AS
SELECT
s.*,
t.user_message_content,
t.agent_message_content,
d.decision as memory_gate_decision,
a.status as write_guardian_status
FROM dataset_samples s
LEFT JOIN conversation_turns t ON s.turn_id = t.id
LEFT JOIN memory_gate_decisions d ON s.decision_id = d.id
LEFT JOIN write_guardian_audit a ON s.audit_id = a.id
WHERE s.annotation_status = 'needs_review'
ORDER BY s.quality_score DESC;// Type 1: memory_gate 正样本
interface MemoryGatePositiveSample {
type: "memory_gate_positive";
conversation: Array<{ role: "user" | "agent"; message: string }>;
correctDecision: MemoryDecision;
correctFact: string;
evidence: {
wasWritten: boolean;
userFeedback?: "confirmed" | "corrected";
};
}
// Type 2: memory_gate 负样本
interface MemoryGateNegativeSample {
type: "memory_gate_negative";
conversation: Array<{ role: "user" | "agent"; message: string }>;
incorrectDecision: MemoryDecision;
incorrectFact: string;
correctDecision: "NO_WRITE" | MemoryDecision;
rejectionReason: string;
}
// Type 3: write_guardian 决策样本
interface WriteGuardianSample {
type: "write_guardian_decision";
candidateFact: string;
targetFile: string;
currentFileContent: string;
decision: "written" | "refused";
userCorrection?: {
originalDecision: string;
correctedDecision: string;
reason: string;
};
}
// Type 4: 完整标注回合
interface AnnotatedTurn {
type: "annotated_turn";
conversationId: string;
turnNumber: number;
context: Array<{ role: "user" | "agent"; message: string }>;
memoryGate: {
input: MemoryGateInput;
output: MemoryGateOutput;
label: "correct" | "incorrect" | "corrected";
correctedOutput?: MemoryGateOutput;
};
writeGuardian: {
input: {
candidateFact: string;
targetFile: string;
currentContent: string;
};
output: WriteGuardianWriteResult;
label: "correct" | "incorrect" | "corrected";
correctedOutput?: WriteGuardianWriteResult;
};
}
// 数据库中的统一格式
interface DatasetSampleRecord {
id: string;
sampleType: "memory_gate_positive" | "memory_gate_negative" | "write_guardian_decision" | "annotated_turn";
turnId?: string;
decisionId?: string;
auditId?: string;
sessionId: string;
content: MemoryGatePositiveSample | MemoryGateNegativeSample | WriteGuardianSample | AnnotatedTurn;
qualityScore?: number;
qualityFactors?: {
conversationClarity: number;
decisionConfidence: number;
factVerifiability: number;
};
annotationStatus: "pending" | "approved" | "rejected" | "needs_review" | "auto_approved";
privacyChecked: boolean;
containsPii: boolean;
piiRedactedContent?: string;
collectionStrategy: string;
createdAt: number;
}interface DatasetCollectionStrategy {
// 自动标记正样本的条件
positiveCriteria: {
// write_guardian 成功写入
wasWritten: boolean;
// 用户没有提出异议(通过反应或后续消息判断)
noUserObjection: boolean;
// N 天后仍然有效(未被回滚)
notRolledBackWithinDays: number;
// 质量分数阈值
minQualityScore: number;
};
// 自动标记负样本的条件
negativeCriteria: {
// memory_gate 提议写入但被 write_guardian 拒绝
gateProposedWrite: boolean;
guardianRefused: boolean;
// 或者用户后续手动要求记录类似内容
userLaterRequestedSimilar: boolean;
// 被人工校正标记为错误
hasBehaviorCorrection: boolean;
};
// 需要人工审核的条件
manualReviewCriteria: {
// 决策置信度低(如 LLM 响应犹豫)
lowConfidence: boolean;
// 涉及敏感信息
containsSensitiveInfo: boolean;
// 用户明确反馈
userFeedbackReceived: boolean;
// 质量分数中等(不确定是否合格)
mediumQualityScore: boolean;
};
// 自动批准的条件(无需人工审核)
autoApproveCriteria: {
// 被明确回滚且用户校正过
hasConfirmedCorrection: boolean;
// 高分且无疑虑
highQualityScore: boolean;
// 重复模式(之前已批准过类似样本)
matchesApprovedPattern: boolean;
};
}
class DatasetCollector {
// 从最近的对话中自动收集样本
async collectSamples(options: {
since?: number;
until?: number;
sessionId?: string;
strategy?: DatasetCollectionStrategy;
}): Promise<CollectionResult>;
// 计算样本质量分数
private calculateQualityScore(
sample: DatasetSampleRecord
): { score: number; factors: QualityFactors };
// 隐私检查和脱敏
private async checkPrivacy(content: unknown): Promise<{
containsPii: boolean;
redactedContent?: string;
}>;
// 根据收集策略判断样本类型
private classifySample(
turn: ConversationTurn,
decision: MemoryGateDecisionRecord,
audit?: WriteGuardianAuditRecord
): DatasetSampleRecord["sampleType"] | null;
}/reflection dataset [subcommand] [options]
Subcommands:
collect 从最近的对话中收集样本
list 列出已收集的样本
review 进入交互式标注模式
approve <sample-id> 批准样本
reject <sample-id> 拒绝样本
export 导出数据集
stats 显示数据集统计
quality 质量分析报告
Options for collect:
--since 收集起始时间
--until 收集结束时间
--session, -s 指定会话
--type, -t 样本类型: all | positive | negative
--auto-approve 自动批准符合条件的样本
--dry-run 预览但不实际收集
Options for list:
--status 按标注状态过滤
--type 按样本类型过滤
--quality-above 质量分数下限
--limit, -l 限制数量
--needs-review 只显示待审核的
Options for export:
--format 导出格式:
- alpaca (Alpaca 格式)
- sharegpt (ShareGPT 格式)
- openai (OpenAI fine-tuning 格式)
- raw (原始 JSONL)
- csv (CSV 格式)
--output, -o 输出文件路径
--train-split 训练集比例(默认: 0.8)
--include-pii 包含敏感信息(默认: false)
--only-approved 只导出已批准的样本
--min-quality 最低质量分数
Options for stats:
--daily 按天统计
--by-type 按类型统计
--by-status 按标注状态统计
--quality-distribution 质量分数分布
Examples:
/reflection dataset collect --since 2026-03-01 --auto-approve
/reflection dataset list --needs-review
/reflection dataset review # 交互式审核
/reflection dataset approve <sample-id> --notes "高质量样本"
/reflection dataset export --format alpaca -o ./sft-data.jsonl
/reflection dataset export --format openai --train-split 0.9 --only-approved
/reflection dataset stats --quality-distribution
Alpaca 格式:
{
"instruction": "Based on the following conversation, decide if this should be written to memory and which file it belongs to.",
"input": "User: I prefer morning check-ins\nAgent: I'll remember that you prefer morning check-ins.",
"output": "Decision: UPDATE_USER\nFact: prefers morning check-ins\nReason: This is a stable user preference about collaboration cadence."
}ShareGPT 格式:
{
"conversations": [
{
"from": "human",
"value": "I prefer morning check-ins"
},
{
"from": "gpt",
"value": "I'll remember that.",
"memory_gate_decision": "UPDATE_USER",
"candidate_fact": "prefers morning check-ins"
}
]
}┌─────────────────────────────────────────────────────────────────────────┐
│ OpenClaw Reflection │
├─────────────────────────────────────────────────────────────────────────┤
│ Existing Components │ New Components (This PRD) │
│ │ │
│ ┌──────────────────────┐ │ ┌──────────────────────────────┐ │
│ │ SessionBufferManager │──────▶│ │ SQLiteStorageManager │ │
│ └──────────────────────┘ │ │ - SQLite 连接管理 │ │
│ │ │ │ - 事务封装 │ │
│ ▼ │ │ - 连接池 │ │
│ ┌──────────────────────┐ │ └──────────────────────────────┘ │
│ │ MemoryGateAnalyzer │──────▶│ ┌──────────────────────────────┐ │
│ │ (memory_gate) │ │ │ ConversationRepository │ │
│ └──────────────────────┘ │ │ - 会话/对话 CRUD │ │
│ │ │ │ - 上下文查询 │ │
│ ▼ │ └──────────────────────────────┘ │
│ ┌──────────────────────┐ │ ┌──────────────────────────────┐ │
│ │ WriteGuardian │──────▶│ │ MemoryGateRepository │ │
│ │ (write_guardian) │ │ │ - 决策记录 CRUD │ │
│ └──────────────────────┘ │ │ - 统计分析 │ │
│ │ └──────────────────────────────┘ │
│ │ ┌──────────────────────────────┐ │
│ │ │ WriteGuardianRepository │ │
│ │ │ - 审计记录 CRUD │ │
│ │ │ - 文件快照管理 │ │
│ │ │ - 回滚操作 │ │
│ │ └──────────────────────────────┘ │
│ │ ┌──────────────────────────────┐ │
│ │ │ DatasetRepository │ │
│ │ │ - 样本 CRUD │ │
│ │ │ - 导出管理 │ │
│ │ │ - 质量评分 │ │
│ │ └──────────────────────────────┘ │
│ │ ┌──────────────────────────────┐ │
│ │ │ JSONLExporter │ │
│ │ │ - 定期导出 │ │
│ │ │ - 格式转换 │ │
│ │ └──────────────────────────────┘ │
└───────────────────────────────────┴─────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ 数据流图 │
└─────────────────────────────────────────────────────────────────────────┘
┌──────────────┐
│ 用户消息 │
└──────┬───────┘
│
▼
┌──────────────────────┐ ┌─────────────────────────────────────┐
│ SessionBufferManager │────▶│ SQLiteStorageManager │
│ (保留,用于缓冲) │ │ - upsertSession() │
└──────────────────────┘ │ - insertTurn() │
│ │ - insertContextBatch() │
▼ └─────────────────────────────────────┘
┌──────────────────────┐ │
│ MemoryGateAnalyzer │ │
│ (分析决策) │ │
└──────────┬───────────┘ │
│ │
┌──────┴──────┐ │
▼ ▼ ▼
┌─────────┐ ┌──────────────────────────────────────────────────────┐
│ NO_WRITE │ │ UPDATE_* │
└─────────┘ │ │
│ ┌────────────────────────────────────────────────┐ │
└─▶│ MemoryGateRepository │ │
│ - insertDecision() │ │
└────────────────────────────────────────────────┘ │
│ │
▼ │
┌──────────────────────────┐ │
│ WriteGuardian │ │
│ (执行写入) │ │
└──────────┬───────────────┘ │
│ │
┌───────────┼───────────┐ │
▼ ▼ ▼ │
┌────────┐ ┌────────┐ ┌────────┐ │
│ written │ │ refused │ │ failed │ │
└────┬───┘ └────┬───┘ └────┬───┘ │
│ │ │ │
▼ ▼ ▼ │
┌──────────────────────────────────────────────────────┐ │
│ WriteGuardianRepository │◀─────┘
│ - insertAudit() │
│ - insertSnapshot() │
│ - rollback() │
└──────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ DatasetRepository (自动收集) │
│ - collectFromDecision() │
│ - calculateQualityScore() │
│ - checkPrivacy() │
└──────────────────────────────────────────────────────┘
┌─────────────────┐ ┌─────────────────────────┐ ┌─────────────────────────┐
│ sessions │ │ conversation_turns │ │ conversation_contexts │
├─────────────────┤ ├─────────────────────────┤ ├─────────────────────────┤
│ id (PK) │◀──────│ session_id (FK) │ │ turn_id (FK) │
│ channel_id │ │ id (PK) │◀──────│ id (PK) │
│ conversation_ │ │ turn_number │ │ role │
│ target │ │ user_message_content │ │ message │
│ account_id │ │ user_message_id │ │ timestamp │
│ created_at │ │ agent_message_content │ │ sequence_number │
│ last_activity_at│ │ agent_message_id │ └─────────────────────────┘
│ metadata │ │ created_at │
└─────────────────┘ └─────────────────────────┘
│ │
│ │
▼ ▼
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│ memory_gate_decisions │
├─────────────────────────────────────────────────────────────────────────────────────────┤
│ id (PK) │
│ turn_id (FK) ───────────────────────────────────────────────────────────────────────────┘
│ session_id (FK) │
│ decision │
│ reason │
│ candidate_fact │
│ llm_model, llm_latency_ms, prompt_tokens, completion_tokens, raw_response │
│ created_at │
└─────────────────────────────────────────────────────────────────────────────────────────┘
│
│
▼
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│ write_guardian_audit │
├─────────────────────────────────────────────────────────────────────────────────────────┤
│ id (PK) │
│ decision_id (FK) ───────────────────────────────────────────────────────────────────────┘
│ turn_id (FK) │
│ session_id (FK) │
│ target_file │
│ candidate_fact │
│ before_snapshot_id (FK) ─────────┐ │
│ after_snapshot_id (FK) ──────────┼──────┐ │
│ diff, lines_added/removed/modified│ │ │
│ status, reason │
│ llm_model, llm_latency_ms, agent_steps │
│ created_at │
└─────────────────────────────────────────────────────────────────────────────────────────┘
│ │ │
│ ▼ ▼
│ ┌─────────────────┐ ┌─────────────────┐
│ │ file_snapshots │ │ file_snapshots │
│ │ (before) │ │ (after) │
│ ├─────────────────┤ ├─────────────────┤
│ │ id (PK) │ │ id (PK) │
│ │ file_name │ │ file_name │
│ │ content │ │ content │
│ │ content_hash │ │ content_hash │
│ │ created_at │ │ created_at │
│ └─────────────────┘ └─────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│ rollbacks │
├─────────────────────────────────────────────────────────────────────────────────────────┤
│ id (PK) │
│ audit_id (FK) ──────────────────────────────────────────────────────────────────────────┘
│ rolled_back_at │
│ rolled_back_by │
│ reason │
│ restored_snapshot_id (FK) │
│ previous_status │
└─────────────────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│ behavior_corrections │
├─────────────────────────────────────────────────────────────────────────────────────────┤
│ id (PK) │
│ audit_id (FK) ──────────────────────────────────────────────────────────────────────────┘
│ original_status, original_candidate_fact, original_target_file │
│ corrected_status, corrected_candidate_fact, corrected_target_file │
│ correction_reason, apply_to_future, correction_pattern │
│ corrected_by, corrected_at │
└─────────────────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│ dataset_samples │
├─────────────────────────────────────────────────────────────────────────────────────────┤
│ id (PK) │
│ sample_type │
│ turn_id (FK) │
│ decision_id (FK) ───────────────────────────────────────────────────────────────────────┘
│ audit_id (FK) │
│ session_id (FK) │
│ content (JSON) │
│ quality_score, quality_factors │
│ annotation_status, annotated_by, annotated_at, annotation_notes │
│ privacy_checked, contains_pii, pii_redacted_content │
│ collection_strategy, created_at │
└─────────────────────────────────────────────────────────────────────────────────────────┘
- 引入 SQLite 依赖(
better-sqlite3或sqlite3) - 创建数据库初始化模块(schema migration)
- 实现
SQLiteStorageManager和ConversationRepository - 修改
SessionBufferManager将数据写入 SQLite - 确保向后兼容(现有日志功能保留)
- 创建
MemoryGateRepository和WriteGuardianRepository - 实现文件快照机制
- 实现回滚功能
- 添加新的
/reflection子命令 - 创建数据库视图和索引优化查询
- 实现
DatasetRepository - 实现自动收集策略
- 实现质量评分算法
- 实现隐私检查和脱敏
- 添加数据集导出功能
{
"dependencies": {
"better-sqlite3": "^9.4.0",
"ulid": "^2.3.0"
}
}better-sqlite3: 高性能 SQLite 驱动,支持同步 API 和 WAL 模式ulid: 生成按时间排序的唯一 ID
-
数据库迁移策略:
- 如何管理 schema 变更?
- 是否需要 ORM(如 Drizzle ORM)?
-
数据保留策略:
- 是否需要自动清理旧数据?
- 归档策略(热数据 SQLite / 冷数据 JSONL)?
-
并发写入:
- 多个 OpenClaw 实例共享同一数据库?
- WAL 模式是否足够?
-
隐私合规:
- 敏感信息自动检测的准确率要求?
- 用户级别的 opt-out 机制?
-- 启用 WAL 模式
PRAGMA journal_mode = WAL;
-- 启用外键约束
PRAGMA foreign_keys = ON;
-- 自动清理碎片
PRAGMA auto_vacuum = INCREMENTAL;
-- 创建所有表(见上文)
-- ...
-- 创建优化索引
CREATE INDEX IF NOT EXISTS idx_sessions_activity ON sessions(last_activity_at);
CREATE INDEX IF NOT EXISTS idx_turns_session_created ON conversation_turns(session_id, created_at);
CREATE INDEX IF NOT EXISTS idx_decisions_session_created ON memory_gate_decisions(session_id, created_at);
CREATE INDEX IF NOT EXISTS idx_audit_session_created ON write_guardian_audit(session_id, created_at);
CREATE INDEX IF NOT EXISTS idx_samples_type_status ON dataset_samples(sample_type, annotation_status);| 组件 | 路径 |
|---|---|
| Logger | src/logger.ts |
| SessionBufferManager | src/session-manager.ts |
| MemoryGateAnalyzer | src/memory-gate/analyzer.ts |
| WriteGuardian | src/write-guardian/index.ts |
| WriteGuardianAuditLog | src/write-guardian/audit-log.ts |
| MessageHandler | src/message-handler.ts |
| Config | src/config.ts |
文档版本: 2.0 (SQLite 版) 更新日期: 2026-03-11 作者: Lia for Sirocco
{ "enabled": true, "config": { "workspaceDir": "/path/to/workspace", // SQLite 存储配置 "storage": { "type": "sqlite", "sqlite": { "dbPath": ".openclaw-reflection/reflection.db", "walMode": true, "autoVacuum": true, "busyTimeoutMs": 5000, "cacheSizePages": -2000, "mmapSizeMB": 256 }, "retentionDays": 365, // 可选的 JSONL 导出 "jsonlExport": { "enabled": true, "schedule": "0 0 * * *", "retentionDays": 30 } }, // memory_gate 审计配置 "memoryGateAuditing": { "enabled": true, "storeRawLLMResponse": true, "enableStats": true }, // write_guardian 审计配置 "writeGuardianAuditing": { "enabled": true, "storeFileSnapshots": true, "enableRollback": true, "maxSnapshotsPerFile": 100 }, // 数据集收集配置 "datasetCollection": { "enabled": true, "autoCollect": true, "positiveCriteria": { "wasWritten": true, "noUserObjection": true, "notRolledBackWithinDays": 7, "minQualityScore": 0.7 }, "negativeCriteria": { "gateProposedWrite": true, "guardianRefused": true, "hasBehaviorCorrection": true }, "privacyFiltering": { "enabled": true, "redactPatterns": [ "api[_-]?key", "password", "token", "secret", "-----BEGIN.*-----" ] }, "qualityScoring": { "enabled": true, "weights": { "conversationClarity": 0.4, "decisionConfidence": 0.35, "factVerifiability": 0.25 } } }, // LLM 配置 "llm": { "baseURL": "https://openrouter.ai/api/v1", "apiKey": "YOUR_API_KEY", "model": "x-ai/grok-4.1-fast" }, // 原有配置 "memoryGate": { "enabled": true, "windowSize": 10 }, "consolidation": { "enabled": false, "schedule": "0 2 * * *" } } }