feat: 扩展风险规则体系、审批动态路由与预算中心列表化改造

- 新增 25+ 条风险规则(预算/报销/申请/通用类),完善风险规则模拟与反馈发布机制
- 引入费用审批动态路由、平台风险分级、预审与风险阶段管理
- 预算中心列表化改造,优化票据夹仪表盘与数字员工工作看板
- 新增 Hermes 风险线索收集器、Agent 链路追踪中心
- 扩展数字员工能力库(18 个领域 Skill)与交通费用自动预估
- 完善报销申请快速预览、权限控制与前端测试覆盖
This commit is contained in:
caoxiaozhu
2026-06-01 17:07:14 +08:00
parent 7989f3a159
commit 92444e7eae
285 changed files with 25075 additions and 2986 deletions

View File

@@ -146,6 +146,38 @@ class AgentAssetRiskRuleRegenerateRequest(BaseModel):
requires_attachment: bool | None = None
class AgentAssetRiskRuleTemplateFieldRead(BaseModel):
key: str
label: str
display: str
source: str
type: str
class AgentAssetRiskRuleTemplateRead(BaseModel):
template_id: str
group: str
group_label: str
title: str
description: str = ""
business_domain: str = "expense"
business_stage: str = "reimbursement"
business_stage_label: str = "费用报销"
expense_category: str | None = None
expense_category_label: str = ""
requires_attachment: bool = False
natural_language: str
fields: list[AgentAssetRiskRuleTemplateFieldRead] = Field(default_factory=list)
dsl_example: dict[str, Any] = Field(default_factory=dict)
class AgentAssetRiskRuleTemplateGroupRead(BaseModel):
group: str
group_label: str
order: int
templates: list[AgentAssetRiskRuleTemplateRead] = Field(default_factory=list)
class AgentAssetRiskRuleSampleCase(BaseModel):
case_id: str | None = Field(default=None, max_length=60)
name: str = Field(default="测试样例", min_length=1, max_length=80)
@@ -211,6 +243,9 @@ class AgentAssetRiskRuleSimulationRead(BaseModel):
trace: dict[str, Any] = Field(default_factory=dict)
attachments: list[dict[str, Any]] = Field(default_factory=list)
recognized_fields: list[dict[str, Any]] = Field(default_factory=list)
ocr_raw_fields: list[dict[str, Any]] = Field(default_factory=list)
hermes_normalized_fields: list[dict[str, Any]] = Field(default_factory=list)
executor_input_fields: list[dict[str, Any]] = Field(default_factory=list)
missing_fields: list[dict[str, Any]] = Field(default_factory=list)
recognition_summary: list[dict[str, Any]] = Field(default_factory=list)
execution_mode: str = "risk_rule_simulation"
@@ -229,6 +264,38 @@ class AgentAssetRiskRuleLevelUpdate(BaseModel):
risk_level: str = Field(pattern="^(low|medium|high|critical)$")
class AgentAssetRiskRuleFeedbackCreate(BaseModel):
version: str | None = Field(default=None, max_length=30)
feedback_type: str = Field(pattern="^(false_positive|false_negative|unclear|improvement)$")
subject_type: str | None = Field(default=None, max_length=50)
subject_key: str | None = Field(default=None, max_length=160)
subject_label: str | None = Field(default=None, max_length=200)
actual_result: dict[str, Any] = Field(default_factory=dict)
expected_result: dict[str, Any] = Field(default_factory=dict)
comment: str = Field(min_length=1, max_length=1000)
payload: dict[str, Any] = Field(default_factory=dict)
class AgentAssetRiskRuleFeedbackRead(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: str
feedback_id: str
asset_id: str
version: str
feedback_type: str
status: str
subject_type: str = ""
subject_key: str = ""
subject_label: str = ""
actual_result_json: dict[str, Any] = Field(default_factory=dict)
expected_result_json: dict[str, Any] = Field(default_factory=dict)
comment: str | None = None
payload_json: dict[str, Any] = Field(default_factory=dict)
created_by: str
created_at: datetime
class AgentAssetRiskRuleTestRunRead(BaseModel):
model_config = ConfigDict(from_attributes=True)

View File

@@ -0,0 +1,63 @@
from __future__ import annotations
from datetime import datetime
from typing import Any
from pydantic import BaseModel, ConfigDict, Field
from app.schemas.agent_run import AgentRunRead, AgentToolCallRead, SemanticParseRead
from app.schemas.orchestrator import ConversationMessageRead
class AgentTraceEventRead(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: str
run_id: str
conversation_id: str | None = None
sequence: int
stage: str
event_name: str
title: str
summary: str | None = None
status: str
input_json: dict[str, Any] = Field(default_factory=dict)
output_json: dict[str, Any] = Field(default_factory=dict)
error_message: str | None = None
started_at: datetime
finished_at: datetime | None = None
duration_ms: int = 0
created_at: datetime
class AgentTraceListItem(BaseModel):
run_id: str
conversation_id: str | None = None
agent: str
source: str
status: str
scenario: str | None = None
intent: str | None = None
title: str
summary: str | None = None
event_count: int = 0
tool_call_count: int = 0
failed_tool_call_count: int = 0
started_at: datetime
finished_at: datetime | None = None
duration_ms: int = 0
class AgentTraceDetailRead(BaseModel):
run: AgentRunRead
conversation_id: str | None = None
events: list[AgentTraceEventRead] = Field(default_factory=list)
semantic_parse: SemanticParseRead | None = None
tool_calls: list[AgentToolCallRead] = Field(default_factory=list)
conversation_messages: list[ConversationMessageRead] = Field(default_factory=list)
fallback_generated: bool = False
class AgentConversationTraceRead(BaseModel):
conversation_id: str
runs: list[AgentTraceDetailRead] = Field(default_factory=list)

View File

@@ -0,0 +1,16 @@
from __future__ import annotations
from typing import Any
from pydantic import BaseModel, Field
class DigitalEmployeeDashboardRead(BaseModel):
window_days: int
generated_at: str
has_real_data: bool
totals: dict[str, Any] = Field(default_factory=dict)
daily_work: list[dict[str, Any]] = Field(default_factory=list)
task_distribution: list[dict[str, Any]] = Field(default_factory=list)
category_distribution: list[dict[str, Any]] = Field(default_factory=list)
recent_runs: list[dict[str, Any]] = Field(default_factory=list)

View File

@@ -130,6 +130,9 @@ class ExpenseClaimRead(BaseModel):
employee_position: str | None = None
employee_grade: str | None = None
manager_name: str | None = None
budget_approver_name: str | None = None
budget_approver_grade: str | None = None
budget_approver_role_code: str | None = None
role_labels: list[str] = Field(default_factory=list)
project_code: str | None
expense_type: str
@@ -202,6 +205,7 @@ class ExpenseClaimAttachmentActionResponse(BaseModel):
item_location: str | None = None
item_amount: Decimal | None = None
claim_amount: Decimal | None = None
claim_risk_flags: list[Any] = Field(default_factory=list)
attachment: ExpenseClaimAttachmentRead | None = None

View File

@@ -117,9 +117,11 @@ class RiskObservationDashboardRead(BaseModel):
window_days: int
total_observations: int
pending_count: int
risk_clue_count: int = 0
high_or_above_count: int
confirmed_count: int
false_positive_count: int
feedback_sample_count: int = 0
total_amount: float = 0.0
average_score: float
level_distribution: dict[str, int] = Field(default_factory=dict)