feat(api+app): 对话阶段化、回忆录流水线与客户端会话体验

- DB: segments 用户输入文本(Alembic 0002)
- Chat: 阶段检测/阶段提示/回复限制,编排与访谈/画像 prompts 调整
- Memoir: 忠实度检查 agent,叙事与分类等链路更新
- Core: agent 日志、Alembic 启动、LangChain/日志/配置等
- Story: time_hints;Memory 检索与相关测试
- Expo: 助手头像、会话页与消息拆分、实时会话与文案/i18n
- Docs/scripts/tests: 迁移脚本、LLM JSON/记忆检索文档、新增单测
This commit is contained in:
Kevin
2026-03-26 12:13:36 +08:00
parent 49b089354c
commit a3f61fcc0f
94 changed files with 3332 additions and 672 deletions

View File

@@ -218,7 +218,7 @@ async def websocket_endpoint(
try:
if websocket.application_state != WebSocketState.CONNECTED:
logger.debug(
"WebSocket 已非连接状态,退出循环: conversation_id=%s",
"WebSocket 已非连接状态,退出循环: conversation_id={}",
conversation_id,
)
break
@@ -251,7 +251,7 @@ async def websocket_endpoint(
segment = Segment(
id=str(uuid.uuid4()),
conversation_id=conversation_id,
transcript_text=text_message,
user_input_text=text_message,
processed=False,
)
db.add(segment)
@@ -414,8 +414,8 @@ async def websocket_endpoint(
if not should_process:
logger.debug(
"收到重复分段,跳过: conversation_id=%s voice_session_id=%s "
"segment_index=%s audio_b64_len=%s duration=%s",
"收到重复分段,跳过: conversation_id={} voice_session_id={} "
"segment_index={} audio_b64_len={} duration={}",
conversation_id,
voice_session_id,
segment_index,
@@ -470,7 +470,7 @@ async def websocket_endpoint(
continue
logger.debug(
"收到音频消息: conversation_id=%s duration_s=%s",
"收到音频消息: conversation_id={} duration_s={}",
conversation_id,
audio_duration,
)
@@ -478,18 +478,16 @@ async def websocket_endpoint(
try:
asr = get_asr_provider()
audio_bytes = base64.b64decode(audio_base64)
transcript_text = await asr.transcribe(
audio_bytes, "m4a"
asr_text = await asr.transcribe(audio_bytes, "m4a")
logger.debug(
"ASR 转写完成: conversation_id={} chars={}",
conversation_id,
len(asr_text or ""),
)
logger.debug(
"ASR 转写完成: conversation_id=%s chars=%s",
"ASR 转写全文: conversation_id={} text={}",
conversation_id,
len(transcript_text or ""),
)
logger.debug(
"ASR 转写全文: conversation_id=%s text=%s",
conversation_id,
transcript_text,
asr_text,
)
await manager.send_message(
@@ -498,7 +496,7 @@ async def websocket_endpoint(
"type": MessageType.TRANSCRIPT,
"conversation_id": conversation_id,
"data": {
"text": transcript_text,
"text": asr_text,
"audio_duration": audio_duration,
},
"timestamp": datetime.now(
@@ -514,7 +512,7 @@ async def websocket_endpoint(
segment = Segment(
id=str(uuid.uuid4()),
conversation_id=conversation_id,
transcript_text=transcript_text,
user_input_text=asr_text,
audio_url=f"audio:{audio_duration}s",
audio_duration_seconds=ads if ads > 0 else None,
processed=False,
@@ -529,12 +527,10 @@ async def websocket_endpoint(
conversation.user_id, segment.id
)
if transcript_text and not transcript_text.startswith(
"转写失败"
):
if asr_text and not asr_text.startswith("转写失败"):
await process_user_message(
conversation_id=conversation_id,
user_message=transcript_text,
user_message=asr_text,
conversation=conversation,
segment=segment,
db=db,
@@ -587,13 +583,13 @@ async def websocket_endpoint(
try:
asr = get_asr_provider()
audio_bytes = base64.b64decode(audio_base64)
transcript_text = await asr.transcribe(audio_bytes, "m4a")
asr_text = await asr.transcribe(audio_bytes, "m4a")
await manager.send_message(
conversation_id,
{
"type": MessageType.TRANSCRIPT,
"conversation_id": conversation_id,
"data": {"text": transcript_text or ""},
"data": {"text": asr_text or ""},
"timestamp": datetime.now(timezone.utc).isoformat(),
},
)
@@ -637,7 +633,7 @@ async def websocket_endpoint(
and "not connected" in error_msg.lower()
):
logger.debug(
"WebSocket 连接已断开或未就绪: conversation_id=%s error=%s",
"WebSocket 连接已断开或未就绪: conversation_id={} error={}",
conversation_id,
error_msg,
)
@@ -661,7 +657,7 @@ async def websocket_endpoint(
break
except WebSocketDisconnect:
logger.debug(
"WebSocket 断开连接: conversation_id=%s", conversation_id
"WebSocket 断开连接: conversation_id={}", conversation_id
)
break
except Exception as e:
@@ -681,7 +677,7 @@ async def websocket_endpoint(
break
except WebSocketDisconnect:
logger.debug("WebSocket 断开连接: conversation_id=%s", conversation_id)
logger.debug("WebSocket 断开连接: conversation_id={}", conversation_id)
await manager.disconnect(conversation_id)
cleanup_segment_states(conversation_id)
except Exception as e: