Files
life-echo/api/app/agents/memoir/classification_agent.py

175 lines
6.3 KiB
Python
Raw Permalink Normal View History

2026-03-19 10:38:11 +08:00
"""
ClassificationAgent将内容分类到 8 个章节类别之一
2026-03-23 13:54:41 +08:00
LLM 返回 none / 零散档案启发式不再跳过 Story统一映射为 ``summary`` 章节
仍走叙事流水线落库 StoryRoute 仍兼容批次内 new/append 规划不变
Memory ingest Celery 任务在批次级先行完成与分类结果独立
2026-03-19 10:38:11 +08:00
"""
2026-03-19 14:36:14 +08:00
2026-03-19 10:38:11 +08:00
from __future__ import annotations
import json
2026-03-23 13:54:41 +08:00
import re
from dataclasses import dataclass
from typing import Any
2026-03-19 10:38:11 +08:00
from pydantic import ValidationError
refactor(agents): 抽取阶段常量与对话上下文;快档 LLM;图片 prompt 可禁止回退 访谈与阶段 - 新增 app/agents/stage_constants.py:集中 CHAT_STAGES、章节分类/顺序、阶段到默认 memoir 类别等,与 MemoirState 默认槽位顺序对齐;减少散落在 prompts 内的重复常量。 - 新增 app/agents/chat/prompt_context.py:以 ChatPromptContext 汇总 guided 系统提示所需字段(阶段、槽位、轮次、人设、记忆证据、回复长度模式、背景声线、职业等),统一走 get_guided_conversation_prompt。 - 大幅收敛 app/agents/chat/prompts_conversation.py;调整 prompts.py、stage_prompts.py、stage_detection.py;同步 interview_agent、profile_agent、helpers 与 state_schema,使对话侧构造提示的方式一致、可测。 回忆录流水线 - memoir/prompts.py 删除已迁至 stage_constants / 独立模板的大段常量与图片占位相关逻辑;classification / extraction / fidelity / narrative agents 与 orchest(全量历史仍可用于计数,注入模型时按轮次与字符上限截断)、image_prompt_fallback_disabled。 - dependencies 增加 get_llm_provider_fast(LRU 缓存,可与默认共用密钥与 base_url)。 任务与编排 - memoir_tasks:prepare_batches 注入 llm_fast;开启独立快档模型时打结构化日志。 - chapter_cover_tasks、story_image_tasks:与图片 prompt / JSON 工具路径或策略变更对齐(import 与行为一致)。 - story_pipeline_sync 等小处同步。 其它核心 - langchain_llm、text_normalize 随上述调用链微调。 开发者体验 - .cursor/settings.json:启用 redis-development、postman 插件。 测试 - 新增 test_image_prompt_policy:覆盖「禁止回退」等图片 prompt 策略。 - 更新 test_interview_prompts、test_interview_reply_length、test_experience_regressions、test_json_and_memory_utils,匹配新常量位置、json_utils 与对话/长度行为。
2026-04-02 12:00:00 +08:00
from app.agents.memoir.prompts import get_chapter_classification_json_prompt
from app.agents.memoir.schemas import ClassificationOutput
from app.agents.stage_constants import (
CHAPTER_CATEGORIES,
STAGE_KEYWORD_WEIGHTS,
STAGE_TO_DEFAULT_CATEGORY,
)
from app.core.config import settings
refactor(agents): 抽取阶段常量与对话上下文;快档 LLM;图片 prompt 可禁止回退 访谈与阶段 - 新增 app/agents/stage_constants.py:集中 CHAT_STAGES、章节分类/顺序、阶段到默认 memoir 类别等,与 MemoirState 默认槽位顺序对齐;减少散落在 prompts 内的重复常量。 - 新增 app/agents/chat/prompt_context.py:以 ChatPromptContext 汇总 guided 系统提示所需字段(阶段、槽位、轮次、人设、记忆证据、回复长度模式、背景声线、职业等),统一走 get_guided_conversation_prompt。 - 大幅收敛 app/agents/chat/prompts_conversation.py;调整 prompts.py、stage_prompts.py、stage_detection.py;同步 interview_agent、profile_agent、helpers 与 state_schema,使对话侧构造提示的方式一致、可测。 回忆录流水线 - memoir/prompts.py 删除已迁至 stage_constants / 独立模板的大段常量与图片占位相关逻辑;classification / extraction / fidelity / narrative agents 与 orchest(全量历史仍可用于计数,注入模型时按轮次与字符上限截断)、image_prompt_fallback_disabled。 - dependencies 增加 get_llm_provider_fast(LRU 缓存,可与默认共用密钥与 base_url)。 任务与编排 - memoir_tasks:prepare_batches 注入 llm_fast;开启独立快档模型时打结构化日志。 - chapter_cover_tasks、story_image_tasks:与图片 prompt / JSON 工具路径或策略变更对齐(import 与行为一致)。 - story_pipeline_sync 等小处同步。 其它核心 - langchain_llm、text_normalize 随上述调用链微调。 开发者体验 - .cursor/settings.json:启用 redis-development、postman 插件。 测试 - 新增 test_image_prompt_policy:覆盖「禁止回退」等图片 prompt 策略。 - 更新 test_interview_prompts、test_interview_reply_length、test_experience_regressions、test_json_and_memory_utils,匹配新常量位置、json_utils 与对话/长度行为。
2026-04-02 12:00:00 +08:00
from app.core.json_utils import extract_json_payload
from app.core.llm_call import LLMCallError, llm_json_call
refactor(api,expo): 多智能体与会话收敛、回忆录兼容层移除、后端测试集大幅删减 - 对齐「多智能体收敛」与「回忆录 stories-first / markdown-first」方向:收紧运行时契约、 删除过渡兼容路径与双轨逻辑,并同步更新客户端与文档。 - Chat:以 ChatOrchestrator 为实时编排入口;删除独立 conversation_agent,精简 prompts。 - Memoir:删除 memory_agent;MemoirOrchestrator、classification / story_route 与 prompts 收敛到 prepare_batches + run_story_pipeline_for_category_batch 主链路。 - 将 agents 侧 processor 迁入 feature 层为 background_runner,并移除 features 下重复/过时 processor 封装。 - 新增 history_store,强化「conversation_messages 为 DB 真源、Redis 为缓存」模型。 - 调整 models、repo、service、session_history;精简 WS message_types,重构 pipeline 与 router。 - 移除章节占位、整章再生等旧路径;章节列表与封面逻辑要求 story 关联;收紧 cover 资格与 enqueue。 - helpers、repo、service、router、reading_segment_materialize、story_pipeline_sync、pdf_service 等按 canonical markdown / cover_asset_id 收缩;删除 memoir_images/provider 等冗余。 - tasks:memoir_tasks、chapter_cover_tasks 等大幅瘦身;story_image_tasks 等与当前图片任务对齐。 - core:config、logging、redis、task_tracker 小幅调整。 - auth / user / payment / quota:路由或服务侧删减过时接口或逻辑(如 payment router 行数减少)。 - pyproject.toml、development.sh、.env.example / .env.production、README 等同步说明或变量。 - Alembic 0001_initial_schema 微调(与当前 schema 叙事一致的小改动)。 - 回忆录:types / mappers / api、章节页与 memoir 页与后端契约对齐;markdown-renderer 调整。 - 语音:删除 voice/player,voice-segment-store 相应精简。 - api/tests:删除 conftest 及绝大部分既有测试文件(websocket_baseline、conversation、memoir 图片、PDF、SMS 等),属有意收缩/待按 backend-test-system 重建的信号。 - docs:新增多智能体收敛与移除兼容层计划摘要;更新 story-first 设计、backend-test-system、 multi-agent-refactor-plan、实施总结等。 BREAKING CHANGE: 后端对外契约、回忆录章节字段与若干路由/任务行为已变更;大量 API 测试被移除, CI 若依赖这些用例需按新策略补测或调整流水线。
2026-03-22 16:45:57 +08:00
from app.core.logging import get_logger
from app.features.memoir.constants import memoir
2026-03-19 10:38:11 +08:00
logger = get_logger(__name__)
# 模型判定 none 或启发式命中零散档案时,仍写入回忆录正文所用的兜底章节
_SUMMARY_FALLBACK_CATEGORY = "summary"
2026-03-23 13:54:41 +08:00
# 与「仅档案句式」组合使用;过短但明显为叙事句的仍交 LLM 判断
_FRAGMENT_SHORT_MAX_LEN = 48
# 整段仅为出生年份/年份声明(零散档案,不成故事)
_BIRTH_YEAR_LINE = re.compile(
r"^[\s\u200b]*(?:我)?\d{4}\s*年\s*(出生|生的|生)?\s*[。.!]?[\s\u200b]*$",
re.UNICODE,
)
# 极短且为「我是某地人」式籍贯标签,无过程描写
_SHORT_HUKOU_STYLE = re.compile(
r"^[\s\u200b]*(?:我)?是[\u4e00-\u9fff]{1,10}(人|籍)\s*[。.!]?[\s\u200b]*$",
re.UNICODE,
)
2026-03-19 10:38:11 +08:00
def _detect_stage(text: str, fallback_stage: str) -> str:
"""根据关键词检测消息所属的 5-stage 阶段(与 stage_constants.STAGE_KEYWORD_WEIGHTS 同源;匹配方式为子串,非加权)。"""
2026-03-19 10:38:11 +08:00
message = (text or "").lower()
for stage, pairs in STAGE_KEYWORD_WEIGHTS.items():
if any(word in message for word, _w in pairs):
2026-03-19 10:38:11 +08:00
return stage
return fallback_stage
2026-03-23 13:54:41 +08:00
def _looks_like_fragment_only(text: str) -> bool:
"""
保守启发式明显为档案点/标签句
命中时仍进回忆录正文章节映射为 ``summary`` LLM 返回 none 一致
2026-03-23 13:54:41 +08:00
"""
s = (text or "").strip()
if not s:
return True
if _BIRTH_YEAR_LINE.match(s):
return True
if len(s) <= _FRAGMENT_SHORT_MAX_LEN and _SHORT_HUKOU_STYLE.match(s):
return True
return False
def _normalize_llm_category(raw: str) -> str:
"""去掉模型偶发的引号、反引号包裹。"""
s = (raw or "").strip().lower()
if s.startswith("`"):
s = s.strip("`").strip()
if (s.startswith('"') and s.endswith('"')) or (
s.startswith("'") and s.endswith("'")
):
s = s[1:-1].strip()
return s
@dataclass(frozen=True)
class ChapterClassifyResult:
"""章节分类结果;``llm_said_none`` 仅当走 LLM 且解析为 none 时为 Truefragment 启发式不为 True"""
category: str
llm_said_none: bool = False
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)
2026-03-19 10:38:11 +08:00
class ClassificationAgent:
"""将内容分类到 8 个章节类别之一none/零散档案映射为 ``summary`` 仍进 Story。"""
2026-03-19 10:38:11 +08:00
def classify(
self,
text: str,
fallback_stage: str,
llm: Any,
*,
segment_id: str | None = None,
language: str = "zh",
) -> ChapterClassifyResult:
2026-03-19 10:38:11 +08:00
"""
分类到 8 个章节类别之一
LLM 返回 none 或启发式为零散档案时``category`` ``summary``仍可走回忆录流水线
``llm_said_none`` 仅在 LLM 明确返回 none 时为 True供空转抑制判断
2026-03-19 10:38:11 +08:00
llm 需支持 .invoke(prompt) 同步调用
"""
2026-03-23 13:54:41 +08:00
if _looks_like_fragment_only(text):
logger.info(
"event=chapter_classification_summary_fallback reason=fragment_heuristic "
"segment_id={} text_len={} category={}",
segment_id or "",
2026-03-23 13:54:41 +08:00
len(text or ""),
_SUMMARY_FALLBACK_CATEGORY,
2026-03-23 13:54:41 +08:00
)
return ChapterClassifyResult(
category=_SUMMARY_FALLBACK_CATEGORY,
llm_said_none=False,
)
2026-03-23 13:54:41 +08:00
2026-03-19 10:38:11 +08:00
if llm:
try:
prompt = get_chapter_classification_json_prompt(text, language=language)
out = llm_json_call(
llm,
prompt,
ClassificationOutput,
max_tokens=memoir.classification_max_tokens,
agent="ClassificationAgent.classify",
)
category = _normalize_llm_category(out.category)
2026-03-19 10:38:11 +08:00
if category == "none":
logger.info(
"event=chapter_classification_summary_fallback reason=llm_none "
"segment_id={} text_len={} category={}",
segment_id or "",
refactor(api,expo): 多智能体与会话收敛、回忆录兼容层移除、后端测试集大幅删减 - 对齐「多智能体收敛」与「回忆录 stories-first / markdown-first」方向:收紧运行时契约、 删除过渡兼容路径与双轨逻辑,并同步更新客户端与文档。 - Chat:以 ChatOrchestrator 为实时编排入口;删除独立 conversation_agent,精简 prompts。 - Memoir:删除 memory_agent;MemoirOrchestrator、classification / story_route 与 prompts 收敛到 prepare_batches + run_story_pipeline_for_category_batch 主链路。 - 将 agents 侧 processor 迁入 feature 层为 background_runner,并移除 features 下重复/过时 processor 封装。 - 新增 history_store,强化「conversation_messages 为 DB 真源、Redis 为缓存」模型。 - 调整 models、repo、service、session_history;精简 WS message_types,重构 pipeline 与 router。 - 移除章节占位、整章再生等旧路径;章节列表与封面逻辑要求 story 关联;收紧 cover 资格与 enqueue。 - helpers、repo、service、router、reading_segment_materialize、story_pipeline_sync、pdf_service 等按 canonical markdown / cover_asset_id 收缩;删除 memoir_images/provider 等冗余。 - tasks:memoir_tasks、chapter_cover_tasks 等大幅瘦身;story_image_tasks 等与当前图片任务对齐。 - core:config、logging、redis、task_tracker 小幅调整。 - auth / user / payment / quota:路由或服务侧删减过时接口或逻辑(如 payment router 行数减少)。 - pyproject.toml、development.sh、.env.example / .env.production、README 等同步说明或变量。 - Alembic 0001_initial_schema 微调(与当前 schema 叙事一致的小改动)。 - 回忆录:types / mappers / api、章节页与 memoir 页与后端契约对齐;markdown-renderer 调整。 - 语音:删除 voice/player,voice-segment-store 相应精简。 - api/tests:删除 conftest 及绝大部分既有测试文件(websocket_baseline、conversation、memoir 图片、PDF、SMS 等),属有意收缩/待按 backend-test-system 重建的信号。 - docs:新增多智能体收敛与移除兼容层计划摘要;更新 story-first 设计、backend-test-system、 multi-agent-refactor-plan、实施总结等。 BREAKING CHANGE: 后端对外契约、回忆录章节字段与若干路由/任务行为已变更;大量 API 测试被移除, CI 若依赖这些用例需按新策略补测或调整流水线。
2026-03-22 16:45:57 +08:00
len(text or ""),
_SUMMARY_FALLBACK_CATEGORY,
2026-03-19 14:36:14 +08:00
)
return ChapterClassifyResult(
category=_SUMMARY_FALLBACK_CATEGORY,
llm_said_none=True,
)
2026-03-19 10:38:11 +08:00
if category in CHAPTER_CATEGORIES:
return ChapterClassifyResult(category=category, llm_said_none=False)
except (LLMCallError, ValidationError, ValueError, KeyError) as e:
logger.warning("ClassificationAgent LLM 章节分类失败: {}", e)
2026-03-19 10:38:11 +08:00
stage = _detect_stage(text, fallback_stage)
refactor(agents): 抽取阶段常量与对话上下文;快档 LLM;图片 prompt 可禁止回退 访谈与阶段 - 新增 app/agents/stage_constants.py:集中 CHAT_STAGES、章节分类/顺序、阶段到默认 memoir 类别等,与 MemoirState 默认槽位顺序对齐;减少散落在 prompts 内的重复常量。 - 新增 app/agents/chat/prompt_context.py:以 ChatPromptContext 汇总 guided 系统提示所需字段(阶段、槽位、轮次、人设、记忆证据、回复长度模式、背景声线、职业等),统一走 get_guided_conversation_prompt。 - 大幅收敛 app/agents/chat/prompts_conversation.py;调整 prompts.py、stage_prompts.py、stage_detection.py;同步 interview_agent、profile_agent、helpers 与 state_schema,使对话侧构造提示的方式一致、可测。 回忆录流水线 - memoir/prompts.py 删除已迁至 stage_constants / 独立模板的大段常量与图片占位相关逻辑;classification / extraction / fidelity / narrative agents 与 orchest(全量历史仍可用于计数,注入模型时按轮次与字符上限截断)、image_prompt_fallback_disabled。 - dependencies 增加 get_llm_provider_fast(LRU 缓存,可与默认共用密钥与 base_url)。 任务与编排 - memoir_tasks:prepare_batches 注入 llm_fast;开启独立快档模型时打结构化日志。 - chapter_cover_tasks、story_image_tasks:与图片 prompt / JSON 工具路径或策略变更对齐(import 与行为一致)。 - story_pipeline_sync 等小处同步。 其它核心 - langchain_llm、text_normalize 随上述调用链微调。 开发者体验 - .cursor/settings.json:启用 redis-development、postman 插件。 测试 - 新增 test_image_prompt_policy:覆盖「禁止回退」等图片 prompt 策略。 - 更新 test_interview_prompts、test_interview_reply_length、test_experience_regressions、test_json_and_memory_utils,匹配新常量位置、json_utils 与对话/长度行为。
2026-04-02 12:00:00 +08:00
cat = STAGE_TO_DEFAULT_CATEGORY.get(
2026-03-19 10:38:11 +08:00
stage,
refactor(agents): 抽取阶段常量与对话上下文;快档 LLM;图片 prompt 可禁止回退 访谈与阶段 - 新增 app/agents/stage_constants.py:集中 CHAT_STAGES、章节分类/顺序、阶段到默认 memoir 类别等,与 MemoirState 默认槽位顺序对齐;减少散落在 prompts 内的重复常量。 - 新增 app/agents/chat/prompt_context.py:以 ChatPromptContext 汇总 guided 系统提示所需字段(阶段、槽位、轮次、人设、记忆证据、回复长度模式、背景声线、职业等),统一走 get_guided_conversation_prompt。 - 大幅收敛 app/agents/chat/prompts_conversation.py;调整 prompts.py、stage_prompts.py、stage_detection.py;同步 interview_agent、profile_agent、helpers 与 state_schema,使对话侧构造提示的方式一致、可测。 回忆录流水线 - memoir/prompts.py 删除已迁至 stage_constants / 独立模板的大段常量与图片占位相关逻辑;classification / extraction / fidelity / narrative agents 与 orchest(全量历史仍可用于计数,注入模型时按轮次与字符上限截断)、image_prompt_fallback_disabled。 - dependencies 增加 get_llm_provider_fast(LRU 缓存,可与默认共用密钥与 base_url)。 任务与编排 - memoir_tasks:prepare_batches 注入 llm_fast;开启独立快档模型时打结构化日志。 - chapter_cover_tasks、story_image_tasks:与图片 prompt / JSON 工具路径或策略变更对齐(import 与行为一致)。 - story_pipeline_sync 等小处同步。 其它核心 - langchain_llm、text_normalize 随上述调用链微调。 开发者体验 - .cursor/settings.json:启用 redis-development、postman 插件。 测试 - 新增 test_image_prompt_policy:覆盖「禁止回退」等图片 prompt 策略。 - 更新 test_interview_prompts、test_interview_reply_length、test_experience_regressions、test_json_and_memory_utils,匹配新常量位置、json_utils 与对话/长度行为。
2026-04-02 12:00:00 +08:00
STAGE_TO_DEFAULT_CATEGORY.get(fallback_stage, "childhood"),
2026-03-19 10:38:11 +08:00
)
return ChapterClassifyResult(category=cat, llm_said_none=False)