- schemas/steward.py:StewardPlanResponse 新增 suggested_prompts 字段 - steward_planner.py:新增 STEWARD_BUSINESS_SIGNAL_KEYWORDS 与 _is_business_irrelevant_input 守卫,在 build_plan 入口前置; 新增 _build_off_topic_plan 构造 plan_status=off_topic 的引导计划 - steward_intent_agent.py:system prompt 追加业务无关约束 - test_steward_planner.py:覆盖 123/你好/纯标点走 off_topic, 并验证正常业务输入不受守卫影响
196 lines
12 KiB
Python
196 lines
12 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any, Literal
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
StewardTaskType = Literal["expense_application", "reimbursement"]
|
|
StewardAssignedAgent = Literal["application_assistant", "reimbursement_assistant"]
|
|
StewardPlanningSource = Literal["llm_function_call", "rule_fallback"]
|
|
StewardPlanNextAction = Literal["confirm_flow", "confirm_task", "delegate_task", "none"]
|
|
StewardSlotDecisionSource = Literal["llm_function_call", "rule_fallback"]
|
|
StewardSlotNextAction = Literal["ask_user", "render_preview"]
|
|
StewardRuntimeDecisionSource = Literal["llm_function_call", "rule_fallback"]
|
|
StewardRuntimeNextAction = Literal[
|
|
"plan_new_tasks",
|
|
"continue_selected_flow",
|
|
"submit_current_application",
|
|
"continue_next_task",
|
|
"fill_current_slot",
|
|
"ask_user",
|
|
"cancel_current_action",
|
|
"no_op",
|
|
]
|
|
StewardTaskStatus = Literal[
|
|
"planned",
|
|
"needs_confirmation",
|
|
"ready_to_delegate",
|
|
"delegated",
|
|
"completed",
|
|
"blocked",
|
|
]
|
|
StewardConfirmationStatus = Literal["pending", "confirmed", "rejected"]
|
|
StewardFlowId = Literal["travel_application", "travel_reimbursement"]
|
|
StewardPendingFlowStatus = Literal["none", "pending", "confirmed", "rejected"]
|
|
|
|
|
|
class StewardAttachmentInput(BaseModel):
|
|
name: str = Field(description="附件原始文件名。")
|
|
media_type: str = Field(default="", description="附件 MIME 类型。")
|
|
ocr_summary: str = Field(default="", description="可选 OCR 摘要。")
|
|
ocr_fields: dict[str, Any] = Field(default_factory=dict, description="可选 OCR 结构化字段。")
|
|
|
|
|
|
class StewardPlanRequest(BaseModel):
|
|
message: str = Field(description="用户在首页输入的自然语言任务。")
|
|
user_id: str | None = Field(default=None, description="当前用户 ID。")
|
|
client_now_iso: str | None = Field(default=None, description="客户端当前时间 ISO 字符串。")
|
|
attachments: list[StewardAttachmentInput] = Field(default_factory=list, description="随本次输入上传的附件。")
|
|
context_json: dict[str, Any] = Field(default_factory=dict, description="调用方上下文。")
|
|
|
|
|
|
class StewardThinkingEvent(BaseModel):
|
|
event_id: str = Field(description="过程摘要事件 ID。")
|
|
stage: str = Field(description="阶段编码。")
|
|
title: str = Field(description="面向用户展示的阶段标题。")
|
|
content: str = Field(description="面向用户展示的过程摘要。")
|
|
status: str = Field(default="completed", description="事件状态。")
|
|
|
|
|
|
class StewardTask(BaseModel):
|
|
task_id: str = Field(description="小财管家任务 ID。")
|
|
task_type: StewardTaskType = Field(description="任务类型。")
|
|
assigned_agent: StewardAssignedAgent = Field(description="建议分派的下游助手。")
|
|
title: str = Field(description="任务标题。")
|
|
summary: str = Field(description="任务摘要。")
|
|
status: StewardTaskStatus = Field(default="needs_confirmation", description="任务状态。")
|
|
confidence: float = Field(default=0.0, ge=0.0, le=1.0, description="识别置信度。")
|
|
ontology_fields: dict[str, str] = Field(default_factory=dict, description="归一化后的业务本体字段。")
|
|
missing_fields: list[str] = Field(default_factory=list, description="仍缺失的本体字段。")
|
|
confirmation_required: bool = Field(default=True, description="执行前是否需要用户确认。")
|
|
|
|
|
|
class StewardAttachmentGroup(BaseModel):
|
|
group_id: str = Field(description="附件归集组 ID。")
|
|
target_task_id: str | None = Field(default=None, description="建议归属的任务 ID。")
|
|
scene: str = Field(description="归集场景编码。")
|
|
scene_label: str = Field(description="归集场景展示名。")
|
|
attachment_names: list[str] = Field(default_factory=list, description="建议纳入的附件名称。")
|
|
excluded_attachment_names: list[str] = Field(default_factory=list, description="建议排除或单独处理的附件名称。")
|
|
confidence: float = Field(default=0.0, ge=0.0, le=1.0, description="归集置信度。")
|
|
rationale: str = Field(default="", description="归集依据。")
|
|
confirmation_required: bool = Field(default=True, description="归集前是否需要用户确认。")
|
|
|
|
|
|
class StewardConfirmationAction(BaseModel):
|
|
confirmation_id: str = Field(description="确认动作 ID。")
|
|
action_type: str = Field(description="确认动作类型。")
|
|
label: str = Field(description="确认按钮文案。")
|
|
description: str = Field(default="", description="确认动作说明。")
|
|
target_task_id: str | None = Field(default=None, description="关联任务 ID。")
|
|
attachment_group_id: str | None = Field(default=None, description="关联附件归集组 ID。")
|
|
status: StewardConfirmationStatus = Field(default="pending", description="确认状态。")
|
|
payload: dict[str, Any] = Field(default_factory=dict, description="确认后继续执行所需载荷。")
|
|
|
|
|
|
class StewardCandidateFlow(BaseModel):
|
|
flow_id: StewardFlowId = Field(description="候选业务流程。")
|
|
label: str = Field(description="用户可见候选流程名称。")
|
|
confidence: float = Field(default=0.0, ge=0.0, le=1.0, description="候选流程置信度。")
|
|
reason: str = Field(default="", description="候选流程依据。")
|
|
ontology_fields: dict[str, str] = Field(default_factory=dict, description="候选流程可继承的 canonical ontology 字段。")
|
|
missing_fields: list[str] = Field(default_factory=list, description="候选流程仍缺失的 canonical ontology 字段。")
|
|
|
|
|
|
class StewardPendingFlowConfirmation(BaseModel):
|
|
status: StewardPendingFlowStatus = Field(default="none", description="候选流程确认状态。")
|
|
source_message: str = Field(default="", description="触发候选流程确认的用户原始输入。")
|
|
reason: str = Field(default="", description="需要确认流程方向的原因。")
|
|
candidate_flows: list[StewardCandidateFlow] = Field(default_factory=list, description="候选业务流程。")
|
|
|
|
|
|
class StewardPlanResponse(BaseModel):
|
|
plan_id: str = Field(description="小财管家计划 ID。")
|
|
plan_status: str = Field(default="needs_confirmation", description="计划状态。")
|
|
planning_source: StewardPlanningSource = Field(default="rule_fallback", description="计划生成来源。")
|
|
next_action: StewardPlanNextAction = Field(default="confirm_task", description="计划完成后的下一步动作。")
|
|
conversation_id: str = Field(default="", description="持久化会话 ID。")
|
|
steward_state: dict[str, Any] = Field(default_factory=dict, description="小财管家跨轮业务状态。")
|
|
summary: str = Field(description="计划摘要。")
|
|
thinking_events: list[StewardThinkingEvent] = Field(default_factory=list, description="过程摘要事件。")
|
|
tasks: list[StewardTask] = Field(default_factory=list, description="拆解后的任务。")
|
|
attachment_groups: list[StewardAttachmentGroup] = Field(default_factory=list, description="附件归集建议。")
|
|
confirmation_groups: list[StewardConfirmationAction] = Field(default_factory=list, description="等待用户确认的动作。")
|
|
pending_flow_confirmation: StewardPendingFlowConfirmation = Field(
|
|
default_factory=StewardPendingFlowConfirmation,
|
|
description="申请/报销流程不明确时等待用户确认的候选流程。",
|
|
)
|
|
candidate_flows: list[StewardCandidateFlow] = Field(default_factory=list, description="等待用户确认的候选流程快捷列表。")
|
|
model_call_traces: list[dict[str, Any]] = Field(default_factory=list, description="模型工具调用轨迹。")
|
|
suggested_prompts: list[str] = Field(
|
|
default_factory=list,
|
|
description="当 plan_status 为 off_topic 等场景时,给用户的推荐话术示例。",
|
|
)
|
|
|
|
|
|
class StewardSlotOption(BaseModel):
|
|
label: str = Field(description="用户可见选项文案。")
|
|
value: str = Field(description="写回本体字段的选项值。")
|
|
field_key: str = Field(description="对应 canonical ontology field。")
|
|
description: str = Field(default="", description="选项说明。")
|
|
|
|
|
|
class StewardSlotDecisionRequest(BaseModel):
|
|
task_type: StewardTaskType = Field(description="当前小财管家正在推进的任务类型。")
|
|
user_message: str = Field(description="用户原始话术或小财管家携带的任务上下文。")
|
|
ontology_fields: dict[str, str] = Field(default_factory=dict, description="当前已抽取的 canonical ontology 字段。")
|
|
missing_fields: list[str] = Field(default_factory=list, description="上游意图识别给出的 canonical 缺失字段。")
|
|
task_context: dict[str, Any] = Field(default_factory=dict, description="当前任务、附件、申请预览等上下文。")
|
|
|
|
|
|
class StewardSlotDecisionResponse(BaseModel):
|
|
decision_source: StewardSlotDecisionSource = Field(default="rule_fallback", description="字段决策来源。")
|
|
next_action: StewardSlotNextAction = Field(description="下一步应追问用户还是展示核对结果。")
|
|
required_fields: list[str] = Field(default_factory=list, description="模型认为当前业务需要的 canonical 字段。")
|
|
missing_fields: list[str] = Field(default_factory=list, description="当前仍缺失的 canonical 字段。")
|
|
question: str = Field(default="", description="需要追问时展示给用户的问题。")
|
|
options: list[StewardSlotOption] = Field(default_factory=list, description="可直接选择的补充选项。")
|
|
rationale: str = Field(default="", description="面向用户的简短判断依据,不暴露推理链。")
|
|
model_call_traces: list[dict[str, Any]] = Field(default_factory=list, description="模型工具调用轨迹。")
|
|
|
|
|
|
class StewardRuntimeDecisionRequest(BaseModel):
|
|
user_message: str = Field(description="用户当前输入。")
|
|
session_type: str = Field(default="steward", description="当前前端会话类型。")
|
|
runtime_state: dict[str, Any] = Field(default_factory=dict, description="小财管家运行时上下文。")
|
|
context_json: dict[str, Any] = Field(default_factory=dict, description="调用方补充上下文。")
|
|
|
|
|
|
class StewardRuntimeDecisionResponse(BaseModel):
|
|
decision_source: StewardRuntimeDecisionSource = Field(default="rule_fallback", description="运行时决策来源。")
|
|
next_action: StewardRuntimeNextAction = Field(description="小财管家下一步动作。")
|
|
target_task_id: str = Field(default="", description="关联的小财管家任务 ID。")
|
|
target_message_id: str = Field(default="", description="关联的前端消息 ID。")
|
|
field_key: str = Field(default="", description="补字段时对应 canonical ontology field。")
|
|
field_value: str = Field(default="", description="补字段时用户提供的字段值。")
|
|
confirmation_required: bool = Field(default=False, description="执行该动作前是否仍需要用户二次确认。")
|
|
question: str = Field(default="", description="需要追问用户时展示的问题。")
|
|
response_text: str = Field(default="", description="无需调用工具时给用户的简短回复。")
|
|
rationale: str = Field(default="", description="面向用户的简短判断依据,不暴露推理链。")
|
|
steward_state: dict[str, Any] = Field(default_factory=dict, description="小财管家更新后的跨轮业务状态。")
|
|
model_call_traces: list[dict[str, Any]] = Field(default_factory=list, description="模型工具调用轨迹。")
|
|
|
|
|
|
class StewardFlowStatePatch(BaseModel):
|
|
active_flow: StewardFlowId = Field(description="本轮对话正在推进的业务流程。")
|
|
flow_id: StewardFlowId = Field(description="需要合并字段的目标业务流程。")
|
|
intent: str = Field(default="", description="本轮识别出的业务意图。")
|
|
status: str = Field(default="collecting", description="流程状态。")
|
|
fields: dict[str, Any] = Field(default_factory=dict, description="待写入流程的本体字段 patch。")
|
|
missing_fields: list[str] = Field(default_factory=list, description="仍缺失的 canonical ontology 字段。")
|
|
application_claim_id: str = Field(default="", description="出差申请流程已生成的申请单 ID。")
|
|
linked_application_claim_id: str = Field(default="", description="报销流程关联的申请单 ID。")
|
|
attachments: list[dict[str, Any]] = Field(default_factory=list, description="流程关联附件摘要。")
|
|
evidence: list[dict[str, Any]] = Field(default_factory=list, description="字段来源证据。")
|