refactor(question_gen): adapt generator/gates/store signatures for strategy

- generator_v2: _load_prompt_template takes template_name str instead of
  QuestionFamilySpec; _build_v2_prompt takes prompt_template + strategy_name
  + sub_pattern_instruction; generate_one_v2 takes discrete params
  (prompt_template, strategy_name, skill_target, sub_pattern_instruction)
- gates: _gate_leak_test and run_gates take leak_probe_template str
  instead of QuestionFamilySpec
- run_store: add sub_pattern column to DDL + idempotent migration;
  record_item accepts optional sub_pattern param
- Remove QuestionFamilySpec imports from generator_v2 and gates modules
- Update test call sites accordingly

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-14 05:50:47 -04:00
parent c49d0ff12f
commit afa77173e3
4 changed files with 155 additions and 76 deletions
+23 -7
View File
@@ -7,7 +7,6 @@ from typing import Any
import pytest
from app.question_gen.families import RETRIEVAL_FAMILY
from app.question_gen.gates import (
CandidateQuestion,
GateVerdict,
@@ -73,7 +72,22 @@ class MockLLM:
self._call_count += 1
if idx < len(self._responses):
return self._responses[idx]
# 默认返回 PASS
return _make_llm_response("pass", "default")
async def chat_with_images(
self,
messages: list[dict[str, Any]],
images: list[Any],
*,
session_id: str | None = None,
parent_call_id: str | None = None,
) -> LLMResponse:
"""记录 VLM 调用并返回预设响应(与 chat 共享计数器)。"""
self.calls.append({"messages": messages, "images": images, "session_id": session_id})
idx = self._call_count
self._call_count += 1
if idx < len(self._responses):
return self._responses[idx]
return _make_llm_response("pass", "default")
@@ -254,11 +268,13 @@ class TestGateLeakTest:
"""leak_test 门:按家族模板执行泄漏探测。"""
@pytest.mark.asyncio()
async def test_per_family_template(self) -> None:
"""使用家族特定的 probe_template 调用 LLM。"""
async def test_per_strategy_template(self) -> None:
"""使用策略特定的 probe_template 调用 LLM。"""
llm = MockLLM([_make_llm_response("pass", "no shortcut detected")])
candidate = _make_candidate()
result = await _gate_leak_test(candidate, RETRIEVAL_FAMILY, llm, session_id="test-session")
result = await _gate_leak_test(
candidate, "gate_leak_retrieval.md", llm, session_id="test-session"
)
assert result.verdict == GateVerdict.PASS
# 验证 session_id 被正确传递
assert llm.calls[0]["session_id"] == "test-session"
@@ -284,7 +300,7 @@ class TestRunGates:
candidate=candidate,
tree=tree,
llm=llm,
family_spec=RETRIEVAL_FAMILY,
leak_probe_template="gate_leak_retrieval.md",
postprocess=postprocess,
session_id="test-session",
)
@@ -303,7 +319,7 @@ class TestRunGates:
candidate=candidate,
tree=tree,
llm=llm,
family_spec=RETRIEVAL_FAMILY,
leak_probe_template="gate_leak_retrieval.md",
postprocess=postprocess,
session_id="test-session",
)