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:
@@ -13,7 +13,7 @@ from app.agents.memoir.prompts import (
|
||||
get_story_batch_plan_prompt,
|
||||
get_story_route_prompt,
|
||||
)
|
||||
from app.core.langchain_llm import bind_json_object_mode
|
||||
from app.core.langchain_llm import invoke_json_object
|
||||
from app.core.logging import get_logger
|
||||
from app.features.story.models import Story
|
||||
|
||||
@@ -90,7 +90,7 @@ def _build_candidate_json(stories: list[Story], *, preview_chars: int = 220) ->
|
||||
def _build_segments_json_for_plan(
|
||||
segments: list[tuple[str, str]], *, text_preview_chars: int = 4000
|
||||
) -> str:
|
||||
"""segments: (id, transcript_text) 按口述顺序。"""
|
||||
"""segments: (id, user_input_text) 按口述顺序。"""
|
||||
rows: list[dict[str, str]] = []
|
||||
for sid, text in segments:
|
||||
t = (text or "").strip()
|
||||
@@ -157,13 +157,16 @@ class StoryRouteAgent:
|
||||
candidate_stories_json=payload,
|
||||
)
|
||||
try:
|
||||
json_llm = bind_json_object_mode(llm, max_tokens=1024)
|
||||
response = json_llm.invoke(prompt)
|
||||
raw = (response.content or "").strip()
|
||||
raw = invoke_json_object(
|
||||
llm,
|
||||
prompt,
|
||||
max_tokens=1024,
|
||||
agent="StoryRouteAgent.decide",
|
||||
).strip()
|
||||
data = json.loads(raw)
|
||||
decision = StoryRouteDecision.model_validate(data)
|
||||
except Exception as e:
|
||||
logger.warning("StoryRouteAgent 解析失败: %s", e)
|
||||
logger.warning("StoryRouteAgent 解析失败: {}", e)
|
||||
return StoryRouteDecision(
|
||||
decision="new_story",
|
||||
new_story_title=None,
|
||||
@@ -174,7 +177,7 @@ class StoryRouteAgent:
|
||||
tid = decision.target_story_id
|
||||
if not tid or tid not in valid_story_ids:
|
||||
logger.warning(
|
||||
"StoryRoute append 无效 target_story_id=%s,回退 new_story",
|
||||
"StoryRoute append 无效 target_story_id={},回退 new_story",
|
||||
tid,
|
||||
)
|
||||
return StoryRouteDecision(
|
||||
@@ -212,18 +215,21 @@ class StoryRouteAgent:
|
||||
candidate_stories_json=payload,
|
||||
)
|
||||
try:
|
||||
json_llm = bind_json_object_mode(llm, max_tokens=4096)
|
||||
response = json_llm.invoke(prompt)
|
||||
raw = (response.content or "").strip()
|
||||
raw = invoke_json_object(
|
||||
llm,
|
||||
prompt,
|
||||
max_tokens=4096,
|
||||
agent="StoryRouteAgent.plan_batch",
|
||||
).strip()
|
||||
data = json.loads(raw)
|
||||
plan = StoryBatchPlan.model_validate(data)
|
||||
except Exception as e:
|
||||
logger.warning("StoryRouteAgent.plan_batch 解析失败: %s", e)
|
||||
logger.warning("StoryRouteAgent.plan_batch 解析失败: {}", e)
|
||||
return None
|
||||
|
||||
ordered = [s[0] for s in segments]
|
||||
ok, err = validate_story_batch_plan(ordered, plan, valid_story_ids)
|
||||
if not ok:
|
||||
logger.warning("StoryRouteAgent.plan_batch 校验失败: %s", err)
|
||||
logger.warning("StoryRouteAgent.plan_batch 校验失败: {}", err)
|
||||
return None
|
||||
return plan
|
||||
|
||||
Reference in New Issue
Block a user