Files
agent_jrxml/CLAUDE.md
T
panda 9bb011e429 feat: v4 multimodal chat input, multi-format support, and annotation detection
- Replace st.chat_input with st-multimodal-chatinput (Ctrl+V paste, drag-drop, file button)
- Extract _process_uploaded_file() shared handler (eliminates ~70 duplicated lines)
- Add XLSX (openpyxl), XLS (xlrd), DOC (olefile) parsers to file_parser.py
- Add backend/annotation_detector.py: circle detection (HoughCircles) + arrow detection (HoughLinesP clustering) + OCR correlation + LLM context formatting
- Add annotation_result field to AgentState with session persistence
- Wire annotation detection into process_input and _format_ocr_context
- Add 11 new tests: 7 annotation detector + 4 multi-format parser
- Update all docs: CLAUDE.md, README.md, CODE_GUIDE.md, ROADMAP.md
2026-05-20 23:43:16 +08:00

210 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 文件。
## 启动命令
```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**: PaddleOCR(精确识别首选,ppocr-v4)→ EasyOCR(回退,ch_sim+en),两者均未安装时仅返回图片元信息
- **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/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 校验
```
## 关键文件映射
| 文件 | 职责 | 修改频率 |
|------|------|---------|
| `app.py` | Streamlit UI 入口,聊天界面 + 侧边栏 + 下载 + 文件上传 | **高** |
| `agent/state.py` | AgentState 类型定义(~26 字段,含 pending_failure_context / annotation_result | 低 |
| `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/XLS/DOC/图片(EasyOCR→PaddleOCR回退)/文本 | 中 |
| `backend/layout_analyzer.py` | A4模板分析: 比例检测/EasyOCR→PaddleOCR元素提取/行分组/JRXML行匹配 | 中 |
| `backend/ocr_extractor.py` | OCR字段精确提取: 4策略(exact→kv_pair→regex→table_match) + 置信度 | 中 |
| `backend/annotation_detector.py` | 批注检测: 圈选(cv2 HoughCircles) + 箭头(HoughLinesP聚类) + OCR关联 + LLM格式化 | 中 |
| `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 通过时自动入库
### 文件上传
- 侧边栏多文件上传(可逐文件移除)
- 支持: PDF(pdfplumber+PIL) / DOCX(python-docx) / 图片(PIL+PaddleOCR可选) / 纯文本
- 上传文本自动注入下一条消息前缀
- 根据 `can_use_vision()` 判断是否走原生多模态(当前 MiniMax 不支持)
### 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 调用)
## 新增功能 (v3/v4)
### OCR 单据字段精确提取 (v3)
- `backend/ocr_extractor.py` — 4 策略优先级提取: exact_match → kv_pair → regex → table_match
- PaddleOCR 首次识别后将原始结果(含所有文本元素 + bbox坐标)持久化
- `_format_ocr_context()` — 将 OCR 结果(字段 + 原始元素坐标)格式化为 LLM prompt 注入
- OCR 结果在 `modify_jrxml``generate` 节点中自动注入 prompt
- `process_input` 节点在上传图片时自动触发 OCR 字段提取
- 结果持久化到会话文件(`save_session_node` / `load_session_node`
### 多模态聊天输入 + 多格式文件 (v4)
- `app.py``st.chat_input` 替换为 `st_multimodal_chatinput`(支持 Ctrl+V 粘贴 + 拖拽 + 文件按钮)
- `_process_uploaded_file()` — 提取共享文件处理逻辑(侧边栏 + 聊天共用,消除 ~70 行重复代码)
- 新增文件格式支持: XLSX (openpyxl)、XLS (xlrd)、DOC (olefile)
- 剪贴板粘贴文件通过 base64 解码 + MIME type → 扩展名推断
- 侧边栏上传器类型列表中新增 xlsx/xls/doc
### 批注检测 (v4)
- `backend/annotation_detector.py` — 识别用户在手写单据上的圈选和箭头标记
- **圆圈检测**: 红色通道增强 → HoughCircles → 圆形度验证
- **箭头检测**: Canny边缘 → HoughLinesP → 线段方向聚类 → 端点边缘密度判定方向
- **OCR 关联**: 批注与附近 OCR 文本元素关联(15% 图片尺寸内)
- **LLM 注入**: `format_annotation_context()` 将批注结果格式化为中文提示
- `process_input` 节点在 OCR 提取后自动运行批注检测
- `annotation_result` 字段持久化到 AgentState + 会话文件
### OCR 上下文提示增强 (v3/v4)
- `prompts/modification.md` — 新增 `{ocr_context}` 占位符
- `modify_jrxml` 节点 — 将 OCR 上下文注入 modification prompt
- OCR 上下文包含: 结构化字段、全部文本元素(含坐标)、批注检测结果
## 已知注意点
- **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 引擎**: 优先 PaddleOCR 2.9.x(精确识别,`pip install paddleocr`),回退 EasyOCR 1.7+。两者均未安装时仅返回图片元信息。PaddlePaddle 3.x 在 Windows 上有 ONEDNN bug,固定在 2.6.x。
- **MAX_RETRY**: 默认 3 次。重试耗尽后 `pending_failure_context` 记录失败信息,下次用户输入时自动注入。
- **验证最小内容检查**: 验证服务额外检查至少 1 个 `<band>` + 1 个 `<textField>``<staticText>`,拦截空壳 JRXML。
- **torchvision**: `transformers` 库的懒加载需要 `torchvision`,已作为依赖安装。
- **opencv-python-headless**: 批注检测(圈选/箭头)依赖,通过 `pip install -r requirements.txt` 安装。
- **st-multimodal-chatinput**: Streamlit 聊天输入增强组件,替代 `st.chat_input`,支持粘贴/拖拽文件。返回 base64 编码文件内容。
- **xlwt**: 仅在测试中使用(生成 .xls 测试文件)。