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
+7 -3
View File
@@ -36,6 +36,7 @@ from sqlalchemy.ext.asyncio import (
)
from app.engine.models import ensure_schema
from app.engine.services.ai_service import PromptProvider
from app.engine.wall_schema import CompiledWall, compile_wall
from app.platform.tenant_config import Site, TenantConfig
@@ -92,8 +93,9 @@ class TenantRuntime:
"""单租户运行时。
加载期就绪:config(TenantConfig:data=快照⊕出厂默认,含 sites)/
db_path(该租户 SQLite 文件路径)/seed_dir(seed_profiles/<slug>/,
Task 6 PromptProvider 从中读 prompts/*.md)/compiled_wall(墙编译产物)。
db_path(该租户 SQLite 文件路径)/seed_dir(seed_profiles/<slug>/)/
compiled_wall(墙编译产物)/prompts(Task 6 PromptProvider,from_dir
(seed_dir/prompts) 四件套——$DEFLECTION_POOL 话术池注入的单一消费口)。
懒建:engine/session_factory(Registry 首次 resolve,ensure_schema 已跑)。
manager(Task 5b)由装配线另行构造注入,不在本类持有。
"""
@@ -102,6 +104,7 @@ class TenantRuntime:
db_path: pathlib.Path
seed_dir: pathlib.Path
compiled_wall: CompiledWall
prompts: PromptProvider
engine: AsyncEngine | None = None
session_factory: async_sessionmaker | None = None
@@ -170,7 +173,8 @@ class TenantRegistry:
config=config,
db_path=self._data_dir / f"{slug}.db",
seed_dir=tenant_dir,
compiled_wall=compile_wall(wall_cfg))
compiled_wall=compile_wall(wall_cfg),
prompts=PromptProvider.from_dir(tenant_dir / "prompts"))
def _build_domain_index(
self, by_slug: dict[str, TenantRuntime]