diff --git a/CLAUDE.md b/CLAUDE.md index 52d2145..39a4868 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,20 +4,7 @@ 一个**本地桌面应用**,通过自然语言多轮对话帮助非技术用户创建 JasperReports 模板(JRXML 文件)。核心技术栈:Vue 3 前端 + FastAPI SSE 后端 + LangGraph 状态机 + LLM 生成/修改 + 自动验证修正循环。 -**一句话**:用户用中文描述报表需求 → LLM 生成 JRXML → 自动验证 → 失败则自动修正(最多5次) → 重试耗尽后失败上下文自动注入下一轮 → 返回可编译的 JRXML 文件。 - -## 架构 - -``` -前端 (Vue 3 + Vite, 端口 5173) - │ 聊天界面 + 统一输入框 + 流式显示 + 文件上传/粘贴/拖拽 - ▼ HTTP + SSE (Server-Sent Events) -后端 API (FastAPI, 端口 8000) - │ REST 接口 + SSE 流式推送 - │ 包装 LangGraph Agent 不变 - ▼ HTTP -验证服务 (FastAPI, 端口 8001) — 不变 -``` +**一句话**:用户用中文描述报表需求 → LLM 生成 JRXML → 自动验证 → 失败则自动修正(最多3次) → 重试耗尽后失败上下文自动注入下一轮 → 返回可编译的 JRXML 文件。 ## 启动命令 @@ -47,7 +34,7 @@ cd frontend && npm run dev - **向量库**: ChromaDB 持久化在 `./db/chroma` - **验证服务**: FastAPI `localhost:8001` - **日志**: 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 字段提取**: `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**: 默认 5 次。重试耗尽后 `pending_failure_context` 记录失败信息,下次用户输入时自动注入。 +- **MAX_RETRY**: 默认 3 次。重试耗尽后 `pending_failure_context` 记录失败信息,下次用户输入时自动注入。 - **验证最小内容检查**: 验证服务额外检查至少 1 个 `` + 1 个 `` 或 ``,拦截空壳 JRXML。 - **XLSX 支持 (v3)**: 需要 `openpyxl>=3.1.0`(已加入 requirements.txt)。表格按工作表逐行读取,单元格用 `|` 分隔。 - **粘贴功能限制**: 文件以 base64 编码在 sessionStorage 中传递,单文件上限 20MB。大文件建议使用 file_uploader 按钮。 diff --git a/README.md b/README.md index 3a4739d..c112acc 100644 --- a/README.md +++ b/README.md @@ -18,18 +18,20 @@ ## 架构 ``` -Streamlit 界面 (app.py) - | -LangGraph 代理 (agent/) - |-- retrieve (Chroma/embeddings) - |-- generate / generate_skeleton → refine_layout → map_fields (分层生成) - |-- validate (FastAPI service) - |-- explain + correct (auto-fix loop) - |-- modify (multi-turn edits) - | +前端 (Vue 3 + Vite, 端口 5173) + │ 聊天界面 + SSE 流式显示 + 文件上传/粘贴/拖拽 + ▼ HTTP + SSE +后端 API (FastAPI, 端口 8000) + │ REST 接口 + SSE 流式推送 + │ 包装 LangGraph Agent ──► agent/ + │ ├─ retrieve (Chroma/embeddings) + │ ├─ generate / generate_skeleton → refine_layout → map_fields (分层生成) + │ ├─ validate (FastAPI service) + │ ├─ explain + correct (auto-fix loop) + │ └─ modify (multi-turn edits) + ▼ FastAPI 验证服务 (:8001) - |-- Structural checks (field references, SQL, page dimensions) - |-- XSD schema validation (if jasperreport.xsd available) + └─ Structural checks + XSD schema validation ``` ## 前置要求 @@ -60,21 +62,24 @@ cp .env.example .env python scripts/init_kb.py ``` -### 4. 启动验证服务 +### 4. 启动服务 + +**一键启动(推荐)**:双击 `start.bat`,自动启动验证服务、后端 API、前端开发服务器。停止用 `stop.bat`。 + +**手动启动**(需要三个终端): -在一个终端中运行: ```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 — 后端 API(SSE + REST) +python -m uvicorn api_server:app --port 8000 --host 0.0.0.0 + +# 终端 3 — 前端开发服务器 +cd frontend && npm install && npm run dev ``` -### 5. 启动 Streamlit 界面 - -在另一个终端中运行: -```bash -streamlit run app.py -``` - -在浏览器中打开 http://localhost:8501。 +在浏览器中打开 http://localhost:5173。 ## 使用示例 @@ -110,7 +115,14 @@ pytest tests/ -v ``` 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/ state.py AgentState 定义(28 字段) nodes.py 图节点(generate, generate_skeleton, refine_layout 等,18 节点) diff --git a/ROADMAP.md b/ROADMAP.md index f145d39..a9e3f7e 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -82,11 +82,11 @@ - [x] `backend/llm.py` — `_LLMLoggingWrapper` 包装所有 LLM 后端 - [x] 记录每次 invoke/stream 的请求 prompt、响应内容、耗时、模型、调用来源 - [x] 异常时也记录完整 prompt -- [x] `agent/nodes.py` — `@log_node` 装饰器覆盖 17 个节点 +- [x] `agent/nodes.py` — `@log_node` 装饰器覆盖 18 个节点 - [x] 入口/出口/异常三个阶段的日志 - [x] 自动记录 state 关键字段摘要(session_id、intent、status、jrxml_length 等) - [x] 每个节点耗时(duration_ms) -- [x] `agent/graph.py` — `@_log_route` 装饰器覆盖 8 个路由函数 +- [x] `agent/graph.py` — `@_log_route` 装饰器覆盖 9 个路由函数 - [x] 记录每次路由决策(来源 → 目标) - [x] `app.py` — 用户交互日志 - [x] 收到用户输入(含上传文件信息) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 58e9e99..b3f19b2 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -66,6 +66,29 @@ "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": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", @@ -393,7 +416,6 @@ "integrity": "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -1054,7 +1076,6 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -1212,7 +1233,6 @@ "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -1234,7 +1254,6 @@ "integrity": "sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", @@ -1319,7 +1338,6 @@ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.34.tgz", "integrity": "sha512-WdLBG9gm02OgJIG9axd5Hpx0TFLdzVgfG2evFFu8Rur5O/IoGc5cMjnjh3tPL6GnRGsYvUhBSKVPYVcxRKpMCA==", "license": "MIT", - "peer": true, "dependencies": { "@vue/compiler-dom": "3.5.34", "@vue/compiler-sfc": "3.5.34", diff --git a/start.bat b/start.bat index 39af46f..0422587 100644 --- a/start.bat +++ b/start.bat @@ -1,31 +1,53 @@ @echo off -echo ============================================ -echo JRXML 代理 - 全自动启动 (验证 + API + UI) -echo ============================================ +setlocal enabledelayedexpansion +echo ============================================ +echo JRXML Agent - One-Click Start +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 -echo [2/3] 启动后端 API (端口 8000)... -start "JRXML API" cmd /c "cd /d %~dp0 && .venv\Scripts\python -m uvicorn api_server:app --port 8000 --host 0.0.0.0" - +echo [2/3] Starting backend API on port 8000... +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 -echo [3/3] 启动前端开发服务器 (端口 5173)... -start "JRXML Frontend" cmd /c "cd /d %~dp0\frontend && npm run dev" - +echo [3/3] Starting frontend dev server on port 5173... +start "JRXML-Frontend" cmd /k "cd /d "%~dp0frontend" && npm run dev" timeout /t 3 /nobreak >nul echo. echo ============================================ -echo 启动完成 -echo 验证服务: http://localhost:8001 -echo 后端 API: http://localhost:8000 -echo 前端界面: http://localhost:5173 +echo All services started! +echo Frontend : http://localhost:5173 +echo Backend : http://localhost:8000 +echo Validator : http://localhost:8001 echo ============================================ echo. -echo 关闭此窗口不会停止服务。关闭服务窗口或运行 stop.bat 停止。 +echo Close the service windows or run stop.bat to stop. pause