PMFlow/backend/tests/test_semantic_decision_serv...

77 lines
2.7 KiB
Python

from __future__ import annotations
from app.config.ai_prompts import PMFLOW_SEMANTIC_CONTEXT_DECISION_PROMPT
from app.services.ai_pmo.semantic_context.decision_schema import (
DecisionGateResult,
ModeCard,
SemanticDecision,
)
from app.services.ai_pmo.semantic_context.gatekeeper import SemanticDecisionGatekeeper
def test_semantic_decision_gatekeeper_accepts_allowed_plan_patch() -> None:
mode_card = ModeCard(
workflow_mode="PLAN",
mode_state="ACTIVE",
allowed_actions=["reply.compose", "draft.patch", "plan.mark_ready_for_confirmation"],
forbidden_actions=["project.create", "task.create", "db.write"],
exit_conditions=["用户明确要求开始执行"],
)
decision = SemanticDecision(
topic_relation="continue_current_topic",
mode_decision="ready_confirm",
draft_patch={"goal": "先收一版需求草稿"},
assistant_reply="我先整理成待确认草稿。",
requires_confirmation=True,
requested_actions=["draft.patch", "reply.compose"],
confidence=0.92,
)
result = SemanticDecisionGatekeeper.evaluate(mode_card=mode_card, decision=decision)
assert isinstance(result, DecisionGateResult)
assert result.accepted is True
assert result.blocked_reasons == []
assert result.allowed_actions == ["draft.patch", "reply.compose"]
assert result.next_plan_status == "awaiting_execute_confirm"
def test_semantic_decision_gatekeeper_blocks_forbidden_action() -> None:
mode_card = ModeCard(
workflow_mode="PLAN",
mode_state="ACTIVE",
allowed_actions=["reply.compose", "draft.patch"],
forbidden_actions=["project.create", "task.create", "db.write"],
exit_conditions=["用户明确要求开始执行"],
)
decision = SemanticDecision(
topic_relation="continue_current_topic",
mode_decision="request_execute",
draft_patch={},
assistant_reply="我准备直接创建项目。",
requires_confirmation=True,
requested_actions=["project.create"],
confidence=0.73,
)
result = SemanticDecisionGatekeeper.evaluate(mode_card=mode_card, decision=decision)
assert result.accepted is False
assert "forbidden_action:project.create" in result.blocked_reasons
assert result.next_plan_status == "active"
def test_semantic_context_decision_prompt_avoids_business_semantic_examples() -> None:
forbidden_fragments = (
"信号源呢?",
"负责人呢?",
"技术路线呢?",
"商业模式呢?",
"给项目2接入",
"GitHub webhook",
"RTMP/WebRTC",
)
for fragment in forbidden_fragments:
assert fragment not in PMFLOW_SEMANTIC_CONTEXT_DECISION_PROMPT