Skip to content

Database

数据库设计

PostgreSQL 存储层。Slice A(对话存档)、Slice Bsemantic_memory + limbic_memory)、Slice C(自我层 + 自传体)已落地;独立 procedural 表尚未建(程序记忆现用 semantic_memory.type=procedural)。 关联:compression.mdmemory.mdsleep.md

状态

阶段范围状态
Slice Asessions + messages(对话主存 PG)✅ 已完成
Slice Bsemantic_memorylimbic_memory✅ 已完成(独立 procedural 表见 Issue #41)
Slice Cself_blocks + autobiographical_memory(自我层 + 自传叙事)✅ 已完成

代码真相源:engine/db/src/schema/

PG 多域架构(路径 C)

职责
@freeanima/engine-dbPG 表 DDL、migration、JSONB 存储 Zod 与 Slice A 领域类型schema/ + domain/
@freeanima/engine-reposSessionStorePortPgRepositories 等仓储端口null* 适配器
@freeanima/engine-conversation会话运行时;re-export engine-db/domain 便利类型
@freeanima/connectors-db-pgPgSessionStore 实现、连接池、mapper、repo

装配:service/service/src/serve.ts 调用 createPgRepositoriescreateEngine({ repos })createConversationService(engine.repos)initServiceContext。运行时对话存档读写经 getServiceContext().conversation 或显式 ConversationService / SessionStorePort,不直接依赖 connector。

新增 PG 域(memory / cron / task):engine-db/schema/{domain}engine-repos 增端口 → connectors-db-pg 实现 → PgRepositories 扩展字段 → serve.ts 装配。


Slice A:Session(2 表)

设计原则

  • sessions 一行 = session_meta(含 compression、todos、clarify、tools 等)
  • messages 只追加payload JSONB 存 MessagePayload(无 pos);pos 列是会话内序号真相源
  • 存储 Zod 以 engine-db/schema 为准;领域便利类型见 engine-db/domainengine-conversation re-export)
  • Drizzle 管 DDL + migration;sessions 仍列化常用 meta 字段
  • 读写:connectors-db-pg mapper(pos 列 + payload 合并为 ConversationMessage

表结构

sessions

类型说明
idTEXT PK会话名
modelTEXT NOT NULL
titleTEXT
cwdTEXT
system_promptTEXT
platform_infoJSONBdiscriminatedUnion("platform"):parlor / discord / weixin / studio-pair-programming / cron
compressionJSONB{ l2, l3, summary?, summary_at? }压缩边界(见 compression.md
todosJSONB{ items, next_id }
awaiting_clarifyJSONBclarify 暂停状态
acp_sessionsJSONBACP session uuid 映射
toolsJSONBOpenAI tools 快照
functionsJSONBstring[]
debugBOOLEAN
created_atTIMESTAMPTZ
updated_atTIMESTAMPTZ

messages

类型说明
idTEXT PK全局唯一行 id(UUID)
session_idTEXT FK → sessions.id
posBIGINT会话内单调序号(compression l2/l3 指向此值;领域层 Message.pos
payloadJSONBConversationPayload(role/content/tool_calls 等,不含 pos
content_ftsTSVECTOR(生成列)STORED;to_tsvector('simple', message_fts_input(content));CJK 按字切分

唯一索引:(session_id, pos)

全文索引:messages_content_fts_gin(GIN on content_fts)。供 recall 历史对话检索;过滤规则:排除 tool 消息与空 content。

配置

database:
url: postgresql://user:pass@localhost:5432/anima
# 或 pass:services/postgres/anima

生产环境必须配置 database.url

驱动(PoC)DATABASE_DRIVER=bun 启用 Bun.sql + drizzle-orm/bun-sql/postgres;默认 postgres(postgres.js)。Bun 驱动下 Drizzle 1.0.0-rc.3 的 RQB 查询仍有兼容问题,完整切换前需上游修复或升级;见 tests/integration/db/bun-sql-driver.test.ts

迁移

  • ✅ 生产anima service 启动且 PG 为主存时,serve.ts 自动调用 runMigrations()
  • 手动bun run --filter @freeanima/engine-db db:migrate — 应用 Drizzle migration(含列化 → payload JSONB 的数据回填)。

运维

  • Schema 变更:drizzle-kit generate + migrate(已应用 migration 不修改)
  • migration 不替代备份;继续每日全盘备份;destructive 变更前 pg_dump

本机 PostgreSQL(Debian)

Terminal window
# 安装 + 创建 anima 库/用户 + 生产向 conf.d 片段(需 sudo)
sudo ./scripts/setup-postgres-debian.sh
# 凭证写入 pass(脚本会打印 anima credential add … 命令)
anima credential add services/postgres/anima url=… host=… password=… database=anima
# Schema
DATABASE_URL="$(anima credential get services/postgres/anima url)" \
bun run --filter @freeanima/engine-db db:migrate
# database:
# url: pass:services/postgres/anima

默认:PostgreSQL 17、仅 localhost 监听、scram-sha-256 本地 TCP、anima 专用库/用户。

集成测试(本机,非 pre-commit)

Docker 运行中。bun test 会通过 Docker CLI 起临时 PostgreSQL 17、跑 migration,并执行根目录 tests/integration/(与单元测试一并运行)。

Terminal window
bun test

单元测试(mapper,不连 PG):bun test connectors/db-pg

Slice B:semantic_memory(已落地)

表结构

类型说明
idTEXT PK保留 f-{seq}-{hex} 格式
typeTEXTworld/experience/opinion/observation/preference/procedural/imprint
pinnedBOOLEAN常驻记忆优先注入
contentTEXT记忆正文
content_ftsTSVECTOR(生成列)to_tsvector('simple', message_fts_input(content)) STORED
source_sessionsTEXT[]来源 session ID 列表,默认 '{}'
observed_atTIMESTAMPTZ首次观察到该事实的时间;旧行回填 created
occurred_atTEXT事实内容中的模糊发生时间
statusTEXTactive / deprecated,默认 active
createdTIMESTAMPTZ
updatedTIMESTAMPTZ

索引:idx_semantic_memory_fts(GIN)、idx_semantic_memory_typeidx_semantic_memory_pinnedidx_semantic_memory_source_sessions(GIN)、idx_semantic_memory_status

端口方法:create / update(覆盖式,未传不变;source_sessions: [] 可清空)/ deprecate / listBySourceSessions / search / searchFtslistResident 与 recall 默认 status=active

端口:SemanticMemoryStorePortengine-repos)→ PgSemanticMemoryStoreconnectors-db-pg)→ registerSemanticMemoryStorelife-memory)。

从文件系统迁移

一次性脚本(读取旧 f-*.md,幂等 INSERT):

Terminal window
DATABASE_URL="$(anima credential get services/postgres/anima url)" \
bun run scripts/migrate-semantic-memory.ts [--dry-run] [--home ~/.anima]

~/.anima/memory/f-*.md~/.anima/index/l3.db遗留路径(非运行时);迁移验证后可手动归档。

limbic_memory 见下文 §Slice C 同文件后续节;独立 procedural 表见 Issue #41

Slice C:自我层与自传体(已落地)

self_blocks(自我层六块)

类型说明
block_keyTEXT PKexistence_anchor / self_model / personality_baseline / direction / metacognition / autobiography_summary
contentTEXTMarkdown 正文
lockedBOOLEANexistence_anchor 默认 true;update 需 force
versionINTEGER变更计数
updated_byTEXTseed / manual / tool / autobiography_cron
created_atTIMESTAMPTZ
updated_atTIMESTAMPTZ

端口:SelfLayerStorePortengine-repos)→ PgSelfLayerStoreconnectors-db-pg)→ registerSelfLayerStorelife-self)。

方法:getBlock / listBlocks / upsertBlock / updateBlocklocked 块需 force)。

运行时由 loadSelfLayerPrompt() 读取;维护通过 get_self_blocks / update_self_block 或直接写表。

autobiographical_memory(自传体叙事,记忆层)

类型说明
idTEXT PKUUID
titleTEXT叙事标题
contentTEXT叙事正文(只追加,无 update)
significanceTEXTnormal / milestone / turning_point
period_startTEXT模糊时间起点
period_endTEXT模糊时间终点
source_factsTEXT[]关联 semantic_memory.id(PG 列名;领域层 source_semantic_memory
source_sessionsTEXT[]关联 session id
statusTEXTactive / deprecated
created_atTIMESTAMPTZ
updated_atTIMESTAMPTZdeprecate 时更新

索引:statussignificanceupdated_atsource_facts(GIN)、source_sessions(GIN)。

端口:AutobiographicalMemoryStorePortPgAutobiographicalMemoryStoreregisterAutobiographicalMemoryStorelife-memory)。

方法:create / get / deprecate / count / listActive / listCreatedSince / listBySourceSemanticMemory / listBySourceSessions content update)。

维护:builtin-self-autobiography cron(04:00 CST)从 experience/imprint 语义记忆叙事加工;autobiography_summary 块由同一任务从本表压缩刷新。

Migration:engine/db/migrations/20260607150000_self_and_autobiographical/migration.sql

limbic_memory(边缘系统情感记忆)

类型说明
idUUID PK
session_idTEXT关联 session
kindTEXTsession_mood / turning_point / spike
valenceREAL效价 -1.0 到 1.0
arousalREAL唤醒度 0.0 到 1.0
contentTEXT第一人称情感描述
intensityREAL强度 0.0 到 1.0,默认 0.5
source_segmentTEXTearly / mid / late 或具体位置
semantic_memory_idsTEXT[]关联 semantic_memory.id
created_atTIMESTAMPTZ

索引:semantic_memory_ids(GIN)、session_idcreated_atkindintensityvalencearousal

端口:LimbicMemoryStorePortPgLimbicMemoryStoreregisterLimbicMemoryStorelife-memory)。

方法:create / get / listBySession不注入 system prompt;浅睡 Phase 2 经 create_limbic_memory 写入。

Migration:engine/db/migrations/20260607160000_limbic_memory/migration.sql

cron_jobs(已落地)

定时任务元数据 PG 存储;output 正文仍在 ~/.anima/cron/output/last_output_ref 存相对 FREEANIMA_HOME 的路径)。

表结构

类型说明
idTEXT PK16 hex 或 builtin-*
nameTEXT任务名称
scheduleTEXTCST 语义调度表达式(cron / interval / oneshot)
promptTEXTLLM 提示词
skillsTEXT[]技能列表
scriptTEXT脚本路径(相对 cron/scripts
no_agentBOOLEAN仅脚本/builtin,不调用 LLM
enabled_toolsetsTEXT[]启用工具集
model_providerTEXT模型 provider
model_nameTEXT模型名
workdirTEXT工作目录
context_fromTEXT[]上游任务 ID
deliverTEXT投递目标
timeout_secINTEGER超时秒数
builtinBOOLEAN内置任务
repeatINTEGER最大运行次数
run_countINTEGER已运行次数
pausedBOOLEAN暂停状态
created_atTIMESTAMPTZ
updated_atTIMESTAMPTZ
last_run_atTIMESTAMPTZ上次运行时间
last_output_refTEXToutput 文件相对 FREEANIMA_HOME 路径

索引:idx_cron_jobs_paused

调度:Bun.cron 进程内调度;5 段 cron 校验与 next_run_at 均经 Bun.cron.parse(注册前 CST→UTC 转换)。next_run_at 不入库,API 层实时计算。

端口:CronJobStorePortengine-repos)→ PgCronJobStoreconnectors-db-pg)→ initCronModuleconnectors-cron / serve.ts)。

Migration:engine/db/migrations/20260607140000_cron_jobs/migration.sql(手写 SQL,无 Drizzle schema 文件)。

从 jobs.json 迁移

一次性脚本(幂等 ON CONFLICT DO NOTHING):

Terminal window
DATABASE_URL="$(anima credential get services/postgres/anima url)" \
bun run scripts/migrate-cron-to-pg.ts [--dry-run] [--home ~/.anima]

~/.anima/cron/jobs.json 为遗留路径;迁移验证后可手动归档。

tasks(已落地)

跨 session 持久待办;status / priority 为 TEXT + Zod enum(engine-db/schema/tasks.ts)。

类型说明
idTEXT PKUUID
titleTEXT标题
descriptionTEXT详情(可选)
statusTEXTpending / in_progress / completed / cancelled
priorityTEXThigh / medium / low / none
due_atTIMESTAMPTZ截止时间(可选)
created_atTIMESTAMPTZ
updated_atTIMESTAMPTZ
completed_atTIMESTAMPTZ完成/取消时间(可选)
source_session_idTEXT FK创建来源 session(sessions.id,ON DELETE SET NULL)

索引:idx_tasks_statusidx_tasks_list(status, priority, created_at)。

端口:TaskStorePortengine-repos)→ PgTaskStoreconnectors-db-pg)→ capabilities/tasks 工具。

Migration:engine/db/migrations/20260608120000_tasks/migration.sql