harden(extract): 提取加写死的 JSON 输出契约兜底,消除"过度依赖 skill 文件"脆点

承上次定位:脚本生成空 skill 也能出 JSON,是因其 system 带写死的 _OUTPUT_PROTOCOL(进镜像、
永远在);提取没有这层兜底,只靠 skill 文件 → skill 一空就吐散文解析失败。本次对齐脚本做法:
给提取也加 _EXTRACT_OUTPUT_CONTRACT(写死的 entities/segments JSON 契约 + 不提商品等铁律),
拼在 skill 正文之后。即便将来 skill 再丢,提取也仍收到「只输出 JSON」硬指令,不会再退化。

真验证:用 dev 库里那条真实失败脚本,system 仅留写死契约(模拟 skill 全空)真调豆包 →
仍稳定出 JSON(角色/场景齐、商品不提)。

测试:152/152 全绿(新增契约兜底单测)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
seaislee1209
2026-06-23 00:24:29 +08:00
co-authored by Claude Opus 4.8
parent 8dab8b4921
commit 66778fa2d3
2 changed files with 33 additions and 1 deletions
+9
View File
@@ -29,6 +29,15 @@ class SkillPromptBundlingTests(SimpleTestCase):
self.assertIn("JSON", prompt)
self.assertIn("entities", prompt)
def test_extract_output_contract_is_hardcoded_safety_net(self):
"""写死的提取输出契约(随代码进镜像)必须存在且含 JSON 铁律 —— 这是 skill 丢失时的兜底,
保证提取永远收到「只输出 JSON」指令(对齐脚本 agent 的 _OUTPUT_PROTOCOL,根治"系统提示词为空"事故)。"""
from apps.ai.services import _EXTRACT_OUTPUT_CONTRACT
self.assertGreater(len(_EXTRACT_OUTPUT_CONTRACT), 200)
for marker in ("entities", "segments", "character", "scene", "JSON"):
self.assertIn(marker, _EXTRACT_OUTPUT_CONTRACT)
class NormalizeDraftTests(SimpleTestCase):
"""normalize_draft 对模型不按契约输出的容错(防「旁白/画面全空」回归)。"""