Files
GraphRAGAgent/docs/frontend_design_specification-v1.0.md
plf b02d3378fc GraphRAG Studio — initial commit: multimodal RAG system with KG visualization
Full-stack application for document-to-knowledge-graph pipeline:
- Backend: FastAPI + LangGraph ReAct agent + DeepSeek + MinerU parsing
- Frontend: React 19 + Vite + D3.js + shadcn/ui
- Pipeline: MinerU parsing → LangExtract entity extraction → KG building

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-07 17:30:04 +08:00

50 KiB
Raw Blame History

GraphRAG Studio — 前端 Web 系统设计规范 v1.0

基于 docs/backend_service_specification-v1.0.md 接口规范 前端架构:原生 HTML + CSS + JS + D3.js v7SPA零构建依赖 更新日期2026-03-05


目录


一、总体架构

1.1 技术选型

组件 选择 理由
应用类型 SPA单页应用 5 页无缝切换,无刷新体验
路由 Hash 路由(原生 JS 无需构建工具,#/dashboard #/documents
框架 原生 HTML + CSS + JS 与现有 index.html 一致,零构建依赖,直接在浏览器运行
图形渲染 D3.js v7CDN 复用现有 KG 可视化逻辑(graphrag_pipeline/static/index.html
Markdown 渲染 marked.js v9CDN Chat 页 AI 答案 Markdown 渲染
API 通信 Fetch API 原生支持,封装统一错误处理
图标 Unicode / SVG 内联 零依赖(无需图标库 CDN

1.2 路由设计

hash 路由 → DOM 区域显示/隐藏

#/dashboard   → 显示 <section id="page-dashboard">
#/documents   → 显示 <section id="page-documents">
#/graph       → 显示 <section id="page-graph">   + 初始化 D3
#/chat        → 显示 <section id="page-chat">
#/search      → 显示 <section id="page-search">
/             → 重定向到 #/dashboard

URL 参数传递hash query

#/graph?doc_id=abc12345      → KG Explorer 按文档筛选
#/graph?node=tech_graphrag_0 → KG Explorer 聚焦节点
#/chat?q=What+is+GraphRAG   → Chat 预填问题

1.3 全局状态管理

// app.js 中维护的全局状态(内存)
const AppState = {
  currentPage: 'dashboard',
  kg: {
    nodes: [],          // 全量节点(加载后缓存)
    edges: [],          // 全量边(加载后缓存)
    loaded: false,
  },
  documents: [],        // 文档列表缓存
  activeJobs: {},       // job_id → polling timer
  chatHistory: [],      // 当前会话消息历史
  health: null,         // 最近一次 health 响应
};

1.4 API 客户端(api.js

const API = {
  BASE: 'http://localhost:8000/api/v1',

  async get(path, params = {}) {
    const url = new URL(this.BASE + path);
    Object.entries(params).forEach(([k, v]) => v != null && url.searchParams.set(k, v));
    const res = await fetch(url);
    const json = await res.json();
    if (json.code !== 0) throw new APIError(json.code, json.msg);
    return json.data;
  },

  async post(path, body = {}) {
    const res = await fetch(this.BASE + path, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(body),
    });
    const json = await res.json();
    if (json.code !== 0) throw new APIError(json.code, json.msg);
    return json.data;
  },

  async postForm(path, formData) {
    const res = await fetch(this.BASE + path, { method: 'POST', body: formData });
    const json = await res.json();
    if (json.code !== 0) throw new APIError(json.code, json.msg);
    return json.data;
  },

  async delete(path) { ... },

  // 轮询直到条件满足
  async poll(path, params = {}, interval = 3000, until = (d) => d.status === 'done') {
    return new Promise((resolve, reject) => {
      const timer = setInterval(async () => {
        try {
          const data = await this.get(path, params);
          if (until(data)) { clearInterval(timer); resolve(data); }
          if (data.status === 'failed') { clearInterval(timer); reject(new Error(data.error)); }
        } catch (e) { clearInterval(timer); reject(e); }
      }, interval);
    });
  }
};

1.5 文件结构

graphrag_pipeline/static/
├── index.html               # 保留(旧 Flask KG 可视化,向后兼容)
└── app/
    ├── index.html           # SPA 主入口GraphRAG Studio
    ├── css/
    │   ├── variables.css    # CSS 变量(颜色 / 间距 / 字体)
    │   ├── base.css         # Reset + 通用组件btn, badge, card, modal...
    │   └── layout.css       # Sidebar + Header + Footer 布局 + 响应式
    └── js/
        ├── app.js           # 路由器 + 页面切换 + 全局状态
        ├── api.js           # Fetch 封装baseURL, 统一错误处理)
        ├── components.js    # Toast / Modal / Progress / Skeleton
        └── pages/
            ├── dashboard.js
            ├── documents.js
            ├── graph.js     # D3 力导向图(基于现有 index.html 重构)
            ├── chat.js
            └── search.js

二、设计语言与风格系统

2.1 调色板(基于现有 index.html 扩展)

:root {
  /* ── 背景层级 ── */
  --bg-base:     #0f1117;   /* 页面底色 */
  --bg-s1:       #161b22;   /* sidebar / header / card surface */
  --bg-s2:       #21262d;   /* hover state / input bg / tag bg */
  --bg-s3:       #1c2128;   /* tooltip / popover / code bg */

  /* ── 边框 ── */
  --border:      #30363d;
  --border-muted:#21262d;

  /* ── 文字 ── */
  --text-1:      #f0f6fc;   /* 主文字 */
  --text-2:      #c9d1d9;   /* 正文 */
  --text-3:      #8b949e;   /* 辅助/label */
  --text-4:      #484f58;   /* placeholder / 极弱 */

  /* ── 强调色 ── */
  --blue:        #58a6ff;   /* 链接 / 激活 / 聚焦 / 进度条 */
  --green:       #3fb950;   /* 成功 / indexed 状态 */
  --green-btn:   #238636;   /* 主操作按钮背景 */
  --green-hover: #2ea043;   /* 主按钮 hover */
  --red:         #f85149;   /* 错误 / 危险 / failed */
  --yellow:      #d29922;   /* 警告 / indexing */
  --purple:      #8957e5;   /* 紫色强调 */

  /* ── 实体类型颜色(与 D3 图谱一一对应)── */
  --type-tech:   #58a6ff;   /* TECHNOLOGY  — 蓝 */
  --type-concept:#bc8cff;   /* CONCEPT     — 紫 */
  --type-person: #3fb950;   /* PERSON      — 绿 */
  --type-org:    #ff7b72;   /* ORGANIZATION— 红 */
  --type-loc:    #ffa657;   /* LOCATION    — 橙 */

  /* ── 字体 ── */
  --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --font-mono: 'SFMono-Regular', Consolas, 'Liberation Mono', monospace;

  /* ── 圆角 ── */
  --r-sm: 4px;
  --r-md: 6px;
  --r-lg: 8px;
  --r-xl: 12px;

  /* ── 阴影 ── */
  --shadow-sm: 0 1px 3px rgba(0,0,0,0.4);
  --shadow-md: 0 4px 16px rgba(0,0,0,0.5);
  --shadow-lg: 0 8px 32px rgba(0,0,0,0.6);

  /* ── 过渡 ── */
  --transition: 150ms ease;
}

2.2 按钮规范4 变体)

变体 背景 边框 文字 用途
.btn-primary --green-btn --green-btn #fff 主操作Upload, Send, Index
.btn-secondary --bg-s2 --border --text-2 次要操作Cancel, Filter
.btn-ghost transparent none --text-3 内联操作(图标按钮)
.btn-danger --bg-s2 --border --red 危险操作Delete
.btn {
  padding: 6px 14px;
  border-radius: var(--r-md);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  transition: all var(--transition);
  display: inline-flex; align-items: center; gap: 6px;
}
.btn-primary { background: var(--green-btn); border: 1px solid var(--green-btn); color: #fff; }
.btn-primary:hover { background: var(--green-hover); }
.btn-sm { padding: 4px 10px; font-size: 12px; }

2.3 Status Badge 规范

/* 通用 badge */
.badge {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 8px;
  border-radius: 20px;
  font-size: 11px; font-weight: 600;
}

/* 状态 badge */
.badge-indexed   { background: #1a3a22; color: var(--green); }
.badge-indexing  { background: #2d2a16; color: var(--yellow); }
.badge-uploaded  { background: var(--bg-s3); color: var(--text-3); }
.badge-failed    { background: #3b1a1a; color: var(--red); }

/* 实体类型 badge */
.badge-TECHNOLOGY   { background: #162032; color: var(--type-tech); }
.badge-CONCEPT      { background: #1e1632; color: var(--type-concept); }
.badge-PERSON       { background: #132318; color: var(--type-person); }
.badge-ORGANIZATION { background: #2e1a1a; color: var(--type-org); }
.badge-LOCATION     { background: #2a1e10; color: var(--type-loc); }

/* 活跃点(动画) */
.badge-indexing::before {
  content: ''; width: 6px; height: 6px; border-radius: 50%;
  background: var(--yellow);
  animation: pulse 1.5s infinite;
}
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.3; } }

2.4 字体层级

层级 size weight color 用途
text-h1 20px 600 --text-1 页面大标题
text-h2 16px 600 --text-1 区块标题
text-h3 13px 600 uppercase --text-3 面板子标题
text-body 14px 400 --text-2 正文
text-sm 12px 400 --text-3 辅助信息
text-badge 11px 600 标签/徽章
text-mono 13px 400 --text-2 代码/路径/工具输出

三、整体布局

3.1 CSS Grid 骨架

┌──────────────────────────────────────────────────────────────┐
│  HEADER  (56px, position: sticky, top: 0, z-index: 100)     │
│  [≡] GraphRAG Studio    [🔍 Search entities...]    [● API]  │
├─────────┬────────────────────────────────────────────────────┤
│         │                                                    │
│ SIDEBAR │         MAIN CONTENT AREA                        │
│ (220px) │         (overflow-y: auto)                       │
│ fixed   │                                                    │
│  ──────  │         — 各页面 <section> 区域 —               │
│  Home   │                                                    │
│  Docs   │                                                    │
│  Graph  │                                                    │
│  Chat   │                                                    │
│  Search │                                                    │
│  ──────  │                                                    │
│  System │                                                    │
│         │                                                    │
├─────────┴────────────────────────────────────────────────────┤
│  STATUS BAR  (32px)    [job 进度]        [v1.0.0] [● ok]   │
└──────────────────────────────────────────────────────────────┘
.app {
  display: grid;
  grid-template-areas: "header header" "sidebar main" "footer footer";
  grid-template-columns: var(--sidebar-w, 220px) 1fr;
  grid-template-rows: 56px 1fr 32px;
  height: 100vh;
  overflow: hidden;
}
header  { grid-area: header; }
.sidebar{ grid-area: sidebar; overflow-y: auto; }
main    { grid-area: main;   overflow-y: auto; }
footer  { grid-area: footer; }

3.2 Sidebar 导航结构

┌─────────────────────────┐
│  G  GraphRAG Studio     │  ← Logo 区16px font, weight 700
├─────────────────────────┤
│  ◈  Dashboard           │  ← 激活状态: bg rgba(88,166,255,0.1), left border 2px #58a6ff
│  ▤  Documents     [  5] │  ← 右侧数量 badge文档总数
│  ◉  KG Explorer         │
│  ◇  Chat         [ 50]  │  ← 查询历史数
│  ⊕  Search              │
├─────────────────────────┤
│  ☰  System              │
└─────────────────────────┘
.nav-item {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 16px;
  border-radius: var(--r-md);
  font-size: 14px; color: var(--text-3);
  cursor: pointer; transition: all var(--transition);
  border-left: 2px solid transparent;
  margin: 1px 8px;
}
.nav-item:hover { background: var(--bg-s2); color: var(--text-2); }
.nav-item.active {
  background: rgba(88,166,255,0.1);
  color: var(--blue);
  border-left-color: var(--blue);
}
.nav-badge {
  margin-left: auto;
  background: var(--bg-s2);
  color: var(--text-3);
  font-size: 11px; font-weight: 600;
  padding: 1px 6px;
  border-radius: 10px;
}

3.3 Header 内容规范

┌──────────────────────────────────────────────────────────────┐
│ [≡]  GraphRAG Studio  │  [🔍 Search entities...        ]  │  [● healthy]  API: 8000 │
└──────────────────────────────────────────────────────────────┘
区域 内容 说明
[≡] 折叠按钮 + Logo 文字 点击折叠/展开 sidebar220px ↔ 72px
全局搜索框 输入后跳转 #/search?q={input},宽度 max 400px
Health 指示器 + API 地址 绿点=healthy, 红点=error

全局搜索框触发逻辑:

  • 输入 3+ 字符 → 实时调用 GET /api/v1/search/entities?q=...&limit=5
  • 下拉展示最多 5 条搜索建议entity name + type badge
  • 按 Enter → 跳转 #/search?q={input}
  • 点击建议项 → 跳转 #/graph?node={node_id}

3.4 Status Bar 内容规范

[Indexing paper.pdf... Stage: Extracting entities 2/4   ████████░░ 65%]    v1.0.0  ● healthy
└── 有 active job 时显示                                                    └── 常驻

四、页面清单与详细设计


Page 1 — Dashboard (#/dashboard)

目的: 系统全局概览,快速导航到各功能,最近活动监控。

4.1.1 布局

┌──────────────────────────────────────────────────────────────┐
│  Overview                               [+ Upload & Index]  │
├──────────┬──────────┬──────────┬────────────────────────────┤
│    40    │   780    │    5     │     50                     │
│  Nodes   │  Edges   │  Docs    │   Queries                  │
│  KG 节点  │  KG 边   │ 文档总数  │  问答次数                  │
├──────────┴──────────┴──────────┴────────────────────────────┤
│  System Health                                               │
│  MinerU venv   ● ok      LangExtract venv  ● ok            │
│  DeepSeek API  ● ok      Storage          ● ok            │
├──────────────────────────────────────────────────────────────┤
│  Recent Documents                              [View All →] │
│  ──────────────────────────────────────────────────────────  │
│  paper.pdf      PDF   4p    ● indexed    2026-03-05  [KG]  │
│  report.docx    DOCX  12p   ● indexing   [███░░░ 65%] [✕]  │
│  slides.pptx    PPTX  24p   ● uploaded   ─           [▶]   │
├──────────────────────────────────────────────────────────────┤
│  Quick Actions                                               │
│  [◉ Explore KG]  [◇ Start Chat]  [⊕ Search]  [⚡ Demo]    │
└──────────────────────────────────────────────────────────────┘

4.1.2 指标卡设计

.metrics-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
  padding: 20px;
}
.metric-card {
  background: var(--bg-s1);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 20px;
  text-align: center;
}
.metric-value { font-size: 32px; font-weight: 700; color: var(--text-1); }
.metric-label { font-size: 12px; color: var(--text-3); margin-top: 4px; }
/* 悬停时用颜色区分不同指标 */
.metric-card:nth-child(1) .metric-value { color: var(--blue); }    /* Nodes */
.metric-card:nth-child(2) .metric-value { color: var(--purple); }  /* Edges */
.metric-card:nth-child(3) .metric-value { color: var(--green); }   /* Docs */
.metric-card:nth-child(4) .metric-value { color: var(--yellow); }  /* Queries */

4.1.3 API 调用时机

调用 时机 接口
加载指标卡 页面初始化 GET /api/v1/system/stats
加载 Health 页面初始化 GET /api/v1/health
加载最近文档 页面初始化 GET /api/v1/documents?page=1&page_size=5
轮询刷新 每 10 秒 GET /api/v1/system/stats + GET /api/v1/health
行内启动 Index 点击 [▶] POST /api/v1/index/start
行内 Index 进度 启动后每 3s GET /api/v1/index/status/{job_id}

4.1.4 交互逻辑

  • [+ Upload & Index] → 打开 Upload Modal同 Documents 页上传区),完成后刷新列表
  • [KG] → 跳转 #/graph?doc_id={doc_id}
  • [▶] → 行内启动 indexing进度条替换操作按钮轮询进度
  • [✕] → 取消正在运行的 indexing job
  • [Explore KG]#/graph
  • [Start Chat]#/chat
  • [Search]#/search
  • [Demo] → 调用 GET /api/v1/system/demo,将数据存入 AppState.kg,跳转 #/graph

Page 2 — Document Manager (#/documents)

目的: 文件上传、列表管理、触发/监控索引任务、查看索引结果。

4.2.1 布局

┌──────────────────────────────────────────────────────────────┐
│  Documents (5)                             [+ Upload Files] │
├──────────────────────────────────────────────────────────────┤
│  ┌──────────────────────────────────────────────────────┐  │
│  │         ⬆                                            │  │
│  │    Drag & Drop files here                            │  │
│  │    PDF · DOCX · DOC · PPTX · PPT · PNG · JPG · HTML │  │
│  │    Max 200MB per file                                │  │
│  │                  [Browse Files]                      │  │
│  └──────────────────────────────────────────────────────┘  │
├──────────────────────────────────────────────────────────────┤
│  [All ▾]  [Status: All ▾]  [🔍 Filter docs...]             │
├──────────────────────────────────────────────────────────────┤
│  Filename            Format  Pages  Status       Date  Act  │
│ ────────────────────────────────────────────────────────    │
│  paper.pdf           PDF      4    ●indexed    03-05  [◉][🗑]│
│                                                             │
│  report.docx         DOCX    12    ●indexed    03-04  [◉][🗑]│
│  ▼ 40 nodes · 780 edges · 4p · 45 ext · 42.1s            │
│    Types: TECHNOLOGY(4) CONCEPT(36)  [View KG] [Show Ext]  │
│                                                             │
│  processing.pptx     PPTX    24    ●indexing        [✕]   │
│  Stage: Extracting entities page 2/4...                    │
│  ████████████░░░░░░░░░░  45%  (18s)                        │
│                                                             │
│  image.png           PNG      1    ●uploaded   03-03  [▶][🗑]│
│                                                             │
│  failed.pdf          PDF      -    ●failed     03-02  [⟳][🗑]│
│  Error: MinerU failed: timeout after 600s                  │
└──────────────────────────────────────────────────────────────┘

4.2.2 上传区交互

拖拽行为:
  dragenter → 上传区边框变蓝 (#58a6ff),背景 rgba(88,166,255,0.05)
  dragleave → 恢复默认
  drop → 提取 event.dataTransfer.files验证后开始上传

客户端预校验drop / browse 后立即执行):
  1. 扩展名检查(对照 ALLOWED_EXTENSIONS 列表)
  2. 文件大小 ≤ 200MB
  → 校验失败: Toast 错误 + 文件名标红(不上传)
  → 校验通过: 开始上传流程

多文件串行处理:
  files[] → 逐一执行 Upload → IndexStart → Poll 流程
  同时上传时显示队列状态

4.2.3 上传 + 索引完整流程

Step 1: POST /api/v1/documents/upload (multipart/form-data)
        ↓ 返回 doc_id
        行项目添加到列表status: "uploaded"

Step 2: 询问确认 "Start indexing now? [Yes] [Later]"
        → Yes: 继续 Step 3
        → Later: 保持 "uploaded" 状态,显示 [▶] 按钮

Step 3: POST /api/v1/index/start { doc_id }
        ↓ 返回 job_id
        status → "indexing",显示进度条

Step 4: 轮询 GET /api/v1/index/status/{job_id}(每 3s
        progress.parsed_pages / total_pages → 进度百分比
        stage → 行内文字说明

Step 5: status=done:
        GET /api/v1/index/result/{job_id} → 获取 stats
        更新 status → "indexed"
        展开结果摘要行(节点数/边数/耗时)
        Toast: "✅ paper.pdf indexed: 40 nodes, 780 edges"
        AppState.kg.loaded = false (触发 KG Explorer 重新加载)

4.2.4 索引结果展开行

▼ [展开]
  40 nodes · 780 edges · 4 pages · 45 extractions · 42.1s
  TECHNOLOGY(4)  CONCEPT(36)
  [◉ View in KG]  [≡ Show Extractions]

[Show Extractions] 展开面板:
  text           type         alignment     page
  ────────────────────────────────────────────
  GraphRAG       TECHNOLOGY   match_exact   0
  knowledge...   CONCEPT      match_exact   0
  MinerU         TECHNOLOGY   match_exact   1
  ...(滚动,最多显示 50 条)

Page 3 — KG Explorer (#/graph)

目的: 全屏交互式知识图谱可视化,节点筛选与详情查看。

4.3.1 布局(三栏)

┌─────────────┬───────────────────────────────────┬─────────────┐
│ FILTER      │    D3 FORCE-DIRECTED GRAPH         │ DETAIL      │
│ (280px)     │    (flex:1)                        │ (300px)     │
│             │    ← 点击节点后出现 →               │             │
│ ─ Docs ─── │                                    │ [× Close]   │
│ [All Docs ▾]│     ●GraphRAG ─────── ●LLMs       │             │
│             │    / │  \                         │ ┄ GraphRAG  │
│ ─ Types ── │   ●  ●   ●                        │   TECHNOLOGY│
│ ☑ TECH  (4) │                                    │ ──────────  │
│ ☑ CONC (36) │        [+ ][ -][⊡]               │ Page:     0 │
│ ☑ PERS  (0) │        [🔍 Search node...]         │ Conf: exact │
│ ☑ ORG   (0) │                                    │ Degree:  39 │
│ ☑ LOC   (0) │                                    │ Central: 1.0│
│             │                                    │ ──────────  │
│ ─ Confidence│                                    │ Neighbors   │
│ ☑ exact     │  [图例]                             │ (39 total)  │
│ ☑ greater   │  ●TECH ●CONC ●PERS ●ORG ●LOC     │ ●knowledge  │
│ ☑ lesser    │                                    │ ●LLMs       │
│ □ fuzzy     │                                    │ ●MinerU     │
│             │                                    │ [All 39 →]  │
│ ─ Export ── │                                    │             │
│ [📷 PNG]    │                                    │ [💬 Ask AI] │
│ [⬇ JSON]   │                                    │             │
└─────────────┴───────────────────────────────────┴─────────────┘

4.3.2 D3 力导向图规范

节点视觉映射:

属性 映射规则
颜色 entity type → 5 色(--type-tech/concept/person/org/loc
半径 r = Math.max(4, Math.log(degree + 1) * 4)
描边 正常: 1.5px same-color opacity 0.6hover: 2.5px white
透明度 正常: 0.9非聚焦highlight 时): 0.1

边视觉映射:

属性 规则
颜色 #30363d
透明度 正常 0.25;高亮节点相关边 0.8
宽度 1px

Force 参数:

d3.forceSimulation(nodes)
  .force('link',   d3.forceLink(edges).id(d => d.id).distance(60).strength(0.3))
  .force('charge', d3.forceManyBody().strength(-120))
  .force('center', d3.forceCenter(width / 2, height / 2))
  .force('collide', d3.forceCollide().radius(d => d.r + 4))
  .alphaDecay(0.02)

交互事件:

node.mouseover → 显示 Tooltipname + type + page + conf + degree
node.click     → 右侧 Detail Panel 展开
                  高亮该节点r 增大 1.5x
                  相连边 opacity 0.8,其余节点 opacity 0.1
canvas.click   → 取消选中Detail Panel 收起,恢复所有透明度
node.drag      → pin 到固定位置fx/fy 设置)
zoom           → d3.zoom().scaleExtent([0.1, 8])

工具栏功能:

[+]        → transform.k * 1.3 (zoom in)
[-]        → transform.k / 1.3 (zoom out)
[⊡ Fit]   → fitToView():计算节点范围,自动缩放和平移
[🔍]       → search input: GET /api/v1/search/entities?q=...
              找到节点 → 闪烁高亮 + 平移居中
[📷 PNG]  → html2canvas 或 SVG → PNG download
[⬇ JSON] → GET /api/v1/kg/export → 触发文件下载

4.3.3 URL 参数处理

// graph.js 初始化时解析
const params = new URLSearchParams(window.location.hash.split('?')[1]);
const docFilter = params.get('doc_id');   // 按文档筛选
const nodeHighlight = params.get('node'); // 聚焦节点

if (docFilter) {
  // 勾选对应 doc 的 checkbox其余节点淡化
}
if (nodeHighlight) {
  // 找到对应节点,高亮 + Detail Panel 展开 + 平移居中
}

4.3.4 API 调用时机

调用 时机 接口
加载全量节点 页面初始化(优先读缓存) GET /api/v1/kg/nodes?page_size=200
加载全量边 页面初始化 GET /api/v1/kg/edges?page_size=500
节点详情 点击节点 GET /api/v1/kg/nodes/{node_id}
邻居列表 点击节点 GET /api/v1/kg/nodes/{node_id}/neighbors?hops=1
工具栏搜索 输入 2+ 字符500ms debounce GET /api/v1/search/entities?q=...

性能说明: 当前 KG 为 40 节点 + 780 边D3 可流畅渲染。若节点超过 500启用 Canvas 渲染模式D3 WebGL fallback


Page 4 — QA Chat (#/chat)

目的: 多轮 KG 问答,可视化 ReAct 推理过程Cited Node 跳转联动。

4.4.1 布局(双栏)

┌──────────────────┬────────────────────────────────────────────┐
│ HISTORY  (240px) │  CHAT AREA                                 │
│                  │                                            │
│ [+ New Chat]     │  ── Welcome ──────────────────────────── ─│
│                  │  KG Assistant                              │
│ ─ Today ──────  │  Ask me anything about the knowledge       │
│ What is GraphRAG │  graph. Try one of the suggestions below. │
│ How does MinerU  │  [💡 Give me an overview of the KG]       │
│                  │  [💡 List all TECHNOLOGY entities]        │
│ ─ Yesterday ─── │  [💡 How does GraphRAG relate to LLMs?]   │
│ List all tech... │                                            │
│                  │  ─── 2026-03-05 10:30 ────────────────── │
│                  │                                            │
│                  │  YOU                              10:30   │
│                  │  What is GraphRAG and how does it          │
│                  │  relate to knowledge graphs?               │
│                  │                                            │
│                  │  KG ASSISTANT                     10:30   │
│                  │  GraphRAG is a knowledge graph-            │
│                  │  enhanced RAG system developed by          │
│                  │  Microsoft Research...                     │
│                  │                                            │
│                  │  ▶ Tool Calls (2 steps)  [展开]           │
│                  │                                            │
│                  │  Cited: [GraphRAG] [knowledge graphs]      │
│                  │          [LLMs] [retrieval-augmented...]  │
│                  │                   ↑ 点击跳转 KG Explorer  │
│                  │  ⏱ 8.4s                                   │
│                  ├────────────────────────────────────────────┤
│                  │  [Ask about the knowledge graph...  ][▶]  │
└──────────────────┴────────────────────────────────────────────┘

4.4.2 消息气泡设计

/* 用户消息(右对齐) */
.msg-user {
  align-self: flex-end;
  background: var(--blue);
  color: #fff;
  border-radius: var(--r-lg) var(--r-lg) var(--r-sm) var(--r-lg);
  padding: 10px 14px;
  max-width: 70%;
  font-size: 14px;
}

/* AI 消息(左对齐) */
.msg-assistant {
  align-self: flex-start;
  background: var(--bg-s1);
  border: 1px solid var(--border);
  border-radius: var(--r-sm) var(--r-lg) var(--r-lg) var(--r-lg);
  padding: 12px 16px;
  max-width: 85%;
}

/* 消息内 Markdown 渲染 */
.msg-assistant .answer-text {
  font-size: 14px; line-height: 1.7; color: var(--text-2);
  /* 使用 marked.js 渲染 */
}

4.4.3 Tool Call 展开面板

▶ Tool Calls (2 steps)    [展开 ▾ / 收起 ▲]

── Step 1: search_entities ─────────────────────────────
Input:
  { "query": "GraphRAG" }
Output:
  Found 1 entity(ies) matching 'GraphRAG':
    [TECHNOLOGY] "GraphRAG" (confidence=match_exact, page=0, id=tech_graphrag_0)

── Step 2: get_neighbors ────────────────────────────────
Input:
  { "entity_name": "GraphRAG", "hops": 1 }
Output:
  Neighbors of 'GraphRAG' [TECHNOLOGY] within 1 hop(s):
  Hop 1 — 39 related entities:
    [CONCEPT] knowledge graphs
    [TECHNOLOGY] LLMs
    ...
.tool-calls-panel {
  background: var(--bg-s3);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  margin: 8px 0;
  font-size: 12px;
  font-family: var(--font-mono);
  color: var(--text-3);
  overflow: hidden;
}
.tool-call-step {
  padding: 10px 14px;
  border-bottom: 1px solid var(--border-muted);
}
.tool-call-name { color: var(--yellow); font-weight: 600; }
.tool-call-section { color: var(--text-4); margin-top: 4px; }

4.4.4 Cited Nodes 设计

Cited: [◉ GraphRAG] [◉ knowledge graphs] [◉ LLMs] ...
           ↑ 点击 → window.location.hash = '#/graph?node=' + node_id
           ↑ hover → 小 Tooltip 显示节点 type
.cited-node-chip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 3px 8px;
  border-radius: 12px;
  border: 1px solid var(--border);
  background: var(--bg-s2);
  color: var(--blue);
  font-size: 12px;
  cursor: pointer;
  transition: all var(--transition);
}
.cited-node-chip:hover {
  background: rgba(88,166,255,0.15);
  border-color: var(--blue);
}

4.4.5 Thinking 动画

<div class="thinking-indicator">
  <span></span><span></span><span></span>
</div>
.thinking-indicator span {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--text-3);
  display: inline-block;
  animation: thinking 1.4s infinite;
}
.thinking-indicator span:nth-child(2) { animation-delay: 0.2s; }
.thinking-indicator span:nth-child(3) { animation-delay: 0.4s; }
@keyframes thinking { 0%,80%,100%{transform:scale(0.7)}40%{transform:scale(1)} }

4.4.6 API 调用时机

调用 时机 接口
发送问题 点击 Send / Enter POST /api/v1/query
加载历史列表 进入 Chat 页 GET /api/v1/query/history?page_size=20
Cited Node 悬停 mouseover 时 GET /api/v1/kg/nodes/{node_id}(可缓存)

多轮对话 history 维护(前端):

// 每次发送携带完整历史
const payload = {
  question: inputText,
  history: AppState.chatHistory.flatMap(msg => [
    { role: 'human', content: msg.question },
    { role: 'ai', content: msg.answer }
  ])
};
const result = await API.post('/query', payload);
AppState.chatHistory.push(result);

目的: 多模式知识图谱搜索(实体关键词 / 路径 / 子图)。

4.5.1 布局

┌──────────────────────────────────────────────────────────────┐
│  Search Knowledge Graph                                      │
│  ┌──────────────────────────────────┐ [Type: All ▾] [Search]│
│  │  🔍 Enter entity name...         │                       │
│  └──────────────────────────────────┘                       │
├──────────────────────────────────────────────────────────────┤
│  [Entity Search ▌]  [Path Search]  [Graph Search]           │
├──────────────────────────┬───────────────────────────────────┤
│ Results (2)              │  Preview Graph                   │
│ ─────────────────        │  ──────────────                  │
│ ┌────────────────────┐  │                                   │
│ │ GraphRAG           │  │    ●GraphRAG                      │
│ │ TECHNOLOGY    pg.0 │  │   ╱│╲                            │
│ │ Degree: 39 · exact │  │  ● ● ●                           │
│ │ [View KG] [Chat]   │  │  ↑ D3 mini-graph                 │
│ ├────────────────────┤  │  (当前选中节点的1-hop邻居子图)      │
│ │ GraphRAG pipeline  │  │                                   │
│ │ CONCEPT       pg.1 │  │                                   │
│ │ Degree: 39 · exact │  │                                   │
│ │ [View KG] [Chat]   │  │                                   │
│ └────────────────────┘  │                                   │
└──────────────────────────┴───────────────────────────────────┘
From: [🔍 GraphRAG...        ▾]   To: [🔍 LLMs...          ▾]   Hops: [3▾]  [Find Path]

─── Results: 1 path found (length 1) ───
  GraphRAG ──CO_OCCURS_IN──→ LLMs

─── Visualization ───
  ●GraphRAG ───────────────── ●LLMs
  (节点颜色 = type边标注 relation)

节点选择器设计:

点击 [🔍] → 展开搜索下拉
输入关键词 → 实时调用 GET /api/v1/search/entities?q=...&limit=10
选择节点 → 记录 node_id显示名称
[retrieval        ]  [☑ Include Neighbors]  [Search]

Found 3 matching nodes · 87 subgraph edges
─────────────────────────────────────
[D3 子图可视化 - 全宽,仅展示匹配节点和邻边]
─────────────────────────────────────
Matched nodes:
  ● retrieval-augmented generation  [CONCEPT]  page 0
  ● RAG systems                     [CONCEPT]  page 0
  ● vector similarity search        [CONCEPT]  page 2

4.5.4 搜索框 URL 同步

用户修改搜索框 → 更新 hash query: #/search?q={query}&type={type}&tab={tab}
页面初始化 → 解析 hash query → 预填表单 → 自动触发搜索
从 Header 全局搜索跳转 → #/search?q={input}

4.5.5 API 调用时机

调用 时机 接口
实体搜索 点击 Search / Enter GET /api/v1/search/entities?q=...&type=...
结果 Preview Graph 点击搜索结果行 GET /api/v1/kg/nodes/{id}/neighbors?hops=1
路径搜索 点击 Find Path GET /api/v1/search/path?from=...&to=...
子图搜索 点击 Search GET /api/v1/search/graph?q=...&include_neighbors=true

五、响应式设计规范

5.1 断点定义

断点名 宽度范围 布局策略
Desktop > 1280px 完整布局Sidebar 220px + 内容 + Detail Panel
Laptop 1024 1280px Sidebar 折叠为图标模式72px内容区扩展
Tablet 768 1024px Sidebar 隐藏,汉堡菜单触发 Drawer内容全屏
Mobile < 768px 底部 Tab Bar全屏内容面板改为底部 Sheet

5.2 CSS 媒体查询框架

/* ── Laptop: Sidebar 折叠 ── */
@media (max-width: 1280px) {
  .app { --sidebar-w: 72px; }
  .nav-label, .nav-badge, .sidebar-logo-text { display: none; }
  .nav-item { justify-content: center; padding: 12px; }
}

/* ── Tablet: Sidebar 变 Drawer ── */
@media (max-width: 1024px) {
  .app { grid-template-columns: 0 1fr; }
  .sidebar {
    position: fixed; left: 0; top: 0; bottom: 0;
    width: 220px; z-index: 500;
    transform: translateX(-220px);
    transition: transform 0.2s ease;
    box-shadow: var(--shadow-lg);
  }
  .sidebar.open { transform: translateX(0); }
  .sidebar-overlay { display: block; }  /* 点击遮罩关闭 */
}

/* ── Mobile: 底部 Tab Bar ── */
@media (max-width: 768px) {
  .app { grid-template-rows: 56px 1fr 56px; }
  .sidebar { display: none; }
  .bottom-nav { display: flex; position: fixed; bottom: 0; ... }

  /* KG Explorer 三栏 → 全屏 */
  .graph-filter-panel { display: none; }
  .graph-detail-panel { position: fixed; bottom: 0; left: 0; right: 0;
                         height: 60vh; border-radius: 16px 16px 0 0; z-index: 400; }

  /* Chat 历史面板 → 顶部 Drawer */
  .chat-history { position: fixed; top: 56px; left: 0; right: 0;
                   height: 50vh; z-index: 400; transform: translateY(-100%); }

  /* 指标卡 4 列 → 2 列 */
  .metrics-grid { grid-template-columns: repeat(2, 1fr); }

  /* Search 结果+预览 → 上下堆叠 */
  .search-results-layout { flex-direction: column; }
}

5.3 各页面移动端特殊处理

页面 桌面布局 移动端变化
Dashboard 4列指标卡 2×2 网格
Documents 表格列表 卡片堆叠(隐藏 Pages、Date 列)
KG Explorer 三栏 图谱全屏Filter 通过 FAB 触发底部 Sheet
Chat 双栏 历史面板隐藏,顶部 [历史] 按钮触发 Drawer
Search 双栏结果+预览 上下堆叠(预览缩小为 200px

六、关键交互模式规范

6.1 Toast 通知系统

位置: right: 24px; top: 72px (header 下方)
宽度: 320px
堆叠: 最多 3 条,从上往下,间距 8px
类型视觉:
  ✅ Success  bg #1a3a22  border-left 3px #3fb950  icon ✓
  ⚠️ Warning  bg #2d2a16  border-left 3px #d29922  icon !
  ❌ Error    bg #3b1a1a  border-left 3px #f85149  icon ✗
   Info     bg #161f2e  border-left 3px #58a6ff  icon i

生命周期:
  出现: slide-in from right (200ms ease-out)
  停留: 4000mshover 时暂停计时)
  消失: fade-out (300ms) → 从 DOM 移除

调用方式 (components.js):
  Toast.success("paper.pdf indexed: 40 nodes, 780 edges")
  Toast.error("Failed to upload: file too large")
  Toast.info("Loading knowledge graph...")

6.2 全局 Loading 状态

1. Header 进度条API 请求时)
   height: 2px; position: absolute; top: 0; width: 100%
   颜色: var(--blue)
   短请求: indeterminate animation
   长请求(轮询中): 真实进度百分比

2. Skeleton Loader列表加载时
   模拟行结构的灰色矩形shimmer 动画
   Document 列表: 3 行 skeleton rows
   Entity 列表: 5 行 skeleton rows

3. 图谱 Loading
   svg 中央显示: "Loading KG... (40 nodes, 780 edges)"
   3 点动画

4. Chat Thinking
   见 4.4.5 三点跳动动画

6.3 空状态Empty State

每个页面无数据时的引导设计:

KG Explorer无 KG 数据):
  ┌────────────────────────────────────┐
  │         ◉ (大图标)                 │
  │   No knowledge graph yet           │
  │   Upload documents and start       │
  │   indexing to build your KG        │
  │   [Upload & Index →]               │
  └────────────────────────────────────┘

Chat无问题历史:
  欢迎页 + Suggested Prompts见 4.4.1

Search无结果:
  "No entities found for 'query'"
  "Try: different keyword, check KG Explorer"
  [Explore KG]

Documents无文档:
  直接聚焦上传区(拖拽提示更突出)

6.4 错误处理规范

错误类型 处理方式
API code ≠ 0 Toast.error(msg) + console.error
fetch 网络失败 Toast.error("Network error. Is API server running on :8000?") + [Retry] 按钮
超时QA > 60s Toast.warning("Request timeout, please try again")
code 3002 (KG 为空) 页面内空状态引导,不弹 Toast
code 2001/3001 (不存在) Toast.error + 刷新当前列表
Indexing failed 行内展示错误信息 + [⟳ Retry] 按钮
文件校验失败 文件名标红 + inline 错误说明(不弹 Toast

6.5 页面间联动Cross-page Navigation

触发位置 操作 目标 参数
Dashboard [KG] 按钮 点击 KG Explorer #/graph?doc_id={doc_id}
Dashboard [Explore KG] 点击 KG Explorer #/graph
Dashboard [Demo] 点击 KG Explorer 加载 demo → #/graph
Documents [◉] 按钮 点击 KG Explorer #/graph?doc_id={doc_id}
Chat Cited Node 标签 点击 KG Explorer #/graph?node={node_id}
KG Detail Panel [💬] 点击 Chat #/chat?q=Tell+me+about+{name}
Search 结果 [View KG] 点击 KG Explorer #/graph?node={node_id}
Search 结果 [Chat] 点击 Chat #/chat?q=What+is+{name}
Header 全局搜索 Enter Search #/search?q={input}
Header 全局搜索 点击建议项 KG Explorer #/graph?node={node_id}

6.6 确认对话框规范

触发场景: 删除文档、取消 indexing job
样式: 居中 Modal360px遮罩层 rgba(0,0,0,0.6)

┌─────────────────────────────────────┐
│  Delete document?                   │
│                                     │
│  "paper.pdf" and all its associated │
│  KG data will be permanently deleted│
│  (40 nodes, 780 edges removed)      │
│                                     │
│                [Cancel] [Delete →]  │
└─────────────────────────────────────┘

[Delete] → bg: --redhover: #d73a2f
[Cancel] → btn-secondary

七、文件结构

7.1 新建文件清单

文件路径 说明
graphrag_pipeline/static/app/index.html SPA 主入口,包含 5 个 <section> 页面区域,引入所有 CSS/JS
graphrag_pipeline/static/app/css/variables.css CSS 变量定义(颜色、字体、圆角、阴影)
graphrag_pipeline/static/app/css/base.css Reset + 通用组件样式btn/badge/card/modal/toast/skeleton
graphrag_pipeline/static/app/css/layout.css 骨架布局app grid+ Sidebar + Header + Footer + 响应式
graphrag_pipeline/static/app/js/app.js 路由器 + 全局 AppState + 页面初始化调度
graphrag_pipeline/static/app/js/api.js Fetch 封装 + 错误处理 + 轮询 helper
graphrag_pipeline/static/app/js/components.js Toast / Modal / Progress / Skeleton / Tooltip
graphrag_pipeline/static/app/js/pages/dashboard.js Dashboard 页面逻辑
graphrag_pipeline/static/app/js/pages/documents.js Documents 页面逻辑(上传 + 索引流程)
graphrag_pipeline/static/app/js/pages/graph.js KG Explorer + D3 力导向图(复用现有 index.html
graphrag_pipeline/static/app/js/pages/chat.js Chat 页面逻辑(消息渲染 + 工具链 + Cited Nodes
graphrag_pipeline/static/app/js/pages/search.js Search 页面逻辑3 个 Tab 模式)

7.2 FastAPI 静态文件服务配置

api_server.py 中添加:

from fastapi.staticfiles import StaticFiles

# 挂载新 SPAGraphRAG Studio
app.mount("/studio", StaticFiles(directory="static/app", html=True), name="studio")

# 访问地址: http://localhost:8000/studio/

7.3 访问入口

地址 说明
http://localhost:8000/studio/ GraphRAG Studio新 SPA
http://localhost:8000/http://localhost:5000/ 旧 Flask KG 可视化(向后兼容)
http://localhost:8000/docs FastAPI 自动生成 Swagger UI

API 端点 → 页面使用矩阵

API 端点 Dashboard Documents KG Explorer Chat Search
GET /system/stats
GET /health
GET /system/demo
GET /system/formats
POST /documents/upload Modal
GET /documents Recent List
GET /documents/{id}
DELETE /documents/{id}
POST /index/start
GET /index/status/{id} Poll Poll
GET /index/result/{id}
DELETE /index/jobs/{id}
GET /kg/nodes
GET /kg/edges
GET /kg/nodes/{id} Click Hover
GET /kg/nodes/{id}/neighbors Detail Preview
GET /kg/stats
GET /kg/export Download
POST /query
GET /query/history Sidebar
POST /query/batch
GET /search/entities Toolbar Tab1
GET /search/path Tab2
GET /search/graph Tab3