Align planner, runtime rules, and policy assets so travel guidance matches the updated reimbursement workflow.
248 lines
10 KiB
Python
248 lines
10 KiB
Python
from app.schemas.steward import StewardRuntimeDecisionRequest
|
|
from app.services.steward_runtime_decision_agent import (
|
|
STEWARD_RUNTIME_DECISION_FUNCTION_NAME,
|
|
StewardRuntimeDecisionAgent,
|
|
)
|
|
|
|
|
|
class _FakeToolCall:
|
|
def __init__(self, name, arguments):
|
|
self.name = name
|
|
self.arguments = arguments
|
|
|
|
|
|
class _FakeRuntimeResult:
|
|
def __init__(self, tool_call=None):
|
|
self.tool_call = tool_call
|
|
|
|
def calls_as_dicts(self):
|
|
return [{"tool": self.tool_call.name if self.tool_call else ""}]
|
|
|
|
|
|
class _FakeRuntime:
|
|
def __init__(self, payload):
|
|
self.payload = payload
|
|
self.last_messages = []
|
|
self.last_tools = []
|
|
self.last_tool_choice = None
|
|
|
|
def complete_with_tool_call(self, messages, tools, tool_choice, **kwargs):
|
|
self.last_messages = messages
|
|
self.last_tools = tools
|
|
self.last_tool_choice = tool_choice
|
|
if self.payload is None:
|
|
return _FakeRuntimeResult()
|
|
return _FakeRuntimeResult(_FakeToolCall(STEWARD_RUNTIME_DECISION_FUNCTION_NAME, self.payload))
|
|
|
|
|
|
def test_steward_runtime_decision_uses_function_calling_context():
|
|
runtime = _FakeRuntime(
|
|
{
|
|
"next_action": "submit_current_application",
|
|
"target_task_id": "task-application-beijing",
|
|
"target_message_id": "msg-application-preview",
|
|
"field_key": "",
|
|
"field_value": "",
|
|
"confirmation_required": False,
|
|
"question": "",
|
|
"response_text": "",
|
|
"rationale": "用户确认当前申请核对表无误。",
|
|
}
|
|
)
|
|
|
|
result = StewardRuntimeDecisionAgent(runtime).decide(
|
|
StewardRuntimeDecisionRequest(
|
|
user_message="确认",
|
|
runtime_state={
|
|
"waiting_for": "application_submit_confirmation",
|
|
"pending_application": {
|
|
"message_id": "msg-application-preview",
|
|
"task_id": "task-application-beijing",
|
|
"ready_to_submit": True,
|
|
},
|
|
"remaining_tasks": [
|
|
{"task_id": "task-reimbursement-meal", "task_type": "reimbursement"}
|
|
],
|
|
},
|
|
)
|
|
)
|
|
|
|
assert result.decision_source == "llm_function_call"
|
|
assert result.next_action == "submit_current_application"
|
|
assert result.target_message_id == "msg-application-preview"
|
|
assert result.target_task_id == "task-application-beijing"
|
|
assert runtime.last_tool_choice["function"]["name"] == STEWARD_RUNTIME_DECISION_FUNCTION_NAME
|
|
assert "runtime_state" in runtime.last_messages[-1]["content"]
|
|
|
|
|
|
def test_steward_runtime_decision_fallback_keeps_current_context():
|
|
runtime = _FakeRuntime(None)
|
|
|
|
result = StewardRuntimeDecisionAgent(runtime).decide(
|
|
StewardRuntimeDecisionRequest(
|
|
user_message="确认",
|
|
runtime_state={
|
|
"pending_steward_action": {
|
|
"message_id": "msg-next-task",
|
|
"target_task_id": "task-reimbursement-meal",
|
|
}
|
|
},
|
|
)
|
|
)
|
|
|
|
assert result.decision_source == "rule_fallback"
|
|
assert result.next_action == "continue_next_task"
|
|
assert result.target_message_id == "msg-next-task"
|
|
assert result.target_task_id == "task-reimbursement-meal"
|
|
|
|
|
|
def test_steward_runtime_decision_fallback_reads_persisted_steward_state():
|
|
runtime = _FakeRuntime(None)
|
|
|
|
result = StewardRuntimeDecisionAgent(runtime).decide(
|
|
StewardRuntimeDecisionRequest(
|
|
user_message="我坐高铁",
|
|
runtime_state={},
|
|
context_json={
|
|
"conversation_state": {
|
|
"steward_state": {
|
|
"active_flow": "travel_application",
|
|
"flows": {
|
|
"travel_application": {
|
|
"flow_id": "travel_application",
|
|
"intent": "travel_application_create",
|
|
"fields": {
|
|
"expense_type": "travel",
|
|
"time_range": "2026-07-02",
|
|
"location": "北京",
|
|
"reason": "客户现场支撑",
|
|
},
|
|
"missing_fields": ["transport_mode"],
|
|
}
|
|
},
|
|
}
|
|
}
|
|
},
|
|
)
|
|
)
|
|
|
|
assert result.decision_source == "rule_fallback"
|
|
assert result.next_action == "fill_current_slot"
|
|
assert result.target_task_id == "travel_application"
|
|
assert result.field_key == "transport_mode"
|
|
assert result.field_value == "我坐高铁"
|
|
assert result.steward_state["flows"]["travel_application"]["fields"]["transport_mode"] == "我坐高铁"
|
|
assert result.steward_state["flows"]["travel_application"]["missing_fields"] == []
|
|
|
|
|
|
def test_steward_runtime_decision_fallback_confirms_selected_flow():
|
|
runtime = _FakeRuntime(None)
|
|
|
|
result = StewardRuntimeDecisionAgent(runtime).decide(
|
|
StewardRuntimeDecisionRequest(
|
|
user_message="补办出差申请",
|
|
runtime_state={},
|
|
context_json={
|
|
"conversation_state": {
|
|
"steward_state": {
|
|
"version": "steward.flow_state.v2",
|
|
"active_flow": "",
|
|
"pending_flow_confirmation": {
|
|
"status": "pending",
|
|
"source_message": "2月20-23日去上海出差辅助国网仿生产环境部署",
|
|
"reason": "缺少申请或报销动作词。",
|
|
"candidate_flows": [
|
|
{
|
|
"flow_id": "travel_application",
|
|
"label": "补办出差申请",
|
|
"confidence": 0.52,
|
|
},
|
|
{
|
|
"flow_id": "travel_reimbursement",
|
|
"label": "发起费用报销",
|
|
"confidence": 0.48,
|
|
},
|
|
],
|
|
},
|
|
"flows": {
|
|
"travel_application": {
|
|
"flow_id": "travel_application",
|
|
"intent": "travel_application_create",
|
|
"status": "pending_flow_confirmation",
|
|
"fields": {
|
|
"time_range": "2026-02-20",
|
|
"location": "上海",
|
|
"expense_type": "travel",
|
|
"reason": "辅助国网仿生产环境部署",
|
|
},
|
|
"missing_fields": ["transport_mode"],
|
|
},
|
|
"travel_reimbursement": {
|
|
"flow_id": "travel_reimbursement",
|
|
"intent": "travel_reimbursement_draft",
|
|
"status": "pending_flow_confirmation",
|
|
"fields": {
|
|
"time_range": "2026-02-20",
|
|
"location": "上海",
|
|
"expense_type": "travel",
|
|
"reason": "辅助国网仿生产环境部署",
|
|
},
|
|
"missing_fields": [],
|
|
},
|
|
},
|
|
}
|
|
}
|
|
},
|
|
)
|
|
)
|
|
|
|
assert result.decision_source == "rule_fallback"
|
|
assert result.next_action == "continue_selected_flow"
|
|
assert result.target_task_id == "travel_application"
|
|
assert result.steward_state["active_flow"] == "travel_application"
|
|
assert result.steward_state["pending_flow_confirmation"]["status"] == "confirmed"
|
|
assert result.steward_state["flows"]["travel_application"]["status"] == "collecting"
|
|
|
|
|
|
def test_steward_runtime_decision_fallback_confirms_reimbursement_flow():
|
|
runtime = _FakeRuntime(None)
|
|
|
|
result = StewardRuntimeDecisionAgent(runtime).decide(
|
|
StewardRuntimeDecisionRequest(
|
|
user_message="发起费用报销",
|
|
runtime_state={
|
|
"steward_state": {
|
|
"version": "steward.flow_state.v2",
|
|
"active_flow": "",
|
|
"pending_flow_confirmation": {
|
|
"status": "pending",
|
|
"candidate_flows": [
|
|
{"flow_id": "travel_application", "label": "补办出差申请"},
|
|
{"flow_id": "travel_reimbursement", "label": "发起费用报销"},
|
|
],
|
|
},
|
|
"flows": {
|
|
"travel_reimbursement": {
|
|
"flow_id": "travel_reimbursement",
|
|
"intent": "travel_reimbursement_draft",
|
|
"status": "pending_flow_confirmation",
|
|
"fields": {
|
|
"time_range": "2026-02-20",
|
|
"location": "上海",
|
|
"expense_type": "travel",
|
|
"reason": "辅助国网仿生产环境部署",
|
|
},
|
|
"missing_fields": [],
|
|
}
|
|
},
|
|
}
|
|
},
|
|
)
|
|
)
|
|
|
|
assert result.decision_source == "rule_fallback"
|
|
assert result.next_action == "continue_selected_flow"
|
|
assert result.target_task_id == "travel_reimbursement"
|
|
assert result.steward_state["active_flow"] == "travel_reimbursement"
|
|
assert result.steward_state["pending_flow_confirmation"]["status"] == "confirmed"
|