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

@@ -1,7 +1,7 @@
from app.models.agent_conversation import AgentConversation, AgentConversationMessage
from app.models.agent_asset import AgentAsset, AgentAssetReview, AgentAssetVersion
from app.models.agent_asset import AgentAsset, AgentAssetReview, AgentAssetRuleFeedback, AgentAssetVersion
from app.models.agent_feedback import AgentOperationFeedback
from app.models.agent_run import AgentRun, AgentToolCall, SemanticParseLog
from app.models.agent_run import AgentRun, AgentToolCall, AgentTraceEvent, SemanticParseLog
from app.models.approval import ApprovalRecord
from app.models.audit_log import AuditLog
from app.models.budget import BudgetAllocation, BudgetReservation, BudgetTransaction
@@ -32,10 +32,12 @@ __all__ = [
"AgentConversationMessage",
"AgentAsset",
"AgentAssetReview",
"AgentAssetRuleFeedback",
"AgentAssetVersion",
"AgentOperationFeedback",
"AgentRun",
"AgentToolCall",
"AgentTraceEvent",
"ApprovalRecord",
"AuditLog",
"BudgetAllocation",

View File

@@ -4,7 +4,7 @@ import uuid
from datetime import datetime
from typing import Any
from sqlalchemy import Boolean, DateTime, ForeignKey, String, Text, UniqueConstraint, func
from sqlalchemy import Boolean, DateTime, ForeignKey, Index, String, Text, UniqueConstraint, func
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.types import JSON
@@ -52,6 +52,12 @@ class AgentAsset(Base):
cascade="all, delete-orphan",
order_by="desc(AgentAssetTestRun.created_at)",
)
rule_feedback_items = relationship(
"AgentAssetRuleFeedback",
back_populates="asset",
cascade="all, delete-orphan",
order_by="desc(AgentAssetRuleFeedback.created_at)",
)
class AgentAssetVersion(Base):
@@ -103,3 +109,34 @@ class AgentAssetTestRun(Base):
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
asset = relationship("AgentAsset", back_populates="test_runs")
class AgentAssetRuleFeedback(Base):
__tablename__ = "agent_asset_rule_feedback"
__table_args__ = (
Index("ix_agent_asset_rule_feedback_asset_version", "asset_id", "version"),
Index("ix_agent_asset_rule_feedback_type_status", "feedback_type", "status"),
)
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
feedback_id: Mapped[str] = mapped_column(
String(50),
unique=True,
index=True,
default=lambda: f"arf_{uuid.uuid4().hex[:16]}",
)
asset_id: Mapped[str] = mapped_column(ForeignKey("agent_assets.id"), index=True)
version: Mapped[str] = mapped_column(String(30), index=True)
feedback_type: Mapped[str] = mapped_column(String(30), index=True)
status: Mapped[str] = mapped_column(String(30), default="open", index=True)
subject_type: Mapped[str] = mapped_column(String(50), default="", index=True)
subject_key: Mapped[str] = mapped_column(String(160), default="", index=True)
subject_label: Mapped[str] = mapped_column(String(200), default="")
actual_result_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
expected_result_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
comment: Mapped[str | None] = mapped_column(Text(), nullable=True)
payload_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
created_by: Mapped[str] = mapped_column(String(100), default="", index=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), index=True)
asset = relationship("AgentAsset", back_populates="rule_feedback_items")

View File

@@ -4,7 +4,7 @@ import uuid
from datetime import datetime
from typing import Any
from sqlalchemy import DateTime, Float, ForeignKey, Integer, String, Text, func
from sqlalchemy import DateTime, Float, ForeignKey, Index, Integer, String, Text, func
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.types import JSON
@@ -84,3 +84,28 @@ class SemanticParseLog(Base):
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
run = relationship("AgentRun", back_populates="semantic_parse_logs")
class AgentTraceEvent(Base):
__tablename__ = "agent_trace_events"
__table_args__ = (
Index("ix_agent_trace_events_run_sequence", "run_id", "sequence"),
Index("ix_agent_trace_events_conversation_sequence", "conversation_id", "sequence"),
)
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
run_id: Mapped[str] = mapped_column(ForeignKey("agent_runs.run_id"), index=True)
conversation_id: Mapped[str | None] = mapped_column(String(50), nullable=True, index=True)
sequence: Mapped[int] = mapped_column(Integer, default=0, index=True)
stage: Mapped[str] = mapped_column(String(50), index=True)
event_name: Mapped[str] = mapped_column(String(100), index=True)
title: Mapped[str] = mapped_column(String(160))
summary: Mapped[str | None] = mapped_column(Text(), nullable=True)
status: Mapped[str] = mapped_column(String(20), index=True)
input_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
output_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
error_message: Mapped[str | None] = mapped_column(Text(), nullable=True)
started_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), index=True)
finished_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
duration_ms: Mapped[int] = mapped_column(Integer, default=0)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())