feat: 持久化会话节点结果与失败通知留痕

This commit is contained in:
2026-06-04 01:02:06 +08:00
parent b8381b3ba1
commit 2b40ddc487
2 changed files with 125 additions and 0 deletions

View File

@@ -1,3 +1,4 @@
from django.utils import timezone
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from agent_core.orchestrator import run_agent from agent_core.orchestrator import run_agent
@@ -70,8 +71,10 @@ def detail(request, conversation_id: str):
conversation_id=conversation.conversation_id, conversation_id=conversation.conversation_id,
product_name=conversation.product_name, product_name=conversation.product_name,
) )
_apply_agent_result_to_conversation(conversation, result)
_persist_notification_records(result) _persist_notification_records(result)
active_node = "risk" active_node = "risk"
conversation.refresh_from_db()
workspace_summary = _build_workspace_summary(conversation, batch) workspace_summary = _build_workspace_summary(conversation, batch)
return render( return render(
@@ -129,3 +132,25 @@ def _build_workspace_summary(conversation: Conversation, batch: SubmissionBatch
"file_count": batch.file_count if batch else 0, "file_count": batch.file_count if batch else 0,
"page_count": batch.page_count if batch else 0, "page_count": batch.page_count if batch else 0,
} }
def _apply_agent_result_to_conversation(conversation: Conversation, result: AgentResult) -> None:
conversation.task_status = result.status
if result.node_results:
conversation.node_results = result.node_results
conversation.latest_summary = {
"answer": result.answer,
"status": result.status,
"error": result.error,
"notification_payload": result.notification_payload,
}
conversation.last_run_at = timezone.now()
conversation.save(
update_fields=[
"task_status",
"node_results",
"latest_summary",
"last_run_at",
"updated_at",
]
)

View File

@@ -172,6 +172,106 @@ def test_chat_execution_creates_notification_record_from_agent_result(client, db
assert record.batch_id == batch.batch_id assert record.batch_id == batch.batch_id
def test_chat_execution_creates_failed_notification_record_and_updates_conversation(client, db, monkeypatch):
batch, conversation = _create_conversation_with_batch()
UploadedDocument.objects.create(
batch=batch,
scenario_id="document_review",
original_name="说明书.md",
file_type="md",
size=1,
status=UploadedDocument.STATUS_INDEXED,
)
monkeypatch.setattr(
"apps.chat.views.run_agent",
lambda *args, **kwargs: AgentResult(
answer="执行失败",
status="failed",
error="规则执行失败",
node_results=[
{"code": "package_import", "label": "资料包导入", "status": "已完成"},
{"code": "overview", "label": "目录汇总", "status": "已完成"},
{"code": "risk", "label": "风险预警", "status": "已阻断"},
{"code": "feishu_notify", "label": "飞书通知", "status": "失败"},
],
notification_payload={
"batch_id": batch.batch_id,
"conversation_id": conversation.conversation_id,
"product_name": batch.product_name,
"notify_reason": "task_failed",
"owners": [
{
"owner_role": "注册申报负责人",
"feishu_user_id": "ou_demo_2",
}
],
},
),
)
response = client.post(
reverse("chat:detail", args=[conversation.conversation_id]),
{"message": "执行失败任务"},
)
assert response.status_code == 200
record = NotificationRecord.objects.get()
conversation.refresh_from_db()
assert record.notify_reason == "task_failed"
assert record.message_status == "failed"
assert conversation.task_status == "failed"
assert conversation.node_results[-1]["label"] == "飞书通知"
def test_chat_execution_persists_agent_node_results_to_conversation(client, db, monkeypatch):
batch, conversation = _create_conversation_with_batch()
UploadedDocument.objects.create(
batch=batch,
scenario_id="document_review",
original_name="说明书.md",
file_type="md",
size=1,
status=UploadedDocument.STATUS_INDEXED,
)
monkeypatch.setattr(
"apps.chat.views.run_agent",
lambda *args, **kwargs: AgentResult(
answer="已生成风险结论",
status="success",
node_results=[
{"code": "package_import", "label": "资料包导入", "status": "已完成"},
{"code": "overview", "label": "目录汇总", "status": "已完成"},
{"code": "completeness", "label": "法规完整性检查", "status": "已完成"},
{"code": "field_extraction", "label": "字段抽取", "status": "已完成"},
{"code": "consistency", "label": "一致性核查", "status": "待复核"},
{"code": "risk", "label": "风险预警", "status": "已阻断", "summary": "存在高风险"},
{"code": "word_export", "label": "Word 回填导出", "status": "待处理"},
{"code": "feishu_notify", "label": "飞书通知", "status": "待处理"},
],
notification_payload={
"batch_id": batch.batch_id,
"conversation_id": conversation.conversation_id,
"product_name": batch.product_name,
"notify_reason": "task_completed",
"owners": [],
},
),
)
response = client.post(
reverse("chat:detail", args=[conversation.conversation_id]),
{"message": "执行节点任务"},
)
assert response.status_code == 200
conversation.refresh_from_db()
assert len(conversation.node_results) == 8
assert conversation.task_status == "success"
assert conversation.latest_summary["answer"] == "已生成风险结论"
def test_create_conversation_for_batch_initializes_eight_workflow_nodes(db): def test_create_conversation_for_batch_initializes_eight_workflow_nodes(db):
conversation = create_conversation_for_batch( conversation = create_conversation_for_batch(
"SUB-20260604-001", "SUB-20260604-001",