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:
Kevin
2026-04-30 09:17:01 +08:00
parent eddb2c3078
commit ac436b87a2
37 changed files with 1400 additions and 199 deletions

View File

@@ -2,6 +2,7 @@
import pytest
from app.core.config import settings
from app.features.memory import evidence as evidence_mod
from app.features.memory.evidence_format import format_evidence_chunks_for_chat_prompt
from app.features.memory.evidence import (
@@ -9,6 +10,7 @@ from app.features.memory.evidence import (
_facts_to_dicts,
_stories_to_dicts,
_timeline_to_dicts,
retrieve_evidence_bundle_async,
retrieve_evidence_bundle_sync,
)
from app.features.memory.schemas import EvidenceBundle
@@ -190,3 +192,69 @@ def test_slice_interview_memory_suppresses_long_new_topic():
s = slice_interview_memory(evidence, long_msg)
assert s.prompt_excerpt == ""
assert s.anchor_source == ""
async def test_retrieve_evidence_bundle_async_non_empty_merges_precomputed_chunks(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""非空 query异步路径以 merged_chunk_dicts 为主,元数据来自 fetch_evidence_metadata_async。"""
meta = {
"relevant_facts": [
{
"id": "f1",
"fact_type": "bio",
"subject": "s",
"predicate": "p",
"object_json": {},
}
],
"timeline_hints": [],
"relevant_summaries": [
{
"id": "s1",
"summary_type": "session",
"content": "sum",
"source_chunk_ids": [],
}
],
"relevant_stories": [],
}
async def fake_fetch_meta(db, user_id, q, top_k):
assert user_id == "u1"
assert q == "hello"
assert top_k == 7
return meta
monkeypatch.setattr(evidence_mod, "fetch_evidence_metadata_async", fake_fetch_meta)
merged = [{"id": "c1", "content": "chunk body", "chunk_index": 0}]
out = await retrieve_evidence_bundle_async(
object(),
"u1",
" hello ",
top_k=7,
merged_chunk_dicts=merged,
)
assert out == {"relevant_chunks": merged, **meta}
async def test_empty_query_evidence_bundle_async_and_sync_aligned_when_rolling_off(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(settings, "memory_evidence_empty_query_include_rolling", False)
out_a = await retrieve_evidence_bundle_async(
object(),
"u1",
" ",
top_k=10,
merged_chunk_dicts=[],
)
assert out_a == dict(EMPTY_EVIDENCE_BUNDLE)
out_s = retrieve_evidence_bundle_sync(
session=object(),
user_id="u1",
query="",
top_k=10,
embedding_provider=None,
)
assert out_s == dict(EMPTY_EVIDENCE_BUNDLE)