From af36ec460d835c85c202e9cb0bcb40d94644f646 Mon Sep 17 00:00:00 2001 From: bruce Date: Thu, 4 Jun 2026 03:03:37 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=BC=BA=E5=AE=A1=E6=A0=B8?= =?UTF-8?q?=E6=99=BA=E8=83=BD=E4=BD=93=E9=A1=B5=E7=9B=AE=E5=BD=95=E4=B8=8E?= =?UTF-8?q?=E4=B8=80=E8=87=B4=E6=80=A7=E8=83=BD=E5=8A=9B=E5=8D=A1=E5=B1=95?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/chat/views.py | 51 ++++++++++++++++++++++++++ templates/chat/index.html | 75 ++++++++++++++++++++++++++++++++++++++- tests/test_chat.py | 60 +++++++++++++++++++++++++++++++ 3 files changed, 185 insertions(+), 1 deletion(-) diff --git a/apps/chat/views.py b/apps/chat/views.py index e230d74..4290a03 100644 --- a/apps/chat/views.py +++ b/apps/chat/views.py @@ -87,6 +87,7 @@ def detail(request, conversation_id: str): workspace_summary = _build_workspace_summary(conversation, batch) conversation_context = _build_conversation_context(conversation, batch, workspace_summary) prompt_templates = _build_prompt_templates() + analysis_card = _build_analysis_card(result, conversation) export_card = _build_export_card(result, conversation) risk_card = _build_risk_card(result, conversation) notify_card = _build_notify_card(result, conversation) @@ -109,6 +110,7 @@ def detail(request, conversation_id: str): "workspace_summary": workspace_summary, "conversation_context": conversation_context, "prompt_templates": prompt_templates, + "analysis_card": analysis_card, "upload_form": upload_form, "export_card": export_card, "risk_card": risk_card, @@ -274,6 +276,55 @@ def _build_prompt_templates() -> list[str]: ] +def _build_analysis_card(result: AgentResult | None, conversation: Conversation) -> dict: + structured_output = {} + if result and result.structured_output: + structured_output = result.structured_output + else: + structured_output = (conversation.latest_summary or {}).get("structured_output") or {} + output_type = structured_output.get("output_type") + if output_type == "registration_overview_report": + return { + "kind": "overview", + "title": "目录汇总能力卡", + "summary": structured_output.get("product_name", ""), + "stats": [ + {"label": "资料文件数", "value": structured_output.get("file_count", 0)}, + {"label": "总页数", "value": structured_output.get("total_page_count", 0)}, + ], + "items": structured_output.get("chapter_summary") or [], + "warnings": structured_output.get("warnings") or [], + } + if output_type == "registration_completeness_report": + return { + "kind": "completeness", + "title": "完整性检查能力卡", + "summary": structured_output.get("summary", ""), + "stats": [{"label": "风险等级", "value": structured_output.get("risk_level", "-")}], + "items": structured_output.get("missing_items") or [], + "warnings": structured_output.get("misplaced_items") or [], + } + if output_type == "registration_field_extraction_report": + return { + "kind": "field_extraction", + "title": "字段抽取能力卡", + "summary": structured_output.get("summary", ""), + "stats": [{"label": "字段数", "value": len(structured_output.get("field_items") or [])}], + "items": structured_output.get("field_items") or [], + "warnings": structured_output.get("low_confidence_items") or [], + } + if output_type == "registration_consistency_report": + return { + "kind": "consistency", + "title": "一致性核查能力卡", + "summary": structured_output.get("summary", ""), + "stats": [{"label": "风险等级", "value": structured_output.get("risk_level", "-")}], + "items": structured_output.get("conflict_items") or [], + "warnings": structured_output.get("mixed_document_risks") or [], + } + return {} + + def _build_export_card(result: AgentResult | None, conversation: Conversation) -> dict: """ 统一组装 Word 导出能力卡上下文。 diff --git a/templates/chat/index.html b/templates/chat/index.html index 23c8db5..6980d90 100644 --- a/templates/chat/index.html +++ b/templates/chat/index.html @@ -115,8 +115,81 @@

节点式结果

- {% if export_card or risk_card or notify_card %} + {% if analysis_card or export_card or risk_card or notify_card %}
+ {% if analysis_card %} +
+ {{ analysis_card.title }} + {% if analysis_card.summary %} +
{{ analysis_card.summary }}
+ {% endif %} +
+ +
+

摘要指标

+
+ {% for item in analysis_card.stats %} + {{ item.label }}:{{ item.value }} + {% endfor %} +
+
+ +
+

节点结果明细

+
    + {% if analysis_card.kind == "overview" %} + {% for item in analysis_card.items %} +
  • {{ item.chapter_code }} / {{ item.document_count }} 份
  • + {% empty %} +
  • 当前无目录汇总结果。
  • + {% endfor %} + {% elif analysis_card.kind == "consistency" %} + {% for item in analysis_card.items %} +
  • + {{ item.field_name|default:"冲突字段" }} +
    {{ item.issue|default:item }}
    +
  • + {% empty %} +
  • 当前无一致性冲突。
  • + {% endfor %} + {% elif analysis_card.kind == "field_extraction" %} + {% for item in analysis_card.items %} +
  • + {{ item.field_name|default:item.name }} +
    {{ item.field_value|default:item.value }}
    +
  • + {% empty %} +
  • 当前无抽取字段。
  • + {% endfor %} + {% else %} + {% for item in analysis_card.items %} +
  • {{ item }}
  • + {% empty %} +
  • 当前无明细结果。
  • + {% endfor %} + {% endif %} +
+
+ +
+

提示与异常

+
    + {% for item in analysis_card.warnings %} +
  • + {% if analysis_card.kind == "field_extraction" %} + {{ item.field_name|default:item.name|default:"低置信度字段" }} +
    {{ item.field_value|default:item.value|default:item }}
    + {% else %} + {{ item }} + {% endif %} +
  • + {% empty %} +
  • 当前无额外提示。
  • + {% endfor %} +
+
+ {% endif %} + {% if risk_card %}
风险预警能力卡 diff --git a/tests/test_chat.py b/tests/test_chat.py index 99c3547..a2b08f5 100644 --- a/tests/test_chat.py +++ b/tests/test_chat.py @@ -406,6 +406,66 @@ def test_chat_page_shows_top_context_and_recommended_prompts(client, db): assert "请给出当前资料包的高风险项、责任人和整改建议" in content +def test_chat_page_shows_overview_card_from_conversation_summary(client, db): + batch, conversation = _create_conversation_with_batch() + conversation.latest_summary = { + "structured_output": { + "output_type": "registration_overview_report", + "batch_id": batch.batch_id, + "product_name": batch.product_name, + "file_count": 2, + "total_page_count": 12, + "chapter_summary": [ + {"chapter_code": "CH1", "document_count": 2}, + ], + "documents": [ + { + "original_name": "说明书.md", + "chapter_code": "CH1", + "page_count": 12, + "document_role": "product_manual", + } + ], + "warnings": ["Word 页数待复核"], + } + } + conversation.save(update_fields=["latest_summary", "updated_at"]) + + response = client.get(reverse("chat:detail", args=[conversation.conversation_id])) + + content = response.content.decode("utf-8") + assert response.status_code == 200 + assert "目录汇总能力卡" in content + assert "资料文件数" in content + assert "CH1 / 2 份" in content + assert "Word 页数待复核" in content + + +def test_chat_page_shows_consistency_card_from_conversation_summary(client, db): + batch, conversation = _create_conversation_with_batch() + conversation.latest_summary = { + "structured_output": { + "output_type": "registration_consistency_report", + "summary": "检测到跨文档字段冲突。", + "conflict_items": [ + {"field_name": "产品名称", "issue": "申请表与说明书不一致"}, + ], + "mixed_document_risks": ["疑似混入其他产品资料"], + "risk_level": "high", + } + } + conversation.save(update_fields=["latest_summary", "updated_at"]) + + response = client.get(reverse("chat:detail", args=[conversation.conversation_id])) + + content = response.content.decode("utf-8") + assert response.status_code == 200 + assert "一致性核查能力卡" in content + assert "申请表与说明书不一致" in content + assert "疑似混入其他产品资料" in content + assert "high" in content + + def test_chat_page_blocks_formal_export_when_word_export_node_is_blocked(client, db): batch, conversation = _create_conversation_with_batch() conversation.node_results = [