Files
life-echo/api/app/features/memory/timeline.py
Kevin ac436b87a2 feat(api): 收敛对话与记忆流程边界,引入 LLM 网关与专用服务
- MemoryService 异步路径委托 MemoryIngestService / MemoryRetrievalService;富化派发经 MemoryEnrichmentScheduler
- WebSocket pipeline 经 ChatTurnService 与显式 DTO 编排单轮对话;回忆录片段入队由 MemoirIngestScheduler 封装
- 新增 LlmGateway(LlmUseCase),各 agent、任务与适配器对齐 ports
- 补充 memory 提示适配、runtime 类型、memory-retrieval 文档、ai-touchpoints 说明与扫描脚本及配套测试

Made-with: Cursor
2026-04-30 09:17:01 +08:00

78 lines
2.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""由已抽取事实生成时间线事件LLM + JSON"""
from __future__ import annotations
import json
from typing import Any
from app.core.langchain_llm import ainvoke_json_object, invoke_json_object
from app.core.llm_gateway import LlmGateway, LlmUseCase
from app.core.logging import get_logger
from app.features.memory.llm_schemas import (
TimelineEventsPayload,
parse_json_payload,
timeline_payload_to_dicts,
)
logger = get_logger(__name__)
MAX_FACTS_JSON = 20000
def build_timeline_events_from_facts_sync(llm: Any, facts: list[dict]) -> list[dict]:
"""facts 须含 id 字段(已落库)。"""
if not llm or not facts:
return []
payload = json.dumps(facts, ensure_ascii=False)[:MAX_FACTS_JSON]
prompt = (
"根据下列事实(含 id生成时间线事件用于回忆录展示。\n"
"每条含 event_year整数或 null、event_date可选、title、description、"
"source_fact_ids必须来自输入中的 id 列表)。\n"
'只输出 JSON{"events":[...]},无事件则 {"events":[]}。最多 15 条。\n\n'
f"{payload}"
)
try:
raw = invoke_json_object(
llm, prompt, max_tokens=4096, agent="memory.timeline_events_sync"
)
parsed = parse_json_payload(raw, TimelineEventsPayload)
if parsed is None:
return []
return timeline_payload_to_dicts(parsed)
except (TypeError, ValueError) as e:
logger.warning("build_timeline_events_from_facts_sync 失败: {}", e)
return []
async def build_timeline_events_from_facts_async(
llm: Any, facts: list[dict]
) -> list[dict]:
if not llm or not facts:
return []
payload = json.dumps(facts, ensure_ascii=False)[:MAX_FACTS_JSON]
prompt = (
"根据下列事实(含 id生成时间线事件。\n"
"每条含 event_year、event_date、title、description、source_fact_ids来自输入 id\n"
'只输出 JSON{"events":[...]}。\n\n'
f"{payload}"
)
try:
raw = await ainvoke_json_object(
llm, prompt, max_tokens=4096, agent="memory.timeline_events_async"
)
parsed = parse_json_payload(raw, TimelineEventsPayload)
if parsed is None:
return []
return timeline_payload_to_dicts(parsed)
except (TypeError, ValueError) as e:
logger.warning("build_timeline_events_from_facts_async 失败: {}", e)
return []
async def build_timeline_events(facts: list[dict]) -> list[dict]:
"""兼容旧接口。"""
llm = LlmGateway().langchain_llm_for(
LlmUseCase("memory.timeline_events.compat", fast=True)
)
return await build_timeline_events_from_facts_async(llm, facts)