From dc86fc0e58284b46487e5d24c53d173792e34633 Mon Sep 17 00:00:00 2001 From: bruce Date: Thu, 4 Jun 2026 03:44:04 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=B6=E5=8F=A3=E9=80=9A=E7=9F=A5?= =?UTF-8?q?=E5=8E=9F=E5=9B=A0=E8=AF=AD=E4=B9=89=E4=B8=8E=E7=95=99=E7=97=95?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- agent_core/orchestrator.py | 14 +++++++++++-- apps/audit/services.py | 5 +++++ tests/test_agent_core.py | 43 ++++++++++++++++++++++++++++++++++++++ tests/test_audit.py | 19 +++++++++++++++++ 4 files changed, 79 insertions(+), 2 deletions(-) diff --git a/agent_core/orchestrator.py b/agent_core/orchestrator.py index 78277f6..67137f4 100644 --- a/agent_core/orchestrator.py +++ b/agent_core/orchestrator.py @@ -181,8 +181,9 @@ def _build_node_results(output_type: str, structured_output: dict) -> list[dict] def _build_notification_payload(structured_output: dict, options: dict, status: str) -> dict: - notify_reason = structured_output.get("notify_reason") or ( - "task_completed" if status == "success" else "task_failed" + notify_reason = _normalize_notify_reason( + structured_output.get("notify_reason"), + status=status, ) owners = structured_output.get("owner_roles") or [] if not owners: @@ -202,6 +203,15 @@ def _build_notification_payload(structured_output: dict, options: dict, status: } +def _normalize_notify_reason(notify_reason: str | None, *, status: str) -> str: + """ + 将通知原因收口到 Demo 固定支持的两类语义。 + """ + if notify_reason in {"task_completed", "task_failed"}: + return notify_reason + return "task_completed" if status == "success" else "task_failed" + + def _build_registration_node_results(output_type: str, structured_output: dict) -> list[dict]: nodes = [ {"code": "package_import", "label": "资料包导入", "status": "已完成"}, diff --git a/apps/audit/services.py b/apps/audit/services.py index 1bdb60a..6b805d4 100644 --- a/apps/audit/services.py +++ b/apps/audit/services.py @@ -5,6 +5,9 @@ from apps.documents.models import SubmissionBatch from .models import AgentAuditLog, NotificationRecord +SUPPORTED_NOTIFY_REASONS = {"task_completed", "task_failed"} + + def create_audit_log( scenario_id: str, scenario_name: str, @@ -84,6 +87,8 @@ def create_notification_record( V1 先把通知载荷和结果状态稳定落库, 真实飞书发送可在后续阶段接入。 """ + if notify_reason not in SUPPORTED_NOTIFY_REASONS: + raise ValueError(f"notify_reason 不受支持:{notify_reason}") return NotificationRecord.objects.create( batch_id=batch_id, conversation_id=conversation_id, diff --git a/tests/test_agent_core.py b/tests/test_agent_core.py index c469053..6935f3c 100644 --- a/tests/test_agent_core.py +++ b/tests/test_agent_core.py @@ -513,6 +513,49 @@ def test_feishu_notification_report_builds_notification_payload_with_receipt_and assert result.notification_payload["receipt"]["message_id"] == "msg-3" +def test_notification_payload_normalizes_unsupported_notify_reason(): + scenario = { + "id": "document_review", + "name": "注册审核智能体", + "agent": { + "role": "注册审核助手", + "goal": "输出通知结果", + "instructions": ["输出结构化通知结果"], + }, + "rag": {"enabled": False}, + "tools": [], + "output": {"type": "feishu_notification_report"}, + } + provider_response = """ + { + "batch_id": "SUB-20260604-004", + "conversation_id": "conv-004", + "notify_reason": "custom_reason", + "mentioned_users": ["ou_demo_1"], + "message_status": "sent" + } + """ + + class FakeProvider: + def generate(self, messages, response_format=None): + from agent_core.llm_provider import LLMResponse + + return LLMResponse(content=provider_response, model_name="demo-model", success=True) + + result = run_agent( + scenario, + "请生成通知结果", + options={ + "llm_provider": FakeProvider(), + "batch_id": "SUB-20260604-004", + "conversation_id": "conv-004", + "product_name": "产品D", + }, + ) + + assert result.notification_payload["notify_reason"] == "task_completed" + + def test_registration_word_export_report_preserves_formal_export_flag_and_blocked_items(): scenario = { "id": "document_review", diff --git a/tests/test_audit.py b/tests/test_audit.py index aef83d3..3598eed 100644 --- a/tests/test_audit.py +++ b/tests/test_audit.py @@ -170,6 +170,25 @@ def test_create_notification_record_persists_task_completed_and_task_failed(db): assert failed.notify_reason == "task_failed" +def test_create_notification_record_rejects_unsupported_notify_reason(db): + try: + create_notification_record( + batch_id="SUB-20260604-001", + conversation_id="conv-001", + product_name="产品A", + trigger_source="risk_report", + notify_reason="custom_reason", + owner_role="注册资料负责人", + feishu_user_id="ou_demo_1", + message_status="sent", + web_detail_url="https://example.com/detail/1", + receipt={"message_id": "msg-1"}, + ) + assert False, "expected ValueError" + except ValueError as exc: + assert "notify_reason" in str(exc) + + def test_audit_list_supports_batch_and_product_filters(client, db): create_audit_log( "document_review",