feat: 新增风险图谱算法与系统仪表盘及操作反馈体系

后端新增风险图谱算法模块、风险观察与反馈服务、规则 DSL
校验器和可解释性引擎,完善系统仪表盘和财务仪表盘统计,
优化 agent 运行和编排执行链路,清理旧开发文档,前端新增
系统趋势、负载热力图等多种仪表盘图表组件,完善操作反馈
对话框和工作台日期选择器,优化报销创建和审批详情交互,
补充单元测试覆盖。
This commit is contained in:
caoxiaozhu
2026-05-30 15:46:51 +08:00
parent 4c59941ec6
commit 7989f3a159
314 changed files with 30073 additions and 20626 deletions

View File

@@ -11,7 +11,12 @@ from app.core.agent_enums import AgentName, AgentPermissionLevel, AgentRunStatus
from app.core.logging import get_logger
from app.models.agent_run import AgentRun, AgentToolCall, SemanticParseLog
from app.repositories.agent_run import AgentRunRepository
from app.schemas.agent_run import AgentRunRead, AgentToolCallRead, SemanticParseRead
from app.schemas.agent_run import (
AgentRunRead,
AgentRunStatsRead,
AgentToolCallRead,
SemanticParseRead,
)
from app.services.agent_foundation import AgentFoundationService
from app.services.knowledge_ingest_log import enrich_knowledge_ingest_route_json
@@ -47,6 +52,86 @@ class AgentRunService:
return None
return self._serialize_run(run, enrich_knowledge_ingest=True)
def summarize_runs(
self,
*,
agent: str | None = None,
status: str | None = None,
source: str | None = None,
limit: int = 200,
) -> AgentRunStatsRead:
self._ensure_ready()
self._reconcile_stale_knowledge_index_runs()
runs = self.repository.list(agent=agent, status=status, source=source, limit=limit)
agents: dict[str, int] = {}
statuses: dict[str, int] = {}
tool_statuses: dict[str, int] = {}
tool_call_count = 0
failed_tool_call_count = 0
llm_call_count = 0
failed_llm_call_count = 0
model_fallback_count = 0
model_guardrail_count = 0
recent_errors: list[dict[str, Any]] = []
for run in runs:
agents[run.agent] = agents.get(run.agent, 0) + 1
statuses[run.status] = statuses.get(run.status, 0) + 1
ontology_json = run.ontology_json or {}
if ontology_json.get("parse_strategy") == "rule_fallback":
model_fallback_count += 1
model_summary = ontology_json.get("model_invocation_summary")
if isinstance(model_summary, dict) and model_summary.get("model_guardrail_reason"):
model_guardrail_count += 1
if run.status == AgentRunStatus.FAILED.value and run.error_message:
recent_errors.append(
{
"run_id": run.run_id,
"agent": run.agent,
"stage": (run.route_json or {}).get("stage"),
"message": run.error_message,
}
)
for tool_call in run.tool_calls:
tool_call_count += 1
tool_statuses[tool_call.status] = tool_statuses.get(tool_call.status, 0) + 1
failed = tool_call.status == "failed"
if failed:
failed_tool_call_count += 1
if tool_call.tool_type == "llm":
llm_call_count += 1
if failed:
failed_llm_call_count += 1
if tool_call.error_message:
recent_errors.append(
{
"run_id": run.run_id,
"agent": run.agent,
"tool_name": tool_call.tool_name,
"tool_type": tool_call.tool_type,
"message": tool_call.error_message,
}
)
return AgentRunStatsRead(
window_limit=limit,
total_runs=len(runs),
succeeded_runs=statuses.get(AgentRunStatus.SUCCEEDED.value, 0),
blocked_runs=statuses.get(AgentRunStatus.BLOCKED.value, 0),
failed_runs=statuses.get(AgentRunStatus.FAILED.value, 0),
tool_call_count=tool_call_count,
failed_tool_call_count=failed_tool_call_count,
llm_call_count=llm_call_count,
failed_llm_call_count=failed_llm_call_count,
model_fallback_count=model_fallback_count,
model_guardrail_count=model_guardrail_count,
agents=agents,
statuses=statuses,
tool_statuses=tool_statuses,
recent_errors=recent_errors[:10],
)
def create_run(
self,
*,