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 @@
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
from app.agents.chat.helpers import format_history_string
from app.agents.chat.interview_state_hints import (
AUTOBIOGRAPHICAL_BOUNDARY_FALLBACK_ZH,
DUPLICATE_QUESTION_GUARD_FALLBACK_ZH,
@@ -10,20 +11,19 @@ from app.agents.chat.interview_state_hints import (
extract_scene_cues,
segments_are_only_duplicate_guard_fallback,
)
from app.agents.chat.output_rules import chat_output_rules
from app.agents.chat.personas import normalize_interview_persona
from app.agents.chat.prompts_conversation import (
get_guided_conversation_prompt,
get_opening_prompt,
)
from app.agents.chat.slot_question_bank import SLOT_QUESTION_OUTLINES
from app.agents.state_schema import (
KnownFact,
MemoirStateSchema,
PersonaThread,
default_slots,
)
from app.agents.chat.helpers import format_history_string
from app.agents.chat.personas import normalize_interview_persona
from app.agents.chat.output_rules import chat_output_rules
from app.agents.chat.prompts_conversation import (
get_guided_conversation_prompt,
get_opening_prompt,
)
from app.agents.chat.slot_question_bank import SLOT_QUESTION_OUTLINES
def test_guided_prompt_does_not_embed_raw_user_message_in_system_text():
@@ -132,6 +132,21 @@ def test_guided_prompt_host_tone_and_context_forward():
assert "行为" in p and "影响" in p
def test_guided_prompt_leaves_turn_level_question_contract_to_turn_plan() -> None:
p = get_guided_conversation_prompt(
current_stage="career",
empty_slots=["job"],
filled_slots={},
detected_user_stage="career",
user_profile_context="",
persona="default",
)
assert "随后**必须**用**一条**" not in p
assert "短承接后须带回一条" not in p
assert "仍须**勾回回忆叙事**" not in p
assert "具体问几问、是否必须追问,见顶部" in p
def test_education_and_family_change_outlines_differ():
edu = SLOT_QUESTION_OUTLINES[("education", "change")]
fam = SLOT_QUESTION_OUTLINES[("family", "change")]