feat: 重构处理历史与通知留痕追踪

This commit is contained in:
2026-06-04 00:49:33 +08:00
parent d0841e533f
commit 77d9420d43
13 changed files with 431 additions and 96 deletions

View File

@@ -1,6 +1,6 @@
from agent_core.results import AgentResult
from .models import AgentAuditLog
from .models import AgentAuditLog, NotificationRecord
def create_audit_log(
@@ -61,3 +61,36 @@ def _mask_token_after_marker(value: str, marker: str) -> str:
secret, separator, rest = suffix.partition(" ")
masked_secret = "sk-***" if secret.startswith("sk-") else "***"
return f"{prefix}{marker}{masked_secret}{separator}{rest}"
def create_notification_record(
*,
batch_id: str,
conversation_id: str,
product_name: str,
trigger_source: str,
notify_reason: str,
owner_role: str,
feishu_user_id: str,
message_status: str,
web_detail_url: str,
receipt: dict,
) -> NotificationRecord:
"""
保存通知留痕。
V1 先把通知载荷和结果状态稳定落库,
真实飞书发送可在后续阶段接入。
"""
return NotificationRecord.objects.create(
batch_id=batch_id,
conversation_id=conversation_id,
product_name=product_name,
trigger_source=trigger_source,
notify_reason=notify_reason,
owner_role=owner_role,
feishu_user_id=feishu_user_id,
message_status=message_status,
web_detail_url=web_detail_url,
receipt=receipt,
)

View File

@@ -1,20 +1,24 @@
from django.shortcuts import get_object_or_404, render
from .models import AgentAuditLog
from .models import AgentAuditLog, NotificationRecord
def log_list(request):
# 列表页支持按场景筛选,方便演示时快速定位同一类场景的执行记录
# 处理历史页支持按批次、产品和状态筛选
scenario_id = (request.GET.get("scenario_id") or "").strip()
keyword = (request.GET.get("keyword") or "").strip()
logs = AgentAuditLog.objects.all()
if scenario_id:
logs = logs.filter(scenario_id=scenario_id)
if keyword:
logs = logs.filter(product_name__icontains=keyword) | logs.filter(batch_id__icontains=keyword)
return render(
request,
"audit/log_list.html",
{
"logs": logs,
"selected_scenario_id": scenario_id,
"keyword": keyword,
},
)
@@ -23,4 +27,12 @@ def log_detail(request, log_id: int):
# 详情页只负责按主键加载审计快照并渲染;
# 所有脱敏和字段映射都应在服务层完成。
audit_log = get_object_or_404(AgentAuditLog, pk=log_id)
return render(request, "audit/log_detail.html", {"log": audit_log})
notifications = NotificationRecord.objects.filter(
conversation_id=audit_log.conversation_id,
batch_id=audit_log.batch_id,
)
return render(
request,
"audit/log_detail.html",
{"log": audit_log, "notifications": notifications},
)

View File

View File

@@ -2,7 +2,7 @@ from django.shortcuts import get_object_or_404, redirect, render
from agent_core.orchestrator import run_agent
from agent_core.results import AgentResult
from apps.audit.services import create_audit_log
from apps.audit.services import create_audit_log, create_notification_record
from apps.documents.models import SubmissionBatch, UploadedDocument
from apps.scenarios.services import get_scenario
@@ -70,6 +70,7 @@ def detail(request, conversation_id: str):
conversation_id=conversation.conversation_id,
product_name=conversation.product_name,
)
_persist_notification_records(result)
active_node = "risk"
return render(
@@ -89,3 +90,23 @@ def detail(request, conversation_id: str):
"active_node": active_node,
},
)
def _persist_notification_records(result: AgentResult) -> None:
payload = result.notification_payload or {}
owners = payload.get("owners") or []
if not owners:
return
for owner in owners:
create_notification_record(
batch_id=payload.get("batch_id", ""),
conversation_id=payload.get("conversation_id", ""),
product_name=payload.get("product_name", ""),
trigger_source="agent_execution",
notify_reason=payload.get("notify_reason", "task_completed"),
owner_role=owner.get("owner_role", ""),
feishu_user_id=owner.get("feishu_user_id", ""),
message_status="sent" if result.status == "success" else "failed",
web_detail_url="",
receipt={"status": result.status},
)