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:
@@ -24,19 +24,36 @@ from app.core.agent_logging import agent_span, log_agent_payload, log_agent_summ
|
||||
from app.core.config import settings
|
||||
from app.core.dependencies import get_llm_provider
|
||||
from app.core.llm_call import allm_json_call
|
||||
from app.core.llm_gateway import LlmGateway, LlmUseCase
|
||||
from app.core.logging import get_logger
|
||||
from app.ports.llm import LLMProvider
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
def _get_langchain_llm():
|
||||
try:
|
||||
provider = get_llm_provider()
|
||||
return getattr(provider, "langchain_llm", None)
|
||||
return LlmGateway().langchain_llm_for(LlmUseCase("chat.profile"))
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _langchain_messages_to_port(messages: List[Any]) -> list[dict]:
|
||||
"""LangChain message 列表 → ``LLMProvider.complete`` 的 ``role/content`` 结构。"""
|
||||
out: list[dict] = []
|
||||
for m in messages:
|
||||
if isinstance(m, SystemMessage):
|
||||
out.append({"role": "system", "content": str(m.content)})
|
||||
elif isinstance(m, HumanMessage):
|
||||
out.append({"role": "user", "content": str(m.content)})
|
||||
elif isinstance(m, AIMessage):
|
||||
out.append({"role": "assistant", "content": str(m.content)})
|
||||
else:
|
||||
c = getattr(m, "content", None)
|
||||
out.append({"role": "user", "content": str(c) if c is not None else ""})
|
||||
return out
|
||||
|
||||
|
||||
def _message_contents_char_count(messages: List[Any]) -> int:
|
||||
n = 0
|
||||
for m in messages:
|
||||
@@ -49,9 +66,15 @@ def _message_contents_char_count(messages: List[Any]) -> int:
|
||||
class ProfileAgent:
|
||||
"""用户资料收集 Specialist Agent"""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, llm_provider: LLMProvider | None = None):
|
||||
self._llm_provider = llm_provider
|
||||
self.llm = _get_langchain_llm()
|
||||
|
||||
def _provider(self) -> LLMProvider:
|
||||
if self._llm_provider is not None:
|
||||
return self._llm_provider
|
||||
return get_llm_provider()
|
||||
|
||||
async def _invoke_chat(
|
||||
self,
|
||||
messages: List[Any],
|
||||
@@ -60,20 +83,21 @@ class ProfileAgent:
|
||||
conversation_id: Optional[str],
|
||||
agent_name: str,
|
||||
) -> str:
|
||||
chat_llm = self.llm.bind(max_tokens=max_tokens)
|
||||
port_messages = _langchain_messages_to_port(messages)
|
||||
llm_t0 = time.perf_counter()
|
||||
with agent_span(
|
||||
logger, f"{agent_name}.llm", conversation_id=conversation_id or ""
|
||||
):
|
||||
response = await chat_llm.ainvoke(messages)
|
||||
response_text = await self._provider().complete(
|
||||
port_messages,
|
||||
max_tokens=max_tokens,
|
||||
)
|
||||
logger.info(
|
||||
"event=chat_llm_done agent={} response_latency_ms={:.2f}",
|
||||
agent_name,
|
||||
(time.perf_counter() - llm_t0) * 1000,
|
||||
)
|
||||
return (
|
||||
response.content if hasattr(response, "content") else str(response)
|
||||
) or ""
|
||||
return response_text or ""
|
||||
|
||||
async def _segments_from_response(
|
||||
self,
|
||||
|
||||
Reference in New Issue
Block a user