Files
dreamweaver/.claude/specs/memory-intelligence/STORY-UNIVERSE-MODEL.md
torin b8d3cb4644
Some checks are pending
Build and Push Docker Images / changes (push) Waiting to run
Build and Push Docker Images / build-backend (push) Blocked by required conditions
Build and Push Docker Images / build-frontend (push) Blocked by required conditions
Build and Push Docker Images / build-admin-frontend (push) Blocked by required conditions
wip: snapshot full local workspace state
2026-04-17 18:58:11 +08:00

6.3 KiB

故事宇宙记忆结构

概述

故事宇宙用于在多次故事生成中保持角色、世界观与成长成就的连续性。每个孩子档案可以拥有多个宇宙,故事生成时可选择“延续上一个故事”,系统自动带入宇宙设定。


一、数据库模型

1.1 主表: story_universes

CREATE TABLE story_universes (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    child_profile_id UUID NOT NULL REFERENCES child_profiles(id) ON DELETE CASCADE,

    -- 宇宙基础
    name VARCHAR(100) NOT NULL,

    -- 记忆结构
    protagonist JSONB NOT NULL,         -- 主角设定
    recurring_characters JSONB DEFAULT '[]',
    world_settings JSONB DEFAULT '{}',
    achievements JSONB DEFAULT '[]',

    -- 时间戳
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

CREATE INDEX idx_story_universes_child_id ON story_universes(child_profile_id);
CREATE INDEX idx_story_universes_updated_at ON story_universes(updated_at);

1.2 JSON 结构示例

protagonist

{
  "name": "小明",
  "role": "星际船长",
  "traits": ["勇敢", "好奇"],
  "goal": "寻找失落的星球",
  "backstory": "来自地球的探险家"
}

recurring_characters

[
  {
    "name": "星星",
    "role": "魔法猫咪",
    "traits": ["聪明", "调皮"],
    "relation": "伙伴",
    "first_story_id": "story-uuid"
  }
]

world_settings

{
  "world_name": "梦幻森林",
  "era": "童话时代",
  "locations": ["彩虹河", "月光山"],
  "rules": ["动物会说话", "星星会指路"],
  "tone": "温暖治愈"
}

achievements

[
  {
    "type": "勇气",
    "description": "克服了对黑暗的恐惧",
    "story_id": "story-uuid",
    "achieved_at": "2025-01-10T12:00:00Z"
  }
]

二、SQLAlchemy 模型

# backend/app/db/models.py

from sqlalchemy import Column, String, ForeignKey, JSON
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship
import uuid

class StoryUniverse(Base):
    __tablename__ = "story_universes"

    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    child_profile_id = Column(UUID(as_uuid=True), ForeignKey("child_profiles.id", ondelete="CASCADE"), nullable=False)

    name = Column(String(100), nullable=False)
    protagonist = Column(JSON, nullable=False)
    recurring_characters = Column(JSON, default=list)
    world_settings = Column(JSON, default=dict)
    achievements = Column(JSON, default=list)

    created_at = Column(DateTime(timezone=True), server_default=func.now())
    updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())

    child_profile = relationship("ChildProfile", back_populates="story_universes")

三、Pydantic Schema

# backend/app/schemas/story_universe.py

from pydantic import BaseModel, Field
from typing import Any
from uuid import UUID

class StoryUniverseCreate(BaseModel):
    name: str = Field(..., min_length=1, max_length=100)
    protagonist: dict[str, Any]
    recurring_characters: list[dict[str, Any]] = Field(default_factory=list)
    world_settings: dict[str, Any] = Field(default_factory=dict)

class StoryUniverseUpdate(BaseModel):
    name: str | None = Field(None, min_length=1, max_length=100)
    protagonist: dict[str, Any] | None = None
    recurring_characters: list[dict[str, Any]] | None = None
    world_settings: dict[str, Any] | None = None

class StoryUniverseResponse(BaseModel):
    id: UUID
    child_profile_id: UUID
    name: str
    protagonist: dict[str, Any]
    recurring_characters: list[dict[str, Any]]
    world_settings: dict[str, Any]
    achievements: list[dict[str, Any]]

    class Config:
        from_attributes = True

四、API 约定

方法 路径 说明
GET /api/profiles/{id}/universes 获取孩子的故事宇宙列表
POST /api/profiles/{id}/universes 创建新宇宙
GET /api/universes/{id} 获取宇宙详情
PUT /api/universes/{id} 更新宇宙设定
POST /api/universes/{id}/achievements 添加成就

五、业务规则

  • 延续故事: “延续上一个故事”默认选最近更新的宇宙(按 updated_at 倒序)。
  • 成就追加: 新成就追加到 achievements,以 type + description 去重。
  • 成长轨迹: 成就保留顺序,优先展示最新项。

六、Prompt 集成

当选择宇宙时,生成 Prompt 需带入宇宙记忆:

【故事宇宙】
- 主角设定: {protagonist}
- 常驻角色: {recurring_characters}
- 世界观: {world_settings}
- 已获成就: {achievements}

未选择宇宙时,提示词忽略该块,避免混淆。


七、数据迁移示例

# backend/alembic/versions/xxx_add_story_universes.py

def upgrade():
    op.create_table(
        "story_universes",
        sa.Column("id", sa.UUID(), nullable=False),
        sa.Column("child_profile_id", sa.UUID(), nullable=False),
        sa.Column("name", sa.String(100), nullable=False),
        sa.Column("protagonist", sa.JSON(), nullable=False),
        sa.Column("recurring_characters", sa.JSON(), server_default='[]'),
        sa.Column("world_settings", sa.JSON(), server_default='{}'),
        sa.Column("achievements", sa.JSON(), server_default='[]'),
        sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
        sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
        sa.ForeignKeyConstraint(["child_profile_id"], ["child_profiles.id"], ondelete="CASCADE"),
        sa.PrimaryKeyConstraint("id"),
    )
    op.create_index("idx_story_universes_child_id", "story_universes", ["child_profile_id"])
    op.create_index("idx_story_universes_updated_at", "story_universes", ["updated_at"])


def downgrade():
    op.drop_index("idx_story_universes_updated_at")
    op.drop_index("idx_story_universes_child_id")
    op.drop_table("story_universes")

八、权限与安全

  • 宇宙数据必须通过 child_profile_id 归属校验,确保仅拥有者可访问。
  • 删除用户或档案时,级联删除所有宇宙数据。

九、相关文档