test: PromptProvider 审查修复(组装缝断言/占位符约定文档与glob守护/边界注记)

- I-1: test_company_facts 捕获完整 prompt 的用例补话术池注入断言
  ("- EN:"/"- ZH:" 分组行在场)——守 AIService 组装缝,防 Task 9
  重接提示词线时静默丢池
- I-2: seed_profiles/README.md 落档 $DEFLECTION_POOL 占位符约定
  (缺失 = 话术池静默不注入);test_prompt_provider 占位符防漂移改
  glob 遍历 seed_profiles/*/prompts/system.md,第三租户自动纳管
  (空 glob 显性兜底,防假守护)
- Minor-1 注记: _format_deflection_pool docstring 记已知边界——条目
  含引号/换行暂不转义,Phase 2 编辑器批次处理
- Minor-5: test_provider_loads_from_tenant_dir 删恒真 or 侧,改单一
  明确断言;registry 加载期显性失败清单补 prompts 读取失败
  (FileNotFoundError 说明);conftest build_jiancai_runtime 注明
  data_dir 仅占位、从不 ensure_ready

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-10-12 20:16:40 +08:00
parent e34378257f
commit 1824bb1590
6 changed files with 20 additions and 7 deletions
+2
View File
@@ -32,6 +32,8 @@ def _format_deflection_pool(deflections) -> str:
紧凑不膨胀(设计 R1: 话术单一事实源,提示词侧只引用不复制)。 紧凑不膨胀(设计 R1: 话术单一事实源,提示词侧只引用不复制)。
只渲染主池(deflections);deflections_contact 是已留资态的墙替换池, 只渲染主池(deflections);deflections_contact 是已留资态的墙替换池,
本就不属于 system.md 教学内容,不注入。 本就不属于 system.md 教学内容,不注入。
已知边界(审查 Minor-1 注记): 条目自身含双引号/换行时暂不转义——
现网池线均为单行纯文本,Phase 2 后台编辑器批次统一处理(输入校验+转义)。
""" """
pools = dict(deflections or {}) pools = dict(deflections or {})
langs = [l for l in _DEFLECTION_LANG_ORDER if pools.get(l)] langs = [l for l in _DEFLECTION_LANG_ORDER if pools.get(l)]
+3 -1
View File
@@ -135,7 +135,9 @@ class TenantRegistry:
加载期显性失败(不流入运行期):目录无任何 tenant.json(seed_dir 加载期显性失败(不流入运行期):目录无任何 tenant.json(seed_dir
路径拼错→静默空注册表→全量 404,必须炸)、无效 JSON、slug 与目录 路径拼错→静默空注册表→全量 404,必须炸)、无效 JSON、slug 与目录
名不一致、零站点、wall 编译错误、域名重复绑定。 名不一致、零站点、wall 编译错误、域名重复绑定、prompts 读取失败
(Task 6: prompts/ 目录缺失或四件套不齐 → PromptProvider.from_dir
的 FileNotFoundError,加载即炸,不流入运行期)。
""" """
reg = cls() reg = cls()
reg._seed_dir = pathlib.Path(seed_dir) reg._seed_dir = pathlib.Path(seed_dir)
+1 -1
View File
@@ -21,7 +21,7 @@ seed_profiles/<slug>/
- **空域名 + `data-site`**:`es` 站 `"domains": []`,与 main 共用挂件域名,嵌入代码以 `data-site="es"` 参数选择站点。 - **空域名 + `data-site`**:`es` 站 `"domains": []`,与 main 共用挂件域名,嵌入代码以 `data-site="es"` 参数选择站点。
- **`site.brand` 是覆盖层**:缺省项继承 `config.brand`(深合并),只覆盖门面(标题/欢迎语等);AI 脑子与线索管道留在租户级。 - **`site.brand` 是覆盖层**:缺省项继承 `config.brand`(深合并),只覆盖门面(标题/欢迎语等);AI 脑子与线索管道留在租户级。
- **wall 不写在 tenant.json 里**:加载器会把同目录 `wall.json` **隐式并入** `config["wall"]`(原始词表,供后台编辑/回滚),编译产物挂 runtime 的 `compiled_wall` 供引擎消费——词表数据源只有 wall.json 一份。 - **wall 不写在 tenant.json 里**:加载器会把同目录 `wall.json` **隐式并入** `config["wall"]`(原始词表,供后台编辑/回滚),编译产物挂 runtime 的 `compiled_wall` 供引擎消费——词表数据源只有 wall.json 一份。
- **`config` 内不含 prompts**:提示词由目录内 `prompts/*.md` 提供(Task 6)。 - **`config` 内不含 prompts**:提示词由目录内 `prompts/*.md` 提供(Task 6);**system.md 必须包含 `$DEFLECTION_POOL` 占位符**——组装时注入 wall.json 的 deflections 话术池(缺失 = 话术池静默不注入;test_prompt_provider 以 glob 遍历守护全部租户)。
- 加载期显性失败(不流入运行期):无效 JSON、slug 与目录名不一致、零站点、wall 编译错误、域名跨站点/跨租户重复绑定。 - 加载期显性失败(不流入运行期):无效 JSON、slug 与目录名不一致、零站点、wall 编译错误、域名跨站点/跨租户重复绑定。
## 纪律 ## 纪律
+2 -1
View File
@@ -82,7 +82,8 @@ def build_jiancai_runtime():
engine/session_factory 保持 None: 直连测试自管 async_db(NullPool 跨 engine/session_factory 保持 None: 直连测试自管 async_db(NullPool 跨
循环纪律),manager.session_factory 为 None 时 lead_push_service 回退 循环纪律),manager.session_factory 为 None 时 lead_push_service 回退
模块级兜底,与旧 runtime=None 行为一致。无 engine 即无句柄,无需清理; 模块级兜底,与旧 runtime=None 行为一致。无 engine 即无句柄,无需清理;
data_dir 仅拼 db_path 字符串(无 IO),模块级缓存单例跨测试复用。 data_dir 仅占位(只拼 db_path 字符串,无任何 IO——从不 ensure_ready,
不会创建 tenants/ 目录或库文件),模块级缓存单例跨测试复用。
""" """
global _JIANCAI_RUNTIME global _JIANCAI_RUNTIME
if _JIANCAI_RUNTIME is None: if _JIANCAI_RUNTIME is None:
+4
View File
@@ -35,6 +35,10 @@ def test_facts_concatenated_between_system_and_context():
asyncio.run(svc.chat([{"role": "user", "content": "hi"}], context="⚠️ 提醒")) asyncio.run(svc.chat([{"role": "user", "content": "hi"}], context="⚠️ 提醒"))
p = svc.provider.prompts[0] p = svc.provider.prompts[0]
assert "HESUPIPE" in p and "HDPE" in p # 建材事实在场 assert "HESUPIPE" in p and "HDPE" in p # 建材事实在场
# 审查 I-1: AIService 组装缝守护——默认服务的 $DEFLECTION_POOL 话术池
# 必须真实注入(锚分组行格式而非具体话术,话术内容随墙演进而变);
# 防 Task 9 重接提示词线时静默丢池
assert "- EN:" in p and "- ZH:" in p
# Task 6: system 组装含 $DEFLECTION_POOL 话术池注入,SYSTEM_PROMPT 原文 # Task 6: system 组装含 $DEFLECTION_POOL 话术池注入,SYSTEM_PROMPT 原文
# 不再是组装结果的连续子串——改以首行/事实卡标题锚定顺序(语义等价) # 不再是组装结果的连续子串——改以首行/事实卡标题锚定顺序(语义等价)
assert (p.index(SYSTEM_PROMPT.splitlines()[0]) assert (p.index(SYSTEM_PROMPT.splitlines()[0])
+8 -4
View File
@@ -30,7 +30,7 @@ def _wall(slug: str) -> dict:
def test_provider_loads_from_tenant_dir(): def test_provider_loads_from_tenant_dir():
p = PromptProvider.from_dir(JC_PROMPTS) p = PromptProvider.from_dir(JC_PROMPTS)
assert "HESUPIPE" in p.company_facts or "管道" in p.system assert "HESUPIPE" in p.company_facts # jiancai 事实卡身份标记(审查 Minor-5: 删恒真 or 侧)
def test_provider_reads_all_four_prompts(): def test_provider_reads_all_four_prompts():
@@ -57,9 +57,13 @@ def test_system_prompt_injects_deflection_pool():
def test_system_md_carries_placeholder(): def test_system_md_carries_placeholder():
# 防漂移锚: 两租户 system.md 必须保留占位符,丢了它话术池就静默失联 # 防漂移锚(审查 I-2): glob 遍历全部租户 system.md——第三租户落地即自动
for prompts_dir in (JC_PROMPTS, AUTO_PROMPTS): # 纳管(约定同步落档 seed_profiles/README.md: 缺占位符 = 话术池静默不注入);
assert "$DEFLECTION_POOL" in PromptProvider.from_dir(prompts_dir).system # 空 glob 恒绿 = 假守护,先断言至少一份
system_mds = sorted((REPO / "seed_profiles").glob("*/prompts/system.md"))
assert system_mds
for md in system_mds:
assert "$DEFLECTION_POOL" in md.read_text(encoding="utf-8"), md
def test_build_system_appends_facts_after_system(): def test_build_system_appends_facts_after_system():