feat: PromptProvider 化 + $DEFLECTION_POOL 单一事实源注入 + runtime 必填化

- PromptProvider(app/engine/services/ai_service.py): 租户提示词四件套
  from_dir 加载;build_system 把 system.md 的 $DEFLECTION_POOL 占位符替换为
  wall.json deflections 话术池(按语言分组一行,EN/ZH/ES)再拼事实卡;
  extractor/evaluator 的 $existing_info/$messages/$profile/$recent_messages
  替换逻辑自 AIService 原样搬入(行为等价);删模块级 import 时读文件的
  PROMPTS_DIR 与四个常量,保留 DEFAULT_PROVIDER(jiancai 出厂,__file__ 锚定)
- 两租户 system.md「三类请求的处理」第2条的迂回话术枚举 → $DEFLECTION_POOL
  占位符(保留段落说明文字),话术只存 wall.json 一份,改一处两边同步变
- TenantRuntime 加 prompts 字段(加载期 from_dir(seed_dir/prompts));
  ConversationManager 的 runtime 转必填(keyword-only),默认 AIService 用
  runtime.prompts + compiled_wall.deflections 组装
- app/engine/prompts/ 删除(grep 无代码消费方,jiancai 出厂由
  seed_profiles/jiancai/prompts 承接);test_company_facts/
  test_system_prompt_dual_contact 的 cwd 相对路径改 Path(__file__) 锚定,
  test_extractor_evaluator_prompts 断言对象改 DEFAULT_PROVIDER(语义等价)
- 新增 tests/test_prompt_provider.py(14 用例): 加载/注入/格式/占位符防漂移/
  extractor-evaluator 组装/registry 接线/runtime 必填/池线过两墙

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-10-12 19:54:47 +08:00
parent 20dddc151e
commit e34378257f
18 changed files with 358 additions and 440 deletions
+13 -9
View File
@@ -327,20 +327,24 @@ def _boundary_rotate() -> int:
class ConversationManager:
def __init__(self, db: AsyncSession, ai_service: AIService | None = None,
runtime: "TenantRuntime | None" = None):
def __init__(self, db: AsyncSession, *,
runtime: "TenantRuntime",
ai_service: AIService | None = None):
self.db = db
self.ai = ai_service or AIService()
# Task 5b 装配线注入: 持有租户运行时引用(session_factory/compiled_wall/
# config 就位;prompts 仍为模块级,Task 6 才 PromptProvider 化;email/push
# 等 settings→config 业务接线是 Task 9)。None = 无租户上下文(直连测试)。
# Task 6: runtime 必填(Task 5b 质量审查关门建议)——提示词/墙/会话
# 工厂全部租户化,无租户上下文的构造不再合法;默认 AI 服务用该租户
# 的 PromptProvider + compiled_wall.deflections 组装($DEFLECTION_POOL
# 单一事实源注入)。email/push 等 settings→config 业务接线是 Task 9。
self.runtime = runtime
self.ai = ai_service or AIService(
prompts=runtime.prompts,
wall_deflections=runtime.compiled_wall.deflections)
@property
def session_factory(self):
"""推送后台任务写结果用的工厂(写回派发租户的库);无 runtime 时
None,lead_push_service 回退模块级兜底。"""
return self.runtime.session_factory if self.runtime is not None else None
"""推送后台任务写结果用的工厂(写回派发租户的库);runtime 未懒建
engine 时 None,lead_push_service 回退模块级兜底。"""
return self.runtime.session_factory
async def create_conversation(
self,