Files
agent_jrxml/CLAUDE.md
T
panda 87ead4fa6a feat: 对话区域文件上传(粘贴/拖拽) + XLSX支持 + 会话切换无限循环修复
- 对话区域: st.file_uploader + 全局 paste/drop 事件监听 + sessionStorage 桥接
- 文件预览芯片: 上传后显示在对话区域,可逐文件移除
- OCR 双层解析全面接入: file_parser(文字) + ocr_extractor(字段提取)
- XLSX 解析: openpyxl 逐工作表/逐行读取
- 修复: create_session 强制写入 agent_state.session_id
- 修复: load_session_node 不再从磁盘覆盖 session_id
- 修复: 切换会话 _last_switched_to 哨兵防止无限 rerun

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 12:04:02 +08:00

191 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CLAUDE.md — JRXML 生成代理
## 项目概述
一个**本地桌面应用**,通过自然语言多轮对话帮助非技术用户创建 JasperReports 模板(JRXML 文件)。核心技术栈:Streamlit UI + LangGraph 状态机 + LLM 生成/修改 + 自动验证修正循环。
**一句话**:用户用中文描述报表需求 → LLM 生成 JRXML → 自动验证 → 失败则自动修正(最多3次) → 重试耗尽后失败上下文自动注入下一轮 → 返回可编译的 JRXML 文件。
## 启动命令
**方式 1 — 一键启动(Windows)**:双击 `start.bat`,自动打开两个窗口分别运行验证服务和 UI。停止用 `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
```
浏览器打开 `http://localhost:8501`
## 当前配置(.env
- **OCR**: EasyOCR(优先,ch_sim+en)→ PaddleOCR(回退),两者均未安装时仅返回图片元信息
- **LLM**: `cloud` / `anthropic` → MiniMax Anthropic 兼容 API (`MiniMax-M2.7`)
- Base URL: `https://api.minimaxi.com/anthropic`
- 认证: Anthropic SDK 自动读取 `ANTHROPIC_API_KEY`fallback `OPENAI_API_KEY`
- **嵌入模型**: `local` / `sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2`
- **向量库**: ChromaDB 持久化在 `./db/chroma`
- **验证服务**: FastAPI `localhost:8001`
- **日志**: JSON 格式化,`logs/app.log` + `logs/llm.log`,中国时区 (UTC+8)
- **MAX_RETRY**: 3
## 架构
```
app.py (Streamlit UI)
│ run_agent(user_input)
│ 功能: 流式输出/节点平铺/文件上传/历史下载/预览/Ctrl+C修复
agent/graph.py (LangGraph 状态机)
│ 节点流程:
│ load_session → process_input → manage_context → save_state_snapshot
│ → classify_intent (8种意图路由)
│ ├─ retrieve → generate → 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 外部化:7 个 .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/图片/文本
├──► backend/layout_analyzer.py A4布局分析: OCR + 行分组 + JRXML行匹配
├──► backend/ocr_extractor.py OCR字段提取: 两阶段4策略精确提取单据字段值
├──► backend/validation.py HTTP 客户端: POST /validate
├──► backend/session.py 会话持久化: JSON 文件 CRUD
└──► validation_service/ 独立 FastAPI: 结构检查 + XSD 校验
```
## 关键文件映射
| 文件 | 职责 | 修改频率 |
|------|------|---------|
| `app.py` | Streamlit UI 入口,聊天界面 + 对话文件上传(粘贴/拖拽) + 侧边栏 + 下载 | **高** |
| `agent/state.py` | AgentState 类型定义(~24 字段,含 pending_failure_context | 低 |
| `agent/nodes.py` | 14 个工作流节点 + 流式生成 + 错误记录 | **高** |
| `agent/graph.py` | 状态图编译 + 路由函数(预览跳过验证) | 中 |
| `prompts/loader.py` | Prompt 加载器(从 .md 文件热重载) | 低 |
| `prompts/*.md` | 7 个独立 Prompt 模板 | **高** |
| `backend/llm.py` | LLM 工厂,统一 `_BaseLLM` 接口(invoke + stream+ `_LLMLoggingWrapper` | 中 |
| `backend/logger.py` | 集中日志模块:JSON 格式化 + trace_id + 独立 llm.log | 低 |
| `backend/rag_adapter.py` | RAGSearcher 单例,语义搜索接口 | 中 |
| `backend/error_kb.py` | ErrorKB — 错误指纹去重 + ChromaDB 持久化 + 语义检索 | 中 |
| `backend/file_parser.py` | 文件解析: PDF/DOCX/XLSX/图片(EasyOCR→PaddleOCR回退)/文本 | 中 |
| `backend/layout_analyzer.py` | A4模板分析: 比例检测/EasyOCR→PaddleOCR元素提取/行分组/JRXML行匹配 | 中 |
| `backend/ocr_extractor.py` | OCR单据字段提取: 两阶段流水线 + 4种策略(精确KV/模糊KV/正则/表格) + 17个默认中文字段 | 中 |
| `backend/embeddings.py` | 嵌入模型工厂 (HuggingFace/OpenAI) | 低 |
| `backend/validation.py` | 验证服务 HTTP 客户端 | 低 |
| `backend/session.py` | 会话 JSON 文件 CRUD | 低 |
| `validation_service/main.py` | FastAPI 验证服务 | 低 |
| `scripts/init_kb.py` | 知识库初始化/模型下载 | 低 |
## 关键约定
1. **LLM 调用接口**: 所有节点通过 `get_llm().invoke(prompt)` 同步调用,或用 `get_llm().stream(prompt)` 流式调用。三个后端(Anthropic/OpenAI/Ollama)通过 `_BaseLLM` 统一接口。
2. **流式生成**: generate/modify_jrxml/correct_jrxml 使用 `get_stream_writer()` 发射自定义事件,UI 通过 `stream_mode=["updates", "custom"]` 捕获逐字输出。
3. **JRXML 提取**: `_extract_jrxml()` 处理 LLM 响应 —— 去掉 markdown 代码块标记,提取 XML 内容。
4. **状态持久化**: 每个会话存为 `sessions/{session_id}.json`LangGraph 节点间通过 AgentState dict 传递。
5. **Token 计数**: 使用 `tiktoken` (gpt-4o encoder) 估算,不管实际模型是什么。
6. **RAG 子模块**: `rag/` 是一个独立的 git submodule,其内部的生成产物 (`models/`, `embeddings/`, `chroma_db/`, `jrxml_source_chunks/`) 不在 git 中。
## Prompt 模板位置
所有 Prompt 在 `prompts/` 目录,`.md` 文件可直接编辑,无需重启应用:
| 文件 | 用途 |
|------|------|
| `prompts/intent_classify.md` | 8 分类意图识别 |
| `prompts/initial_generation.md` | 首次生成 JRXML |
| `prompts/modification.md` | 修改现有 JRXML |
| `prompts/correction.md` | 自动修正错误 |
| `prompts/explain_error.md` | 错误转人话 |
| `prompts/compression.md` | 对话压缩摘要 |
| `prompts/consult.md` | 咨询解答 |
## 新增功能 (v2)
### 流式输出 + 节点平铺
- LLM 生成时逐字展示 XML(不再是空白等待)
- 节点以"处理过程"折叠区展开,不相互覆盖
- 完成后自动折叠,展示总结卡片
### 错误自增长知识库
- `backend/error_kb.py` — ChromaDB 集合 `jrxml_error_cases`
- 错误指纹去重(标准化 + MD5):相同结构错误不重复录入
- 记录内容:错误信息 + 修正前后 JRXML + 修正 prompt + 工具链
- `retrieve` 节点自动注入历史修正案例
- 流程:correct_jrxml 保存 last_error_case → validate 通过时自动入库
### 文件上传
- **对话区域上传(v3**: `st.file_uploader` 位于聊天输入框上方,支持图片/PDF/DOCX/XLSX/文本
- **粘贴/拖拽(v3**: 全局 paste/drop 事件监听 + `sessionStorage` + 轮询桥接组件,Ctrl+V 粘贴或拖拽文件到页面任意位置
- **文件预览芯片(v3)**: 上传后显示在对话区域,可逐文件移除(自动清理临时文件)
- 侧边栏多文件上传(可逐文件移除,向后兼容保留)
- 支持: PDF(pdfplumber+PIL) / DOCX(python-docx) / XLSX(openpyxl, v3) / 图片(PIL+EasyOCR优先→PaddleOCR回退) / 纯文本
- 上传文本自动注入下一条消息前缀
- 根据 `can_use_vision()` 判断是否走原生多模态(当前 MiniMax 不支持)
### 对话区域文件粘贴/拖拽技术方案(v3)
- `st.html()` 注入全局 paste/drop/dragover 监听器 → 文件转 base64 → 写入 `sessionStorage`
- `components.html(height=0)` 桥接组件每 800ms 轮询 `sessionStorage``Streamlit.setComponentValue` 回传 Python
- Python 解码 base64 → 临时文件 → `parse_file` + `analyze_layout` 双层 OCR 解析
- 上限:单文件 20MB,单次最多 10 个文件
### A4 模板识别
- `backend/layout_analyzer.py` — 三种处理路径:
- **完整 A4**: 比例匹配 + OCR 元素 → 全量布局描述
- **行片段 + 有现有报表**: 行匹配到 JRXML section → 定位修改
- **行片段 + 无现有报表**: 按 A4 模板生成完整报表
- PaddleOCR(可选安装)提供精确元素位置/字号
- 行分组:Y 轴容差自动聚类;行匹配:文本相似度搜索 JRXML band
### 会话历史下载
- `AgentState.jrxml_versions` 追踪每次生成/修改的版本
- 侧边栏"历史版本"折叠区,每版本独立下载按钮
### 预览修复
- `route_after_save` 新增意图判断:预览/导出跳过验证直通 finalize
### Ctrl+C 修复
- JS 注入拦截 Streamlit 裸 `c` 键清缓存,保留 Ctrl+C 复制
### 结构化日志系统
- `backend/logger.py` — JSON 格式化 + trace_id + 国际时区
- `_LLMLoggingWrapper` — 包装所有 LLM 后端,记录完整 prompt/response
- `@log_node` / `@_log_route` — 装饰器自动记录节点和路由
- 日志分离: `logs/app.log` (业务) + `logs/llm.log` (AI 调用)
## 已知注意点
- **Anthropic SDK**: 使用原始 `anthropic` 包(非 `langchain-anthropic`),因为需要直连 MiniMax 兼容端点。API Key 优先读 `ANTHROPIC_API_KEY`fallback `OPENAI_API_KEY`。Anthropic SDK 会自动将 key 放入 `x-api-key` header。
- **MiniMax 模型名称**: `MiniMax-M2.7`(不是 `minimax-2.7`),大小写敏感。
- **Streamlit headless**: Windows 下必须设 `STREAMLIT_SERVER_HEADLESS=true` 跳过邮箱采集提示。
- **日志分析**: 通过 `trace_id` 字段可追踪一次请求的全链路。LLM 调用日志在 `logs/llm.log`,包含完整 prompt 和 response(各截断 10000 字符)。
- **验证服务结构检查**: 字段引用一致性 (`$F{field}` vs `<field>` 声明)、SQL SELECT 存在性、pageWidth/pageHeight/name 属性。
- **XSD 校验可选**: 需要 `validation_service/schemas/jasperreport_7_0_6.xsd` 存在。
- **rag 子模块**: 内部有独立的管线脚本(`batch_chunker.py``embed_chunks.py``import_to_chroma.py`),通常不需要在主项目中运行。
- **OCR 引擎**: 优先使用 EasyOCRWindows 兼容性更好,`pip install easyocr`),回退 PaddleOCR。两者均未安装时仅返回图片元信息,建议至少安装 EasyOCR。
- **OCR 字段提取**: `process_input` 自动检测上传图片,调用 `OcrExtractor` 提取 17 个常见中文字段(发票代码/号码/金额/日期等),提取结果自动注入 LLM 上下文。
- **会话持久化**: `session_id` 现已包含在 `save_session_node` 的持久化字段中,避免切换会话时因 `session_id` 丢失导致的无限 rerun bug。
- **v3 修复**: `create_session` 现在存盘前强制写入 `agent_state["session_id"] = sid``load_session_node` 不再从磁盘覆盖 `session_id`。切换会话增加 `_last_switched_to` 哨兵防止重复触发。
- **MAX_RETRY**: 默认 3 次。重试耗尽后 `pending_failure_context` 记录失败信息,下次用户输入时自动注入。
- **验证最小内容检查**: 验证服务额外检查至少 1 个 `<band>` + 1 个 `<textField>``<staticText>`,拦截空壳 JRXML。
- **XLSX 支持 (v3)**: 需要 `openpyxl>=3.1.0`(已加入 requirements.txt)。表格按工作表逐行读取,单元格用 `|` 分隔。
- **粘贴功能限制**: 文件以 base64 编码在 sessionStorage 中传递,单文件上限 20MB。大文件建议使用 file_uploader 按钮。
- **torchvision**: `transformers` 库的懒加载需要 `torchvision`,已作为依赖安装。