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

@@ -29,7 +29,7 @@ class TencentASRProvider:
self._client = asr_client.AsrClient(cred, "", client_profile)
return self._client
except Exception as e:
logger.error("Tencent ASR client init failed: %s", e)
logger.error("Tencent ASR client init failed: {}", e)
return None
def ensure_ready(self) -> bool:
@@ -53,5 +53,5 @@ class TencentASRProvider:
resp = client.SentenceRecognition(req)
return (resp.Result or "").strip()
except Exception as e:
logger.error("Tencent ASR transcribe failed: %s", e)
logger.error("Tencent ASR transcribe failed: {}", e)
return ""

View File

@@ -63,7 +63,7 @@ class WhisperASRProvider:
)
return True
except Exception as e:
logger.error("Failed to load Whisper model: %s", e)
logger.error("Failed to load Whisper model: {}", e)
return False
def ensure_ready(self) -> bool:
@@ -89,7 +89,7 @@ class WhisperASRProvider:
)
return "".join(seg.text for seg in segments).strip()
except Exception as e:
logger.error("Whisper transcribe failed: %s", e)
logger.error("Whisper transcribe failed: {}", e)
return ""
finally:
if tmp_path and os.path.exists(tmp_path):

View File

@@ -163,7 +163,7 @@ class LiblibImageProvider:
f"Liblib returned undocumented status 7 for {job['job_id']}"
)
logger.debug(
"Liblib poll attempt %d/%d, status=%s, job=%s",
"Liblib poll attempt {}/{}, status={}, job={}",
attempt + 1,
max_attempts,
status,
@@ -218,6 +218,7 @@ def _redact_sensitive_query_values(value):
def _install_http_log_redaction() -> None:
"""对 httpx/httpcore 的标准库 Logger 挂 Filter非 loguru get_logger"""
for logger_name in (
"httpx",
"httpcore",
@@ -225,7 +226,7 @@ def _install_http_log_redaction() -> None:
"httpcore.http11",
"httpcore.proxy",
):
target_logger = get_logger(logger_name)
target_logger = logging.getLogger(logger_name)
if getattr(target_logger, "_liblib_auth_redaction_installed", False):
continue
target_logger.addFilter(_LiblibAuthRedactionFilter())

View File

@@ -9,7 +9,8 @@ class DeepSeekLLMProvider:
"""LangChain-based LLM adapter for DeepSeek and OpenAI-compatible APIs.
`langchain_llm` 供 Agent / 任务同步调用;若需 JSON object 模式,请用
`app.core.langchain_llm.bind_json_object_mode` 绑定,勿使用已废弃的
`app.core.langchain_llm.bind_json_object_mode` 或 `invoke_json_object` /
`ainvoke_json_object`(见 `api/docs/llm-json-mode.md`),勿使用已废弃的
`bind(model_kwargs={"response_format": ...})`。
"""

View File

@@ -64,7 +64,7 @@ class TencentSmsSender:
if "TemplateParamSetNotMatchApprovedTemplate" in error_code:
continue
logger.error(
"SMS send failed: %s - %s",
"SMS send failed: {} - {}",
error_code,
status.Message if status else "",
)
@@ -73,10 +73,10 @@ class TencentSmsSender:
except TencentCloudSDKException as e:
if "TemplateParamSetNotMatchApprovedTemplate" in str(e):
continue
logger.error("Tencent SMS SDK error: %s", e)
logger.error("Tencent SMS SDK error: {}", e)
return False
except Exception as e:
logger.error("SMS send exception: %s", e)
logger.error("SMS send exception: {}", e)
return False
logger.error("All SMS template param configs failed")

View File

@@ -51,4 +51,4 @@ class TencentCosStorage:
try:
self._client.delete_object(Bucket=self._bucket, Key=key)
except (CosClientError, CosServiceError) as e:
logger.warning("COS delete failed: key=%s, error=%s", key, e)
logger.warning("COS delete failed: key={}, error={}", key, e)

View File

@@ -90,7 +90,7 @@ class TencentTTSProvider:
self._client = tts_client.TtsClient(cred, "", client_profile)
return self._client
except Exception as e:
logger.error("Tencent TTS client init failed: %s", e)
logger.error("Tencent TTS client init failed: {}", e)
return None
def _synthesize_sync(self, text: str, voice_type: int) -> bytes:
@@ -116,10 +116,10 @@ class TencentTTSProvider:
return b""
return base64.b64decode(resp.Audio)
except TencentCloudSDKException as e:
logger.error("Tencent TTS SDK error: %s", e)
logger.error("Tencent TTS SDK error: {}", e)
return b""
except Exception as e:
logger.error("Tencent TTS synthesize failed: %s", e)
logger.error("Tencent TTS synthesize failed: {}", e)
return b""
async def synthesize(self, text: str, voice: str = "alloy") -> bytes: