137 lines
5.0 KiB
Python
137 lines
5.0 KiB
Python
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
from app.schemas.steward import StewardSlotDecisionRequest
|
|||
|
|
from app.services.runtime_chat import RuntimeChatCallTrace, RuntimeChatToolCall, RuntimeToolCallResult
|
|||
|
|
from app.services.steward_slot_decision_agent import (
|
|||
|
|
STEWARD_SLOT_DECISION_FUNCTION_NAME,
|
|||
|
|
StewardSlotDecisionAgent,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class FakeSlotRuntime:
|
|||
|
|
def __init__(self, arguments=None):
|
|||
|
|
self.arguments = arguments
|
|||
|
|
self.messages = None
|
|||
|
|
|
|||
|
|
def complete_with_tool_call(self, messages, **kwargs):
|
|||
|
|
self.messages = messages
|
|||
|
|
if self.arguments is None:
|
|||
|
|
return RuntimeToolCallResult(tool_call=None, calls=[])
|
|||
|
|
return RuntimeToolCallResult(
|
|||
|
|
tool_call=RuntimeChatToolCall(
|
|||
|
|
name=STEWARD_SLOT_DECISION_FUNCTION_NAME,
|
|||
|
|
arguments=self.arguments,
|
|||
|
|
),
|
|||
|
|
calls=[
|
|||
|
|
RuntimeChatCallTrace(
|
|||
|
|
slot="main",
|
|||
|
|
provider="OpenAI Compatible",
|
|||
|
|
model="fake",
|
|||
|
|
attempt=1,
|
|||
|
|
status="succeeded",
|
|||
|
|
)
|
|||
|
|
],
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_steward_slot_decision_uses_function_calling_result() -> None:
|
|||
|
|
runtime = FakeSlotRuntime(
|
|||
|
|
{
|
|||
|
|
"next_action": "ask_user",
|
|||
|
|
"required_fields": ["expense_type", "time_range", "location", "reason", "transport_mode"],
|
|||
|
|
"missing_fields": ["transport_mode"],
|
|||
|
|
"question": "请问你这次打算怎么出行?",
|
|||
|
|
"options": [
|
|||
|
|
{"field_key": "transport_mode", "label": "飞机", "value": "飞机"},
|
|||
|
|
{"field_key": "transport_mode", "label": "火车", "value": "火车"},
|
|||
|
|
],
|
|||
|
|
"rationale": "出行方式会影响交通费用测算。",
|
|||
|
|
}
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
result = StewardSlotDecisionAgent(runtime).decide(
|
|||
|
|
StewardSlotDecisionRequest(
|
|||
|
|
task_type="expense_application",
|
|||
|
|
user_message="明天出差北京3天,支撑国网仿生产部署",
|
|||
|
|
ontology_fields={
|
|||
|
|
"expense_type": "travel",
|
|||
|
|
"time_range": "2026-06-05 至 2026-06-07",
|
|||
|
|
"location": "北京",
|
|||
|
|
"reason": "支撑国网仿生产部署",
|
|||
|
|
},
|
|||
|
|
missing_fields=["transport_mode"],
|
|||
|
|
)
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
assert result.decision_source == "llm_function_call"
|
|||
|
|
assert result.next_action == "ask_user"
|
|||
|
|
assert result.missing_fields == ["transport_mode"]
|
|||
|
|
assert [item.value for item in result.options] == ["飞机", "火车"]
|
|||
|
|
assert "出行方式会影响" in result.rationale
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_steward_slot_decision_falls_back_to_intent_missing_fields_only() -> None:
|
|||
|
|
runtime = FakeSlotRuntime(arguments=None)
|
|||
|
|
|
|||
|
|
result = StewardSlotDecisionAgent(runtime).decide(
|
|||
|
|
StewardSlotDecisionRequest(
|
|||
|
|
task_type="expense_application",
|
|||
|
|
user_message="还需要补充:出行方式(例如高铁、飞机、自驾、出租车)",
|
|||
|
|
ontology_fields={
|
|||
|
|
"expense_type": "travel",
|
|||
|
|
"location": "北京",
|
|||
|
|
"reason": "支撑国网仿生产部署",
|
|||
|
|
},
|
|||
|
|
missing_fields=["transport_mode"],
|
|||
|
|
)
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
assert result.decision_source == "rule_fallback"
|
|||
|
|
assert result.next_action == "ask_user"
|
|||
|
|
assert result.missing_fields == ["transport_mode"]
|
|||
|
|
assert [item.value for item in result.options] == ["火车", "飞机", "轮船"]
|
|||
|
|
assert "高铁" not in result.required_fields
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_steward_slot_decision_does_not_ask_user_for_application_profile_or_attachments() -> None:
|
|||
|
|
runtime = FakeSlotRuntime(
|
|||
|
|
{
|
|||
|
|
"next_action": "ask_user",
|
|||
|
|
"required_fields": [
|
|||
|
|
"expense_type",
|
|||
|
|
"time_range",
|
|||
|
|
"location",
|
|||
|
|
"reason",
|
|||
|
|
"amount",
|
|||
|
|
"attachments",
|
|||
|
|
"employee_no",
|
|||
|
|
],
|
|||
|
|
"missing_fields": ["attachments", "employee_no"],
|
|||
|
|
"question": "请补充附件和员工编号。",
|
|||
|
|
"options": [],
|
|||
|
|
"rationale": "附件/凭证和员工编号为合规必需字段。",
|
|||
|
|
}
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
result = StewardSlotDecisionAgent(runtime).decide(
|
|||
|
|
StewardSlotDecisionRequest(
|
|||
|
|
task_type="expense_application",
|
|||
|
|
user_message="明天出差北京3天,支撑国网仿生产部署",
|
|||
|
|
ontology_fields={
|
|||
|
|
"expense_type": "travel",
|
|||
|
|
"time_range": "2026-06-05 至 2026-06-07",
|
|||
|
|
"location": "北京",
|
|||
|
|
"reason": "支撑国网仿生产部署",
|
|||
|
|
},
|
|||
|
|
missing_fields=["attachments", "employee_no"],
|
|||
|
|
)
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
assert result.decision_source == "llm_function_call"
|
|||
|
|
assert result.next_action == "render_preview"
|
|||
|
|
assert result.missing_fields == []
|
|||
|
|
assert "attachments" not in result.required_fields
|
|||
|
|
assert "employee_no" not in result.required_fields
|
|||
|
|
assert result.options == []
|
|||
|
|
assert "合规必需字段" not in result.rationale
|