✨ feat: WP-04 记忆与知识——候选版本闭环、检索管线与大周天(ADR-011/018/023)
- 迁移 9:memory_candidate(来源/敏感度/作用域/状态检查约束)、memory(resource_version 作 ETag 源、有效期、当前版本指针)、memory_version((memory_id, version) 唯一、取代链)、big_cycle(cycle_ 聚合根、phase_memory、产物版本引用) - 候选闭环:手动提交;白名单自动批准仅限 auto+fact+低敏感(撤销窗 72h 记录于审计);敏感/高影响默认人工审核;批准同事务写 Memory+Version+Audit+Outbox(契约 4.4) - 纠正:If-Match 校验 resource_version(缺失/过期 412),创建新版本不原地覆盖(ADR-011) - 删除:立即阻断召回 + 清除全部版本原文(墓碑不存被删原文,契约 4.4) - 检索管线:数据库层域/角色预过滤 → 状态/有效期过滤 → 文本匹配 → 响应级召回证据(语义检索待 embedding 端点) - 大周天最小实现:手动触发 → 后台四阶段(回顾/对照/提议/提交)→ 模型真实评估产出 pending persona_candidate(候选先于写入);无端点 fail-closed - 测试 60 项全绿(+6):审批流、白名单三例、ETag 正/缺失/过期、检索隔离与召回证据、删除传播与墓碑原文清除、大周天端到端
This commit is contained in:
1 parent
b4242f3844
commit
e333dc5ea5
8 files changed
+1069
No files matched your search
@@ -472,3 +472,145 @@ class ModelCall(Base):
|
||||
),
|
||||
Index("uq_model_call_turn_attempt", "turn_id", "attempt", unique=True),
|
||||
)
|
||||
|
||||
|
||||
MEMORY_KIND_CK = CheckConstraint(
|
||||
"kind IN ('fact', 'preference', 'event', 'relationship')", name="kind_values"
|
||||
)
|
||||
SENSITIVITY_CK = CheckConstraint(
|
||||
"sensitivity IN ('none', 'low', 'medium', 'high')", name="sensitivity_values"
|
||||
)
|
||||
MEMORY_REALM_CK = CheckConstraint(
|
||||
"realm IN ('reality', 'world', 'simulation')", name="realm_values"
|
||||
)
|
||||
|
||||
|
||||
class MemoryCandidate(Base):
|
||||
"""记忆候选:待判定提取结果,不参与召回(ADR-011)。"""
|
||||
|
||||
__tablename__ = "memory_candidate"
|
||||
|
||||
id: Mapped[str] = mapped_column(
|
||||
String, primary_key=True, default=lambda: new_id("memory_candidate_")
|
||||
)
|
||||
owner_personal_domain_id: Mapped[str] = mapped_column(
|
||||
ForeignKey("personal_domain.id", ondelete="cascade"), nullable=False, index=True
|
||||
)
|
||||
character_id: Mapped[str | None] = mapped_column(
|
||||
ForeignKey("character.id", ondelete="cascade"), nullable=True
|
||||
)
|
||||
kind: Mapped[str] = mapped_column(String, nullable=False)
|
||||
sensitivity: Mapped[str] = mapped_column(String, nullable=False, default="none")
|
||||
realm: Mapped[str] = mapped_column(String, nullable=False, default="reality")
|
||||
payload: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False, default=dict)
|
||||
source: Mapped[str] = mapped_column(String, nullable=False, default="manual")
|
||||
source_ref: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False, default=dict)
|
||||
content_hash: Mapped[str | None] = mapped_column(String, nullable=True)
|
||||
status: Mapped[str] = mapped_column(String, nullable=False, default="pending")
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False, server_default=func.now()
|
||||
)
|
||||
decided_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
__table_args__ = (
|
||||
MEMORY_KIND_CK,
|
||||
SENSITIVITY_CK,
|
||||
MEMORY_REALM_CK,
|
||||
CheckConstraint(
|
||||
"status IN ('pending', 'approved', 'rejected', 'expired')", name="status_values"
|
||||
),
|
||||
CheckConstraint(
|
||||
"source IN ('manual', 'auto', 'big_cycle', 'canonization')", name="source_values"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class MemoryVersion(Base):
|
||||
"""不可变记忆版本:纠正/取代/恢复都创建新版本(ADR-011)。"""
|
||||
|
||||
__tablename__ = "memory_version"
|
||||
|
||||
id: Mapped[str] = mapped_column(
|
||||
String, primary_key=True, default=lambda: new_id("memory_version_")
|
||||
)
|
||||
memory_id: Mapped[str] = mapped_column(
|
||||
ForeignKey("memory.id", ondelete="cascade"), nullable=False, index=True
|
||||
)
|
||||
version: Mapped[int] = mapped_column(nullable=False)
|
||||
payload: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False, default=dict)
|
||||
supersedes_version_id: Mapped[str | None] = mapped_column(
|
||||
ForeignKey("memory_version.id", ondelete="set null"), nullable=True
|
||||
)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False, server_default=func.now()
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("uq_memory_version_number", "memory_id", "version", unique=True),
|
||||
)
|
||||
|
||||
|
||||
class Memory(Base):
|
||||
"""权威长期记忆:候选批准后创建;当前版本由指针引用。"""
|
||||
|
||||
__tablename__ = "memory"
|
||||
|
||||
id: Mapped[str] = mapped_column(String, primary_key=True, default=lambda: new_id("memory_"))
|
||||
owner_personal_domain_id: Mapped[str] = mapped_column(
|
||||
ForeignKey("personal_domain.id", ondelete="cascade"), nullable=False, index=True
|
||||
)
|
||||
character_id: Mapped[str | None] = mapped_column(
|
||||
ForeignKey("character.id", ondelete="cascade"), nullable=True, index=True
|
||||
)
|
||||
kind: Mapped[str] = mapped_column(String, nullable=False)
|
||||
sensitivity: Mapped[str] = mapped_column(String, nullable=False, default="none")
|
||||
realm: Mapped[str] = mapped_column(String, nullable=False, default="reality")
|
||||
status: Mapped[str] = mapped_column(String, nullable=False, default="active")
|
||||
valid_from: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
expires_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
pinned: Mapped[bool] = mapped_column(nullable=False, default=False)
|
||||
current_version_id: Mapped[str | None] = mapped_column(
|
||||
ForeignKey("memory_version.id", ondelete="set null", use_alter=True), nullable=True
|
||||
)
|
||||
resource_version: Mapped[int] = mapped_column(nullable=False, default=1) # ETag 源
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False, server_default=func.now()
|
||||
)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now()
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
MEMORY_KIND_CK,
|
||||
SENSITIVITY_CK,
|
||||
MEMORY_REALM_CK,
|
||||
CheckConstraint(
|
||||
"status IN ('active', 'superseded', 'archived', 'deleted')", name="status_values"
|
||||
),
|
||||
Index("ix_memory_domain_status_kind", "owner_personal_domain_id", "status", "kind"),
|
||||
)
|
||||
|
||||
|
||||
class BigCycle(Base):
|
||||
"""大周天循环:cycle_ 聚合根,四阶段评估(ADR-018/023)。"""
|
||||
|
||||
__tablename__ = "big_cycle"
|
||||
|
||||
id: Mapped[str] = mapped_column(String, primary_key=True, default=lambda: new_id("cycle_"))
|
||||
character_id: Mapped[str] = mapped_column(
|
||||
ForeignKey("character.id", ondelete="cascade"), nullable=False, index=True
|
||||
)
|
||||
status: Mapped[str] = mapped_column(String, nullable=False, default="pending")
|
||||
trigger: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False, default=dict)
|
||||
phase_memory: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False, default=dict)
|
||||
produced_version_ids: Mapped[list[str]] = mapped_column(JSONB, nullable=False, default=list)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False, server_default=func.now()
|
||||
)
|
||||
completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
__table_args__ = (
|
||||
CheckConstraint(
|
||||
"status IN ('pending', 'running', 'completed', 'failed')", name="status_values"
|
||||
),
|
||||
)
|
||||
Reference in new issue
Block a user