fix(P0-03): Prediction 幂等指纹——只追加,不覆盖
_upsert_prediction 改为 _insert_or_find_by_fingerprint: - 同 input_hash → 返回已有行(绝不 UPDATE pred_/reasoning/agent_outputs) - 不同 input_hash → INSERT 新行 input_hash 升级为规范 JSON SHA-256,捕获:match_id, cutoff, prompt_version, prompt_hash, system_prompt_hash, provider, model, mode, run_type, temperature, context_hash, agent_ids。移除旧 (match, provider, model, mode, run_type) 唯一约束, 改为 partial unique index(WHERE input_hash IS NOT NULL,兼容旧 NULL 数据)。 三条路径(single/multi/baseline)统一传足指纹字段。 迁移 0024 + 测试 test_p0_prediction_fingerprint(10/10);全量 295 通过。
This commit is contained in:
@@ -61,7 +61,7 @@ class TestOrchestratorWritesAgentWeights:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_orchestrator_writes_agent_weights_to_upsert(self):
|
||||
"""orchestrator 应将 agent_weights 传入 _upsert_prediction。"""
|
||||
"""orchestrator 应将 agent_weights 传入 _insert_or_find_by_fingerprint。"""
|
||||
from src.llm.agents import orchestrator as orch_mod
|
||||
from src.llm.agents.base import AgentReport
|
||||
from src.llm.context_builder import MatchHeader
|
||||
@@ -103,8 +103,8 @@ class TestOrchestratorWritesAgentWeights:
|
||||
"agent_weights": {"form": 0.3, "home_away": 0.5, "stats": 0.2},
|
||||
}, 100, 50
|
||||
|
||||
async def mock_upsert(session, **kw):
|
||||
captured_values.update(kw.get("values", {}))
|
||||
async def mock_upsert(session, *, values):
|
||||
captured_values.update(values)
|
||||
p = MagicMock()
|
||||
p.id = 1
|
||||
p.provider = "test"
|
||||
@@ -116,7 +116,7 @@ class TestOrchestratorWritesAgentWeights:
|
||||
p.subjective_confidence = 0.7
|
||||
p.reasoning = "test"
|
||||
p.agent_outputs = []
|
||||
p.agent_weights = kw["values"].get("agent_weights")
|
||||
p.agent_weights = values.get("agent_weights")
|
||||
return p
|
||||
|
||||
class FakeUow:
|
||||
@@ -130,14 +130,14 @@ class TestOrchestratorWritesAgentWeights:
|
||||
with patch.object(orch_mod, "run_specialists", mock_specialists), \
|
||||
patch.object(orch_mod, "_agent_provider", mock_provider), \
|
||||
patch.object(orch_mod, "load_match_header", mock_header), \
|
||||
patch.object(orch_mod, "_upsert_prediction", mock_upsert), \
|
||||
patch.object(orch_mod, "_insert_or_find_by_fingerprint", mock_upsert), \
|
||||
patch.object(orch_mod, "run_aggregator", mock_aggregator), \
|
||||
patch.object(orch_mod, "get_uow", FakeUow):
|
||||
|
||||
result = await orch_mod.predict_match_multi(999)
|
||||
|
||||
# 断言 agent_weights 被写入
|
||||
assert "agent_weights" in captured_values, "agent_weights 应传入 _upsert_prediction"
|
||||
assert "agent_weights" in captured_values, "agent_weights 应传入 _insert_or_find_by_fingerprint"
|
||||
assert captured_values["agent_weights"] is not None, "agent_weights 不应为 None"
|
||||
assert "form" in captured_values["agent_weights"], "agent_weights 应包含专家权重"
|
||||
print(f"PASS: agent_weights = {captured_values['agent_weights']}")
|
||||
|
||||
Reference in New Issue
Block a user