feat(ui): 重构注册审核平台原型界面
This commit is contained in:
@@ -33,6 +33,13 @@ def index(request, scenario_id: str):
|
||||
status=UploadedDocument.STATUS_INDEXED,
|
||||
)
|
||||
form = ChatForm(request.POST or None, documents=documents)
|
||||
task_modes = [
|
||||
{"name": "目录汇总", "description": "汇总文件、页数、章节点和目录型文档。"},
|
||||
{"name": "完整性检查", "description": "对照法规模板检查齐套性、缺失项和错放项。"},
|
||||
{"name": "字段抽取", "description": "抽取产品名称、规格、适用范围、储存条件等核心字段。"},
|
||||
{"name": "一致性核查", "description": "比较申请表、说明书和产品列表的字段一致性。"},
|
||||
{"name": "综合风险报告", "description": "形成高优先级问题、建议动作和责任人通知。"},
|
||||
]
|
||||
if request.method == "POST" and form.is_valid():
|
||||
message = form.cleaned_data["message"]
|
||||
try:
|
||||
@@ -56,5 +63,6 @@ def index(request, scenario_id: str):
|
||||
"document_count": documents.count(),
|
||||
"result": result,
|
||||
"audit_log": audit_log,
|
||||
"task_modes": task_modes,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -12,7 +12,34 @@ from .services import create_uploaded_document, index_document
|
||||
def document_list(request):
|
||||
# 列表页只负责展示文档元数据和可执行操作,不处理入库细节。
|
||||
documents = UploadedDocument.objects.all()
|
||||
return render(request, "documents/document_list.html", {"documents": documents})
|
||||
status_counts = {
|
||||
"uploaded": documents.filter(status=UploadedDocument.STATUS_UPLOADED).count(),
|
||||
"indexed": documents.filter(status=UploadedDocument.STATUS_INDEXED).count(),
|
||||
"failed": documents.filter(status=UploadedDocument.STATUS_FAILED).count(),
|
||||
"total": documents.count(),
|
||||
}
|
||||
processing_pipeline = [
|
||||
{"title": "原始文件接收", "detail": "校验格式、大小和场景归属后保存原件。"},
|
||||
{"title": "文本与表格抽取", "detail": "按 PDF / DOCX / MD / TXT 使用不同解析策略。"},
|
||||
{"title": "页数统计与可信度评估", "detail": "对 Word 页数采用估算与可信度标记。"},
|
||||
{"title": "章节点归类", "detail": "基于文件名、标题和正文线索识别 CH 节点。"},
|
||||
{"title": "切片与索引入库", "detail": "生成知识切片,供 RAG、规则定位和审计引用使用。"},
|
||||
]
|
||||
exception_items = [
|
||||
{"level": "待确认", "title": "CH1.2 监管信息目录.docx", "detail": "目录页码与正文页数存在偏差,建议人工复核。"},
|
||||
{"level": "低可信度", "title": "目标产品说明书.docx", "detail": "Word 页数为估算值,表格抽取质量良好。"},
|
||||
{"level": "失败", "title": "沟通记录扫描件.pdf", "detail": "疑似扫描件,需补做 OCR 或重新上传清晰版。"},
|
||||
]
|
||||
return render(
|
||||
request,
|
||||
"documents/document_list.html",
|
||||
{
|
||||
"documents": documents,
|
||||
"status_counts": status_counts,
|
||||
"processing_pipeline": processing_pipeline,
|
||||
"exception_items": exception_items,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def upload(request):
|
||||
@@ -28,7 +55,16 @@ def upload(request):
|
||||
return render(
|
||||
request,
|
||||
"documents/upload.html",
|
||||
{"form": form, "scenarios": list_scenarios()},
|
||||
{
|
||||
"form": form,
|
||||
"scenarios": list_scenarios(),
|
||||
"upload_checks": [
|
||||
"文件格式支持 PDF、DOCX、MD、TXT",
|
||||
"业务资料与法规依据资料需分开归属",
|
||||
"目录类文件会优先参与完整性校验",
|
||||
"上传完成后建议立即进入解析与入库流程",
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
|
||||
1
apps/platform_ui/__init__.py
Normal file
1
apps/platform_ui/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
6
apps/platform_ui/apps.py
Normal file
6
apps/platform_ui/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class PlatformUiConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "apps.platform_ui"
|
||||
172
apps/platform_ui/services.py
Normal file
172
apps/platform_ui/services.py
Normal file
@@ -0,0 +1,172 @@
|
||||
def get_platform_demo_context():
|
||||
batch = {
|
||||
"name": "2026Q2-呼吸道多联检测试剂注册批次",
|
||||
"product_name": "呼吸道病原体核酸联合检测试剂盒(PCR-荧光探针法)",
|
||||
"owner": "临床注册事务组",
|
||||
"stage": "法规完整性复核中",
|
||||
"completion": "74%",
|
||||
"next_action": "补齐 CH1.11.5 沟通记录并确认产品列表版本",
|
||||
}
|
||||
metrics = [
|
||||
{"label": "资料齐套率", "value": "82%", "note": "42 / 51 个目录项已命中"},
|
||||
{"label": "法规命中率", "value": "89%", "note": "公告附件包 6 类规则已生效"},
|
||||
{"label": "字段抽取完成度", "value": "67%", "note": "48 个核心字段已进入统一字段池"},
|
||||
{"label": "高风险问题", "value": "03", "note": "2 个缺失项,1 个跨文档冲突"},
|
||||
]
|
||||
workflow_overview = [
|
||||
{"title": "资料进入系统", "detail": "批量上传 18 份申报资料与 6 份法规原文"},
|
||||
{"title": "规则与知识装载", "detail": "按章-条-要求项-模板字段建立知识底座"},
|
||||
{"title": "解析与切片", "detail": "页数统计、目录识别、表格抽取、Chroma 入库"},
|
||||
{"title": "Agent 审核执行", "detail": "完整性检查、字段抽取、一致性核查"},
|
||||
{"title": "结论输出", "detail": "形成风险清单、证据引用、责任人动作建议"},
|
||||
]
|
||||
risk_board = [
|
||||
{"level": "高", "title": "CH1.11.5 沟通记录缺失", "owner": "监管信息专员", "action": "补充 NMPA 沟通留痕"},
|
||||
{"level": "高", "title": "产品名称跨文档表述不一致", "owner": "产品资料负责人", "action": "统一申请表与说明书命名"},
|
||||
{"level": "中", "title": "2 份 Word 页数为估算值", "owner": "文控支持", "action": "补做版式校验"},
|
||||
]
|
||||
quick_links = [
|
||||
{"title": "知识库配置", "url_name": "platform_ui:knowledge-base", "desc": "维护法规规则树与切片策略"},
|
||||
{"title": "文件中心", "url_name": "documents:list", "desc": "查看上传、解析、切片与异常状态"},
|
||||
{"title": "审核工作台", "url_name": "chat:index", "url_arg": "document_review", "desc": "发起审核、抽取与一致性核查演示"},
|
||||
{"title": "工作台大屏", "url_name": "platform_ui:command-center", "desc": "面向演示的 Agent 流程解释大屏"},
|
||||
]
|
||||
knowledge_sources = [
|
||||
{
|
||||
"code": "KB-001",
|
||||
"name": "公告附件包 / 资料要求说明",
|
||||
"type": "法规依据",
|
||||
"scope": "registration",
|
||||
"updated_at": "今天 09:20",
|
||||
"status": "已生效",
|
||||
"owner": "法规专员",
|
||||
},
|
||||
{
|
||||
"code": "KB-002",
|
||||
"name": "批准证明文件格式要求",
|
||||
"type": "模板规则",
|
||||
"scope": "registration",
|
||||
"updated_at": "今天 09:35",
|
||||
"status": "已生效",
|
||||
"owner": "模板管理员",
|
||||
},
|
||||
{
|
||||
"code": "KB-003",
|
||||
"name": "安全和性能基本原则清单",
|
||||
"type": "原则规则",
|
||||
"scope": "registration",
|
||||
"updated_at": "今天 10:02",
|
||||
"status": "待人工校订",
|
||||
"owner": "法规专员",
|
||||
},
|
||||
{
|
||||
"code": "KB-004",
|
||||
"name": "CH1 监管信息目录样例",
|
||||
"type": "业务资料",
|
||||
"scope": "batch",
|
||||
"updated_at": "今天 10:30",
|
||||
"status": "已入库",
|
||||
"owner": "文控专员",
|
||||
},
|
||||
]
|
||||
rule_tree = [
|
||||
{
|
||||
"code": "RULE-001",
|
||||
"chapter": "CH1 监管信息",
|
||||
"item": "CH1.2 监管信息目录",
|
||||
"requirement": "必须提供目录与页码映射",
|
||||
"field": "目录文件 / 页码可信度",
|
||||
"status": "启用",
|
||||
},
|
||||
{
|
||||
"code": "RULE-002",
|
||||
"chapter": "CH1 监管信息",
|
||||
"item": "CH1.4 申请表",
|
||||
"requirement": "申请表字段需与说明书一致",
|
||||
"field": "产品名称 / 规格 / 申请人",
|
||||
"status": "启用",
|
||||
},
|
||||
{
|
||||
"code": "RULE-003",
|
||||
"chapter": "CH1 监管信息",
|
||||
"item": "CH1.11.5 沟通记录",
|
||||
"requirement": "涉及沟通事项时需补齐记录",
|
||||
"field": "沟通对象 / 时间 / 结论",
|
||||
"status": "待校订",
|
||||
},
|
||||
{
|
||||
"code": "RULE-004",
|
||||
"chapter": "批准证明文件格式",
|
||||
"item": "注册证输出模板",
|
||||
"requirement": "字段映射需满足版式模板",
|
||||
"field": "注册证字段池 / Word 模板",
|
||||
"status": "启用",
|
||||
},
|
||||
]
|
||||
knowledge_stats = [
|
||||
{"label": "法规知识源", "value": "06"},
|
||||
{"label": "结构化规则项", "value": "128"},
|
||||
{"label": "业务资料切片", "value": "342"},
|
||||
{"label": "最近人工校订", "value": "2 次"},
|
||||
]
|
||||
mcp_connectors = [
|
||||
{"name": "飞书任务通知", "kind": "协同办公", "auth": "App Token", "status": "已连接", "sync": "5 分钟前"},
|
||||
{"name": "法规规则源导入", "kind": "法规服务", "auth": "文件轮询", "status": "待验证", "sync": "今天 08:50"},
|
||||
{"name": "Word 模板服务", "kind": "文档服务", "auth": "API Key", "status": "已连接", "sync": "刚刚"},
|
||||
{"name": "企业主数据源", "kind": "业务系统", "auth": "MCP Bridge", "status": "未启用", "sync": "未同步"},
|
||||
]
|
||||
skills = [
|
||||
{"name": "完整性检查 Skill", "trigger": "目录齐套性 / 章节点核查", "tools": "规则树 + RAG + 风险映射", "status": "发布中"},
|
||||
{"name": "字段抽取 Skill", "trigger": "申请表 / 说明书 / 产品列表抽取", "tools": "表格抽取 + 字段池", "status": "已发布"},
|
||||
{"name": "一致性核查 Skill", "trigger": "跨文档字段冲突检查", "tools": "字段比对 + 解释生成", "status": "灰度测试"},
|
||||
{"name": "Word 回填 Skill", "trigger": "报送版 Word 输出", "tools": "模板映射 + 导出服务", "status": "待校验"},
|
||||
]
|
||||
workflow_steps = [
|
||||
{"time": "09:32", "title": "载入批次资料", "detail": "识别 18 份业务资料与 6 份法规原文,建立本轮审核上下文。"},
|
||||
{"time": "09:34", "title": "规则树装载", "detail": "按注册申报主流程装载 CH1 监管信息与批准证明文件格式规则。"},
|
||||
{"time": "09:36", "title": "字段池初始化", "detail": "从申请表、说明书、产品列表抽取统一字段并建立来源映射。"},
|
||||
{"time": "09:39", "title": "一致性检查", "detail": "检测到产品名称和样本类型存在跨文档冲突,升级为人工复核。"},
|
||||
{"time": "09:42", "title": "风险输出", "detail": "生成 3 条风险项、2 条补件建议与 1 条责任人通知任务。"},
|
||||
]
|
||||
knowledge_filters = [
|
||||
{"label": "全部", "active": True},
|
||||
{"label": "法规依据", "active": False},
|
||||
{"label": "模板规则", "active": False},
|
||||
{"label": "业务资料", "active": False},
|
||||
]
|
||||
knowledge_form = {
|
||||
"title": "新增 / 编辑知识源",
|
||||
"fields": [
|
||||
{"label": "知识源名称", "value": "体外诊断试剂注册申报资料要求及说明"},
|
||||
{"label": "知识源类型", "value": "法规依据"},
|
||||
{"label": "适用流程", "value": "registration"},
|
||||
{"label": "状态", "value": "已生效"},
|
||||
{"label": "切片策略", "value": "按章条切片,保留条款编号"},
|
||||
],
|
||||
}
|
||||
rule_form = {
|
||||
"title": "新增 / 编辑规则项",
|
||||
"fields": [
|
||||
{"label": "规则编码", "value": "RULE-005"},
|
||||
{"label": "所属章节", "value": "CH1 监管信息"},
|
||||
{"label": "规则名称", "value": "申请表签章完整性检查"},
|
||||
{"label": "模板字段", "value": "签章页 / 申请人签字"},
|
||||
{"label": "规则说明", "value": "若申请表缺少签章,则标记为高优先级缺失项"},
|
||||
],
|
||||
}
|
||||
return {
|
||||
"batch": batch,
|
||||
"metrics": metrics,
|
||||
"workflow_overview": workflow_overview,
|
||||
"risk_board": risk_board,
|
||||
"quick_links": quick_links,
|
||||
"knowledge_sources": knowledge_sources,
|
||||
"rule_tree": rule_tree,
|
||||
"knowledge_stats": knowledge_stats,
|
||||
"knowledge_filters": knowledge_filters,
|
||||
"knowledge_form": knowledge_form,
|
||||
"rule_form": rule_form,
|
||||
"mcp_connectors": mcp_connectors,
|
||||
"skills": skills,
|
||||
"workflow_steps": workflow_steps,
|
||||
}
|
||||
13
apps/platform_ui/urls.py
Normal file
13
apps/platform_ui/urls.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
|
||||
app_name = "platform_ui"
|
||||
|
||||
urlpatterns = [
|
||||
path("knowledge-base/", views.knowledge_base, name="knowledge-base"),
|
||||
path("mcp-center/", views.mcp_center, name="mcp-center"),
|
||||
path("skills/", views.skill_studio, name="skills"),
|
||||
path("command-center/", views.command_center, name="command-center"),
|
||||
]
|
||||
23
apps/platform_ui/views.py
Normal file
23
apps/platform_ui/views.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
from .services import get_platform_demo_context
|
||||
|
||||
|
||||
def knowledge_base(request):
|
||||
context = get_platform_demo_context()
|
||||
return render(request, "platform_ui/knowledge_base.html", context)
|
||||
|
||||
|
||||
def mcp_center(request):
|
||||
context = get_platform_demo_context()
|
||||
return render(request, "platform_ui/mcp_center.html", context)
|
||||
|
||||
|
||||
def skill_studio(request):
|
||||
context = get_platform_demo_context()
|
||||
return render(request, "platform_ui/skill_studio.html", context)
|
||||
|
||||
|
||||
def command_center(request):
|
||||
context = get_platform_demo_context()
|
||||
return render(request, "platform_ui/command_center.html", context)
|
||||
@@ -12,5 +12,23 @@ def index(request):
|
||||
{
|
||||
"scenarios": list_scenarios(),
|
||||
"scenario_issues": list_scenario_issues(),
|
||||
"hero_metrics": [
|
||||
{"label": "资料齐套率", "value": "82%", "note": "已识别 42 / 51 个法规模板项"},
|
||||
{"label": "法规命中率", "value": "89%", "note": "公告附件包 6 类规则已加载"},
|
||||
{"label": "字段抽取完成度", "value": "67%", "note": "48 个核心字段进入统一字段池"},
|
||||
{"label": "高风险问题", "value": "03", "note": "含 1 个缺失项与 1 个命名冲突"},
|
||||
],
|
||||
"workflow_overview": [
|
||||
{"title": "资料进入系统", "detail": "导入本批次注册申报资料、法规原文与模板文件。"},
|
||||
{"title": "知识底座装载", "detail": "以章-条-要求项-模板字段组织法规规则。"},
|
||||
{"title": "解析与切片", "detail": "完成页数统计、表格抽取、章节点归类和向量入库。"},
|
||||
{"title": "Agent 审核执行", "detail": "发起完整性检查、字段抽取和一致性比对。"},
|
||||
{"title": "结论输出与留痕", "detail": "形成风险清单、证据引用、责任人动作和审计日志。"},
|
||||
],
|
||||
"risk_board": [
|
||||
{"level": "高风险", "title": "CH1.11.5 沟通记录缺失", "detail": "监管沟通事项未形成可追溯文件,无法支撑本轮章节齐套性。"},
|
||||
{"level": "高风险", "title": "产品名称在申请表与说明书中不一致", "detail": "Agent 判定为跨文档命名冲突,需进入人工复核。"},
|
||||
{"level": "中风险", "title": "2 份 Word 文档页数可信度不足", "detail": "版式页数使用估算值,建议复核目录页码与正文总页数。"},
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
@@ -55,6 +55,7 @@ INSTALLED_APPS = [
|
||||
"apps.documents",
|
||||
"apps.chat",
|
||||
"apps.audit",
|
||||
"apps.platform_ui",
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
||||
@@ -11,6 +11,7 @@ urlpatterns = [
|
||||
path("chat/", include("apps.chat.urls")),
|
||||
path("documents/", include("apps.documents.urls")),
|
||||
path("audit/", include("apps.audit.urls")),
|
||||
path("platform/", include("apps.platform_ui.urls")),
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
|
||||
@@ -0,0 +1,376 @@
|
||||
# Registration Agent Prototype Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Build a new high-fidelity, demo-ready prototype UI for the registration review agent platform across homepage, knowledge base, document processing, agent workspace, MCP, Skills, and leadership dashboard screens.
|
||||
|
||||
**Architecture:** Keep the existing Django monolith and template routing boundaries, but replace the current visual system with a unified prototype shell and add a dedicated platform app for new governance pages. Use shared presentation data in service/view code so the pages tell one coherent business story without coupling design code to the existing agent execution internals.
|
||||
|
||||
**Tech Stack:** Django templates, Django views/URLs, Python service helpers, shared inline CSS in base template, existing forms/models where useful.
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
- Modify: `F:\PyCharm\DEMO-AGENT\templates\base.html`
|
||||
- Modify: `F:\PyCharm\DEMO-AGENT\templates\scenarios\index.html`
|
||||
- Modify: `F:\PyCharm\DEMO-AGENT\templates\documents\document_list.html`
|
||||
- Modify: `F:\PyCharm\DEMO-AGENT\templates\documents\upload.html`
|
||||
- Modify: `F:\PyCharm\DEMO-AGENT\templates\chat\index.html`
|
||||
- Create: `F:\PyCharm\DEMO-AGENT\apps\platform_ui\__init__.py`
|
||||
- Create: `F:\PyCharm\DEMO-AGENT\apps\platform_ui\apps.py`
|
||||
- Create: `F:\PyCharm\DEMO-AGENT\apps\platform_ui\views.py`
|
||||
- Create: `F:\PyCharm\DEMO-AGENT\apps\platform_ui\urls.py`
|
||||
- Create: `F:\PyCharm\DEMO-AGENT\apps\platform_ui\services.py`
|
||||
- Create: `F:\PyCharm\DEMO-AGENT\templates\platform_ui\knowledge_base.html`
|
||||
- Create: `F:\PyCharm\DEMO-AGENT\templates\platform_ui\mcp_center.html`
|
||||
- Create: `F:\PyCharm\DEMO-AGENT\templates\platform_ui\skill_studio.html`
|
||||
- Create: `F:\PyCharm\DEMO-AGENT\templates\platform_ui\command_center.html`
|
||||
- Modify: `F:\PyCharm\DEMO-AGENT\config\settings.py`
|
||||
- Modify: `F:\PyCharm\DEMO-AGENT\config\urls.py`
|
||||
- Test: `F:\PyCharm\DEMO-AGENT\tests\`
|
||||
|
||||
### Task 1: Register the new platform prototype app
|
||||
|
||||
**Files:**
|
||||
- Create: `apps/platform_ui/__init__.py`
|
||||
- Create: `apps/platform_ui/apps.py`
|
||||
- Create: `apps/platform_ui/views.py`
|
||||
- Create: `apps/platform_ui/urls.py`
|
||||
- Modify: `config/settings.py`
|
||||
- Modify: `config/urls.py`
|
||||
|
||||
- [ ] **Step 1: Add the Django app module skeleton**
|
||||
|
||||
```python
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class PlatformUiConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "apps.platform_ui"
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Register the app in settings**
|
||||
|
||||
```python
|
||||
INSTALLED_APPS = [
|
||||
# ...
|
||||
"apps.platform_ui",
|
||||
]
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Add prototype page routes**
|
||||
|
||||
```python
|
||||
urlpatterns = [
|
||||
path("platform/", include("apps.platform_ui.urls")),
|
||||
]
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Define view names for prototype pages**
|
||||
|
||||
```python
|
||||
urlpatterns = [
|
||||
path("knowledge-base/", views.knowledge_base, name="knowledge-base"),
|
||||
path("mcp-center/", views.mcp_center, name="mcp-center"),
|
||||
path("skills/", views.skill_studio, name="skills"),
|
||||
path("command-center/", views.command_center, name="command-center"),
|
||||
]
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Run framework validation**
|
||||
|
||||
Run: `python manage.py check`
|
||||
Expected: PASS with no URL or app import errors
|
||||
|
||||
### Task 2: Create shared presentation data for the new prototype
|
||||
|
||||
**Files:**
|
||||
- Create: `apps/platform_ui/services.py`
|
||||
- Modify: `apps/platform_ui/views.py`
|
||||
|
||||
- [ ] **Step 1: Define one coherent demo dataset**
|
||||
|
||||
```python
|
||||
def get_platform_demo_context():
|
||||
return {
|
||||
"batch": {...},
|
||||
"knowledge_sources": [...],
|
||||
"mcp_connectors": [...],
|
||||
"skills": [...],
|
||||
"workflow_steps": [...],
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Keep each page view thin**
|
||||
|
||||
```python
|
||||
def knowledge_base(request):
|
||||
context = get_platform_demo_context()
|
||||
return render(request, "platform_ui/knowledge_base.html", context)
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Run framework validation**
|
||||
|
||||
Run: `python manage.py check`
|
||||
Expected: PASS with importable views and service helpers
|
||||
|
||||
### Task 3: Replace the global visual system and navigation shell
|
||||
|
||||
**Files:**
|
||||
- Modify: `templates/base.html`
|
||||
|
||||
- [ ] **Step 1: Rewrite the global shell**
|
||||
|
||||
```html
|
||||
<body>
|
||||
<div class="app-shell">
|
||||
<aside class="sidebar">...</aside>
|
||||
<main class="main-shell">
|
||||
<header class="topbar">...</header>
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Define the new design tokens and shared components**
|
||||
|
||||
```css
|
||||
:root {
|
||||
--bg: #eef3f7;
|
||||
--surface: #f8fbfd;
|
||||
--panel: #ffffff;
|
||||
--ink: #102033;
|
||||
--accent: #1e5eff;
|
||||
--signal: #d77a2b;
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Add global helpers for panels, metric cards, timelines, tables, pills, and section headers**
|
||||
|
||||
```css
|
||||
.panel { ... }
|
||||
.metric-card { ... }
|
||||
.section-heading { ... }
|
||||
.timeline-step { ... }
|
||||
.data-table { ... }
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Render shared navigation links to all prototype surfaces**
|
||||
|
||||
```html
|
||||
<a href="{% url 'scenarios:index' %}">任务总览</a>
|
||||
<a href="{% url 'platform_ui:knowledge-base' %}">知识库配置</a>
|
||||
<a href="{% url 'documents:list' %}">文件中心</a>
|
||||
<a href="{% url 'platform_ui:command-center' %}">工作台大屏</a>
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Open a few pages manually**
|
||||
|
||||
Run: `python manage.py runserver`
|
||||
Expected: the shell renders and every nav item resolves
|
||||
|
||||
### Task 4: Redesign the homepage as the business-closure entry point
|
||||
|
||||
**Files:**
|
||||
- Modify: `templates/scenarios/index.html`
|
||||
- Modify: `apps/scenarios/views.py` if extra presentation fields are needed
|
||||
|
||||
- [ ] **Step 1: Add a structured homepage context if needed**
|
||||
|
||||
```python
|
||||
return render(
|
||||
request,
|
||||
"scenarios/index.html",
|
||||
{
|
||||
"scenarios": list_scenarios(),
|
||||
"scenario_issues": list_scenario_issues(),
|
||||
"hero_metrics": [...],
|
||||
"workflow_overview": [...],
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Replace the page body with hero, metrics, workflow strip, risk board, and quick-entry modules**
|
||||
|
||||
```html
|
||||
<section class="hero-band">...</section>
|
||||
<section class="metrics-grid">...</section>
|
||||
<section class="workflow-strip">...</section>
|
||||
<section class="two-column-board">...</section>
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Verify the homepage**
|
||||
|
||||
Run: `python manage.py runserver`
|
||||
Expected: `/` shows the new dashboard-like homepage
|
||||
|
||||
### Task 5: Redesign the document pages around parsing and slicing workflow
|
||||
|
||||
**Files:**
|
||||
- Modify: `templates/documents/document_list.html`
|
||||
- Modify: `templates/documents/upload.html`
|
||||
- Optionally modify: `apps/documents/views.py`
|
||||
|
||||
- [ ] **Step 1: Add any lightweight display-only summary fields in the view if needed**
|
||||
|
||||
```python
|
||||
return render(
|
||||
request,
|
||||
"documents/document_list.html",
|
||||
{
|
||||
"documents": documents,
|
||||
"processing_summary": {...},
|
||||
"exception_items": [...],
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Rebuild the list page into upload stats, pipeline board, anomaly box, and structured directory table**
|
||||
|
||||
```html
|
||||
<section class="metrics-grid">...</section>
|
||||
<section class="tri-column">...</section>
|
||||
<section class="panel">
|
||||
<table class="data-table">...</table>
|
||||
</section>
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Rebuild the upload page into a guided import experience**
|
||||
|
||||
```html
|
||||
<section class="dropzone-panel">...</section>
|
||||
<section class="checklist-panel">...</section>
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Verify both pages**
|
||||
|
||||
Run: `python manage.py runserver`
|
||||
Expected: `/documents/` and `/documents/upload/` match the new prototype style
|
||||
|
||||
### Task 6: Redesign the chat page as a controlled audit workspace
|
||||
|
||||
**Files:**
|
||||
- Modify: `templates/chat/index.html`
|
||||
- Optionally modify: `apps/chat/views.py`
|
||||
|
||||
- [ ] **Step 1: Add presentation-only helper blocks if the existing context is too sparse**
|
||||
|
||||
```python
|
||||
return render(
|
||||
request,
|
||||
"chat/index.html",
|
||||
{
|
||||
...
|
||||
"task_modes": [...],
|
||||
"result_highlights": [...],
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Replace the template with a three-zone workspace**
|
||||
|
||||
```html
|
||||
<section class="workspace-grid">
|
||||
<div class="left-rail">...</div>
|
||||
<div class="conversation-stage">...</div>
|
||||
<div class="result-rail">...</div>
|
||||
</section>
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Preserve the real form submission path while upgrading the visual output areas**
|
||||
|
||||
```html
|
||||
<form method="post">...</form>
|
||||
{% if result %} ... {% endif %}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Verify one scenario page**
|
||||
|
||||
Run: `python manage.py runserver`
|
||||
Expected: `/chat/<scenario_id>/` still submits and now renders as a workbench
|
||||
|
||||
### Task 7: Implement the governance and platform pages
|
||||
|
||||
**Files:**
|
||||
- Create: `templates/platform_ui/knowledge_base.html`
|
||||
- Create: `templates/platform_ui/mcp_center.html`
|
||||
- Create: `templates/platform_ui/skill_studio.html`
|
||||
- Create: `templates/platform_ui/command_center.html`
|
||||
- Modify: `apps/platform_ui/views.py`
|
||||
- Modify: `apps/platform_ui/services.py`
|
||||
|
||||
- [ ] **Step 1: Build the knowledge base page**
|
||||
|
||||
```html
|
||||
<section class="three-column-board">...</section>
|
||||
<section class="panel">...</section>
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Build the MCP center page**
|
||||
|
||||
```html
|
||||
<section class="connector-grid">...</section>
|
||||
<section class="import-flow">...</section>
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Build the Skill studio page**
|
||||
|
||||
```html
|
||||
<section class="editor-layout">...</section>
|
||||
<section class="version-board">...</section>
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Build the leadership command center page**
|
||||
|
||||
```html
|
||||
<section class="command-stage">...</section>
|
||||
<section class="risk-heatmap">...</section>
|
||||
<section class="evidence-matrix">...</section>
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Verify page routing**
|
||||
|
||||
Run: `python manage.py check`
|
||||
Expected: PASS and all `/platform/...` routes resolve
|
||||
|
||||
### Task 8: Regression, polish, and documentation sync
|
||||
|
||||
**Files:**
|
||||
- Modify: `README.md` if navigation or page descriptions need updating
|
||||
- Modify: `AGENTS.md` only if current implementation status needs sync
|
||||
|
||||
- [ ] **Step 1: Run core validation**
|
||||
|
||||
Run: `python manage.py check`
|
||||
Expected: PASS
|
||||
|
||||
- [ ] **Step 2: Run targeted tests if template/view assumptions changed**
|
||||
|
||||
Run: `pytest`
|
||||
Expected: PASS or clearly identified existing failures unrelated to the prototype
|
||||
|
||||
- [ ] **Step 3: Review the new screens for consistent terminology**
|
||||
|
||||
Check: page titles, batch name, product name, risk labels, and workflow terms all match the new design spec
|
||||
|
||||
- [ ] **Step 4: Update high-level documentation if needed**
|
||||
|
||||
```markdown
|
||||
## 当前原型页面
|
||||
- 任务总览
|
||||
- 知识库配置
|
||||
- 文件中心
|
||||
- 审核工作台
|
||||
- MCP 中心
|
||||
- Skill Studio
|
||||
- 工作台大屏
|
||||
```
|
||||
|
||||
## Self-Review
|
||||
|
||||
- Spec coverage: the plan covers homepage, knowledge base, document center, chat workspace, MCP, Skill studio, and leadership dashboard.
|
||||
- Placeholder scan: no TODO/TBD markers remain.
|
||||
- Type consistency: route names, app names, and page concepts are consistent across tasks.
|
||||
@@ -0,0 +1,185 @@
|
||||
# 注册审核智能体平台产品原型设计
|
||||
|
||||
## 目标
|
||||
|
||||
本次原型围绕“试剂盒临床注册文件准备与审核智能体平台”重新设计一套高保真、可点击、适合复试演示的 Web 产品界面。设计不沿用当前前端视觉,而是以新版需求分析为准,突出资料治理、法规知识底座、Agent 审核闭环、平台治理能力和可解释工作台。
|
||||
|
||||
## 设计范围
|
||||
|
||||
本次原型聚焦一条完整演示主线:
|
||||
|
||||
1. 首页进入批次级任务总览。
|
||||
2. 查看法规知识库与结构化规则配置。
|
||||
3. 上传并解析申报资料,查看切片、页数、章节点归类和异常状态。
|
||||
4. 进入 AI Agent 审核工作台执行完整性检查、字段抽取和一致性核查。
|
||||
5. 查看外部 MCP 能力接入情况。
|
||||
6. 查看和编辑 Skill 编排能力。
|
||||
7. 进入领导演示型工作台大屏查看 Agent 工作流程、解释依据和风险态势。
|
||||
|
||||
## 设计原则
|
||||
|
||||
### 1. 业务闭环优先
|
||||
|
||||
页面组织必须服务“资料准备到审核闭环”,而不是把系统包装成泛化 Agent 工具箱。
|
||||
|
||||
### 2. 高层能看懂,操作员也能讲清
|
||||
|
||||
首页与大屏承担讲故事职责;知识库、文件中心、Agent 工作台承担业务可信度职责;MCP 与 Skill 页面承担平台能力说明职责。
|
||||
|
||||
### 3. 可解释性强于炫技
|
||||
|
||||
任何页面都应尽量展示来源、阶段、规则命中、风险等级、人工复核状态,而不是堆叠模型术语。
|
||||
|
||||
### 4. 信息密度高但不压抑
|
||||
|
||||
整体风格走“专业监管科技工作台”,采用明亮底色、深色信息层、铜橙风险强调、清晰分栏和大面积结构化留白。
|
||||
|
||||
## 视觉方向
|
||||
|
||||
### 色彩
|
||||
|
||||
- 主背景采用浅米白与冷灰蓝混合渐变,强调专业与稳定。
|
||||
- 主强调色采用深青蓝,承担导航、激活态、关键信息。
|
||||
- 风险强调采用铜橙与深红,用于高风险、缺失、待处理。
|
||||
- 成功状态采用低饱和绿色,用于已完成、已入库、规则已生效。
|
||||
|
||||
### 排版
|
||||
|
||||
- 页级标题使用强对比的中文大标题,突出“阶段感”。
|
||||
- 模块级标题使用中等字号与清晰副标题。
|
||||
- 指标数字使用紧凑等宽风格,强调可视化汇报感。
|
||||
|
||||
### 组件风格
|
||||
|
||||
- 顶部为平台导航与当前批次状态条。
|
||||
- 页面内部以大面板、信息条、时间线、矩阵卡片、指标舱和结构树为主。
|
||||
- 尽量避免传统后台里大量相同卡片堆叠,强化带状布局、流程图感和分析板感。
|
||||
|
||||
## 页面设计
|
||||
|
||||
### 1. 首页 / 任务总览
|
||||
|
||||
**目标:**
|
||||
让评委在 10 秒内理解系统价值和当前批次进展。
|
||||
|
||||
**核心内容:**
|
||||
|
||||
- 批次概览 Hero:当前申报批次、产品名、流程阶段、批次状态。
|
||||
- 四个关键指标:资料齐套率、法规命中率、字段抽取完成度、高风险项数量。
|
||||
- 演示主流程带:资料进入、规则配置、解析入库、审核执行、结果输出。
|
||||
- 今日待办与高风险问题板。
|
||||
- 快速入口:知识库、文件中心、审核工作台、大屏。
|
||||
|
||||
### 2. 知识库搭建与配置页
|
||||
|
||||
**目标:**
|
||||
把“双层知识底座”讲透。
|
||||
|
||||
**核心内容:**
|
||||
|
||||
- 左侧法规规则树:按章、条、要求项、模板字段展示。
|
||||
- 中部知识源列表:法规依据、业务资料、模板库、公告附件包。
|
||||
- 右侧切片与生效配置:切片策略、召回阈值、适用流程、最近更新时间。
|
||||
- 底部人工校订记录与知识更新入口。
|
||||
|
||||
### 3. 文件上传、切片与解析中心
|
||||
|
||||
**目标:**
|
||||
展示 Documents 模块是“资料治理中心”。
|
||||
|
||||
**核心内容:**
|
||||
|
||||
- 顶部上传拖拽区与批次选择。
|
||||
- 左侧批量导入队列:文件名、类型、页数、识别状态。
|
||||
- 中部处理流水:保存原件、提取文本、识别表格、页数统计、章节点归类、切片入库。
|
||||
- 右侧异常箱:疑似扫描件、归类待确认、页数低可信度、切片失败。
|
||||
- 下方目录总览表:章节点、资料名称、状态、模板命中情况。
|
||||
|
||||
### 4. AI Agent 审核工作台
|
||||
|
||||
**目标:**
|
||||
把自由问答升级为受控审核任务工作台。
|
||||
|
||||
**核心内容:**
|
||||
|
||||
- 顶部任务切换:目录汇总、完整性检查、字段抽取、一致性核查、综合风险报告。
|
||||
- 左侧自然语言输入与资料范围选择。
|
||||
- 中间 Agent 对话流与执行阶段条。
|
||||
- 右侧结构化结果舱:结论摘要、字段表、缺失项、冲突项、风险建议。
|
||||
- 下方证据区:法规条款引用、文档片段、来源页码、人工复核提示。
|
||||
|
||||
### 5. 外部 MCP 导入页
|
||||
|
||||
**目标:**
|
||||
说明平台可扩展但不喧宾夺主。
|
||||
|
||||
**核心内容:**
|
||||
|
||||
- MCP 连接卡:法规源、飞书通知、模板服务、文档转换、企业数据源。
|
||||
- 接入状态、鉴权方式、最近同步时间。
|
||||
- 输入输出能力摘要。
|
||||
- 一个简单的导入向导区块。
|
||||
|
||||
### 6. Skill 编辑与使用页
|
||||
|
||||
**目标:**
|
||||
展示 Agent 可配置、可维护、可复用。
|
||||
|
||||
**核心内容:**
|
||||
|
||||
- Skill 列表:完整性检查、字段抽取、一致性核查、Word 回填、飞书通知。
|
||||
- 中部编辑区:角色说明、工具绑定、输入输出约束、启停配置。
|
||||
- 右侧运行预览:最近测试结果、命中工具、失败原因。
|
||||
- 底部版本历史和发布状态。
|
||||
|
||||
### 7. Agent 工作台大屏
|
||||
|
||||
**目标:**
|
||||
作为演示高潮页,向领导/评委解释“这一轮审核发生了什么”。
|
||||
|
||||
**核心内容:**
|
||||
|
||||
- 顶部大标题与批次状态。
|
||||
- 左侧流程总览:资料进入、规则装载、字段池建立、一致性比对、风险汇总。
|
||||
- 中央主舞台:Agent 工作流时间轴,展示每一步的输入、动作、输出和解释。
|
||||
- 右侧风险热力与关键告警。
|
||||
- 底部证据矩阵:法规依据、命中文档、责任人、待补动作。
|
||||
|
||||
## 跳转与演示动线
|
||||
|
||||
推荐演示顺序:
|
||||
|
||||
1. 首页说明平台定位。
|
||||
2. 进入知识库页说明法规与规则如何维护。
|
||||
3. 进入文件中心说明资料如何入库、切片、归类。
|
||||
4. 进入审核工作台发起一次完整性或一致性检查。
|
||||
5. 进入 Skill 页解释为何 Agent 行为可控。
|
||||
6. 进入 MCP 页说明可接飞书与外部能力。
|
||||
7. 最后切到大屏汇总整轮审核过程。
|
||||
|
||||
## 数据策略
|
||||
|
||||
本轮原型以高保真演示为优先,允许使用页面级演示数据,但必须满足:
|
||||
|
||||
- 术语与字段名称来自新版需求分析。
|
||||
- 状态设计贴近真实业务流程。
|
||||
- 页面间批次名称、产品名称、风险项和规则口径保持一致。
|
||||
|
||||
## 实现策略
|
||||
|
||||
基于当前 Django 模板工程直接重构前端:
|
||||
|
||||
1. 保留现有 Django 应用与路由边界。
|
||||
2. 新增平台页应用承接知识库、MCP、Skill、大屏页面。
|
||||
3. 重做 `base.html` 及所有主要模板的布局和样式。
|
||||
4. 使用共享演示数据构造页面内容,保证视觉统一和讲解一致。
|
||||
|
||||
## 测试与验证
|
||||
|
||||
原型完成后至少验证:
|
||||
|
||||
1. Django 路由可访问。
|
||||
2. 首页、知识库、文件中心、审核工作台、MCP、Skill、大屏页面均可打开。
|
||||
3. 页面导航互通。
|
||||
4. `python manage.py check` 通过。
|
||||
5. 如无模板语法问题,关键页面可在本地服务下正常渲染。
|
||||
597
docs/需求分析/9.业务确认问答清单.md
Normal file
597
docs/需求分析/9.业务确认问答清单.md
Normal file
@@ -0,0 +1,597 @@
|
||||
# 业务确认问答清单
|
||||
|
||||
## 1. 文档目的
|
||||
|
||||
本文档用于和业务方进一步确认需求边界、交付口径和演示重点。
|
||||
|
||||
它和 `0.需求重构总览与待确认事项.md` 的区别是:
|
||||
|
||||
1. 总览文档更偏内部需求分析。
|
||||
2. 本文档更偏“可直接拿去问业务”的沟通清单。
|
||||
3. 每个问题尽量使用业务语言,避免陷入实现细节。
|
||||
|
||||
建议使用方式:
|
||||
|
||||
1. 按章节逐条和业务确认。
|
||||
2. 每个问题尽量记录“明确答案 + 备注 + 示例材料来源”。
|
||||
3. 若业务方回答不确定,可先记为“当前默认口径”。
|
||||
4. 沟通完成后,再回填更新需求分析和设计文档。
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前已确认口径摘要
|
||||
|
||||
以下内容目前已经有较明确的方向,和业务确认时可以先作为“我们的当前理解”说给对方听,请对方确认是否同意:
|
||||
|
||||
1. 系统目标是“通用的试剂盒临床注册文件准备与审核智能体”,不是只服务某一个固定产品。
|
||||
2. 一致性核查当前按完全一致处理,不采用“语义差不多即可”的宽松口径。
|
||||
3. 风险按高 / 中 / 低三级,任一高风险即不允许通过。
|
||||
4. 输出结果必须支持生成新的 Word 文档,并达到可直接报送级版式。
|
||||
5. 用户后续编写的新模板可以纳入模板库,后续按模板回填。
|
||||
6. 规则来源以公告附件包为主,`附件 4` 视作同源补充材料。
|
||||
7. 法规知识按“章 -> 条 -> 要求项 -> 模板字段”四级结构维护。
|
||||
8. 飞书接入属于本次 Demo 范围,需要支持群聊机器人,并在飞书内完成任务选择、结果查看和责任人通知。
|
||||
9. 系统需要提供后台管理页面,支持人工校订、知识库更新、模板管理和责任人维护。
|
||||
|
||||
---
|
||||
|
||||
## 3. 建议的沟通原则
|
||||
|
||||
和业务沟通时,建议优先确认四类问题:
|
||||
|
||||
1. 交付结果长什么样。
|
||||
2. 哪些结论能自动判,哪些必须人工复核。
|
||||
3. 哪些材料算输入,哪些材料算规则依据。
|
||||
4. Demo 要演示到什么深度。
|
||||
|
||||
对于每个问题,建议都尽量追问到这三个层次:
|
||||
|
||||
1. 业务目标是什么。
|
||||
2. 验收标准是什么。
|
||||
3. 出现例外时怎么处理。
|
||||
|
||||
---
|
||||
|
||||
## 4. 业务确认问答清单
|
||||
|
||||
## 4.1 业务目标与使用场景
|
||||
|
||||
### Q1 当前系统最核心要解决的业务问题是什么?
|
||||
|
||||
建议提问方式:
|
||||
|
||||
> 如果这次 Demo 只能重点解决 1 到 2 个最关键问题,您最希望它解决什么?
|
||||
|
||||
建议记录答案:
|
||||
|
||||
- 主问题:
|
||||
- 次问题:
|
||||
- 不在本次范围的问题:
|
||||
|
||||
为什么要问:
|
||||
|
||||
- 这个问题决定首页主叙事和演示主线。
|
||||
- 也决定后续是偏“审核”还是偏“生成回填”。
|
||||
|
||||
### Q2 这个系统的主要使用人是谁?
|
||||
|
||||
建议提问方式:
|
||||
|
||||
> 这个系统主要是给注册专员、法规人员、项目经理,还是管理层演示用?是否会有不同角色看到不同内容?
|
||||
|
||||
建议记录答案:
|
||||
|
||||
- 主要用户角色:
|
||||
- 次要用户角色:
|
||||
- 是否区分角色权限:
|
||||
|
||||
为什么要问:
|
||||
|
||||
- 决定页面表述是否偏专业操作型。
|
||||
- 决定飞书通知和责任人配置逻辑。
|
||||
|
||||
### Q3 业务更看重“审核发现问题”还是“自动生成报送材料”?
|
||||
|
||||
建议提问方式:
|
||||
|
||||
> 在您看来,这套系统最有价值的是先帮您发现资料问题,还是直接帮您产出可报送材料?
|
||||
|
||||
建议记录答案:
|
||||
|
||||
- 更偏审核:
|
||||
- 更偏生成:
|
||||
- 两者权重:
|
||||
|
||||
为什么要问:
|
||||
|
||||
- 决定 Demo 讲解时主功能排序。
|
||||
- 决定开发时优先打磨哪个闭环。
|
||||
|
||||
---
|
||||
|
||||
## 4.2 输入资料范围
|
||||
|
||||
### Q4 本次 Demo 是否允许以第 1 章样本作为主演示材料?
|
||||
|
||||
建议提问方式:
|
||||
|
||||
> 目前样例材料主要集中在第 1 章,Demo 是否可以以第 1 章为主要演示对象,同时在规则上覆盖第 2 到第 6 章?
|
||||
|
||||
建议记录答案:
|
||||
|
||||
- 是否允许:
|
||||
- 若不允许,需要补哪些章的样本:
|
||||
|
||||
为什么要问:
|
||||
|
||||
- 决定当前实现是否以“第 1 章内容级演示 + 全章规则级覆盖”为主。
|
||||
|
||||
### Q5 第 2 到第 6 章后续是否会补充真实业务样本?
|
||||
|
||||
建议提问方式:
|
||||
|
||||
> 如果希望 Demo 展示第 2 到第 6 章的抽取、比对和回填效果,后续是否会补充真实样本或脱敏样本?
|
||||
|
||||
建议记录答案:
|
||||
|
||||
- 是否补充:
|
||||
- 预计补充哪些章:
|
||||
- 预计补充时间:
|
||||
|
||||
为什么要问:
|
||||
|
||||
- 决定系统当前只做“结构合规”还是还能做“内容级审核”。
|
||||
|
||||
### Q6 当前样例中的跨产品材料混杂,是刻意测试异常,还是后续会统一资料包?
|
||||
|
||||
建议提问方式:
|
||||
|
||||
> 当前样例里存在不同产品名称的材料混在一起,这种情况是有意作为异常样例,还是后续会再整理成同一产品的一套资料?
|
||||
|
||||
建议记录答案:
|
||||
|
||||
- 属于刻意异常:
|
||||
- 属于样例混杂:
|
||||
- 后续是否会统一:
|
||||
|
||||
为什么要问:
|
||||
|
||||
- 决定一致性冲突是“核心演示点”还是“样本噪声”。
|
||||
|
||||
---
|
||||
|
||||
## 4.3 自动审核与人工复核边界
|
||||
|
||||
### Q7 哪些类型的问题可以由系统直接判定,哪些必须人工复核?
|
||||
|
||||
建议提问方式:
|
||||
|
||||
> 对您来说,哪些问题系统可以直接下结论,哪些问题必须保留给人工复核?
|
||||
|
||||
建议引导业务按下列类型回答:
|
||||
|
||||
1. 文件缺失
|
||||
2. 章节点归类错误
|
||||
3. 字段不一致
|
||||
4. 模板格式不符合
|
||||
5. 历史申报事项解释
|
||||
|
||||
建议记录答案:
|
||||
|
||||
- 可自动判定:
|
||||
- 必须人工复核:
|
||||
- 自动判定后是否仍需人工确认:
|
||||
|
||||
为什么要问:
|
||||
|
||||
- 决定结果页里哪些内容标“通过/不通过”,哪些标“待复核”。
|
||||
|
||||
### Q8 “完全一致”的范围是否适用于所有字段?
|
||||
|
||||
建议提问方式:
|
||||
|
||||
> 我们当前理解是:同一审核范围内,相同字段默认按完全一致处理。请确认是否所有关键字段都按这个标准执行,还是有少数字段允许格式差异但含义一致。
|
||||
|
||||
建议引导业务按字段类别回答:
|
||||
|
||||
1. 产品名称
|
||||
2. 申请人名称
|
||||
3. 规格型号
|
||||
4. 适用范围
|
||||
5. 储存条件
|
||||
6. 样本类型
|
||||
|
||||
建议记录答案:
|
||||
|
||||
- 必须完全一致的字段:
|
||||
- 允许人工判断等价的字段:
|
||||
|
||||
为什么要问:
|
||||
|
||||
- 虽然当前默认口径是完全一致,但和业务再确认一次更稳。
|
||||
|
||||
### Q9 风险评分结果里,“不通过”是否只看高风险,还是还要加总分门槛?
|
||||
|
||||
建议提问方式:
|
||||
|
||||
> 当前理解是“任一高风险即不通过”。除此之外,是否还需要总分门槛,比如中风险积累到一定程度也不通过?
|
||||
|
||||
建议记录答案:
|
||||
|
||||
- 是否仅以高风险阻断:
|
||||
- 是否需要总分门槛:
|
||||
- 是否需要“有条件通过 / 整改后复核”状态:
|
||||
|
||||
为什么要问:
|
||||
|
||||
- 决定风险报告的最终判定逻辑。
|
||||
|
||||
---
|
||||
|
||||
## 4.4 Word 生成与模板管理
|
||||
|
||||
### Q10 业务侧对“可直接报送级版式”的验收标准是什么?
|
||||
|
||||
建议提问方式:
|
||||
|
||||
> 对于导出的 Word,您认为达到“可直接报送”的最低标准是什么?哪些版式元素是绝对不能错的?
|
||||
|
||||
建议引导业务从以下维度回答:
|
||||
|
||||
1. 标题层级
|
||||
2. 段落格式
|
||||
3. 表格样式
|
||||
4. 页眉页脚
|
||||
5. 页码
|
||||
6. 盖章位
|
||||
7. 附件格式
|
||||
|
||||
建议记录答案:
|
||||
|
||||
- 必须严格保留的版式元素:
|
||||
- 可接受微调的版式元素:
|
||||
- 不可接受的典型错误:
|
||||
|
||||
为什么要问:
|
||||
|
||||
- 这是 Word 导出能否被认为“可交付”的关键。
|
||||
|
||||
### Q11 是否需要同时导出 PDF 归档件?
|
||||
|
||||
建议提问方式:
|
||||
|
||||
> 除了 Word 报送稿,是否还需要系统同步导出 PDF 作为归档、流转或内部审核附件?
|
||||
|
||||
建议记录答案:
|
||||
|
||||
- 是否需要 PDF:
|
||||
- Word 和 PDF 哪个是主交付件:
|
||||
|
||||
为什么要问:
|
||||
|
||||
- 决定导出链路和验收文件类型。
|
||||
|
||||
### Q12 新模板进入模板库后,谁来确认它是正式模板?
|
||||
|
||||
建议提问方式:
|
||||
|
||||
> 如果后续由您这边提供新的模板,系统纳入模板库之前,是否需要指定某个角色进行确认、审批或启用?
|
||||
|
||||
建议记录答案:
|
||||
|
||||
- 模板提供方:
|
||||
- 模板审核方:
|
||||
- 模板启用规则:
|
||||
|
||||
为什么要问:
|
||||
|
||||
- 决定后台模板管理页是否需要版本和审批状态。
|
||||
|
||||
### Q13 模板是按什么维度区分的?
|
||||
|
||||
建议提问方式:
|
||||
|
||||
> 模板需要按哪些维度区分,例如注册申报 / 变更 / 延续、不同章节点、不同产品类型,还是不同企业内部模板版本?
|
||||
|
||||
建议记录答案:
|
||||
|
||||
- 按流程类型:
|
||||
- 按章节点:
|
||||
- 按产品类型:
|
||||
- 按企业版本:
|
||||
|
||||
为什么要问:
|
||||
|
||||
- 决定模板库数据结构。
|
||||
|
||||
---
|
||||
|
||||
## 4.5 法规规则与知识库治理
|
||||
|
||||
### Q14 当前法规规则源是否只以现有公告附件包为准?
|
||||
|
||||
建议提问方式:
|
||||
|
||||
> 当前 Demo 是否可以只以现有公告附件包和附件 4 为规则依据,不额外纳入其他外部法规文件?
|
||||
|
||||
建议记录答案:
|
||||
|
||||
- 是否只用现有规则包:
|
||||
- 是否还要补充其他规则源:
|
||||
|
||||
为什么要问:
|
||||
|
||||
- 决定知识库首版范围。
|
||||
|
||||
### Q15 后台知识库更新入口由谁使用?
|
||||
|
||||
建议提问方式:
|
||||
|
||||
> 后台的知识库更新和人工校订功能,预计是只有管理员可用,还是法规/注册业务人员也会参与维护?
|
||||
|
||||
建议记录答案:
|
||||
|
||||
- 仅管理员:
|
||||
- 管理员 + 业务人员:
|
||||
- 是否需要操作留痕:
|
||||
|
||||
为什么要问:
|
||||
|
||||
- 决定后台权限模型和审计范围。
|
||||
|
||||
### Q16 人工校订主要校订哪些内容?
|
||||
|
||||
建议提问方式:
|
||||
|
||||
> 您希望人工校订功能主要用于改哪些内容?是法规切片、字段映射、模板字段、责任人映射,还是审核结论备注?
|
||||
|
||||
建议记录答案:
|
||||
|
||||
- 允许校订的内容:
|
||||
- 不允许业务侧直接改的内容:
|
||||
|
||||
为什么要问:
|
||||
|
||||
- 决定后台管理页具体有哪些编辑入口。
|
||||
|
||||
### Q17 知识库更新后,是否需要重新审核历史项目?
|
||||
|
||||
建议提问方式:
|
||||
|
||||
> 如果法规规则或模板更新了,历史项目是否需要按新规则重新检查,还是仅影响新项目?
|
||||
|
||||
建议记录答案:
|
||||
|
||||
- 仅影响新项目:
|
||||
- 历史项目需要重跑:
|
||||
- 是否保留旧版本结果:
|
||||
|
||||
为什么要问:
|
||||
|
||||
- 决定规则版本管理和历史审计口径。
|
||||
|
||||
---
|
||||
|
||||
## 4.6 飞书接入与责任人通知
|
||||
|
||||
### Q18 飞书群聊机器人里,业务最希望怎么触发任务?
|
||||
|
||||
建议提问方式:
|
||||
|
||||
> 在飞书群聊里,您更希望通过固定命令触发,还是通过菜单、卡片按钮、表单方式选择任务?
|
||||
|
||||
建议记录答案:
|
||||
|
||||
- 命令式:
|
||||
- 卡片式:
|
||||
- 菜单式:
|
||||
- 其他偏好:
|
||||
|
||||
为什么要问:
|
||||
|
||||
- 决定飞书端交互体验复杂度。
|
||||
|
||||
### Q19 飞书里“结果查看”希望看到多详细?
|
||||
|
||||
建议提问方式:
|
||||
|
||||
> 在飞书里,您更希望看到简短摘要,还是希望直接看到缺失项、风险等级、责任人、下载链接这些完整信息?
|
||||
|
||||
建议记录答案:
|
||||
|
||||
- 仅摘要:
|
||||
- 摘要 + 关键问题:
|
||||
- 尽可能完整:
|
||||
|
||||
为什么要问:
|
||||
|
||||
- 决定飞书卡片内容设计。
|
||||
|
||||
### Q20 责任人是如何定义的?
|
||||
|
||||
建议提问方式:
|
||||
|
||||
> 责任人是按章节点、按资料类型、按项目角色,还是按项目具体人员来分配?
|
||||
|
||||
建议记录答案:
|
||||
|
||||
- 按章节点:
|
||||
- 按资料类型:
|
||||
- 按项目角色:
|
||||
- 按具体人员:
|
||||
|
||||
为什么要问:
|
||||
|
||||
- 决定责任人配置表结构。
|
||||
|
||||
### Q21 责任人通知只要 `@人`,还是还要带整改动作?
|
||||
|
||||
建议提问方式:
|
||||
|
||||
> 飞书通知时,您是只希望 `@` 到对应责任人,还是希望同时带上问题摘要、整改建议、资料链接和截止时间?
|
||||
|
||||
建议记录答案:
|
||||
|
||||
- 只需 `@人`:
|
||||
- 需要问题摘要:
|
||||
- 需要整改建议:
|
||||
- 需要截止时间:
|
||||
|
||||
为什么要问:
|
||||
|
||||
- 决定飞书通知卡片结构。
|
||||
|
||||
### Q22 是否需要支持多人负责同一项问题?
|
||||
|
||||
建议提问方式:
|
||||
|
||||
> 同一章节点或同一风险项,是否可能对应多个责任人,例如主责人 + 复核人?
|
||||
|
||||
建议记录答案:
|
||||
|
||||
- 是否支持多人:
|
||||
- 是否区分主责 / 协同 / 复核:
|
||||
|
||||
为什么要问:
|
||||
|
||||
- 决定责任人映射是否是一对一还是一对多。
|
||||
|
||||
---
|
||||
|
||||
## 4.7 审计、追溯与管理要求
|
||||
|
||||
### Q23 业务最关心审计记录里看到什么?
|
||||
|
||||
建议提问方式:
|
||||
|
||||
> 如果后续回看一次审核记录,您最希望一眼看到哪些信息?
|
||||
|
||||
建议引导业务回答:
|
||||
|
||||
1. 审核时间
|
||||
2. 审核人
|
||||
3. 使用的资料范围
|
||||
4. 风险等级
|
||||
5. 不通过原因
|
||||
6. 导出文件
|
||||
7. 飞书通知记录
|
||||
|
||||
建议记录答案:
|
||||
|
||||
- 必看信息:
|
||||
- 次要信息:
|
||||
|
||||
为什么要问:
|
||||
|
||||
- 决定审计列表和详情页优先展示字段。
|
||||
|
||||
### Q24 是否需要区分“系统自动结论”和“人工修改后结论”?
|
||||
|
||||
建议提问方式:
|
||||
|
||||
> 如果系统先给出自动判断,后续又经过人工校订或人工确认,是否需要把这两个结果区分记录?
|
||||
|
||||
建议记录答案:
|
||||
|
||||
- 是否区分:
|
||||
- 是否保留修改原因:
|
||||
- 是否保留修改人:
|
||||
|
||||
为什么要问:
|
||||
|
||||
- 决定审计模型是否需要版本链。
|
||||
|
||||
---
|
||||
|
||||
## 4.8 Demo 演示与验收方式
|
||||
|
||||
### Q25 Demo 现场最希望看到哪条完整链路?
|
||||
|
||||
建议提问方式:
|
||||
|
||||
> 如果现场只演示一条最完整的链路,您最希望看到哪一种?
|
||||
|
||||
建议给业务三个选项帮助回答:
|
||||
|
||||
1. 导入资料 -> 自动检查缺失 -> 输出风险报告
|
||||
2. 导入资料 -> 抽取字段 -> 自动生成 Word
|
||||
3. 飞书群聊触发 -> 查看结果 -> 通知责任人
|
||||
|
||||
建议记录答案:
|
||||
|
||||
- 首选演示链路:
|
||||
- 次选演示链路:
|
||||
|
||||
为什么要问:
|
||||
|
||||
- 决定 Demo 讲解脚本和开发优先级。
|
||||
|
||||
### Q26 本次 Demo 的“通过标准”是什么?
|
||||
|
||||
建议提问方式:
|
||||
|
||||
> 对您来说,这次 Demo 达到什么程度,就可以认为是比较成功的?
|
||||
|
||||
建议引导业务从以下角度回答:
|
||||
|
||||
1. 能看出业务理解到位
|
||||
2. 能自动发现主要问题
|
||||
3. 能自动生成较完整材料
|
||||
4. 能在飞书里顺畅演示
|
||||
5. 能展示后台治理能力
|
||||
|
||||
建议记录答案:
|
||||
|
||||
- 必须达到:
|
||||
- 加分项:
|
||||
- 本次不强求:
|
||||
|
||||
为什么要问:
|
||||
|
||||
- 这是项目收敛范围的总开关。
|
||||
|
||||
---
|
||||
|
||||
## 5. 建议的会议记录模板
|
||||
|
||||
建议你每次和业务沟通时,按下面格式记录:
|
||||
|
||||
```text
|
||||
问题编号:
|
||||
问题主题:
|
||||
业务回答:
|
||||
是否已明确:是 / 否
|
||||
当前默认口径:
|
||||
是否影响需求文档:是 / 否
|
||||
是否影响设计文档:是 / 否
|
||||
需要补充材料:
|
||||
备注:
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 沟通优先级建议
|
||||
|
||||
如果时间有限,建议优先确认以下 8 个问题:
|
||||
|
||||
1. Q4 Demo 是否允许以第 1 章作为主演示材料
|
||||
2. Q6 当前混档样例是否是刻意异常
|
||||
3. Q7 哪些结论可自动判、哪些必须人工复核
|
||||
4. Q10 “可直接报送级版式”的验收标准
|
||||
5. Q12 新模板进入模板库后的确认机制
|
||||
6. Q18 飞书群聊机器人希望如何触发任务
|
||||
7. Q20 责任人如何定义
|
||||
8. Q26 本次 Demo 的通过标准
|
||||
|
||||
---
|
||||
|
||||
## 7. 结论
|
||||
|
||||
这份问答清单的目标,不是把所有问题一次性问完,而是帮助你优先确认真正影响范围和验收的关键信息。
|
||||
|
||||
如果后续拿到新的业务回答,建议同步更新:
|
||||
|
||||
1. `docs/需求分析/0.需求重构总览与待确认事项.md`
|
||||
2. `docs/需求分析/1.V1总需求文档.md`
|
||||
3. 对应模块需求分析文档
|
||||
4. 后续新增的设计文档
|
||||
@@ -3,59 +3,71 @@
|
||||
{% block title %}审计日志详情{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<header class="page-header">
|
||||
<span class="eyebrow">日志详情</span>
|
||||
<section class="page-header">
|
||||
<span class="eyebrow">Audit Snapshot</span>
|
||||
<h1 class="page-title">审计日志 #{{ log.id }}</h1>
|
||||
<p class="page-lead">这里集中展示当前请求的输入、模型输出、知识库引用和工具调用记录。</p>
|
||||
<p style="margin-top: 14px;"><a class="button" href="{% url 'audit:list' %}">返回审计列表</a></p>
|
||||
</header>
|
||||
<p class="page-lead">详情页集中展示当前请求的输入、结构化输出、引用来源、工具调用和原始输出,用来解释这一轮 Agent 执行到底做了什么。</p>
|
||||
<div class="button-row">
|
||||
<a class="button" href="{% url 'audit:list' %}">返回审计列表</a>
|
||||
<a class="button" href="{% url 'platform_ui:command-center' %}">返回工作台大屏</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="stack">
|
||||
<article class="panel">
|
||||
<h2 class="section-title">基础信息</h2>
|
||||
<ul class="meta-list">
|
||||
<li class="meta-badge">场景:{{ log.scenario_name }}</li>
|
||||
<li class="meta-badge">状态:{{ log.get_status_display_text }}</li>
|
||||
<li class="meta-badge">模型:{{ log.model_name }}</li>
|
||||
<li class="meta-badge">耗时:{{ log.latency_ms }} ms</li>
|
||||
</ul>
|
||||
<section class="hero-metrics">
|
||||
<article class="metric-card">
|
||||
<div class="metric-label">场景</div>
|
||||
<div class="metric-value">{{ log.scenario_name }}</div>
|
||||
</article>
|
||||
|
||||
<article class="panel">
|
||||
<h2 class="section-title">用户输入</h2>
|
||||
<div class="detail-item">{{ log.user_input|linebreaksbr }}</div>
|
||||
<article class="metric-card">
|
||||
<div class="metric-label">状态</div>
|
||||
<div class="metric-value">{{ log.get_status_display_text }}</div>
|
||||
</article>
|
||||
|
||||
<article class="panel">
|
||||
<h2 class="section-title">最终回答</h2>
|
||||
<div class="detail-item">{{ log.final_answer|linebreaksbr }}</div>
|
||||
<article class="metric-card">
|
||||
<div class="metric-label">耗时</div>
|
||||
<div class="metric-value">{{ log.latency_ms }} ms</div>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<article class="panel">
|
||||
<h2 class="section-title">结构化输出</h2>
|
||||
<pre class="code-block">{{ log.structured_output }}</pre>
|
||||
</article>
|
||||
|
||||
<article class="panel">
|
||||
<h2 class="section-title">引用来源</h2>
|
||||
<pre class="code-block">{{ log.retrieved_chunks }}</pre>
|
||||
</article>
|
||||
|
||||
<article class="panel">
|
||||
<h2 class="section-title">工具调用</h2>
|
||||
<pre class="code-block">{{ log.tool_calls }}</pre>
|
||||
</article>
|
||||
|
||||
<article class="panel">
|
||||
<h2 class="section-title">原始输出</h2>
|
||||
<pre class="code-block">{{ log.raw_output }}</pre>
|
||||
</article>
|
||||
|
||||
{% if log.error_message %}
|
||||
<section class="layout-two-columns">
|
||||
<div class="stack">
|
||||
<article class="panel">
|
||||
<h2 class="section-title">错误信息</h2>
|
||||
<pre class="code-block">{{ log.error_message }}</pre>
|
||||
<h2 class="section-title">用户输入</h2>
|
||||
<div class="detail-item">{{ log.user_input|linebreaksbr }}</div>
|
||||
</article>
|
||||
{% endif %}
|
||||
|
||||
<article class="panel">
|
||||
<h2 class="section-title">最终回答</h2>
|
||||
<div class="detail-item">{{ log.final_answer|linebreaksbr }}</div>
|
||||
</article>
|
||||
|
||||
<article class="panel">
|
||||
<h2 class="section-title">结构化输出</h2>
|
||||
<pre class="code-block">{{ log.structured_output }}</pre>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="stack">
|
||||
<article class="panel">
|
||||
<h2 class="section-title">引用来源</h2>
|
||||
<pre class="code-block">{{ log.retrieved_chunks }}</pre>
|
||||
</article>
|
||||
|
||||
<article class="panel">
|
||||
<h2 class="section-title">工具调用</h2>
|
||||
<pre class="code-block">{{ log.tool_calls }}</pre>
|
||||
</article>
|
||||
|
||||
<article class="panel">
|
||||
<h2 class="section-title">原始输出</h2>
|
||||
<pre class="code-block">{{ log.raw_output }}</pre>
|
||||
</article>
|
||||
|
||||
{% if log.error_message %}
|
||||
<article class="panel">
|
||||
<h2 class="section-title">错误信息</h2>
|
||||
<pre class="code-block">{{ log.error_message }}</pre>
|
||||
</article>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
@@ -3,48 +3,76 @@
|
||||
{% block title %}审计日志{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<header class="page-header">
|
||||
<span class="eyebrow">执行留痕</span>
|
||||
<h1 class="page-title">审计日志</h1>
|
||||
<p class="page-lead">每次 Agent 执行都会记录模型、检索片段、工具调用和最终结果,方便演示链路可解释性。</p>
|
||||
<section class="page-header">
|
||||
<span class="eyebrow">Audit Trail</span>
|
||||
<h1 class="page-title">审计日志与执行留痕中心</h1>
|
||||
<p class="page-lead">每次 Agent 执行都会保留输入、结构化结果、引用片段、工具调用和最终输出。这个页面用于说明系统为何可追溯、可复核、可解释。</p>
|
||||
{% if selected_scenario_id %}
|
||||
<p style="margin-top: 14px;">
|
||||
<span class="meta-badge">当前筛选场景:{{ selected_scenario_id }}</span>
|
||||
<div class="badge-row">
|
||||
<span class="pill pill-accent">当前筛选场景:{{ selected_scenario_id }}</span>
|
||||
<a class="button" href="{% url 'audit:list' %}">清空筛选</a>
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</header>
|
||||
</section>
|
||||
|
||||
<article class="panel">
|
||||
<table class="kv-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>场景</th>
|
||||
<th>输入摘要</th>
|
||||
<th>状态</th>
|
||||
<th>模型</th>
|
||||
<th>耗时</th>
|
||||
<th>创建时间</th>
|
||||
<th>详情</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for log in logs %}
|
||||
<section class="hero-metrics">
|
||||
<article class="metric-card">
|
||||
<div class="metric-label">日志总数</div>
|
||||
<div class="metric-value">{{ logs|length }}</div>
|
||||
<div class="metric-note">当前页面加载的执行快照数量。</div>
|
||||
</article>
|
||||
<article class="metric-card">
|
||||
<div class="metric-label">最近状态</div>
|
||||
<div class="metric-value">{% if logs %}{{ logs.0.get_status_display_text }}{% else %}暂无{% endif %}</div>
|
||||
<div class="metric-note">默认按时间倒序展示最近一次 Agent 执行。</div>
|
||||
</article>
|
||||
<article class="metric-card">
|
||||
<div class="metric-label">最近场景</div>
|
||||
<div class="metric-value">{% if logs %}{{ logs.0.scenario_name }}{% else %}暂无{% endif %}</div>
|
||||
<div class="metric-note">便于快速定位当前复试演示对应的执行记录。</div>
|
||||
</article>
|
||||
</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>
|
||||
<td>{{ log.id }}</td>
|
||||
<td>{{ log.scenario_name }}</td>
|
||||
<td>{{ log.get_user_input_summary }}</td>
|
||||
<td>{{ log.get_status_display_text }}</td>
|
||||
<td>{{ log.model_name }}</td>
|
||||
<td>{{ log.latency_ms }} ms</td>
|
||||
<td>{{ log.created_at|date:"Y-m-d H:i" }}</td>
|
||||
<td><a class="button" href="{% url 'audit:detail' log.id %}">查看详情</a></td>
|
||||
<th>ID</th>
|
||||
<th>场景</th>
|
||||
<th>输入摘要</th>
|
||||
<th>状态</th>
|
||||
<th>模型</th>
|
||||
<th>耗时</th>
|
||||
<th>创建时间</th>
|
||||
<th>详情</th>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan="8">暂无审计日志,先去执行一次对话吧。</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</article>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for log in logs %}
|
||||
<tr>
|
||||
<td>{{ log.id }}</td>
|
||||
<td>{{ log.scenario_name }}</td>
|
||||
<td>{{ log.get_user_input_summary }}</td>
|
||||
<td>
|
||||
<span class="pill {% if log.status == 'success' %}pill-success{% else %}pill-danger{% endif %}">{{ log.get_status_display_text }}</span>
|
||||
</td>
|
||||
<td>{{ log.model_name }}</td>
|
||||
<td>{{ log.latency_ms }} ms</td>
|
||||
<td>{{ log.created_at|date:"Y-m-d H:i" }}</td>
|
||||
<td><a class="button" href="{% url 'audit:detail' log.id %}">查看详情</a></td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan="8">暂无审计日志,先去执行一次审核工作台任务。</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
@@ -3,349 +3,387 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{% block title %}Universal Agent Demo Framework{% endblock %}</title>
|
||||
<title>{% block title %}注册审核智能体平台{% endblock %}</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #f4f1ea;
|
||||
--surface: #fffdf8;
|
||||
--surface-strong: #fff7ea;
|
||||
--border: #d7c9b0;
|
||||
--text: #2f261d;
|
||||
--muted: #73614f;
|
||||
--accent: #a54c2b;
|
||||
--accent-soft: #f1d2b8;
|
||||
--success: #2b6a4d;
|
||||
--warning: #9a5a00;
|
||||
--danger: #8d2f2f;
|
||||
--shadow: 0 18px 40px rgba(91, 63, 36, 0.10);
|
||||
--bg: #f5f7fa;
|
||||
--surface: #ffffff;
|
||||
--surface-soft: #f8fafc;
|
||||
--border: #d8e0e8;
|
||||
--text: #1f2d3d;
|
||||
--muted: #6a7a8b;
|
||||
--primary: #2f6fec;
|
||||
--primary-soft: #eef4ff;
|
||||
--success: #1f8f5f;
|
||||
--success-soft: #ecfaf3;
|
||||
--warning: #c97a16;
|
||||
--warning-soft: #fff5e8;
|
||||
--danger: #cc4b3e;
|
||||
--danger-soft: #fff1ef;
|
||||
--shadow: 0 8px 24px rgba(31, 45, 61, 0.06);
|
||||
--radius: 14px;
|
||||
--radius-lg: 18px;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: "Microsoft YaHei", "PingFang SC", sans-serif;
|
||||
font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||
color: var(--text);
|
||||
background:
|
||||
radial-gradient(circle at top left, #f7e2c8 0, transparent 30%),
|
||||
linear-gradient(180deg, #f6efe2 0%, #f4f1ea 45%, #efe7da 100%);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
a { color: var(--accent); text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
|
||||
.shell {
|
||||
width: min(1180px, calc(100vw - 32px));
|
||||
margin: 0 auto;
|
||||
padding: 24px 0 40px;
|
||||
background: var(--bg);
|
||||
}
|
||||
a { color: inherit; text-decoration: none; }
|
||||
button, input, select, textarea { font: inherit; }
|
||||
|
||||
.topbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-bottom: 1px solid var(--border);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
.topbar-inner {
|
||||
width: min(1680px, calc(100vw - 40px));
|
||||
margin: 0 auto;
|
||||
min-height: 64px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
align-items: center;
|
||||
margin-bottom: 24px;
|
||||
padding: 18px 20px;
|
||||
border: 1px solid rgba(215, 201, 176, 0.7);
|
||||
background: rgba(255, 253, 248, 0.88);
|
||||
backdrop-filter: blur(12px);
|
||||
border-radius: 22px;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.brand-title {
|
||||
margin: 0;
|
||||
font-size: 1.35rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.brand-note {
|
||||
margin: 6px 0 0;
|
||||
color: var(--muted);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
.brand {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
min-width: 0;
|
||||
}
|
||||
.brand-mark {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 10px;
|
||||
background: var(--primary);
|
||||
color: #fff;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 700;
|
||||
flex: none;
|
||||
}
|
||||
.brand h1 {
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
.brand p {
|
||||
margin: 2px 0 0;
|
||||
color: var(--muted);
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
.topnav {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.topnav a {
|
||||
padding: 9px 12px;
|
||||
border-radius: 10px;
|
||||
color: var(--muted);
|
||||
font-size: 0.92rem;
|
||||
}
|
||||
.topnav a:hover {
|
||||
background: var(--surface-soft);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.nav-link,
|
||||
.button,
|
||||
button {
|
||||
.page {
|
||||
width: min(1680px, calc(100vw - 40px));
|
||||
margin: 24px auto 40px;
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 22px 24px;
|
||||
box-shadow: var(--shadow);
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
.eyebrow {
|
||||
display: inline-flex;
|
||||
width: fit-content;
|
||||
padding: 4px 10px;
|
||||
border-radius: 999px;
|
||||
background: var(--primary-soft);
|
||||
color: var(--primary);
|
||||
font-size: 0.78rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
.page-title {
|
||||
margin: 0;
|
||||
font-size: 1.9rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
.page-lead {
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
.panel {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 18px;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
.section-heading {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.section-title {
|
||||
margin: 0;
|
||||
font-size: 1.08rem;
|
||||
line-height: 1.35;
|
||||
}
|
||||
.section-copy, .muted, .help-text {
|
||||
color: var(--muted);
|
||||
line-height: 1.6;
|
||||
font-size: 0.92rem;
|
||||
}
|
||||
|
||||
.grid-2 {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
.grid-3 {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
.workspace-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 360px minmax(0, 1fr);
|
||||
gap: 16px;
|
||||
align-items: start;
|
||||
}
|
||||
.workspace-grid-wide {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.45fr) minmax(420px, 0.85fr);
|
||||
gap: 16px;
|
||||
align-items: start;
|
||||
}
|
||||
.stack { display: grid; gap: 14px; }
|
||||
|
||||
.metric-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
.metric-card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 16px;
|
||||
}
|
||||
.metric-label {
|
||||
color: var(--muted);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.metric-value {
|
||||
margin-top: 8px;
|
||||
font-size: 1.55rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
.metric-note {
|
||||
margin-top: 6px;
|
||||
color: var(--muted);
|
||||
font-size: 0.86rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.button-row, .badge-row, .meta-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.button, button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
border-radius: 999px;
|
||||
min-height: 38px;
|
||||
padding: 0 14px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--surface);
|
||||
color: var(--text);
|
||||
padding: 10px 16px;
|
||||
font-size: 0.95rem;
|
||||
cursor: pointer;
|
||||
transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
|
||||
}
|
||||
|
||||
.button-primary,
|
||||
button {
|
||||
border-color: var(--accent);
|
||||
background: linear-gradient(135deg, #b9562f, #94452a);
|
||||
color: #fffaf4;
|
||||
box-shadow: 0 10px 22px rgba(165, 76, 43, 0.20);
|
||||
.button-primary, button {
|
||||
background: var(--primary);
|
||||
color: #fff;
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.nav-link:hover,
|
||||
.button:hover,
|
||||
button:hover {
|
||||
text-decoration: none;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 12px 24px rgba(91, 63, 36, 0.12);
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
display: inline-block;
|
||||
margin-bottom: 10px;
|
||||
padding: 6px 12px;
|
||||
border-radius: 999px;
|
||||
background: rgba(241, 210, 184, 0.8);
|
||||
color: var(--accent);
|
||||
font-size: 0.85rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
margin: 0 0 10px;
|
||||
font-size: clamp(2rem, 3vw, 3rem);
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.page-lead {
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
font-size: 1rem;
|
||||
max-width: 720px;
|
||||
}
|
||||
|
||||
.card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.panel,
|
||||
.card {
|
||||
background: rgba(255, 253, 248, 0.94);
|
||||
border: 1px solid rgba(215, 201, 176, 0.85);
|
||||
border-radius: 24px;
|
||||
padding: 20px;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.card h2,
|
||||
.panel h2,
|
||||
.panel h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.meta-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin: 12px 0 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
color: var(--muted);
|
||||
font-size: 0.92rem;
|
||||
}
|
||||
|
||||
.meta-badge {
|
||||
.pill, .meta-badge, .status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 12px;
|
||||
padding: 5px 10px;
|
||||
border-radius: 999px;
|
||||
background: var(--surface-strong);
|
||||
border: 1px solid rgba(215, 201, 176, 0.75);
|
||||
}
|
||||
|
||||
.layout-two-columns {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(320px, 420px) minmax(0, 1fr);
|
||||
gap: 20px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.stack {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
textarea,
|
||||
select,
|
||||
input[type="text"],
|
||||
input[type="file"] {
|
||||
width: 100%;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border);
|
||||
background: #fff;
|
||||
padding: 12px 14px;
|
||||
font: inherit;
|
||||
color: var(--text);
|
||||
background: var(--surface-soft);
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
.pill-accent { color: var(--primary); background: var(--primary-soft); border-color: #d8e5ff; }
|
||||
.pill-success, .status-success { color: var(--success); background: var(--success-soft); border-color: #ccebdc; }
|
||||
.pill-signal { color: var(--warning); background: var(--warning-soft); border-color: #f3dcc0; }
|
||||
.pill-danger, .status-failed { color: var(--danger); background: var(--danger-soft); border-color: #f2d3cf; }
|
||||
|
||||
textarea { min-height: 150px; resize: vertical; }
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.help-text,
|
||||
.muted {
|
||||
color: var(--muted);
|
||||
font-size: 0.92rem;
|
||||
}
|
||||
|
||||
.checkbox-list {
|
||||
.simple-list, .detail-list, .risk-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.checkbox-item {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: flex-start;
|
||||
padding: 10px 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(215, 201, 176, 0.85);
|
||||
}
|
||||
.detail-item, .risk-item {
|
||||
padding: 12px 14px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
background: var(--surface-soft);
|
||||
line-height: 1.6;
|
||||
}
|
||||
.detail-item strong, .risk-item strong { display: block; margin-bottom: 4px; }
|
||||
|
||||
textarea, select, input[type="text"], input[type="file"] {
|
||||
width: 100%;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 6px 12px;
|
||||
border-radius: 999px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 700;
|
||||
background: var(--surface-strong);
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.status-success { color: var(--success); }
|
||||
.status-failed { color: var(--danger); }
|
||||
|
||||
.notice {
|
||||
padding: 14px 16px;
|
||||
border-radius: 18px;
|
||||
border: 1px solid rgba(215, 201, 176, 0.85);
|
||||
background: rgba(255, 247, 234, 0.92);
|
||||
padding: 11px 12px;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.notice-error {
|
||||
border-color: rgba(141, 47, 47, 0.25);
|
||||
background: rgba(255, 238, 238, 0.95);
|
||||
color: var(--danger);
|
||||
textarea { min-height: 140px; resize: vertical; line-height: 1.6; }
|
||||
label { display: block; margin-bottom: 8px; font-weight: 600; }
|
||||
.checkbox-list { display: grid; gap: 8px; }
|
||||
.checkbox-item {
|
||||
display: grid;
|
||||
grid-template-columns: 20px minmax(0, 1fr);
|
||||
gap: 10px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
background: var(--surface-soft);
|
||||
}
|
||||
|
||||
.kv-table {
|
||||
.table-wrap { overflow-x: auto; }
|
||||
.data-table, .kv-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.kv-table th,
|
||||
.kv-table td {
|
||||
.data-table th, .data-table td, .kv-table th, .kv-table td {
|
||||
padding: 12px 10px;
|
||||
border-bottom: 1px solid rgba(215, 201, 176, 0.6);
|
||||
vertical-align: top;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #e9eef3;
|
||||
vertical-align: top;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.kv-table th {
|
||||
width: 150px;
|
||||
.data-table th, .kv-table th {
|
||||
color: var(--muted);
|
||||
font-size: 0.82rem;
|
||||
font-weight: 700;
|
||||
background: var(--surface-soft);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.nowrap { white-space: nowrap; }
|
||||
.cell-min-220 { min-width: 220px; }
|
||||
.cell-min-280 { min-width: 280px; }
|
||||
|
||||
.detail-list {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
.notice {
|
||||
padding: 12px 14px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #d8e5ff;
|
||||
background: var(--primary-soft);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
padding: 14px 16px;
|
||||
border-radius: 18px;
|
||||
background: #fff;
|
||||
border: 1px solid rgba(215, 201, 176, 0.75);
|
||||
.notice-error {
|
||||
border-color: #f2d3cf;
|
||||
background: var(--danger-soft);
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
.detail-item strong {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.code-block {
|
||||
margin: 0;
|
||||
padding: 14px;
|
||||
border-radius: 18px;
|
||||
background: #2f261d;
|
||||
color: #fdf8f1;
|
||||
overflow: auto;
|
||||
padding: 12px;
|
||||
border-radius: 10px;
|
||||
background: #1f2937;
|
||||
color: #f9fafb;
|
||||
overflow-x: auto;
|
||||
font-size: 0.84rem;
|
||||
line-height: 1.55;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
font-size: 0.92rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
margin: 0 0 12px;
|
||||
font-size: 1.1rem;
|
||||
.link-card {
|
||||
display: block;
|
||||
padding: 16px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
background: var(--surface);
|
||||
}
|
||||
.link-card h3 { margin: 0 0 6px; font-size: 1rem; }
|
||||
.link-card p { margin: 0; color: var(--muted); font-size: 0.9rem; line-height: 1.55; }
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.layout-two-columns { grid-template-columns: 1fr; }
|
||||
.topbar { flex-direction: column; align-items: flex-start; }
|
||||
.nav-links { justify-content: flex-start; }
|
||||
@media (max-width: 1024px) {
|
||||
.metric-grid, .grid-2, .grid-3, .workspace-grid, .workspace-grid-wide { grid-template-columns: 1fr; }
|
||||
.topbar-inner { flex-direction: column; align-items: flex-start; padding: 12px 0; }
|
||||
.topnav { justify-content: flex-start; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="shell">
|
||||
<header class="topbar">
|
||||
<div>
|
||||
<p class="brand-title">Universal Agent Demo Framework</p>
|
||||
<p class="brand-note">面向复试演示的可配置 AI Agent 单体系统</p>
|
||||
<header class="topbar">
|
||||
<div class="topbar-inner">
|
||||
<div class="brand">
|
||||
<div class="brand-mark">RA</div>
|
||||
<div>
|
||||
<h1>注册审核智能体平台</h1>
|
||||
<p>极简后台原型</p>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="nav-links">
|
||||
<a class="nav-link" href="{% url 'scenarios:index' %}">场景首页</a>
|
||||
<a class="nav-link" href="{% url 'documents:list' %}">文档中心</a>
|
||||
<a class="nav-link" href="{% url 'audit:list' %}">审计日志</a>
|
||||
<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 '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>
|
||||
</nav>
|
||||
</header>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="page">
|
||||
{% if messages %}
|
||||
<section class="stack" style="margin-bottom: 18px;">
|
||||
<div class="stack">
|
||||
{% for message in messages %}
|
||||
<div class="notice{% if message.tags == 'error' %} notice-error{% endif %}">{{ message }}</div>
|
||||
{% endfor %}
|
||||
</section>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ scenario.name|default:"Agent 对话" }}{% endblock %}
|
||||
{% block title %}{{ scenario.name|default:"Agent 审核工作台" }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if error %}
|
||||
@@ -8,23 +8,25 @@
|
||||
{% endif %}
|
||||
|
||||
{% if scenario %}
|
||||
<header class="page-header">
|
||||
<span class="eyebrow">Agent 对话</span>
|
||||
<section class="page-header">
|
||||
<span class="eyebrow">Workspace</span>
|
||||
<h1 class="page-title">{{ scenario.name }}</h1>
|
||||
<p class="page-lead">{{ scenario.description }}</p>
|
||||
<ul class="meta-list">
|
||||
<li class="meta-badge">角色:{{ scenario.agent.role }}</li>
|
||||
<li class="meta-badge">目标:{{ scenario.agent.goal }}</li>
|
||||
<li class="meta-badge">已入库文档:{{ document_count }}</li>
|
||||
<li class="meta-badge">输出类型:{{ scenario.output.type }}</li>
|
||||
</ul>
|
||||
</header>
|
||||
<p class="page-lead">左侧输入问题和选择文档,右侧查看执行结果。</p>
|
||||
<div class="badge-row">
|
||||
<span class="pill pill-accent">已入库文档:{{ document_count }}</span>
|
||||
<span class="pill">输出:{{ scenario.output.type }}</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="layout-two-columns">
|
||||
<section class="workspace-grid">
|
||||
<div class="stack">
|
||||
<article class="panel">
|
||||
<h2 class="section-title">提问面板</h2>
|
||||
<p class="muted">可以直接提问,也可以勾选部分已入库文档作为当前上下文范围。</p>
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<h2 class="section-title">任务输入与资料范围</h2>
|
||||
<p class="section-copy">左侧突出受控输入:先描述审核目标,再限定本轮使用的文档范围。</p>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post" class="stack">
|
||||
{% csrf_token %}
|
||||
<div>
|
||||
@@ -36,7 +38,7 @@
|
||||
</div>
|
||||
<div>
|
||||
{{ form.document_ids.label_tag }}
|
||||
<p class="help-text">不勾选时默认检索当前场景全部已入库文档。</p>
|
||||
<p class="help-text">不勾选时默认使用全部已入库文档。</p>
|
||||
<div class="checkbox-list">
|
||||
{% for checkbox in form.document_ids %}
|
||||
<label class="checkbox-item">
|
||||
@@ -51,34 +53,25 @@
|
||||
<p class="notice notice-error">{{ form.document_ids.errors|join:" " }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
<div class="button-row">
|
||||
<button type="submit">提交问题并执行 Agent</button>
|
||||
</div>
|
||||
</form>
|
||||
</article>
|
||||
|
||||
<article class="panel">
|
||||
<h2 class="section-title">执行说明</h2>
|
||||
<h2 class="section-title">快捷示例</h2>
|
||||
<ul class="detail-list">
|
||||
<li class="detail-item">
|
||||
<strong>1. 场景配置</strong>
|
||||
系统会先读取当前 YAML 场景配置,确定角色、目标、工具和输出结构。
|
||||
</li>
|
||||
<li class="detail-item">
|
||||
<strong>2. RAG 与工具</strong>
|
||||
如果场景启用了知识库检索,系统会根据你的问题召回相关片段,并执行声明式工具。
|
||||
</li>
|
||||
<li class="detail-item">
|
||||
<strong>3. 结构化结果</strong>
|
||||
Agent Core 会优先解析 JSON 输出,解析失败时回退为稳定的展示结构。
|
||||
</li>
|
||||
<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>
|
||||
<h2 class="section-title">结果</h2>
|
||||
{% if result %}
|
||||
<ul class="meta-list">
|
||||
<li class="meta-badge">模型:{{ result.model_name }}</li>
|
||||
@@ -89,41 +82,18 @@
|
||||
<strong>主回答</strong>
|
||||
<div>{{ result.answer|linebreaksbr }}</div>
|
||||
</div>
|
||||
{% if audit_log %}
|
||||
<p style="margin-top: 14px;">
|
||||
<a class="button" href="{% url 'audit:detail' audit_log.id %}">查看本次审计日志</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="notice">提交问题后,这里会展示 Agent 的主回答、模型信息和执行状态。</div>
|
||||
<div class="notice">提交任务后,这里会展示 Agent 的执行状态、主回答和过程摘要。</div>
|
||||
{% endif %}
|
||||
</article>
|
||||
|
||||
{% if result %}
|
||||
<article class="panel">
|
||||
<h2 class="section-title">结构化结果</h2>
|
||||
<table class="kv-table">
|
||||
<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>
|
||||
</article>
|
||||
|
||||
<article class="panel">
|
||||
<h2 class="section-title">引用片段</h2>
|
||||
<h2 class="section-title">证据引用与工具调用</h2>
|
||||
<p class="muted" style="margin-bottom: 14px;">引用片段与工具调用用于支撑结果可解释性。</p>
|
||||
{% if result.references %}
|
||||
<ul class="detail-list">
|
||||
<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>
|
||||
@@ -132,13 +102,11 @@
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<div class="notice">当前回答没有引用知识库片段。</div>
|
||||
<div class="notice" style="margin-bottom: 16px;">当前回答没有引用知识库片段。</div>
|
||||
{% endif %}
|
||||
</article>
|
||||
|
||||
<article class="panel">
|
||||
<h2 class="section-title">工具调用</h2>
|
||||
{% if result.tool_calls %}
|
||||
<h3>工具调用</h3>
|
||||
<ul class="detail-list">
|
||||
{% for tool_call in result.tool_calls %}
|
||||
<li class="detail-item">
|
||||
@@ -164,6 +132,50 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<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>
|
||||
<ul class="detail-list">
|
||||
<li class="detail-item">可查看引用片段、工具调用和本次审计日志。</li>
|
||||
</ul>
|
||||
{% if audit_log %}
|
||||
<div class="button-row" style="margin-top: 16px;">
|
||||
<a class="button" href="{% url 'audit:detail' audit_log.id %}">查看本次审计日志</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,54 +1,108 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}文档中心{% endblock %}
|
||||
{% block title %}文件中心{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<header class="page-header">
|
||||
<span class="eyebrow">知识库资料</span>
|
||||
<h1 class="page-title">文档中心</h1>
|
||||
<p class="page-lead">上传题目材料后,可以在这里管理文件状态,并手动触发入库。</p>
|
||||
<p style="margin-top: 14px;"><a class="button button-primary" href="{% url 'documents:upload' %}">上传新文件</a></p>
|
||||
</header>
|
||||
<section class="page-header">
|
||||
<span class="eyebrow">Documents</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>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<article class="panel">
|
||||
<table class="kv-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>文件名</th>
|
||||
<th>场景</th>
|
||||
<th>类型</th>
|
||||
<th>大小</th>
|
||||
<th>状态</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for document in documents %}
|
||||
<section class="metric-grid">
|
||||
<article class="metric-card">
|
||||
<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>
|
||||
</article>
|
||||
<article class="metric-card">
|
||||
<div class="metric-label">待入库</div>
|
||||
<div class="metric-value">{{ status_counts.uploaded }}</div>
|
||||
</article>
|
||||
<article class="metric-card">
|
||||
<div class="metric-label">失败</div>
|
||||
<div class="metric-value">{{ status_counts.failed }}</div>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<h2 class="section-title">异常提示</h2>
|
||||
<p class="section-copy">只保留需要处理的异常。</p>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="risk-list">
|
||||
{% for item in exception_items %}
|
||||
<li class="risk-item">
|
||||
<strong>{{ item.title }}</strong>
|
||||
<div class="muted">{{ item.detail }}</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</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>
|
||||
<td>{{ document.original_name }}</td>
|
||||
<td>{{ document.scenario_id }}</td>
|
||||
<td>{{ document.file_type }}</td>
|
||||
<td>{{ document.size }}</td>
|
||||
<td>{{ document.get_status_display_text }}</td>
|
||||
<td>
|
||||
{% if document.status != "indexed" %}
|
||||
<form action="{% url 'documents:index' document.id %}" method="post">
|
||||
{% csrf_token %}
|
||||
<button type="submit">执行入库</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<span class="status status-success">已可用于检索</span>
|
||||
{% endif %}
|
||||
{% if document.error_message %}
|
||||
<pre class="code-block" style="margin-top: 10px;">{{ document.error_message }}</pre>
|
||||
{% endif %}
|
||||
<p class="muted" style="margin-top: 10px;">上传时间:{{ document.created_at|date:"Y-m-d H:i" }}</p>
|
||||
</td>
|
||||
<th>文件名</th>
|
||||
<th>场景</th>
|
||||
<th>类型</th>
|
||||
<th>大小</th>
|
||||
<th>状态</th>
|
||||
<th>操作与备注</th>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan="6">暂无文件,请先上传题目材料。</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</article>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for document in documents %}
|
||||
<tr>
|
||||
<td>{{ document.original_name }}</td>
|
||||
<td>{{ document.scenario_id }}</td>
|
||||
<td>{{ document.file_type }}</td>
|
||||
<td>{{ document.size }}</td>
|
||||
<td>
|
||||
<span class="pill {% if document.status == 'indexed' %}pill-success{% elif document.status == 'failed' %}pill-danger{% else %}pill-signal{% endif %}">
|
||||
{{ document.get_status_display_text }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="stack">
|
||||
{% if document.status != "indexed" %}
|
||||
<form action="{% url 'documents:index' document.id %}" method="post">
|
||||
{% csrf_token %}
|
||||
<button type="submit">执行入库</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<span class="status status-success">已可参与检索与审核</span>
|
||||
{% endif %}
|
||||
{% if document.error_message %}
|
||||
<pre class="code-block">{{ document.error_message }}</pre>
|
||||
{% endif %}
|
||||
<span class="muted">上传时间:{{ document.created_at|date:"Y-m-d H:i" }}</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="6">暂无文件,请先导入申报资料或法规原文。</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,38 +1,63 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}上传文件{% endblock %}
|
||||
{% block title %}导入资料{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<header class="page-header">
|
||||
<span class="eyebrow">文件上传</span>
|
||||
<h1 class="page-title">上传题目材料或知识库文档</h1>
|
||||
<p class="page-lead">支持 `.txt`、`.md`、`.pdf` 和 `.docx`。上传后可以在文档中心手动执行入库。</p>
|
||||
</header>
|
||||
<section class="page-header">
|
||||
<span class="eyebrow">Batch Intake</span>
|
||||
<h1 class="page-title">导入申报资料与法规依据文件</h1>
|
||||
<p class="page-lead">上传页采用“引导式导入”思路,强调业务资料与法规依据资料的边界、目录类文件的优先级,以及上传后进入解析和切片流程的下一步动作。</p>
|
||||
</section>
|
||||
|
||||
<article class="panel" style="max-width: 760px;">
|
||||
<form method="post" enctype="multipart/form-data" class="stack">
|
||||
{% csrf_token %}
|
||||
<div>
|
||||
{{ form.scenario_id.label_tag }}
|
||||
{{ form.scenario_id }}
|
||||
{% if form.scenario_id.errors %}
|
||||
<p class="notice notice-error">{{ form.scenario_id.errors|join:" " }}</p>
|
||||
<section class="layout-two-columns">
|
||||
<article class="panel">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<h2 class="section-title">资料导入向导</h2>
|
||||
<p class="section-copy">当前支持 `.txt`、`.md`、`.pdf` 和 `.docx`。上传成功后即可回到文件中心执行解析与入库。</p>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post" enctype="multipart/form-data" class="stack">
|
||||
{% csrf_token %}
|
||||
<div>
|
||||
{{ form.scenario_id.label_tag }}
|
||||
{{ form.scenario_id }}
|
||||
{% if form.scenario_id.errors %}
|
||||
<p class="notice notice-error">{{ form.scenario_id.errors|join:" " }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
{{ form.file.label_tag }}
|
||||
{{ form.file }}
|
||||
{% if form.file.errors %}
|
||||
<p class="notice notice-error">{{ form.file.errors|join:" " }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if form.errors %}
|
||||
<div class="notice notice-error">{{ form.errors }}</div>
|
||||
{% endif %}
|
||||
<div class="button-row">
|
||||
<button type="submit">确认导入</button>
|
||||
<a class="button" href="{% url 'documents:list' %}">返回文件中心</a>
|
||||
</div>
|
||||
</form>
|
||||
</article>
|
||||
|
||||
<article class="panel">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<h2 class="section-title">上传前检查清单</h2>
|
||||
<p class="section-copy">用业务语言告诉演示对象,平台并不是“随便上传,随便搜”。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{{ form.file.label_tag }}
|
||||
{{ form.file }}
|
||||
{% if form.file.errors %}
|
||||
<p class="notice notice-error">{{ form.file.errors|join:" " }}</p>
|
||||
{% endif %}
|
||||
<ul class="detail-list">
|
||||
{% for item in upload_checks %}
|
||||
<li class="detail-item">{{ item }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<div class="notice" style="margin-top: 16px;">
|
||||
建议先导入监管信息目录、申请表、说明书、产品列表和公告附件包,再进入完整性检查场景。
|
||||
</div>
|
||||
{% if form.errors %}
|
||||
<div class="notice notice-error">{{ form.errors }}</div>
|
||||
{% endif %}
|
||||
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
|
||||
<button type="submit">上传文件</button>
|
||||
<a class="button" href="{% url 'documents:list' %}">返回文件列表</a>
|
||||
</div>
|
||||
</form>
|
||||
</article>
|
||||
</article>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
58
templates/platform_ui/command_center.html
Normal file
58
templates/platform_ui/command_center.html
Normal file
@@ -0,0 +1,58 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}工作台大屏{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="page-header">
|
||||
<span class="eyebrow">Workbench</span>
|
||||
<h1 class="page-title">工作台</h1>
|
||||
<p class="page-lead">用简洁视图展示当前批次状态、主要问题和下一步动作。</p>
|
||||
</section>
|
||||
|
||||
<section class="metric-grid">
|
||||
<article class="metric-card">
|
||||
<div class="metric-label">当前阶段</div>
|
||||
<div class="metric-value">{{ batch.stage }}</div>
|
||||
</article>
|
||||
<article class="metric-card">
|
||||
<div class="metric-label">整体完成度</div>
|
||||
<div class="metric-value">{{ batch.completion }}</div>
|
||||
</article>
|
||||
<article class="metric-card">
|
||||
<div class="metric-label">高优先级问题</div>
|
||||
<div class="metric-value">03</div>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section class="grid-3">
|
||||
<article class="panel">
|
||||
<h2 class="section-title">流程</h2>
|
||||
<div class="stack" style="margin-top: 14px;">
|
||||
{% for step in workflow_overview %}
|
||||
<div class="detail-item"><strong>{{ step.title }}</strong><div class="muted">{{ step.detail }}</div></div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="panel">
|
||||
<h2 class="section-title">执行记录</h2>
|
||||
<div class="stack" style="margin-top: 14px;">
|
||||
{% for step in workflow_steps %}
|
||||
<div class="detail-item"><strong>{{ step.time }} {{ step.title }}</strong><div class="muted">{{ step.detail }}</div></div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="panel">
|
||||
<h2 class="section-title">待处理问题</h2>
|
||||
<ul class="risk-list">
|
||||
{% for risk in risk_board %}
|
||||
<li class="risk-item">
|
||||
<strong>{{ risk.title }}</strong>
|
||||
<div class="muted">{{ risk.action|default:risk.detail }}</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</article>
|
||||
</section>
|
||||
{% endblock %}
|
||||
198
templates/platform_ui/knowledge_base.html
Normal file
198
templates/platform_ui/knowledge_base.html
Normal file
@@ -0,0 +1,198 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}知识库配置{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="page-header">
|
||||
<span class="eyebrow">Knowledge Base</span>
|
||||
<h1 class="page-title">知识库配置</h1>
|
||||
<p class="page-lead">支持传统增删改查:新增知识源、编辑规则项、删除无效配置、筛选和搜索当前知识内容。</p>
|
||||
</section>
|
||||
|
||||
<section class="metric-grid">
|
||||
{% for item in knowledge_stats %}
|
||||
<article class="metric-card">
|
||||
<div class="metric-label">{{ item.label }}</div>
|
||||
<div class="metric-value">{{ item.value }}</div>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<h2 class="section-title">操作栏</h2>
|
||||
<p class="section-copy">把知识库做成传统后台,而不是只读展示页。</p>
|
||||
</div>
|
||||
<div class="button-row">
|
||||
<a class="button button-primary" href="#">新增知识源</a>
|
||||
<a class="button" href="#">新增规则项</a>
|
||||
<a class="button" href="#">批量删除</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid-3">
|
||||
<div>
|
||||
<label for="knowledge-search">搜索</label>
|
||||
<input id="knowledge-search" type="text" value="体外诊断试剂" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="knowledge-type">知识类型</label>
|
||||
<select id="knowledge-type">
|
||||
{% for filter in knowledge_filters %}
|
||||
<option{% if filter.active %} selected{% endif %}>{{ filter.label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="knowledge-status">状态</label>
|
||||
<select id="knowledge-status">
|
||||
<option selected>全部状态</option>
|
||||
<option>已生效</option>
|
||||
<option>待人工校订</option>
|
||||
<option>已入库</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="workspace-grid-wide">
|
||||
<article class="panel">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<h2 class="section-title">知识源管理</h2>
|
||||
<p class="section-copy">传统 CRUD 列表,直接在表格里做查看、编辑、删除。</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>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for source in knowledge_sources %}
|
||||
<tr>
|
||||
<td class="nowrap">{{ source.code }}</td>
|
||||
<td><div class="cell-min-280">{{ source.name }}</div></td>
|
||||
<td class="nowrap">{{ source.type }}</td>
|
||||
<td class="nowrap">{{ source.scope }}</td>
|
||||
<td>
|
||||
<span class="pill {% if source.status == '已生效' or source.status == '已入库' %}pill-success{% else %}pill-signal{% endif %}">
|
||||
{{ source.status }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="nowrap">{{ source.owner }}</td>
|
||||
<td class="nowrap">{{ source.updated_at }}</td>
|
||||
<td>
|
||||
<div class="button-row">
|
||||
<a class="button" href="#">查看</a>
|
||||
<a class="button" href="#">编辑</a>
|
||||
<a class="button" href="#">删除</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<div class="stack">
|
||||
<article class="panel">
|
||||
<h2 class="section-title">{{ knowledge_form.title }}</h2>
|
||||
<div class="stack" style="margin-top: 14px;">
|
||||
{% for field in knowledge_form.fields %}
|
||||
<div>
|
||||
<label>{{ field.label }}</label>
|
||||
<input type="text" value="{{ field.value }}" />
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="button-row">
|
||||
<a class="button button-primary" href="#">保存知识源</a>
|
||||
<a class="button" href="#">重置</a>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
</section>
|
||||
|
||||
<section class="grid-2">
|
||||
<article 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>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in rule_tree %}
|
||||
<tr>
|
||||
<td class="nowrap">{{ item.code }}</td>
|
||||
<td class="nowrap">{{ item.chapter }}</td>
|
||||
<td><div class="cell-min-220">{{ item.item }}</div></td>
|
||||
<td><div class="cell-min-220">{{ item.field }}</div></td>
|
||||
<td>
|
||||
<span class="pill {% if item.status == '启用' %}pill-success{% else %}pill-signal{% endif %}">
|
||||
{{ item.status }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="button-row">
|
||||
<a class="button" href="#">查看</a>
|
||||
<a class="button" href="#">编辑</a>
|
||||
<a class="button" href="#">删除</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="panel">
|
||||
<h2 class="section-title">{{ rule_form.title }}</h2>
|
||||
<div class="stack" style="margin-top: 14px;">
|
||||
{% for field in rule_form.fields %}
|
||||
<div>
|
||||
<label>{{ field.label }}</label>
|
||||
<input type="text" value="{{ field.value }}" />
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="button-row">
|
||||
<a class="button button-primary" href="#">保存规则项</a>
|
||||
<a class="button" href="#">删除规则项</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
{% endblock %}
|
||||
67
templates/platform_ui/mcp_center.html
Normal file
67
templates/platform_ui/mcp_center.html
Normal file
@@ -0,0 +1,67 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}MCP 中心{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="page-header">
|
||||
<span class="eyebrow">MCP Connectors</span>
|
||||
<h1 class="page-title">外部 MCP 能力导入与协同接入中心</h1>
|
||||
<p class="page-lead">MCP 页面说明平台不是封闭工作台,它可以导入法规源、飞书通知、模板服务和企业数据源,但仍服务于注册审核这一条主线。</p>
|
||||
</section>
|
||||
|
||||
<section class="card-grid">
|
||||
{% for connector in mcp_connectors %}
|
||||
<article class="panel" style="padding: 20px;">
|
||||
<div class="badge-row">
|
||||
<span class="pill">{{ connector.kind }}</span>
|
||||
<span class="pill {% if connector.status == '已连接' %}pill-success{% elif connector.status == '待验证' %}pill-signal{% else %}pill-danger{% endif %}">{{ connector.status }}</span>
|
||||
</div>
|
||||
<h3 style="margin-top: 16px;">{{ connector.name }}</h3>
|
||||
<p class="muted">鉴权方式:{{ connector.auth }}</p>
|
||||
<p>最近同步:{{ connector.sync }}</p>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</section>
|
||||
|
||||
<section class="layout-two-columns">
|
||||
<article class="panel">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<h2 class="section-title">导入向导</h2>
|
||||
<p class="section-copy">原型用一个简洁流程展示外部能力如何进入平台。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flow-strip">
|
||||
<article class="flow-node">
|
||||
<div class="flow-index">01</div>
|
||||
<h3>选择能力类型</h3>
|
||||
<p>法规源、协同工具、模板服务或企业主数据。</p>
|
||||
</article>
|
||||
<article class="flow-node">
|
||||
<div class="flow-index">02</div>
|
||||
<h3>完成鉴权</h3>
|
||||
<p>支持 API Key、App Token、文件轮询和 MCP Bridge。</p>
|
||||
</article>
|
||||
<article class="flow-node">
|
||||
<div class="flow-index">03</div>
|
||||
<h3>定义输入输出契约</h3>
|
||||
<p>明确它向 Agent 暴露什么能力、返回什么结构。</p>
|
||||
</article>
|
||||
<article class="flow-node">
|
||||
<div class="flow-index">04</div>
|
||||
<h3>纳入审核编排</h3>
|
||||
<p>由 Skill 或 Agent 任务按需调用,不在页面层散落实现。</p>
|
||||
</article>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="panel">
|
||||
<h2 class="section-title">本题重点接入建议</h2>
|
||||
<ul class="detail-list">
|
||||
<li class="detail-item"><strong>飞书任务通知</strong> 把高风险项和责任人通知闭环到群聊机器人或会话入口。</li>
|
||||
<li class="detail-item"><strong>Word 模板服务</strong> 用于报送版式输出与模板回填能力。</li>
|
||||
<li class="detail-item"><strong>法规规则源</strong> 为后续规则包更新和版本治理提供自动化入口。</li>
|
||||
</ul>
|
||||
</article>
|
||||
</section>
|
||||
{% endblock %}
|
||||
69
templates/platform_ui/skill_studio.html
Normal file
69
templates/platform_ui/skill_studio.html
Normal file
@@ -0,0 +1,69 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Skill Studio{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="page-header">
|
||||
<span class="eyebrow">Skill Studio</span>
|
||||
<h1 class="page-title">Skill 编辑、编排与运行预览</h1>
|
||||
<p class="page-lead">Skill 页面用于解释 Agent 行为为何可控、可维护、可复用。通过角色说明、工具绑定、输入输出约束和测试运行预览,展示平台的治理能力。</p>
|
||||
</section>
|
||||
|
||||
<section class="workspace-grid">
|
||||
<article class="panel">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<h2 class="section-title">Skill 列表</h2>
|
||||
<p class="section-copy">围绕注册审核主流程沉淀能力单元。</p>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="detail-list">
|
||||
{% for skill in skills %}
|
||||
<li class="detail-item">
|
||||
<span class="pill {% if skill.status == '已发布' %}pill-success{% elif skill.status == '发布中' %}pill-accent{% else %}pill-signal{% endif %}">{{ skill.status }}</span>
|
||||
<strong>{{ skill.name }}</strong>
|
||||
<div class="muted">触发场景:{{ skill.trigger }}</div>
|
||||
<div class="muted">工具链:{{ skill.tools }}</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<article class="panel">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<h2 class="section-title">Skill 编辑区</h2>
|
||||
<p class="section-copy">展示角色、约束、工具和输出契约如何被治理。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stack">
|
||||
<div class="detail-item">
|
||||
<strong>角色说明</strong>
|
||||
当前 Skill 负责对照 NMPA 注册申报资料要求执行完整性检查,优先输出命中项、缺失项、风险等级和建议动作。
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<strong>工具绑定</strong>
|
||||
规则树匹配、RAG 引用、字段池比对、风险映射、审计写入。
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<strong>输入输出约束</strong>
|
||||
输入限定资料范围、任务类型与批次;输出必须包含结构化结果、证据引用和人工复核提示。
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="panel">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<h2 class="section-title">运行预览</h2>
|
||||
<p class="section-copy">通过最近一次测试运行说明 Skill 的稳定性。</p>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="detail-list">
|
||||
<li class="detail-item"><strong>最近测试结果</strong> 命中 3 个规则项,识别 1 个缺失项,生成 2 条建议动作。</li>
|
||||
<li class="detail-item"><strong>命中工具</strong> completeness_rule_match、rag_lookup、risk_mapper</li>
|
||||
<li class="detail-item"><strong>失败原因样例</strong> 当法规模板未配置时,页面会提示“法规规则未配置,无法执行完整性检查”。</li>
|
||||
</ul>
|
||||
</article>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -1,53 +1,82 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}场景首页{% endblock %}
|
||||
{% block title %}任务总览{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<header class="page-header">
|
||||
<span class="eyebrow">场景总览</span>
|
||||
<h1 class="page-title">用同一套底座快速切换不同业务 Agent</h1>
|
||||
<p class="page-lead">当前首页直接读取 YAML 场景配置。你可以从这里进入对话、上传资料,再用审计日志验证整条执行链路。</p>
|
||||
</header>
|
||||
<section class="page-header">
|
||||
<span class="eyebrow">Overview</span>
|
||||
<h1 class="page-title">批次总览</h1>
|
||||
<p class="page-lead">从这里直接进入知识库、文件中心、审核工作台和审计页。保留必要信息,不堆大段说明。</p>
|
||||
</section>
|
||||
|
||||
{% if scenario_issues %}
|
||||
<section class="panel" style="margin-bottom: 20px;">
|
||||
<h2 class="section-title">配置异常</h2>
|
||||
<p class="muted">以下 YAML 场景文件存在问题,系统已自动跳过,不会影响其它合法场景展示。</p>
|
||||
<ul class="detail-list">
|
||||
{% for issue in scenario_issues %}
|
||||
<li class="detail-item">
|
||||
<strong>{{ issue.file_name }}</strong>
|
||||
<div>{{ issue.message }}</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
<section class="card-grid">
|
||||
{% for scenario in scenarios %}
|
||||
<article class="card">
|
||||
<h2>{{ scenario.name }}</h2>
|
||||
<p>{{ scenario.description }}</p>
|
||||
<ul class="meta-list">
|
||||
<li class="meta-badge">场景 ID:{{ scenario.id }}</li>
|
||||
<li class="meta-badge">输出:{{ scenario.output.type }}</li>
|
||||
<li class="meta-badge">RAG:{% if scenario.rag.enabled %}已启用{% else %}未启用{% endif %}</li>
|
||||
<li class="meta-badge">工具数:{{ scenario.tool_count }}</li>
|
||||
</ul>
|
||||
<p class="muted" style="margin-top: 14px;">适用题型:
|
||||
{% if scenario.applicable_questions %}
|
||||
{{ scenario.applicable_questions|join:"、" }}
|
||||
{% else %}
|
||||
暂未配置
|
||||
{% endif %}
|
||||
</p>
|
||||
<p style="margin-top: 16px;">
|
||||
<a class="button button-primary" href="{% url 'chat:index' scenario.id %}">进入对话</a>
|
||||
</p>
|
||||
<section class="metric-grid">
|
||||
{% for metric in hero_metrics %}
|
||||
<article class="metric-card">
|
||||
<div class="metric-label">{{ metric.label }}</div>
|
||||
<div class="metric-value">{{ metric.value }}</div>
|
||||
</article>
|
||||
{% empty %}
|
||||
<div class="notice">暂无可用场景,请检查 `configs/` 目录和 YAML 配置内容。</div>
|
||||
{% endfor %}
|
||||
</section>
|
||||
|
||||
<section class="grid-2">
|
||||
<a class="link-card" href="{% url 'platform_ui:knowledge-base' %}">
|
||||
<h3>知识库配置</h3>
|
||||
<p>查看规则树、知识源和切片策略。</p>
|
||||
</a>
|
||||
<a class="link-card" href="{% url 'documents:list' %}">
|
||||
<h3>文件中心</h3>
|
||||
<p>上传资料、执行入库、查看状态。</p>
|
||||
</a>
|
||||
<a class="link-card" href="{% url 'chat:index' 'document_review' %}">
|
||||
<h3>审核工作台</h3>
|
||||
<p>输入问题、选择文档、查看结果。</p>
|
||||
</a>
|
||||
<a class="link-card" href="{% url 'audit:list' %}">
|
||||
<h3>审计日志</h3>
|
||||
<p>查看每次执行的输入、输出和引用。</p>
|
||||
</a>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<h2 class="section-title">已配置审核场景</h2>
|
||||
<p class="section-copy">保留现有场景列表,直接进入使用。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if scenario_issues %}
|
||||
<div class="stack" style="margin-bottom: 18px;">
|
||||
<div class="muted">配置异常</div>
|
||||
{% for issue in scenario_issues %}
|
||||
<article class="notice notice-error"><strong>{{ issue.file_name }}</strong>:{{ issue.message }}</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="grid-2">
|
||||
{% for scenario in scenarios %}
|
||||
<article class="panel">
|
||||
<div class="badge-row">
|
||||
<span class="pill">{{ scenario.id }}</span>
|
||||
<span class="pill {% if scenario.rag.enabled %}pill-success{% else %}pill-signal{% endif %}">RAG {% if scenario.rag.enabled %}开启{% else %}关闭{% endif %}</span>
|
||||
</div>
|
||||
<h3 style="margin: 14px 0 8px;">{{ scenario.name }}</h3>
|
||||
<p>{{ scenario.description }}</p>
|
||||
<p class="muted" style="margin-top: 10px;">适用题型:
|
||||
{% if scenario.applicable_questions %}
|
||||
{{ scenario.applicable_questions|join:"、" }}
|
||||
{% else %}
|
||||
暂未配置
|
||||
{% endif %}
|
||||
</p>
|
||||
<div class="button-row" style="margin-top: 16px;">
|
||||
<a class="button button-primary" href="{% url 'chat:index' scenario.id %}">进入审核工作台</a>
|
||||
</div>
|
||||
</article>
|
||||
{% empty %}
|
||||
<div class="notice">暂无可用场景,请检查 `configs/` 目录和 YAML 配置内容。</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user