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:
@@ -8,14 +8,17 @@ ClassificationAgent:将内容分类到 8 个章节类别,或判定无价值
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
from typing import Any, Optional
|
||||
|
||||
from app.agents.memoir.prompts import (
|
||||
CHAPTER_CATEGORIES,
|
||||
get_chapter_classification_prompt,
|
||||
get_chapter_classification_json_prompt,
|
||||
)
|
||||
from app.core.langchain_llm import invoke_json_object
|
||||
from app.core.logging import get_logger
|
||||
from app.features.memoir.memoir_images.json_payload import extract_json_payload
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
@@ -89,6 +92,20 @@ def _normalize_llm_category(raw: str) -> str:
|
||||
return s
|
||||
|
||||
|
||||
def _parse_category_from_llm_response(raw: str) -> str:
|
||||
"""优先解析 JSON ``{"category": "..."}``,失败则按纯文本 key 处理。"""
|
||||
s = (raw or "").strip()
|
||||
if not s:
|
||||
return ""
|
||||
try:
|
||||
data = json.loads(extract_json_payload(s))
|
||||
if isinstance(data, dict) and "category" in data:
|
||||
return _normalize_llm_category(str(data["category"]))
|
||||
except (json.JSONDecodeError, TypeError, ValueError):
|
||||
pass
|
||||
return _normalize_llm_category(s)
|
||||
|
||||
|
||||
class ClassificationAgent:
|
||||
"""将内容分类到 8 个章节类别之一,或判定无价值返回 None"""
|
||||
|
||||
@@ -105,7 +122,7 @@ class ClassificationAgent:
|
||||
"""
|
||||
if _looks_like_fragment_only(text):
|
||||
logger.debug(
|
||||
"零散档案/极短标签句,跳过回忆录 Story: text_len=%s text=%s",
|
||||
"零散档案/极短标签句,跳过回忆录 Story: text_len={} text={}",
|
||||
len(text or ""),
|
||||
text or "",
|
||||
)
|
||||
@@ -113,12 +130,17 @@ class ClassificationAgent:
|
||||
|
||||
if llm:
|
||||
try:
|
||||
prompt = get_chapter_classification_prompt(text)
|
||||
response = llm.invoke(prompt)
|
||||
category = _normalize_llm_category(response.content or "")
|
||||
prompt = get_chapter_classification_json_prompt(text)
|
||||
raw = invoke_json_object(
|
||||
llm,
|
||||
prompt,
|
||||
max_tokens=256,
|
||||
agent="ClassificationAgent.classify",
|
||||
)
|
||||
category = _parse_category_from_llm_response(raw)
|
||||
if category == "none":
|
||||
logger.debug(
|
||||
"LLM 判定内容不足以成篇,跳过: text_len=%s text=%s",
|
||||
"LLM 判定内容不足以成篇,跳过: text_len={} text={}",
|
||||
len(text or ""),
|
||||
text or "",
|
||||
)
|
||||
@@ -126,7 +148,7 @@ class ClassificationAgent:
|
||||
if category in CHAPTER_CATEGORIES:
|
||||
return category
|
||||
except Exception as e:
|
||||
logger.warning("ClassificationAgent LLM 章节分类失败: %s", e)
|
||||
logger.warning("ClassificationAgent LLM 章节分类失败: {}", e)
|
||||
|
||||
stage = _detect_stage(text, fallback_stage)
|
||||
return _STAGE_TO_DEFAULT_CATEGORY.get(
|
||||
|
||||
Reference in New Issue
Block a user