docs: update architecture docs for Vue 3 + FastAPI separation, add one-click start.bat

- CLAUDE.md: remove duplicate architecture section, fix MAX_RETRY 5→3
- README.md: update architecture diagram to 3-tier, add start.bat instructions
- ROADMAP.md: add 阶段六 layered generation v5 (items 16-20)
- start.bat: one-click startup with auto port-kill and path-with-spaces fix
- package-lock.json: updated from npm install
This commit is contained in:
2026-05-21 22:10:22 +08:00
parent 74f3f03d2c
commit 7c1aa7d934
5 changed files with 101 additions and 62 deletions
+3 -16
View File
@@ -4,20 +4,7 @@
一个**本地桌面应用**,通过自然语言多轮对话帮助非技术用户创建 JasperReports 模板(JRXML 文件)。核心技术栈:Vue 3 前端 + FastAPI SSE 后端 + LangGraph 状态机 + LLM 生成/修改 + 自动验证修正循环。 一个**本地桌面应用**,通过自然语言多轮对话帮助非技术用户创建 JasperReports 模板(JRXML 文件)。核心技术栈:Vue 3 前端 + FastAPI SSE 后端 + LangGraph 状态机 + LLM 生成/修改 + 自动验证修正循环。
**一句话**:用户用中文描述报表需求 → LLM 生成 JRXML → 自动验证 → 失败则自动修正(最多5次) → 重试耗尽后失败上下文自动注入下一轮 → 返回可编译的 JRXML 文件。 **一句话**:用户用中文描述报表需求 → LLM 生成 JRXML → 自动验证 → 失败则自动修正(最多3次) → 重试耗尽后失败上下文自动注入下一轮 → 返回可编译的 JRXML 文件。
## 架构
```
前端 (Vue 3 + Vite, 端口 5173)
│ 聊天界面 + 统一输入框 + 流式显示 + 文件上传/粘贴/拖拽
▼ HTTP + SSE (Server-Sent Events)
后端 API (FastAPI, 端口 8000)
│ REST 接口 + SSE 流式推送
│ 包装 LangGraph Agent 不变
▼ HTTP
验证服务 (FastAPI, 端口 8001) — 不变
```
## 启动命令 ## 启动命令
@@ -47,7 +34,7 @@ cd frontend && npm run dev
- **向量库**: ChromaDB 持久化在 `./db/chroma` - **向量库**: ChromaDB 持久化在 `./db/chroma`
- **验证服务**: FastAPI `localhost:8001` - **验证服务**: FastAPI `localhost:8001`
- **日志**: JSON 格式化,`logs/app.log` + `logs/llm.log`,中国时区 (UTC+8) - **日志**: JSON 格式化,`logs/app.log` + `logs/llm.log`,中国时区 (UTC+8)
- **MAX_RETRY**: 5 - **MAX_RETRY**: 3
## 架构 ## 架构
@@ -246,7 +233,7 @@ validation_service/ (FastAPI, 端口 8001) — 不变
- **OCR 引擎**: 优先 PaddleOCR 2.9.x(精确识别,`pip install paddleocr`),回退 EasyOCR 1.7+。两者均未安装时仅返回图片元信息。PaddlePaddle 3.x 在 Windows 上有 ONEDNN bug,固定在 2.6.x。 - **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 上下文。 - **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` 哨兵防止重复触发。 - **会话持久化**: `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**: 默认 5 次。重试耗尽后 `pending_failure_context` 记录失败信息,下次用户输入时自动注入。 - **MAX_RETRY**: 默认 3 次。重试耗尽后 `pending_failure_context` 记录失败信息,下次用户输入时自动注入。
- **验证最小内容检查**: 验证服务额外检查至少 1 个 `<band>` + 1 个 `<textField>``<staticText>`,拦截空壳 JRXML。 - **验证最小内容检查**: 验证服务额外检查至少 1 个 `<band>` + 1 个 `<textField>``<staticText>`,拦截空壳 JRXML。
- **XLSX 支持 (v3)**: 需要 `openpyxl>=3.1.0`(已加入 requirements.txt)。表格按工作表逐行读取,单元格用 `|` 分隔。 - **XLSX 支持 (v3)**: 需要 `openpyxl>=3.1.0`(已加入 requirements.txt)。表格按工作表逐行读取,单元格用 `|` 分隔。
- **粘贴功能限制**: 文件以 base64 编码在 sessionStorage 中传递,单文件上限 20MB。大文件建议使用 file_uploader 按钮。 - **粘贴功能限制**: 文件以 base64 编码在 sessionStorage 中传递,单文件上限 20MB。大文件建议使用 file_uploader 按钮。
+35 -23
View File
@@ -18,18 +18,20 @@
## 架构 ## 架构
``` ```
Streamlit 界面 (app.py) 前端 (Vue 3 + Vite, 端口 5173)
| │ 聊天界面 + SSE 流式显示 + 文件上传/粘贴/拖拽
LangGraph 代理 (agent/) ▼ HTTP + SSE
|-- retrieve (Chroma/embeddings) 后端 API (FastAPI, 端口 8000)
|-- generate / generate_skeleton → refine_layout → map_fields (分层生成) │ REST 接口 + SSE 流式推送
|-- validate (FastAPI service) │ 包装 LangGraph Agent ──► agent/
|-- explain + correct (auto-fix loop) ├─ retrieve (Chroma/embeddings)
|-- modify (multi-turn edits) ├─ generate / generate_skeleton → refine_layout → map_fields (分层生成)
| │ ├─ validate (FastAPI service)
│ ├─ explain + correct (auto-fix loop)
│ └─ modify (multi-turn edits)
FastAPI 验证服务 (:8001) FastAPI 验证服务 (:8001)
|-- Structural checks (field references, SQL, page dimensions) └─ Structural checks + XSD schema validation
|-- XSD schema validation (if jasperreport.xsd available)
``` ```
## 前置要求 ## 前置要求
@@ -60,21 +62,24 @@ cp .env.example .env
python scripts/init_kb.py python scripts/init_kb.py
``` ```
### 4. 启动验证服务 ### 4. 启动服务
**一键启动(推荐)**:双击 `start.bat`,自动启动验证服务、后端 API、前端开发服务器。停止用 `stop.bat`
**手动启动**(需要三个终端):
在一个终端中运行:
```bash ```bash
python -m uvicorn validation_service.main:app --port 8001 # 终端 1 — 验证服务(必须先启动)
python -m uvicorn validation_service.main:app --port 8001 --host 0.0.0.0
# 终端 2 — 后端 APISSE + REST
python -m uvicorn api_server:app --port 8000 --host 0.0.0.0
# 终端 3 — 前端开发服务器
cd frontend && npm install && npm run dev
``` ```
### 5. 启动 Streamlit 界面 在浏览器中打开 http://localhost:5173。
在另一个终端中运行:
```bash
streamlit run app.py
```
在浏览器中打开 http://localhost:8501。
## 使用示例 ## 使用示例
@@ -110,7 +115,14 @@ pytest tests/ -v
``` ```
jrxml-agent/ jrxml-agent/
app.py Streamlit 聊天界面(多模态输入 api_server.py FastAPI SSE 后端(REST + 流式推送
start.bat 一键启动脚本
stop.bat 一键停止脚本
frontend/ Vue 3 + Vite 前端(聊天 UI
src/
api/client.ts SSE 客户端 + fetch 封装
stores/ Pinia 状态管理(chat + session
components/ 聊天界面组件(6 个)
agent/ agent/
state.py AgentState 定义(28 字段) state.py AgentState 定义(28 字段)
nodes.py 图节点(generate, generate_skeleton, refine_layout 等,18 节点) nodes.py 图节点(generate, generate_skeleton, refine_layout 等,18 节点)
+2 -2
View File
@@ -82,11 +82,11 @@
- [x] `backend/llm.py``_LLMLoggingWrapper` 包装所有 LLM 后端 - [x] `backend/llm.py``_LLMLoggingWrapper` 包装所有 LLM 后端
- [x] 记录每次 invoke/stream 的请求 prompt、响应内容、耗时、模型、调用来源 - [x] 记录每次 invoke/stream 的请求 prompt、响应内容、耗时、模型、调用来源
- [x] 异常时也记录完整 prompt - [x] 异常时也记录完整 prompt
- [x] `agent/nodes.py``@log_node` 装饰器覆盖 17 个节点 - [x] `agent/nodes.py``@log_node` 装饰器覆盖 18 个节点
- [x] 入口/出口/异常三个阶段的日志 - [x] 入口/出口/异常三个阶段的日志
- [x] 自动记录 state 关键字段摘要(session_id、intent、status、jrxml_length 等) - [x] 自动记录 state 关键字段摘要(session_id、intent、status、jrxml_length 等)
- [x] 每个节点耗时(duration_ms - [x] 每个节点耗时(duration_ms
- [x] `agent/graph.py``@_log_route` 装饰器覆盖 8 个路由函数 - [x] `agent/graph.py``@_log_route` 装饰器覆盖 9 个路由函数
- [x] 记录每次路由决策(来源 → 目标) - [x] 记录每次路由决策(来源 → 目标)
- [x] `app.py` — 用户交互日志 - [x] `app.py` — 用户交互日志
- [x] 收到用户输入(含上传文件信息) - [x] 收到用户输入(含上传文件信息)
+23 -5
View File
@@ -66,6 +66,29 @@
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@emnapi/core": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
"integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/wasi-threads": "1.2.1",
"tslib": "^2.4.0"
}
},
"node_modules/@emnapi/runtime": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
"integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@emnapi/wasi-threads": { "node_modules/@emnapi/wasi-threads": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
@@ -393,7 +416,6 @@
"integrity": "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==", "integrity": "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"undici-types": "~7.16.0" "undici-types": "~7.16.0"
} }
@@ -1054,7 +1076,6 @@
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },
@@ -1212,7 +1233,6 @@
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
"devOptional": true, "devOptional": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
"tsserver": "bin/tsserver" "tsserver": "bin/tsserver"
@@ -1234,7 +1254,6 @@
"integrity": "sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw==", "integrity": "sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"lightningcss": "^1.32.0", "lightningcss": "^1.32.0",
"picomatch": "^4.0.4", "picomatch": "^4.0.4",
@@ -1319,7 +1338,6 @@
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.34.tgz", "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.34.tgz",
"integrity": "sha512-WdLBG9gm02OgJIG9axd5Hpx0TFLdzVgfG2evFFu8Rur5O/IoGc5cMjnjh3tPL6GnRGsYvUhBSKVPYVcxRKpMCA==", "integrity": "sha512-WdLBG9gm02OgJIG9axd5Hpx0TFLdzVgfG2evFFu8Rur5O/IoGc5cMjnjh3tPL6GnRGsYvUhBSKVPYVcxRKpMCA==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@vue/compiler-dom": "3.5.34", "@vue/compiler-dom": "3.5.34",
"@vue/compiler-sfc": "3.5.34", "@vue/compiler-sfc": "3.5.34",
+38 -16
View File
@@ -1,31 +1,53 @@
@echo off @echo off
echo ============================================ setlocal enabledelayedexpansion
echo JRXML 代理 - 全自动启动 (验证 + API + UI)
echo ============================================
echo ============================================
echo JRXML Agent - One-Click Start
echo ============================================
echo. echo.
echo [1/3] 启动验证服务 (端口 8001)...
start "JRXML 验证服务" cmd /c "cd /d %~dp0 && .venv\Scripts\python -m uvicorn validation_service.main:app --port 8001 --host 0.0.0.0"
REM ========== Kill processes on ports ==========
echo [Pre-check] Cleaning up occupied ports...
for /f "tokens=5" %%a in ('netstat -ano ^| findstr ":8001.*LISTENING" 2^>nul') do (
echo Killing PID %%a on port 8001...
taskkill /PID %%a /F 2>nul
)
for /f "tokens=5" %%a in ('netstat -ano ^| findstr ":8000.*LISTENING" 2^>nul') do (
echo Killing PID %%a on port 8000...
taskkill /PID %%a /F 2>nul
)
for /f "tokens=5" %%a in ('netstat -ano ^| findstr ":5173.*LISTENING" 2^>nul') do (
echo Killing PID %%a on port 5173...
taskkill /PID %%a /F 2>nul
)
echo.
REM ========== Detect Python ==========
set PYTHON=python
if exist "%~dp0.venv\Scripts\python.exe" set "PYTHON=%~dp0.venv\Scripts\python.exe"
echo Using Python: %PYTHON%
echo.
REM ========== Start services ==========
echo [1/3] Starting validation service on port 8001...
start "JRXML-Validator" cmd /k "cd /d "%~dp0" && %PYTHON% -m uvicorn validation_service.main:app --port 8001 --host 0.0.0.0"
timeout /t 3 /nobreak >nul timeout /t 3 /nobreak >nul
echo [2/3] 启动后端 API (端口 8000)... echo [2/3] Starting backend API on port 8000...
start "JRXML API" cmd /c "cd /d %~dp0 && .venv\Scripts\python -m uvicorn api_server:app --port 8000 --host 0.0.0.0" start "JRXML-API" cmd /k "cd /d "%~dp0" && %PYTHON% -m uvicorn api_server:app --port 8000 --host 0.0.0.0"
timeout /t 3 /nobreak >nul timeout /t 3 /nobreak >nul
echo [3/3] 启动前端开发服务器 (端口 5173)... echo [3/3] Starting frontend dev server on port 5173...
start "JRXML Frontend" cmd /c "cd /d %~dp0\frontend && npm run dev" start "JRXML-Frontend" cmd /k "cd /d "%~dp0frontend" && npm run dev"
timeout /t 3 /nobreak >nul timeout /t 3 /nobreak >nul
echo. echo.
echo ============================================ echo ============================================
echo 启动完成 echo All services started!
echo 验证服务: http://localhost:8001 echo Frontend : http://localhost:5173
echo 后端 API: http://localhost:8000 echo Backend : http://localhost:8000
echo 前端界面: http://localhost:5173 echo Validator : http://localhost:8001
echo ============================================ echo ============================================
echo. echo.
echo 关闭此窗口不会停止服务。关闭服务窗口或运行 stop.bat 停止。 echo Close the service windows or run stop.bat to stop.
pause pause