171 lines
6.9 KiB
Python
171 lines
6.9 KiB
Python
from __future__ import annotations
|
|
|
|
from app.schemas.pmflow_ai_turn_runtime import PMFlowAITurnState
|
|
from app.schemas.rag import RAGAskRequest
|
|
from app.services.pmflow_ai_turn_runtime_service import PMFlowAITurnRuntimeService
|
|
from app.services.pmflow_ai_turn_state_reviser import PMFlowAITurnStateReviser
|
|
|
|
|
|
def test_packet_e_goal_frames_drive_working_state_without_legacy_decision_fields() -> None:
|
|
state = PMFlowAITurnRuntimeService(object(), tenant_id=1).build_initial_turn_state(
|
|
request=RAGAskRequest(question="现在最新的 Agent 应用机会是什么"),
|
|
route_plan={"legacy_policy": "internal_only"},
|
|
turn_hypothesis={
|
|
"normalized_goal": "判断当前最新 Agent 应用机会",
|
|
"desired_outcome": "给出机会、风险和下一步",
|
|
"goal_frames": [
|
|
{
|
|
"frame_id": "agent-opportunity",
|
|
"operation_kind": "judge_direction",
|
|
"target_scope": {
|
|
"scope_type": "external_public",
|
|
"binding_strength": "surface",
|
|
"source": "turn_goal_assessment",
|
|
},
|
|
"target_object_requirement": "none",
|
|
"answer_shape": "strategic_judgement",
|
|
"confidence": 0.82,
|
|
}
|
|
],
|
|
"operation_gaps": [
|
|
{
|
|
"key": "fresh_public_market_evidence",
|
|
"gap_type": "freshness_gap",
|
|
"blocking": False,
|
|
"user_actionable": False,
|
|
"system_action_hint": "external_search",
|
|
"reason": "需要当前公开市场证据。",
|
|
}
|
|
],
|
|
"can_answer_with_assumptions": True,
|
|
"primary_object": {"binding_strength": "none"},
|
|
},
|
|
)
|
|
|
|
decision = PMFlowAITurnRuntimeService(object(), tenant_id=1).assess_sufficiency(state)
|
|
|
|
assert state.selected_frame is not None
|
|
assert state.selected_frame.operation_kind == "judge_direction"
|
|
assert state.answer_state.answer_shape == "strategic_judgement"
|
|
assert [gap.key for gap in state.operation_gaps] == ["fresh_public_market_evidence"]
|
|
assert decision.next_action_type == "external_search"
|
|
|
|
|
|
def test_packet_e_goal_frames_beat_conflicting_legacy_fields() -> None:
|
|
state = PMFlowAITurnRuntimeService(object(), tenant_id=1).build_initial_turn_state(
|
|
request=RAGAskRequest(question="暂存想法里有没有值得推进的"),
|
|
route_plan={},
|
|
turn_hypothesis={
|
|
"normalized_goal": "列出暂存想法",
|
|
"answer_shape": "status_answer",
|
|
"decision_type": "project_status",
|
|
"evidence_needs": ["project_status"],
|
|
"goal_frames": [
|
|
{
|
|
"frame_id": "saved-ideas",
|
|
"operation_kind": "list_collection",
|
|
"target_scope": {
|
|
"scope_type": "current_space",
|
|
"binding_strength": "surface",
|
|
"source": "turn_goal_assessment",
|
|
},
|
|
"target_collection": {
|
|
"object_type": "saved_idea",
|
|
"required": True,
|
|
},
|
|
"target_object_requirement": "none",
|
|
"answer_shape": "fact_list",
|
|
"confidence": 0.86,
|
|
}
|
|
],
|
|
"operation_gaps": [
|
|
{
|
|
"key": "saved_ideas",
|
|
"gap_type": "system_evidence_gap",
|
|
"system_action_hint": "inspect_saved_ideas",
|
|
"reason": "需要读取暂存想法集合。",
|
|
}
|
|
],
|
|
"can_answer_with_assumptions": True,
|
|
},
|
|
)
|
|
|
|
action = PMFlowAITurnRuntimeService(object(), tenant_id=1).select_next_minimal_action(state)
|
|
|
|
assert state.selected_frame is not None
|
|
assert state.selected_frame.operation_kind == "list_collection"
|
|
assert state.answer_state.answer_shape == "fact_list"
|
|
assert action is not None
|
|
assert action.action_type == "inspect_saved_ideas"
|
|
|
|
|
|
def test_packet_e_user_actionable_operation_gap_controls_clarification_gate() -> None:
|
|
state = PMFlowAITurnRuntimeService(object(), tenant_id=1).build_initial_turn_state(
|
|
request=RAGAskRequest(question="帮我处理一下"),
|
|
route_plan={},
|
|
turn_hypothesis={
|
|
"normalized_goal": "确认执行动作边界",
|
|
"goal_frames": [
|
|
{
|
|
"frame_id": "execution-boundary",
|
|
"operation_kind": "guide_execution",
|
|
"target_scope": {
|
|
"scope_type": "current_space",
|
|
"binding_strength": "none",
|
|
"source": "turn_goal_assessment",
|
|
},
|
|
"target_object_requirement": "authorization",
|
|
"answer_shape": "execution_guidance",
|
|
"confidence": 0.7,
|
|
}
|
|
],
|
|
"operation_gaps": [
|
|
{
|
|
"key": "execution_boundary_required",
|
|
"gap_type": "user_clarification_gap",
|
|
"blocking": True,
|
|
"user_actionable": True,
|
|
"customer_question": "请补充要处理的对象和动作边界。",
|
|
"reason": "执行前缺少对象和动作边界。",
|
|
}
|
|
],
|
|
"can_answer_with_assumptions": False,
|
|
},
|
|
)
|
|
|
|
decision = PMFlowAITurnRuntimeService(object(), tenant_id=1).assess_sufficiency(state)
|
|
|
|
assert decision.must_ask_user is True
|
|
assert decision.next_action_type == "ask_clarification"
|
|
assert state.operation_gaps[0].user_actionable is True
|
|
|
|
|
|
def test_packet_e_reviser_accepts_direct_goal_frame_payload() -> None:
|
|
state = PMFlowAITurnState()
|
|
PMFlowAITurnStateReviser().initialize_working_state(
|
|
turn_state=state,
|
|
hypothesis={
|
|
"goal_frames": [
|
|
{
|
|
"frame_id": "assistant-profile",
|
|
"operation_kind": "explain_assistant",
|
|
"target_scope": {
|
|
"scope_type": "current_space",
|
|
"binding_strength": "surface",
|
|
"source": "turn_goal_assessment",
|
|
},
|
|
"target_object_requirement": "none",
|
|
"answer_shape": "general_answer",
|
|
"confidence": 0.9,
|
|
}
|
|
],
|
|
"operation_gaps": [],
|
|
"decision_type": "project_status",
|
|
"evidence_needs": ["project_status"],
|
|
},
|
|
)
|
|
|
|
assert state.selected_frame is not None
|
|
assert state.selected_frame.operation_kind == "explain_assistant"
|
|
assert state.operation_gaps == []
|