"""息壤(记忆域)模型(data-model.md §3)。 - 三级:微痕 trace(短期)/ 明忆 explicit(低敏长期)/ 重忆 sensitive(须确认); - 双源:user_explicit / self_inferred; - 版本链:正影=新行 prev_version_id 指旧行,旧行 superseded 停止普通召回,内容不改写; - 删除:tombstoned 墓碑不承载原文,主存储/检索/上下文全链路停用。 """ from datetime import datetime from sqlalchemy import DateTime, Float, ForeignKey, Integer, String, Text from sqlalchemy.orm import Mapped, mapped_column from app.core.ids import new_factual_record_id, new_memory_id, new_memory_trigger_id from app.core.time import utc_now from app.db.base import Base class Memory(Base): """统一记忆表:三级 × 类型 × 状态 × 双源(契约词典 §10)。""" __tablename__ = "memories" id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_memory_id) character_id: Mapped[str] = mapped_column(String(80), index=True, nullable=False) # trace / explicit / sensitive(§10.1) tier: Mapped[str] = mapped_column(String(20), nullable=False, index=True) # persona / episodic / instruction / relationship_view / fact_candidate(§10.2) type: Mapped[str] = mapped_column(String(40), nullable=False, index=True) # candidate / pending / active / superseded / tombstoned(§10.3) status: Mapped[str] = mapped_column(String(20), nullable=False, default="candidate", index=True) # user_explicit / self_inferred(§10.4) source: Mapped[str] = mapped_column(String(20), nullable=False, default="self_inferred") content: Mapped[str] = mapped_column(Text, nullable=False) # 可解释性:指向镜树节点/事件(撤销与来源展开的依据) source_refs_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]") sensitivity: Mapped[str] = mapped_column(String(20), nullable=False, default="none") priority: Mapped[int] = mapped_column(Integer, nullable=False, default=50) confidence: Mapped[float] = mapped_column(Float, nullable=False, default=0.5) # 活力值:2^(-Δdays/半衰期) 衰减、命中强化(§6.2) vitality: Mapped[float] = mapped_column(Float, nullable=False, default=1.0) # 微痕有效期;空表示不过期 valid_until: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) # 记忆界域:境界记忆不进真界上下文(§5) realm: Mapped[str] = mapped_column(String(40), nullable=False, default="reality") prev_version_id: Mapped[str | None] = mapped_column(String(80), index=True) # 召回统计(召回透明) recall_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0) last_recalled_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=utc_now, onupdate=utc_now ) class MemoryTrigger(Base): """记忆触发条件:keyword(微痕激活管线共用)与 disclosure(单一触发)。""" __tablename__ = "memory_triggers" id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_memory_trigger_id) memory_id: Mapped[str] = mapped_column(String(80), ForeignKey("memories.id"), index=True) # keyword / disclosure / vector(§10.5) kind: Mapped[str] = mapped_column(String(20), nullable=False) pattern: Mapped[str] = mapped_column(String(200), nullable=False) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now) class FactualRecord(Base): """实录(不可变):发生了什么 + 证据引用;与心痕(relationship_view)分离。""" __tablename__ = "factual_records" id: Mapped[str] = mapped_column(String(80), primary_key=True, default=new_factual_record_id) character_id: Mapped[str] = mapped_column(String(80), index=True, nullable=False) canonization_id: Mapped[str] = mapped_column(String(80), index=True) # 证据:路径节点引用 + 摘要 evidence_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}") summary: Mapped[str] = mapped_column(Text, nullable=False, default="") occurred_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)