feat: 重构资料包模型与会话绑定主链路

This commit is contained in:
2026-06-04 00:43:13 +08:00
parent ddf5e7d15c
commit d0841e533f
18 changed files with 1000 additions and 263 deletions

View File

@@ -363,14 +363,10 @@
</div>
</div>
<nav class="topnav">
<a href="{% url 'scenarios:index' %}">总览</a>
<a href="{% url 'documents:list' %}">文件中心</a>
<a href="{% url 'chat:index' 'document_review' %}">审核工作台</a>
<a href="{% url 'chat:index' %}">审核智能体</a>
<a href="{% url 'documents:list' %}">资料包</a>
<a href="{% url 'platform_ui:knowledge-base' %}">知识库</a>
<a href="{% url 'platform_ui:mcp-center' %}">MCP</a>
<a href="{% url 'platform_ui:skills' %}">Skills</a>
<a href="{% url 'platform_ui:command-center' %}">工作台</a>
<a href="{% url 'audit:list' %}">审计</a>
<a href="{% url 'audit:list' %}">处理历史</a>
</nav>
</div>
</header>

View File

@@ -1,31 +1,53 @@
{% extends "base.html" %}
{% block title %}{{ scenario.name|default:"Agent 审核工作台" }}{% endblock %}
{% block title %}审核智能体{% endblock %}
{% block content %}
{% if error %}
<section class="notice notice-error">{{ error }}</section>
{% endif %}
{% if scenario %}
<section class="page-header">
<span class="eyebrow">Workspace</span>
<h1 class="page-title">{{ scenario.name }}</h1>
<p class="page-lead">左侧输入问题和选择文档,右侧查看执行结果。</p>
<section class="page-header">
<span class="eyebrow">Agent Workspace</span>
<h1 class="page-title">审核智能体</h1>
<p class="page-lead">以会话为中心组织资料包上传、节点式审核结果和动态任务信息卡。</p>
{% if conversation %}
<div class="badge-row">
<span class="pill pill-accent">已入库文档:{{ document_count }}</span>
<span class="pill">输出{{ scenario.output.type }}</span>
<span class="pill pill-accent">批次:{{ conversation.batch_id }}</span>
<span class="pill">产品{{ conversation.product_name|default:"未识别产品名称" }}</span>
<span class="pill">阶段:{{ conversation.task_status }}</span>
</div>
</section>
{% endif %}
</section>
<section class="workspace-grid">
<div class="stack">
<article class="panel">
<div class="section-heading">
<div>
<h2 class="section-title">任务输入与资料范围</h2>
<p class="section-copy">左侧突出受控输入:先描述审核目标,再限定本轮使用的文档范围。</p>
</div>
<section class="workspace-grid" style="grid-template-columns: 320px minmax(0, 1fr) 360px;">
<div class="stack">
<article class="panel">
<h2 class="section-title">会话历史</h2>
<p class="section-copy">左侧保留历史会话,标题默认使用解析后的产品名称。</p>
<ul class="detail-list">
{% for item in conversations %}
<li class="detail-item">
<strong><a href="{% url 'chat:detail' item.conversation_id %}">{{ item.title }}</a></strong>
<div class="muted">产品:{{ item.product_name|default:"未识别" }}</div>
<div class="muted">批次:{{ item.batch_id }}</div>
</li>
{% empty %}
<li class="detail-item">暂无会话,请先从资料包页面导入资料。</li>
{% endfor %}
</ul>
</article>
</div>
<div class="stack">
<article class="panel">
<div class="section-heading">
<div>
<h2 class="section-title">对话区与节点导航</h2>
<p class="section-copy">中间区域承接用户问题、Agent 回答和节点式结果摘要。</p>
</div>
</div>
{% if conversation %}
<div class="badge-row" style="margin-bottom: 14px;">
{% for node in node_results %}
<span class="pill {% if node.status == '已完成' %}pill-success{% else %}pill-signal{% endif %}">{{ node.label }} / {{ node.status }}</span>
{% endfor %}
</div>
<form method="post" class="stack">
{% csrf_token %}
@@ -38,7 +60,6 @@
</div>
<div>
{{ form.document_ids.label_tag }}
<p class="help-text">不勾选时默认使用全部已入库文档。</p>
<div class="checkbox-list">
{% for checkbox in form.document_ids %}
<label class="checkbox-item">
@@ -46,136 +67,76 @@
<span>{{ checkbox.choice_label }}</span>
</label>
{% empty %}
<div class="notice">当前场景还没有已入库文档,系统将仅依赖工具和模型能力生成结果</div>
<div class="notice">当前资料包还没有可选文档</div>
{% endfor %}
</div>
{% if form.document_ids.errors %}
<p class="notice notice-error">{{ form.document_ids.errors|join:" " }}</p>
{% endif %}
</div>
<div class="button-row">
<button type="submit">提交问题并执行 Agent</button>
<button type="submit">提交审核任务</button>
</div>
</form>
</article>
<article class="panel">
<h2 class="section-title">快捷示例</h2>
<ul class="detail-list">
<li class="detail-item">检查当前资料是否存在缺失项</li>
<li class="detail-item">抽取说明书中的关键字段</li>
<li class="detail-item">比较两份文档中的产品名称是否一致</li>
</ul>
</article>
</div>
<div class="stack">
<article class="panel">
<h2 class="section-title">结果</h2>
{% if result %}
<ul class="meta-list">
<li class="meta-badge">模型:{{ result.model_name }}</li>
<li class="meta-badge {% if result.status == 'success' %}status-success{% else %}status-failed{% endif %}">状态:{{ result.status }}</li>
<li class="meta-badge">耗时:{{ result.latency_ms }} ms</li>
</ul>
<div class="detail-item" style="margin-top: 16px;">
<strong>回答</strong>
<strong>Agent 回答</strong>
<div>{{ result.answer|linebreaksbr }}</div>
</div>
{% else %}
<div class="notice">提交任务后,这里会展示 Agent 的执行状态、主回答和过程摘要。</div>
{% endif %}
</article>
{% if result %}
<article class="panel">
<h2 class="section-title">证据引用与工具调用</h2>
<p class="muted" style="margin-bottom: 14px;">引用片段与工具调用用于支撑结果可解释性。</p>
{% if result.references %}
<h3 style="margin-top: 0;">引用片段</h3>
<ul class="detail-list" style="margin-bottom: 16px;">
{% for reference in result.references %}
<li class="detail-item">
<strong>{{ reference.source }}</strong>
<div>{{ reference.content|default:"无正文内容"|linebreaksbr }}</div>
</li>
{% endfor %}
</ul>
{% else %}
<div class="notice" style="margin-bottom: 16px;">当前回答没有引用知识库片段。</div>
{% endif %}
{% if result.tool_calls %}
<h3>工具调用</h3>
<ul class="detail-list">
{% for tool_call in result.tool_calls %}
<li class="detail-item">
<strong>{{ tool_call.tool_name }}</strong>
<p class="muted">执行状态:{{ tool_call.success }}</p>
{% if tool_call.error %}
<p class="notice notice-error">{{ tool_call.error }}</p>
{% endif %}
<pre class="code-block">{{ tool_call.result }}</pre>
</li>
{% endfor %}
</ul>
{% else %}
<div class="notice">当前场景没有声明工具,或本次执行无需调用工具。</div>
{% endif %}
</article>
{% if result.error %}
<article class="panel">
<h2 class="section-title">错误信息</h2>
<pre class="code-block">{{ result.error }}</pre>
</article>
{% endif %}
{% else %}
<div class="notice">暂无会话,请先导入资料包。</div>
{% endif %}
</div>
</article>
<div class="stack">
<article class="panel">
<div class="section-heading">
<div>
<h2 class="section-title">结构化审核结果</h2>
<p class="section-copy">右侧结果舱用于展示缺失项、冲突项、字段池结果或风险清单。</p>
</div>
</div>
{% if result %}
<table class="kv-table">
<caption style="text-align:left; padding-bottom:12px; color:var(--ink-soft);">结构化结果</caption>
<tbody>
{% for key, value in result.structured_output.items %}
<tr>
<th>{{ key }}</th>
<td>
{% if key == "answer" or key == "summary" or key == "reply" %}
{{ value|linebreaksbr }}
{% else %}
<pre class="code-block">{{ value }}</pre>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div class="notice">执行任务后,这里会展示结构化审核结果和回填准备信息。</div>
{% endif %}
</article>
<article class="panel">
<h2 class="section-title">节点式结果</h2>
{% if result and result.structured_output %}
<table class="kv-table">
<tbody>
{% for key, value in result.structured_output.items %}
<tr>
<th>{{ key }}</th>
<td><pre class="code-block">{{ value }}</pre></td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div class="notice">执行任务后,这里会展示结构化节点结果。</div>
{% endif %}
</article>
</div>
<article class="panel">
<h2 class="section-title">引用与审计</h2>
<div class="stack">
<article class="panel">
<h2 class="section-title">上传区</h2>
<p class="section-copy">资料包导入入口在资料包页统一维护,当前会话只展示绑定关系。</p>
{% if batch %}
<ul class="detail-list">
<li class="detail-item">可查看引用片段、工具调用和本次审计日志。</li>
<li class="detail-item">
<strong>当前资料包</strong>
<div>批次:{{ batch.batch_id }}</div>
<div>文件数:{{ batch.file_count }}</div>
<div>页数:{{ batch.page_count }}</div>
<div>导入状态:{{ batch.get_import_status_display_text }}</div>
</li>
</ul>
<div class="button-row" style="margin-top: 16px;">
<a class="button" href="{% url 'documents:list' %}">返回资料包</a>
</div>
{% else %}
<div class="notice">暂无绑定资料包。</div>
{% endif %}
</article>
<article class="panel">
<h2 class="section-title">动态信息卡</h2>
<ul class="detail-list">
<li class="detail-item">当前会话围绕 `conversation_id / batch_id / product_name` 串联。</li>
<li class="detail-item">任务模式:目录汇总、完整性检查、字段抽取、一致性核查、风险预警。</li>
{% if audit_log %}
<div class="button-row" style="margin-top: 16px;">
<a class="button" href="{% url 'audit:detail' audit_log.id %}">查看本次审计日志</a>
</div>
<li class="detail-item"><a href="{% url 'audit:detail' audit_log.id %}">查看本次处理历史</a></li>
{% endif %}
</article>
</div>
</section>
{% endif %}
</ul>
</article>
</div>
</section>
{% endblock %}

View File

@@ -1,36 +1,114 @@
{% extends "base.html" %}
{% block title %}文件中心{% endblock %}
{% block title %}资料包{% endblock %}
{% block content %}
<section class="page-header">
<span class="eyebrow">Documents</span>
<h1 class="page-title">文件中心</h1>
<p class="page-lead">上传资料、查看状态、执行入库。页面只保留最常用操作</p>
<span class="eyebrow">Submission Batches</span>
<h1 class="page-title">资料包</h1>
<p class="page-lead">按产品名称管理资料包,并查看会话绑定、目录概览和待复核状态</p>
<div class="button-row">
<a class="button button-primary" href="{% url 'documents:upload' %}">上传文件</a>
<a class="button button-primary" href="{% url 'documents:upload' %}">导入资料包</a>
</div>
</section>
<section class="metric-grid">
<article class="metric-card">
<div class="metric-label">文件总数</div>
<div class="metric-label">资料包总数</div>
<div class="metric-value">{{ status_counts.total }}</div>
</article>
<article class="metric-card">
<div class="metric-label">已完成入库</div>
<div class="metric-value">{{ status_counts.indexed }}</div>
<div class="metric-label">已完成</div>
<div class="metric-value">{{ status_counts.completed }}</div>
</article>
<article class="metric-card">
<div class="metric-label">入库</div>
<div class="metric-value">{{ status_counts.uploaded }}</div>
<div class="metric-label">复核</div>
<div class="metric-value">{{ status_counts.review_required }}</div>
</article>
<article class="metric-card">
<div class="metric-label">失败</div>
<div class="metric-value">{{ status_counts.failed }}</div>
<div class="metric-label">待导入</div>
<div class="metric-value">{{ status_counts.pending }}</div>
</article>
</section>
<section class="panel">
<div class="section-heading">
<div>
<h2 class="section-title">按产品名称搜索</h2>
<p class="section-copy">支持按产品名称定位资料包,并跳转到关联会话。</p>
</div>
</div>
<form method="get" class="grid-2">
<div>
<label for="id_keyword">产品名称</label>
<input id="id_keyword" type="text" name="keyword" value="{{ keyword }}" placeholder="请输入产品名称关键词">
</div>
<div class="button-row" style="align-items: end;">
<button type="submit">搜索资料包</button>
<a class="button" href="{% url 'documents:list' %}">清空</a>
</div>
</form>
</section>
<section class="panel">
<div class="section-heading">
<div>
<h2 class="section-title">资料包列表</h2>
<p class="section-copy">资料包与会话一一绑定,会话标题默认采用解析后的产品名称。</p>
</div>
</div>
<div class="table-wrap">
<table class="data-table">
<thead>
<tr>
<th>批次号</th>
<th>产品名称</th>
<th>会话</th>
<th>文件数</th>
<th>页数</th>
<th>状态</th>
<th>章节点概览</th>
</tr>
</thead>
<tbody>
{% for batch in batches %}
<tr>
<td class="nowrap">{{ batch.batch_id }}</td>
<td>{{ batch.product_name|default:"未识别产品名称" }}</td>
<td class="cell-min-220">
{% if batch.conversation_id %}
<a class="button" href="{% url 'chat:detail' batch.conversation_id %}">查看对话 {{ batch.conversation_id }}</a>
{% else %}
<span class="muted">尚未绑定</span>
{% endif %}
</td>
<td>{{ batch.file_count }}</td>
<td>{{ batch.page_count }}</td>
<td>
<span class="pill {% if batch.import_status == 'completed' %}pill-success{% elif batch.import_status == 'review_required' %}pill-signal{% else %}pill-danger{% endif %}">
{{ batch.get_import_status_display_text }}
</span>
</td>
<td class="cell-min-280">
{% if batch.chapter_summary %}
{% for chapter in batch.chapter_summary %}
<div>{{ chapter.chapter_code }} / {{ chapter.document_count }} 份</div>
{% endfor %}
{% else %}
<span class="muted">暂无目录汇总</span>
{% endif %}
</td>
</tr>
{% empty %}
<tr>
<td colspan="7">暂无资料包,请先导入申报资料。</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</section>
<section class="panel">
<div class="section-heading">
<div>
@@ -52,7 +130,7 @@
<div class="section-heading">
<div>
<h2 class="section-title">资料目录总览</h2>
<p class="section-copy">页面下方保留真实文件记录与手动入库动作,保证演示原型仍基于当前系统能力运行</p>
<p class="section-copy">保留文件明细,便于说明目录识别、页数统计和异常定位</p>
</div>
</div>
<div class="table-wrap">
@@ -60,7 +138,9 @@
<thead>
<tr>
<th>文件名</th>
<th>批次</th>
<th>场景</th>
<th>章节点</th>
<th>类型</th>
<th>大小</th>
<th>状态</th>
@@ -71,7 +151,9 @@
{% for document in documents %}
<tr>
<td>{{ document.original_name }}</td>
<td>{{ document.batch.batch_id|default:"-" }}</td>
<td>{{ document.scenario_id }}</td>
<td>{{ document.chapter_code|default:"待识别" }}</td>
<td>{{ document.file_type }}</td>
<td>{{ document.size }}</td>
<td>
@@ -98,7 +180,7 @@
</tr>
{% empty %}
<tr>
<td colspan="6">暂无文件,请先导入申报资料或法规原文。</td>
<td colspan="8">暂无文件,请先导入申报资料或法规原文。</td>
</tr>
{% endfor %}
</tbody>

View File

@@ -1,6 +1,6 @@
{% extends "base.html" %}
{% block title %}任务总览{% endblock %}
{% block title %}平台总览{% endblock %}
{% block content %}
<section class="page-header">
@@ -24,16 +24,16 @@
<p>查看规则树、知识源和切片策略。</p>
</a>
<a class="link-card" href="{% url 'documents:list' %}">
<h3>文件中心</h3>
<p>上传资料、执行入库、查看状态</p>
<h3>资料包</h3>
<p>导入资料包,按产品名称搜索并跳转关联会话</p>
</a>
<a class="link-card" href="{% url 'chat:index' 'document_review' %}">
<h3>审核工作台</h3>
<p>输入问题、选择文档、查看结果。</p>
<a class="link-card" href="{% url 'chat:index' %}">
<h3>审核智能体</h3>
<p>进入会话工作台,查看节点式审核结果。</p>
</a>
<a class="link-card" href="{% url 'audit:list' %}">
<h3>审计日志</h3>
<p>查看每次执行的输入、输出和引用</p>
<h3>处理历史</h3>
<p>查看每次执行的输入、输出和通知留痕</p>
</a>
</section>
@@ -71,7 +71,7 @@
{% endif %}
</p>
<div class="button-row" style="margin-top: 16px;">
<a class="button button-primary" href="{% url 'chat:index' scenario.id %}">进入审核工作台</a>
<a class="button button-primary" href="{% url 'chat:index' %}">进入审核智能体</a>
</div>
</article>
{% empty %}