feat(character): appearance/motive 全栈穿线 + motive/traits 口径重切

打通 appearance/motive 从生成→schema→契约→落库→前端整条穿线
(DB 列早在初始迁移、此前一直 NULL,无新迁移),并原子重切人物
内驱口径。

- @llm: CharacterCard/character-gen.md 加 appearance/motive(给默认值守
  解析韧性),重切——动机唯一落 motive、traits 只留核心/表层/阴影三层、
  arc 引用 motive;regen prompt_hashes.json 金标准。
- @backend: CharacterCardView + CharacterWriteRepo/SqlCharacterWriteRepo
  (create + update backfill 分支)+ CharacterWriteFields + 路由双向形变
  与 _existing_characters 带上两字段。
- @frontend: pnpm gen:api 重生成 TS 客户端;CharacterCardItem 展示/编辑
  两字段。
- 测试: schema 默认值/解析、动机不双写重切回归、card_to_view 双向、
  ingest 透传、list 暴露、SqlCharacterWriteRepo create/backfill 真 pg 集成。
This commit is contained in:
Yaojia Wang
2026-07-06 14:36:11 +02:00
parent 6854dac98f
commit acde715515
13 changed files with 297 additions and 15 deletions

View File

@@ -148,6 +148,8 @@ async def _existing_characters(memory: MemoryRepos, project_id: uuid.UUID) -> li
name=v.name,
role=v.role or "",
traits=traits,
appearance=v.appearance or "",
motive=v.motive or "",
backstory=v.backstory or "",
arc=arc or "",
speech_tics=tics,
@@ -329,6 +331,8 @@ async def ingest_characters(
name=card.name,
role=card.role,
traits=list(card.traits),
appearance=card.appearance,
motive=card.motive,
backstory=card.backstory,
arc=card.arc,
speech_tics=list(card.speech_tics),
@@ -434,6 +438,8 @@ def _card_to_view(card: CharacterCard) -> CharacterCardView:
name=card.name,
role=card.role,
traits=list(card.traits),
appearance=card.appearance,
motive=card.motive,
backstory=card.backstory,
arc=card.arc,
speech_tics=list(card.speech_tics),
@@ -449,6 +455,8 @@ def _view_to_card(view: CharacterCardView) -> CharacterCard:
name=view.name,
role=view.role,
traits=list(view.traits),
appearance=view.appearance,
motive=view.motive,
backstory=view.backstory,
arc=view.arc,
speech_tics=list(view.speech_tics),