feat(memory,conversation): 记忆富化/证据包、时间线幂等字段与对话分段全链路

数据库
- 新增迁移 0003:timeline_events.memory_source_id 外键 → memory_sources,便于按 ingest 源做时间线幂等

后端 - 记忆
- 新增 ingest 后 LLM 富化(摘要/事实/时间线),可配置开关与最大字符数
- 新增证据包组装:合并 chunk、摘要、事实、时间线、故事等检索结果;支持空 query 时是否仍带 rolling 等开关
- repo/retriever/service/router/schemas/summarizer/timeline/extractor 等扩展;文档 memory-retrieval.md 更新

后端 - 对话 WS
- 增加 PING/PONG;分段 ASR 日志与空音频处理;转写失败与「无助手回复」错误提示更明确
- 助手多段回复持久化使用统一分隔符,与分段逻辑一致

后端 - Agent
- reply_limits:按 [SPLIT] 与段落拆段,并保证非空 fallback,供 WS 与 TTS 多段下发

后端 - 回忆录任务
- transcript ingest 记录 source_id;任务成功结?
This commit is contained in:
Kevin
2026-03-27 16:01:28 +08:00
parent 1374f6e8f5
commit e4bf0710c7
70 changed files with 3404 additions and 557 deletions

View File

@@ -1,6 +1,6 @@
"""User 数据访问:查询与「清空用户业务数据」批量删除。"""
from sqlalchemy import delete, select
from sqlalchemy import delete, select, update
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.cos_url_keys import (
@@ -9,7 +9,7 @@ from app.core.cos_url_keys import (
)
from app.features.asset.models import Asset
from app.features.auth.models import RefreshToken
from app.features.conversation.models import Conversation, Segment
from app.features.conversation.models import Conversation, ConversationMessage, Segment
from app.features.memoir.models import (
Book,
Chapter,
@@ -34,6 +34,23 @@ async def get_user_by_id(user_id: str, db: AsyncSession) -> User | None:
return await db.get(User, user_id)
async def clear_user_demographics(db: AsyncSession, user_id: str) -> None:
"""
清空 users 表上由访谈收集的档案字段(不删账号行;手机号、密码等登录字段保留)。
聊天助手会从这些字段注入「出生地」等上下文,清空后才会真正「忘记」。
"""
await db.execute(
update(User)
.where(User.id == user_id)
.values(
birth_year=None,
birth_place=None,
grew_up_place=None,
occupation=None,
)
)
async def collect_purge_context(
db: AsyncSession, user_id: str
) -> tuple[list[str], list[str], list[str]]:
@@ -119,7 +136,8 @@ async def collect_object_storage_keys_before_purge(
async def purge_user_related_rows(db: AsyncSession, user_id: str) -> None:
"""
物理删除当前用户除账号users 行)外的业务数据。
顺序按外键依赖memory → 资源意图关联的 assets → story/chapter/book → 对话 → 订单与 refresh token 等。
顺序按外键依赖memory → 资源意图关联的 assets → story/chapter/book →
conversation_messages引用 segments→ segments → conversations → …
"""
await db.execute(delete(MemoryFact).where(MemoryFact.user_id == user_id))
await db.execute(delete(MemoryChunk).where(MemoryChunk.user_id == user_id))
@@ -138,6 +156,13 @@ async def purge_user_related_rows(db: AsyncSession, user_id: str) -> None:
await db.execute(delete(Chapter).where(Chapter.user_id == user_id))
await db.execute(delete(Book).where(Book.user_id == user_id))
await db.execute(
delete(ConversationMessage).where(
ConversationMessage.conversation_id.in_(
select(Conversation.id).where(Conversation.user_id == user_id)
)
)
)
await db.execute(
delete(Segment).where(
Segment.conversation_id.in_(