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
This commit is contained in:
@@ -4,6 +4,7 @@ ChatOrchestrator:AI 回复用户模块的编排层
|
||||
"""
|
||||
|
||||
import time
|
||||
from collections.abc import Callable
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, List, Optional
|
||||
|
||||
@@ -24,7 +25,8 @@ from app.agents.chat.stage_detection import (
|
||||
from app.agents.state_schema import MemoirStateSchema
|
||||
from app.core.agent_logging import agent_summary_enabled, log_agent_detail
|
||||
from app.core.config import settings
|
||||
from app.core.dependencies import get_llm_provider
|
||||
from app.core.dependencies import get_embedding_provider
|
||||
from app.core.llm_gateway import LlmGateway
|
||||
from app.core.logging import get_logger
|
||||
from app.features.conversation.input_normalize import normalize_chat_input_for_agent
|
||||
from app.features.memoir.state_service import (
|
||||
@@ -32,18 +34,20 @@ from app.features.memoir.state_service import (
|
||||
save_interview_state_meta,
|
||||
switch_stage,
|
||||
)
|
||||
from app.features.memory.prompt_adapter import MemoryPromptAdapter
|
||||
|
||||
|
||||
def _llm_for_chat_input_normalize():
|
||||
try:
|
||||
p = get_llm_provider()
|
||||
return getattr(p, "langchain_llm", None)
|
||||
return LlmGateway().langchain_llm_for()
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from app.features.user.models import User
|
||||
from app.ports.embedding import EmbeddingProvider
|
||||
from app.ports.llm import LLMProvider
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
@@ -56,9 +60,10 @@ async def _fetch_interview_memory_bundle(
|
||||
db: AsyncSession,
|
||||
user_id: str,
|
||||
user_message: str,
|
||||
*,
|
||||
get_embedding_provider_fn: Callable[[], "EmbeddingProvider"],
|
||||
) -> tuple[dict | None, object | None]:
|
||||
"""检索记忆 bundle(原始结构);是否进主 prompt 由 `slice_interview_memory` 再筛。"""
|
||||
from app.core.dependencies import get_embedding_provider
|
||||
"""检索记忆 bundle(原始结构);是否进主 prompt 由 adapter 再筛。"""
|
||||
from app.features.memory.retrieval_trace import (
|
||||
chat_memory_retrieval_trace_from_bundle,
|
||||
)
|
||||
@@ -76,7 +81,7 @@ async def _fetch_interview_memory_bundle(
|
||||
)
|
||||
return None, None
|
||||
try:
|
||||
emb = get_embedding_provider()
|
||||
emb = get_embedding_provider_fn()
|
||||
ms = MemoryService(db, embedding_provider=emb)
|
||||
top_k = settings.chat_memory_top_k
|
||||
bundle = await ms.retrieve(user_id, msg, top_k=top_k)
|
||||
@@ -103,11 +108,22 @@ class ChatOrchestrator:
|
||||
"""
|
||||
聊天编排器:根据用户资料完成度路由到 ProfileAgent 或 InterviewAgent。
|
||||
不直接写入 Redis/DB;由 WS pipeline / ConversationHistoryStore 落库并同步缓存。
|
||||
|
||||
``get_embedding_provider_fn`` / ``llm_provider`` 供测试或脚本注入;默认使用全局依赖。
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.profile_agent = ProfileAgent()
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
get_embedding_provider_fn: Callable[[], "EmbeddingProvider"] | None = None,
|
||||
llm_provider: "LLMProvider | None" = None,
|
||||
):
|
||||
self._get_embedding_provider_fn = (
|
||||
get_embedding_provider_fn or get_embedding_provider
|
||||
)
|
||||
self.profile_agent = ProfileAgent(llm_provider=llm_provider)
|
||||
self.interview_agent = InterviewAgent()
|
||||
self.memory_prompt_adapter = MemoryPromptAdapter()
|
||||
|
||||
async def process_user_message(
|
||||
self,
|
||||
@@ -272,12 +288,16 @@ class ChatOrchestrator:
|
||||
background_voice = infer_background_voice(user.occupation)
|
||||
occupation = user.occupation or ""
|
||||
|
||||
from app.features.memory.chat_memory_injection import slice_interview_memory
|
||||
|
||||
memory_bundle, mem_trace = await _fetch_interview_memory_bundle(
|
||||
db, user_id, normalized_user_message
|
||||
db,
|
||||
user_id,
|
||||
normalized_user_message,
|
||||
get_embedding_provider_fn=self._get_embedding_provider_fn,
|
||||
)
|
||||
mem_slices = self.memory_prompt_adapter.slice_for_interview(
|
||||
memory_bundle,
|
||||
normalized_user_message,
|
||||
)
|
||||
mem_slices = slice_interview_memory(memory_bundle, normalized_user_message)
|
||||
# 场景关键词仅作为 focus planner 的辅助输入,不直接拼进记忆块,避免抢过用户明确的关系/身份线索
|
||||
scene_cues_for_planner = extract_scene_cues(normalized_user_message)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user