From 7fff0171e83648f11ab0e47787efca00b03613a6 Mon Sep 17 00:00:00 2001 From: BlueX888 <140241684+BlueX888@users.noreply.github.com> Date: Wed, 2 Sep 2026 15:41:19 +0800 Subject: [PATCH] fix(teams): clamp bid award scores to 0-10 parse_bid_award wrote TL verdict scores straight to bid.score without the 0-10 clamp that parse_bid_scores applies. An out-of-range verdict score (e.g. 12 or -3) therefore flowed into the persisted bid record and into candidate_hp, which computes HP loss as 10 - score and could nudge an eliminated candidate back above zero. Align the award path with the scoring path so the score domain is consistent everywhere. Add a regression test asserting 12.0 clamps to 10.0 and -3.0 clamps to 0.0. --- backend/app/teams/service.py | 3 ++- backend/tests/test_teams_bidding.py | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/backend/app/teams/service.py b/backend/app/teams/service.py index 43800b138..73110125f 100644 --- a/backend/app/teams/service.py +++ b/backend/app/teams/service.py @@ -266,8 +266,9 @@ def parse_bid_award(reply: str, candidate_ids: set[str]) -> dict[str, Any] | Non score = float(item.get("score")) except (TypeError, ValueError): continue + # 与 parse_bid_scores 一致:裁决分数同样落在 0-10,避免污染血条与看板 scores[str(agent_id)] = { - "score": score, + "score": min(10.0, max(0.0, score)), "rationale": str(item.get("rationale") or "").strip(), } return { diff --git a/backend/tests/test_teams_bidding.py b/backend/tests/test_teams_bidding.py index 841445c65..0f4a3e5ab 100644 --- a/backend/tests/test_teams_bidding.py +++ b/backend/tests/test_teams_bidding.py @@ -171,6 +171,23 @@ def test_parse_bid_award_requires_candidate_winner() -> None: assert parse_bid_award("没有块", candidates) is None +def test_parse_bid_award_clamps_scores_to_zero_ten() -> None: + candidates = {"agent_a", "agent_b"} + reply = ( + "```json\n" + '{"bid_award": {"winner_agent_id": "agent_a", ' + '"scores": {"agent_a": {"score": 12.0, "rationale": "超出"}, ' + '"agent_b": {"score": -3.0, "rationale": "负分"}}, ' + '"comment": "甲更匹配"}}\n```' + ) + award = parse_bid_award(reply, candidates) + assert award is not None + assert award["winner_agent_id"] == "agent_a" + # 与 parse_bid_scores 一致,裁决分数也截断到 0-10,避免污染血条(HP)与看板 + assert award["scores"]["agent_a"] == {"score": 10.0, "rationale": "超出"} + assert award["scores"]["agent_b"] == {"score": 0.0, "rationale": "负分"} + + def test_parse_bid_scores() -> None: candidates = {"agent_a", "agent_b"} scores = parse_bid_scores(_score_reply(("agent_a", 9.0), ("agent_b", 8.0)), candidates)