feat: 前后端分离架构 — FastAPI SSE后端 + Vue 3前端

将单体 Streamlit 应用拆分为三层架构:
- api_server.py: FastAPI SSE 流式后端 (端口 8000)
- frontend/: Vue 3 + Vite + Pinia 聊天前端 (端口 5173)
- agent/graph.py: 新增 node_start 回调支持
- 更新启动脚本为三服务模式

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-21 20:04:27 +08:00
parent 2befd44430
commit 74f3f03d2c
29 changed files with 3668 additions and 72 deletions
+54 -42
View File
@@ -2,24 +2,40 @@
## 项目概述
一个**本地桌面应用**,通过自然语言多轮对话帮助非技术用户创建 JasperReports 模板(JRXML 文件)。核心技术栈:Streamlit UI + LangGraph 状态机 + LLM 生成/修改 + 自动验证修正循环。
一个**本地桌面应用**,通过自然语言多轮对话帮助非技术用户创建 JasperReports 模板(JRXML 文件)。核心技术栈:Vue 3 前端 + FastAPI SSE 后端 + LangGraph 状态机 + LLM 生成/修改 + 自动验证修正循环。
**一句话**:用户用中文描述报表需求 → LLM 生成 JRXML → 自动验证 → 失败则自动修正(最多3次) → 重试耗尽后失败上下文自动注入下一轮 → 返回可编译的 JRXML 文件。
**一句话**:用户用中文描述报表需求 → LLM 生成 JRXML → 自动验证 → 失败则自动修正(最多5次) → 重试耗尽后失败上下文自动注入下一轮 → 返回可编译的 JRXML 文件。
## 架构
```
前端 (Vue 3 + Vite, 端口 5173)
│ 聊天界面 + 统一输入框 + 流式显示 + 文件上传/粘贴/拖拽
▼ HTTP + SSE (Server-Sent Events)
后端 API (FastAPI, 端口 8000)
│ REST 接口 + SSE 流式推送
│ 包装 LangGraph Agent 不变
▼ HTTP
验证服务 (FastAPI, 端口 8001) — 不变
```
## 启动命令
**方式 1 — 一键启动(Windows)**:双击 `start.bat`,自动打开个窗口分别运行验证服务和 UI。停止用 `stop.bat`
**方式 1 — 一键启动(Windows)**:双击 `start.bat`,自动打开个窗口分别运行验证服务、后端 API、前端开发服务器。停止用 `stop.bat`
**方式 2 — 手动启动**
```bash
# 终端 1 — 验证服务(必须先启动)
python -m uvicorn validation_service.main:app --port 8001 --host 0.0.0.0
# 终端 2 — Streamlit UI
STREAMLIT_SERVER_HEADLESS=true streamlit run app.py --server.port 8501
# 终端 2 — 后端 APISSE + REST
python -m uvicorn api_server:app --port 8000 --host 0.0.0.0
# 终端 3 — 前端开发服务器
cd frontend && npm run dev
```
浏览器打开 `http://localhost:8501`
浏览器打开 `http://localhost:5173`
## 当前配置(.env
@@ -31,53 +47,48 @@ STREAMLIT_SERVER_HEADLESS=true streamlit run app.py --server.port 8501
- **向量库**: ChromaDB 持久化在 `./db/chroma`
- **验证服务**: FastAPI `localhost:8001`
- **日志**: JSON 格式化,`logs/app.log` + `logs/llm.log`,中国时区 (UTC+8)
- **MAX_RETRY**: 3
- **MAX_RETRY**: 5
## 架构
```
app.py (Streamlit UI)
run_agent(user_input)
功能: 流式输出/节点平铺/文件上传/历史下载/预览/Ctrl+C修复
前端 (Vue 3 + Vite, 端口 5173)
src/
├── api/client.ts SSE 客户端 + fetch 封装
│ ├── stores/chat.ts Pinia: 消息/流式/节点进度
│ ├── stores/session.ts Pinia: 会话管理
│ ├── components/
│ │ ├── Sidebar.vue 会话列表 + 下载
│ │ ├── ChatMessages.vue 消息列表渲染
│ │ ├── StreamingMessage.vue 流式文本展示
│ │ ├── UnifiedInput.vue 统一输入框(文本+文件拖拽/粘贴)
│ │ ├── NodeProgress.vue 节点进度指示
│ │ └── SummaryCard.vue 结果摘要卡片
│ └── utils/format.ts 工具函数
▼ HTTP + SSE (Server-Sent Events)
api_server.py (FastAPI, 端口 8000)
│ POST /api/sessions/{id}/chat → SSE 流式响应
│ CRUD /api/sessions/... → 会话管理
│ POST /api/upload → 文件上传
│ GET /api/download/... → JRXML 下载
│ GET /api/health, /api/config
│ 包装 LangGraph Agent(不变)──► agent/
agent/graph.py (LangGraph 状态机)
│ 节点流程:
│ load_session → process_input → manage_context → save_state_snapshot
│ → classify_intent (8种意图路由)
│ ├─ retrieve → route_after_retrieve
│ ├─ [有布局schema] generate_skeleton → refine_layout → map_fields
│ └─ [无布局schema] generate
├─ generate/map_fields → save_session → validate → ... → finalize
│ ├─ modify_jrxml → save_session → validate → ... → finalize
│ ├─ handle_consult / handle_undo / handle_reset → finalize
│ └─ preview/export → save_session → finalize (跳过验证)
│ 验证修正循环: validate ─fail─► explain_error ─► correct_jrxml ─► validate
│ ▲ │
│ └──────── (retry < MAX_RETRY=3) ───────────────────┘
├──► prompts/loader.py Prompt 外部化:10 个 .md 文件热重载
├──► backend/llm.py LLM 工厂: Anthropic SDK / OpenAI / Ollama (统一 stream/invoke)
├──► backend/logger.py 集中日志: JSON + trace_id + llm.log/app.log 分离
├──► backend/rag_adapter.py 语义搜索: ChromaDB + SentenceTransformer
├──► backend/error_kb.py 错误知识库: 指纹去重 + ChromaDB 持久化
├──► backend/file_parser.py 文件解析: PDF/DOCX/XLSX/XLS/DOC/图片/文本
├──► backend/layout_analyzer.py A4布局分析: OCR + 行分组 + JRXML行匹配
├──► backend/ocr_extractor.py OCR字段精确提取: 4策略优先级 + 置信度
├──► backend/annotation_detector.py 批注检测: 圈选(HoughCircles) + 箭头(HoughLinesP) + OCR关联
├──► backend/validation.py HTTP 客户端: POST /validate
├──► backend/session.py 会话持久化: JSON 文件 CRUD
└──► validation_service/ 独立 FastAPI: 结构检查 + XSD 校验
validation_service/ (FastAPI, 端口 8001) — 不变
```
## 关键文件映射
| 文件 | 职责 | 修改频率 |
|------|------|---------|
| `app.py` | Streamlit UI 入口,聊天界面 + 对话文件上传(粘贴/拖拽) + 侧边栏 + 下载 | **高** |
| `agent/state.py` | AgentState 类型定义(~28 字段,含 layout_schema / annotation_result | 低 |
| `api_server.py` | FastAPI SSE 后端,REST API + 流式推送 | **高** |
| `frontend/src/` | Vue 3 聊天 UI(替代旧 app.py | **高** |
| `agent/state.py` | AgentState 类型定义(~28 字段) | 低 |
| `agent/nodes.py` | 18 个工作流节点 + 流式生成 + 错误记录 | **高** |
| `agent/graph.py` | 状态图编译 + 路由函数(预览跳过验证) | 中 |
| `agent/graph.py` | 状态图编译 + 路由函数 + node_start 回调 | 中 |
| `prompts/loader.py` | Prompt 加载器(从 .md 文件热重载) | 低 |
| `prompts/*.md` | 10 个独立 Prompt 模板 | **高** |
| `backend/llm.py` | LLM 工厂,统一 `_BaseLLM` 接口(invoke + stream+ `_LLMLoggingWrapper` | 中 |
@@ -93,6 +104,7 @@ agent/graph.py (LangGraph 状态机)
| `backend/session.py` | 会话 JSON 文件 CRUD | 低 |
| `validation_service/main.py` | FastAPI 验证服务 | 低 |
| `scripts/init_kb.py` | 知识库初始化/模型下载 | 低 |
| `app.py` | ~~旧 Streamlit UI~~(已由 api_server.py + frontend/ 替代) | 废弃 |
## 关键约定
@@ -234,7 +246,7 @@ agent/graph.py (LangGraph 状态机)
- **OCR 引擎**: 优先 PaddleOCR 2.9.x(精确识别,`pip install paddleocr`),回退 EasyOCR 1.7+。两者均未安装时仅返回图片元信息。PaddlePaddle 3.x 在 Windows 上有 ONEDNN bug,固定在 2.6.x。
- **OCR 字段提取**: `process_input` 自动检测上传图片,调用 `OcrExtractor` 提取常见中文字段(发票代码/号码/金额/日期等),提取结果自动注入 LLM 上下文。
- **会话持久化**: `session_id` 现已包含在 `save_session_node` 的持久化字段中,避免切换会话时因 `session_id` 丢失导致的无限 rerun bug。`create_session` 存盘前强制写入 `agent_state["session_id"] = sid``load_session_node` 不从磁盘覆盖 `session_id`。切换会话增加 `_last_switched_to` 哨兵防止重复触发。
- **MAX_RETRY**: 默认 3 次。重试耗尽后 `pending_failure_context` 记录失败信息,下次用户输入时自动注入。
- **MAX_RETRY**: 默认 5 次。重试耗尽后 `pending_failure_context` 记录失败信息,下次用户输入时自动注入。
- **验证最小内容检查**: 验证服务额外检查至少 1 个 `<band>` + 1 个 `<textField>``<staticText>`,拦截空壳 JRXML。
- **XLSX 支持 (v3)**: 需要 `openpyxl>=3.1.0`(已加入 requirements.txt)。表格按工作表逐行读取,单元格用 `|` 分隔。
- **粘贴功能限制**: 文件以 base64 编码在 sessionStorage 中传递,单文件上限 20MB。大文件建议使用 file_uploader 按钮。