feat: 增强审核智能体页目录与一致性能力卡展示
This commit is contained in:
@@ -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 导出能力卡上下文。
|
||||
|
||||
@@ -115,8 +115,81 @@
|
||||
|
||||
<article class="panel">
|
||||
<h2 class="section-title">节点式结果</h2>
|
||||
{% if export_card or risk_card or notify_card %}
|
||||
{% if analysis_card or export_card or risk_card or notify_card %}
|
||||
<div class="stack">
|
||||
{% if analysis_card %}
|
||||
<div class="detail-item">
|
||||
<strong>{{ analysis_card.title }}</strong>
|
||||
{% if analysis_card.summary %}
|
||||
<div>{{ analysis_card.summary }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="panel" style="padding: 14px;">
|
||||
<h3 class="section-title" style="font-size: 1rem;">摘要指标</h3>
|
||||
<div class="badge-row">
|
||||
{% for item in analysis_card.stats %}
|
||||
<span class="pill pill-accent">{{ item.label }}:{{ item.value }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel" style="padding: 14px;">
|
||||
<h3 class="section-title" style="font-size: 1rem;">节点结果明细</h3>
|
||||
<ul class="detail-list">
|
||||
{% if analysis_card.kind == "overview" %}
|
||||
{% for item in analysis_card.items %}
|
||||
<li class="detail-item">{{ item.chapter_code }} / {{ item.document_count }} 份</li>
|
||||
{% empty %}
|
||||
<li class="detail-item">当前无目录汇总结果。</li>
|
||||
{% endfor %}
|
||||
{% elif analysis_card.kind == "consistency" %}
|
||||
{% for item in analysis_card.items %}
|
||||
<li class="detail-item">
|
||||
<strong>{{ item.field_name|default:"冲突字段" }}</strong>
|
||||
<div>{{ item.issue|default:item }}</div>
|
||||
</li>
|
||||
{% empty %}
|
||||
<li class="detail-item">当前无一致性冲突。</li>
|
||||
{% endfor %}
|
||||
{% elif analysis_card.kind == "field_extraction" %}
|
||||
{% for item in analysis_card.items %}
|
||||
<li class="detail-item">
|
||||
<strong>{{ item.field_name|default:item.name }}</strong>
|
||||
<div>{{ item.field_value|default:item.value }}</div>
|
||||
</li>
|
||||
{% empty %}
|
||||
<li class="detail-item">当前无抽取字段。</li>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% for item in analysis_card.items %}
|
||||
<li class="detail-item">{{ item }}</li>
|
||||
{% empty %}
|
||||
<li class="detail-item">当前无明细结果。</li>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="panel" style="padding: 14px;">
|
||||
<h3 class="section-title" style="font-size: 1rem;">提示与异常</h3>
|
||||
<ul class="detail-list">
|
||||
{% for item in analysis_card.warnings %}
|
||||
<li class="detail-item">
|
||||
{% if analysis_card.kind == "field_extraction" %}
|
||||
<strong>{{ item.field_name|default:item.name|default:"低置信度字段" }}</strong>
|
||||
<div>{{ item.field_value|default:item.value|default:item }}</div>
|
||||
{% else %}
|
||||
{{ item }}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% empty %}
|
||||
<li class="detail-item">当前无额外提示。</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if risk_card %}
|
||||
<div class="detail-item">
|
||||
<strong>风险预警能力卡</strong>
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
Reference in New Issue
Block a user