124 lines
4.9 KiB
Python
124 lines
4.9 KiB
Python
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from datetime import UTC, datetime
|
||
|
|
from time import perf_counter
|
||
|
|
from typing import Any
|
||
|
|
|
||
|
|
from sqlalchemy.orm import Session
|
||
|
|
|
||
|
|
from app.core.agent_enums import (
|
||
|
|
AgentName,
|
||
|
|
AgentPermissionLevel,
|
||
|
|
AgentRunSource,
|
||
|
|
AgentRunStatus,
|
||
|
|
AgentToolType,
|
||
|
|
)
|
||
|
|
from app.services.agent_runs import AgentRunService
|
||
|
|
from app.services.hermes_employee_profile_scanner import HermesEmployeeProfileScannerService
|
||
|
|
|
||
|
|
EMPLOYEE_PROFILE_SCAN_TASK_TYPE = "employee_behavior_profile_scan"
|
||
|
|
EMPLOYEE_PROFILE_SCAN_TOOL_NAME = "digital_employee.employee_behavior_profile.scan"
|
||
|
|
|
||
|
|
|
||
|
|
class EmployeeProfileScanTaskService:
|
||
|
|
def __init__(self, db: Session) -> None:
|
||
|
|
self.db = db
|
||
|
|
|
||
|
|
def refresh_profiles(self, *, source: str = AgentRunSource.SCHEDULE.value) -> dict[str, Any]:
|
||
|
|
run_service = AgentRunService(self.db)
|
||
|
|
run = run_service.create_run(
|
||
|
|
agent=AgentName.HERMES.value,
|
||
|
|
source=source,
|
||
|
|
user_id="digital_employee",
|
||
|
|
ontology_json={
|
||
|
|
"scenario": "employee_behavior_profile",
|
||
|
|
"intent": "scan",
|
||
|
|
},
|
||
|
|
route_json={
|
||
|
|
"task_type": EMPLOYEE_PROFILE_SCAN_TASK_TYPE,
|
||
|
|
"job_type": EMPLOYEE_PROFILE_SCAN_TASK_TYPE,
|
||
|
|
"selected_agent": AgentName.HERMES.value,
|
||
|
|
"phase": "running",
|
||
|
|
"heartbeat_at": datetime.now(UTC).isoformat(),
|
||
|
|
},
|
||
|
|
permission_level=AgentPermissionLevel.READ.value,
|
||
|
|
status=AgentRunStatus.RUNNING.value,
|
||
|
|
)
|
||
|
|
timer = perf_counter()
|
||
|
|
try:
|
||
|
|
# 画像快照表的 source_task_log_id 外键指向 Hermes 任务日志。
|
||
|
|
# 这里用 agent_runs 记录数字员工轨迹,因此不写入该外键,避免错误关联。
|
||
|
|
summary = HermesEmployeeProfileScannerService(self.db).scan_employee_profiles(
|
||
|
|
log_id=None
|
||
|
|
)
|
||
|
|
duration_ms = int((perf_counter() - timer) * 1000)
|
||
|
|
report = self._build_report(summary)
|
||
|
|
response = {
|
||
|
|
"task_type": EMPLOYEE_PROFILE_SCAN_TASK_TYPE,
|
||
|
|
"summary": summary,
|
||
|
|
"report": report,
|
||
|
|
}
|
||
|
|
run_service.record_tool_call(
|
||
|
|
run_id=run.run_id,
|
||
|
|
tool_type=AgentToolType.DATABASE.value,
|
||
|
|
tool_name=EMPLOYEE_PROFILE_SCAN_TOOL_NAME,
|
||
|
|
request_json={"task_type": EMPLOYEE_PROFILE_SCAN_TASK_TYPE},
|
||
|
|
response_json=response,
|
||
|
|
status=AgentRunStatus.SUCCEEDED.value,
|
||
|
|
duration_ms=duration_ms,
|
||
|
|
)
|
||
|
|
run_service.merge_route_json(
|
||
|
|
run.run_id,
|
||
|
|
{
|
||
|
|
"phase": "succeeded",
|
||
|
|
"summary": summary,
|
||
|
|
"report": report,
|
||
|
|
"heartbeat_at": datetime.now(UTC).isoformat(),
|
||
|
|
},
|
||
|
|
status=AgentRunStatus.SUCCEEDED.value,
|
||
|
|
result_summary=(
|
||
|
|
"员工行为画像已生成:"
|
||
|
|
f"覆盖 {summary.get('target_employee_count', 0)} 人,"
|
||
|
|
f"快照 {summary.get('snapshot_count', 0)} 条,"
|
||
|
|
f"重点关注 {summary.get('high_attention_employee_count', 0)} 人。"
|
||
|
|
),
|
||
|
|
finished_at=datetime.now(UTC),
|
||
|
|
)
|
||
|
|
return response
|
||
|
|
except Exception as exc:
|
||
|
|
run_service.record_tool_call(
|
||
|
|
run_id=run.run_id,
|
||
|
|
tool_type=AgentToolType.DATABASE.value,
|
||
|
|
tool_name=EMPLOYEE_PROFILE_SCAN_TOOL_NAME,
|
||
|
|
request_json={"task_type": EMPLOYEE_PROFILE_SCAN_TASK_TYPE},
|
||
|
|
response_json={},
|
||
|
|
status=AgentRunStatus.FAILED.value,
|
||
|
|
duration_ms=int((perf_counter() - timer) * 1000),
|
||
|
|
error_message=str(exc),
|
||
|
|
)
|
||
|
|
run_service.merge_route_json(
|
||
|
|
run.run_id,
|
||
|
|
{
|
||
|
|
"phase": "failed",
|
||
|
|
"heartbeat_at": datetime.now(UTC).isoformat(),
|
||
|
|
},
|
||
|
|
status=AgentRunStatus.FAILED.value,
|
||
|
|
error_message=str(exc),
|
||
|
|
finished_at=datetime.now(UTC),
|
||
|
|
)
|
||
|
|
raise
|
||
|
|
|
||
|
|
@staticmethod
|
||
|
|
def _build_report(summary: dict[str, Any]) -> dict[str, Any]:
|
||
|
|
return {
|
||
|
|
"title": "员工财务行为画像扫描报告",
|
||
|
|
"targetEmployeeCount": int(summary.get("target_employee_count") or 0),
|
||
|
|
"profileSnapshotCount": int(summary.get("snapshot_count") or 0),
|
||
|
|
"highAttentionEmployeeCount": int(
|
||
|
|
summary.get("high_attention_employee_count") or 0
|
||
|
|
),
|
||
|
|
"windowDays": list(summary.get("window_days") or []),
|
||
|
|
"algorithmVersion": str(summary.get("algorithm_version") or ""),
|
||
|
|
"baselineSummary": summary.get("baseline_summary") or {},
|
||
|
|
}
|