Files
agent_jrxml/docs/conversation-scenarios.md
T
panda e362f530ea chore: remove 13 stale files and clean up project structure
Removed:
- app.py (deprecated Streamlit UI, replaced by api_server.py + frontend/)
- start_agent_jrxml.py (old launcher, replaced by start.py)
- test_reorder.py, e2e_test.py (ad-hoc/outdated test scripts)
- ocr_raw_positions.json (debug output)
- ARCHITECTURE.md, CODE_GUIDE.md, RAG_INTEGRATION.md, ROADMAP.md (superseded by CLAUDE.md)
- EVALUATION_REPORT.md (auto-generated)
- scripts/init_kb.py (replaced by init_default_kb.py)
- validation_service/validate.bat (redundant, start.py covers it)
- sessions/*.json (34 test session files, already gitignored)

Updated:
- CLAUDE.md: removed stale file entries from key mapping table
- README.md: updated init script reference and removed validate.bat
- .gitignore: removed EVALUATION_REPORT.md entry
2026-05-24 09:07:15 +08:00

26 KiB
Raw Blame History

对话场景遍历文档

agent/graph.py 状态图递归遍历生成,覆盖所有用户意图 → 节点路径 → 退出条件。 最后更新: 2026-05-24


状态图总览

                        ┌──────────────────────────────────────────────────┐
                        │              修正循环 (最多 MAX_RETRY=5 次)       │
                        │    ┌─────────┐    ┌──────────────┐    ┌────────┐ │
                        │    │ validate │───→│ explain_error│───→│correct │ │
                        │    └────┬─────┘    └──────────────┘    │_jrxml  │ │
                        │         │ pass                          └───┬────┘ │
                        │         ▼                                   │      │
                        │    ┌─────────┐                         retry<5  │
                        │    │finalize │◄────────────────────────────────┘ │
                        │    └─────────┘  retry>=5                        │
                        └──────────────────────────────────────────────────┘

load_session ──→ process_input ──→ manage_context ──→ save_state_snapshot
                                                           │
                                                           ▼
                                                    classify_intent
                                                           │
              ┌────────────┬──────────┬────────┬───────────┼───────────┬──────────┐
              ▼            ▼          ▼        ▼           ▼           ▼          ▼
         retrieve    modify_jrxml  save_   handle_     handle_     handle_    (兜底)
         (新建报表)   (修改报表)   session  consult     undo        reset
              │            │       (预览)  (咨询)      (撤销)      (重置)
     ┌────────┴────┐       │          │        │           │           │
     ▼             ▼       │          │        │           │           │
  generate    generate_    │          │        │           │           │
  (1-shot)    skeleton     │          │        │           │           │
     │             │        │          │        │           │           │
     │        refine_       │          │        │           │           │
     │        layout        │          │        │           │           │
     │             │        │          │        │           │           │
     │        map_fields    │          │        │           │           │
     │             │        │          │        │           │           │
     └──────┬──────┘        │          │        │           │           │
            ▼               ▼          ▼        ▼           ▼           ▼
         save_session ◄─────┴──────────┘    finalize ◄─── finalize ◄── finalize
            │                                     ▲
            │ (预览/导出跳过验证)                    │
            ├───────────────────────────────────────┘
            │ (其他意图走验证)
            ▼
         validate ──→ explain_error ──→ correct_jrxml ──→ validate (循环)
            │ pass                                     │ retry>=MAX
            ▼                                          ▼
         finalize ────────────────────────────────→ finalize

节点详细清单

每个节点标注了 代码行号 (agent/nodes.pyagent/graph.py)、前驱节点 (predecessors)、后继节点 (successors)。

1. load_session — 加载会话

属性
代码位置 agent/nodes.py:77
前驱 (入口节点, graph entry_point)
后继 process_input (固定边 graph.py:198)
功能 sessions/{session_id}.json 磁盘加载状态,注入 agent_state。不从磁盘覆盖 session_id
LLM

2. process_input — 处理用户输入

属性
代码位置 agent/nodes.py:98
前驱 load_session (graph.py:198)
后继 manage_context (graph.py:199)
功能 文件解析(PDF/DOCX/XLSX/图片/文本)→ OCR 字段提取 → 批注检测 → 模板 JRXML 解析。注入 ocr_extraction_resultlayout_schemaocr_elementsuploaded_template_jrxml
LLM 否(OCR 用 PaddleOCR/EasyOCR

3. manage_context — 上下文管理

属性
代码位置 agent/nodes.py:143
前驱 process_input (graph.py:199)
后继 save_state_snapshot (graph.py:200)
功能 Token 计数 → 对话压缩(超限时 LLM 压缩为摘要)→ compressed_history
LLM 是(压缩时调 LLM

4. save_state_snapshot — 状态快照

属性
代码位置 agent/nodes.py:191
前驱 manage_context (graph.py:200)
后继 classify_intent (graph.py:201)
功能 深拷贝当前状态 → 推入 history_states 列表。最多保留 5 个快照。撤销时恢复到最新快照。
LLM

5. classify_intent — 意图分类

属性
代码位置 agent/nodes.py:200
前驱 save_state_snapshot (graph.py:201)
后继 6 路条件分发 (graph.py:204-215)
功能 LLM 分类用户意图为 8 种之一。prompt: prompts/intent_classify.md
LLM
路由函数 route_by_intent (graph.py:67)

分类逻辑与路由目标:

意图值 路由目标 说明
initial_generation retrieve 新建报表
modify_report modify_jrxml 修改现有报表
preview_report save_session 预览(跳过生成)
export_pdf save_session 导出 PDF(跳过生成)
export_jrxml save_session 下载 JRXML(跳过生成)
consult_question handle_consult 咨询问答
undo_modification handle_undo 撤销
reset_session handle_reset 重置
未知/兜底 current_jrxmlmodify_jrxml; 无 → retrieve

6. retrieve — RAG/知识库检索

属性
代码位置 agent/nodes.py:442
前驱 classify_intent (graph.py:204-215, intent=initial_generation)
后继 条件分发: generate_skeletongenerate (graph.py:218-224)
功能 ① ErrorKB 检索历史修正案例 → ② KB 模板检索 → ③ KB 字段定义检索。注入 retrieved_contextkb_template_jrxmlkb_fields
LLM 否(向量搜索 + 字段匹配)
路由函数 route_after_retrieve (graph.py:94)

路由逻辑 (route_after_retrieve, graph.py:94-99):

  • layout_schema.total_rows > 0generate_skeleton (3 阶段)
  • 否则 → generate (1-shot)

7. generate — 1-shot 生成

属性
代码位置 agent/nodes.py:578
前驱 retrieve (graph.py:218-224, 无 layout_schema 时)
后继 save_session (graph.py:227-231)
功能 LLM 一次生成完整 JRXML。注入 OCR 上下文 + 模板上下文。流式输出。截断时续写(最多 3 轮)。
LLM
Prompt prompts/initial_generation.md

8. generate_skeleton — 骨架生成(3 阶段-1)

属性
代码位置 agent/nodes.py:657
前驱 retrieve (graph.py:218-224, 有 layout_schema 时)
后继 refine_layout (固定边 graph.py:233)
功能 压缩布局 schema → LLM 生成骨架 JRXML。字段用 $F{field_N} 占位。流式输出 + 续写。
LLM
Prompt prompts/skeleton_generation.md

9. refine_layout — 坐标精调(3 阶段-2)

属性
代码位置 agent/nodes.py:879
前驱 generate_skeleton (graph.py:233)
后继 map_fields (固定边 graph.py:234)
功能 decompose_jrxml() 拆解为 header + bands → ② 每个 band 窗口化(>4000 字符切分)→ ③ 逐窗口 LLM 精调坐标 → ④ reassemble_jrxml() 重组 → ⑤ validate_element_count() 校验(>10% 回退)。header 完全不发给 LLM。
LLM 是(N 次,N = band 窗口数)
Prompt prompts/refine_layout.md

10. map_fields — 字段映射(3 阶段-3)

属性
代码位置 agent/nodes.py:978
前驱 refine_layout (graph.py:234)
后继 save_session (graph.py:235-239)
功能 纯程序化正则替换 $F{field_N} → OCR 真实字段名。_sanitize_field_name() 净化非 ASCII 字符。零 LLM 调用。
LLM

11. modify_jrxml — 修改报表

属性
代码位置 agent/nodes.py:1022
前驱 classify_intent (graph.py:204-215, intent=modify_report)
后继 save_session (graph.py:242-246)
功能 基于现有 JRXML + 用户修改描述 + OCR 上下文 + 模板上下文 → LLM 修改。流式输出 + 续写。空响应守卫。
LLM
Prompt prompts/modification.md

12. handle_consult — 咨询解答

属性
代码位置 agent/nodes.py:261
前驱 classify_intent (graph.py:204-215, intent=consult_question)
后继 finalize (固定边 graph.py:280)
功能 LLM 回答 JasperReports 相关知识问题。回答写入 conversation_history
LLM
Prompt prompts/consult.md

13. handle_undo — 撤销

属性
代码位置 agent/nodes.py:281
前驱 classify_intent (graph.py:204-215, intent=undo_modification)
后继 save_session (graph.py:249-253)
功能 history_states 弹出最近快照,恢复 current_jrxmlconversation_historystatus。无快照时提示"无可撤销状态"。
LLM

14. handle_reset — 重置

属性
代码位置 agent/nodes.py:309
前驱 classify_intent (graph.py:204-215, intent=reset_session)
后继 finalize (固定边 graph.py:281)
功能 清空所有状态到 create_initial_state() 默认值(保留 session_idsession_name)。
LLM

15. save_session — 保存会话

属性
代码位置 agent/nodes.py:325
前驱 generatemap_fieldsmodify_jrxmlhandle_undoclassify_intent(预览/导出)
后继 条件分发: validatefinalize (graph.py:256-260)
功能 原子持久化会话 JSON (tempfile + os.replace)。序列化 agent_statesessions/{session_id}.json
LLM
路由函数 route_after_save (graph.py:118)

路由逻辑 (route_after_save, graph.py:118-123):

  • intent in (preview_report, export_pdf, export_jrxml)finalize (跳过验证)
  • 其他 → validate

16. validate — 验证

属性
代码位置 agent/nodes.py:1235
前驱 save_session (graph.py:256-260)、correct_jrxml (graph.py:273-277)
后继 条件分发: finalizeexplain_error (graph.py:263-267)
功能 ① 结构检查(字段引用一致性/SQL 存在/pageWidth/pageHeight/name)→ ② XSD 校验(可选)→ ③ 像素对比(有上传图片时 Java 渲染 JRXML→PNG + OpenCV SSIM)。
LLM
路由函数 route_after_validate (graph.py:127)

路由逻辑 (route_after_validate, graph.py:127-131):

  • status == "pass"finalize
  • status == "fail"explain_error

17. explain_error — 错误解释

属性
代码位置 agent/nodes.py:1310
前驱 validate (graph.py:263-267, status=fail)
后继 correct_jrxml (graph.py:268-272)
功能 LLM 将编译错误翻译为自然语言解释。注入 natural_explanation
LLM
Prompt prompts/explain_error.md

18. correct_jrxml — 自动修正

属性
代码位置 agent/nodes.py:1355
前驱 explain_error (graph.py:268-272)
后继 条件分发: validatefinalize (graph.py:273-277)
功能 基于错误解释 + OCR 上下文 + 模板上下文 → LLM 修正 JRXML。注入 last_error_case。去重检测(输入输出相同则 retry_count+=2)。
LLM
Prompt prompts/correction.md
路由函数 route_after_correct (graph.py:139)

路由逻辑 (route_after_correct, graph.py:139-143):

  • retry_count >= MAX_RETRY (默认5) → finalize (放弃修正)
  • retry_count < MAX_RETRYvalidate (重新验证)

19. finalize — 最终处理

属性
代码位置 agent/nodes.py:1452
前驱 validate(pass)、correct_jrxml(retry>=MAX)、handle_consulthandle_resetsave_session(预览/导出)
后继 END (graph.py:284)
功能 记录 jrxml_versions 版本历史。验证通过时设置 final_jrxml。失败时记录 pending_failure_context 供下次输入自动注入。
LLM

路由函数索引

# 路由函数 代码位置 条件 分支
R1 route_by_intent graph.py:67 state.intent 6 路: retrieve / modify_jrxml / save_session / handle_consult / handle_undo / handle_reset
R2 route_after_retrieve graph.py:94 layout_schema.total_rows > 0 2 路: generate_skeleton / generate
R3 route_after_generate graph.py:103 无条件 save_session
R4 route_after_modify graph.py:108 无条件 save_session
R5 route_after_undo graph.py:113 无条件 save_session
R6 route_after_save graph.py:118 intent in (preview, export) 2 路: finalize / validate
R7 route_after_validate graph.py:127 status == "pass" 2 路: finalize / explain_error
R8 route_after_explain graph.py:133 无条件 correct_jrxml
R9 route_after_correct graph.py:139 retry_count >= MAX_RETRY 2 路: finalize / validate

完整对话场景

场景 1: 新建报表 — 1-shot(无布局 schema

触发: intent=initial_generation + 无图片/无结构化布局

用户示例: "帮我生成一个销售报表"、"生成一个包含客户名和金额的表格"

  load_session                 nodes.py:77
→ process_input                nodes.py:98
→ manage_context               nodes.py:143
→ save_state_snapshot          nodes.py:191
→ classify_intent              nodes.py:200   意图=initial_generation
  └─ R1: route_by_intent       graph.py:67    → retrieve
→ retrieve                     nodes.py:442
  └─ R2: route_after_retrieve  graph.py:94    layout_schema 为空 → generate
→ generate                     nodes.py:578    LLM 1-shot 生成完整 JRXML
  └─ R3: route_after_generate  graph.py:103   → save_session
→ save_session                 nodes.py:325    持久化到磁盘
  └─ R6: route_after_save      graph.py:118   intent=initial_generation → validate
→ validate                     nodes.py:1235   结构检查 + XSD + 像素对比
  └─ R7: route_after_validate  graph.py:127
       ├─ status=pass → finalize              nodes.py:1452 → END ✓
       └─ status=fail → explain_error         nodes.py:1310
                         └─ R8 → correct_jrxml nodes.py:1355
                                  └─ R9:
                                     retry<5  → validate (循环)
                                     retry>=5 → finalize → END ✗

LLM 调用: classify_intent + generate + 最多 5× (explain_error + correct_jrxml) 退出好结局: final_jrxml 有值, status=pass 退出坏结局: pending_failure_context 有值, retry_count=5


场景 2: 新建报表 — 3 阶段分层生成(有布局 schema)

触发: intent=initial_generation + 上传图片 + OCR 提取到 layout_schema.total_rows > 0

用户示例: 上传销售单图片 → "根据这个模板生成报表"

  load_session                 nodes.py:77
→ process_input                nodes.py:98    OCR提取 + 布局分析
→ manage_context               nodes.py:143
→ save_state_snapshot          nodes.py:191
→ classify_intent              nodes.py:200   意图=initial_generation
  └─ R1: route_by_intent       graph.py:67    → retrieve
→ retrieve                     nodes.py:442   KB检索模板+字段
  └─ R2: route_after_retrieve  graph.py:94    layout_schema.total_rows>0 → generate_skeleton
→ generate_skeleton            nodes.py:657   阶段1: 骨架JRXML ($F{field_N}占位)
→ refine_layout                nodes.py:879   阶段2: Band级窗口化坐标精调
→ map_fields                   nodes.py:978   阶段3: 程序化字段映射
  └─ R3: route_after_generate  graph.py:103   → save_session
→ save_session                 nodes.py:325
  └─ R6: route_after_save      graph.py:118   → validate
→ validate                     nodes.py:1235
  └─ R7                        同场景1的验证循环

内容保护:

  • refine_layout: header (field/param/queryString) 完全不发给 LLM
  • refine_layout: 每窗口 ~4000 字符, LLM 无法重写整个报表
  • map_fields: 纯正则替换, 零 LLM, 100% 确定性
  • validate_element_count(): 每阶段后校验, >10% 变化回退

LLM 调用: classify_intent + generate_skeleton + N×refine_layout(N=band窗口数) + 可能的修正循环


场景 3: 修改已有报表

触发: intent=modify_report(已有 current_jrxml

用户示例: "把标题字体改大"、"在底部加合计行"、"删除第三列"

  load_session → process_input → manage_context → save_state_snapshot
→ classify_intent              nodes.py:200   意图=modify_report
  └─ R1: route_by_intent       graph.py:67    → modify_jrxml
→ modify_jrxml                 nodes.py:1022  LLM修改现有JRXML
  └─ R4: route_after_modify    graph.py:108   → save_session
→ save_session                 nodes.py:325
  └─ R6: route_after_save      graph.py:118   → validate
→ (同场景1的验证循环)

特殊逻辑: correct_jrxml 去重检测: 输入输出相同 → retry_count += 2


场景 4: 预览 / 导出(跳过验证)

触发: intent in (preview_report, export_pdf, export_jrxml)

用户示例: "预览报表"、"导出 PDF"、"下载 JRXML"

  load_session → process_input → manage_context → save_state_snapshot
→ classify_intent              nodes.py:200   意图=preview/export
  └─ R1: route_by_intent       graph.py:67    → save_session
→ save_session                 nodes.py:325
  └─ R6: route_after_save      graph.py:118   intent=preview/export → finalize
→ finalize                     nodes.py:1452  → END ✓

LLM 调用: 仅 classify_intent (1次) 跳过: generate / modify_jrxml / validate / correct_jrxml


场景 5: 咨询问答

触发: intent=consult_question

用户示例: "JasperReports 里 $F 和 $P 有什么区别?"、"怎么设置页脚?"

  load_session → process_input → manage_context → save_state_snapshot
→ classify_intent              nodes.py:200   意图=consult_question
  └─ R1: route_by_intent       graph.py:67    → handle_consult
→ handle_consult               nodes.py:261   LLM回答
→ finalize                     nodes.py:1452  → END ✓

LLM 调用: classify_intent + handle_consult (2次)


场景 6: 撤销

触发: intent=undo_modification

用户示例: "撤销"、"回退"、"恢复到修改前"

  load_session → process_input → manage_context → save_state_snapshot
→ classify_intent              nodes.py:200   意图=undo_modification
  └─ R1: route_by_intent       graph.py:67    → handle_undo
→ handle_undo                  nodes.py:281   恢复history_states快照
  └─ R5: route_after_undo      graph.py:113   → save_session
→ save_session                 nodes.py:325
  └─ R6 → validate → (验证循环)

LLM 调用: 仅 classify_intent (1次) 特殊: 无快照时提示"无可撤销状态",不改变当前状态


场景 7: 重置

触发: intent=reset_session

用户示例: "重置"、"重新开始"、"清空对话"

  load_session → process_input → manage_context → save_state_snapshot
→ classify_intent              nodes.py:200   意图=reset_session
  └─ R1: route_by_intent       graph.py:67    → handle_reset
→ handle_reset                 nodes.py:309   清空到初始状态
→ finalize                     nodes.py:1452  → END ✓

LLM 调用: 仅 classify_intent (1次)


场景 8: 兜底路由(未知意图)

触发: LLM 分类返回非标准意图

  load_session → ... → classify_intent → [未知意图]
  └─ R1 fallback (graph.py:87-90):
       ├─ state有current_jrxml → modify_jrxml (走修改路径, →场景3)
       └─ state无current_jrxml → retrieve      (走生成路径, →场景1/2)

AgentState 字段速查

字段 类型 写节点 读节点
intent str classify_intent R1 route_by_intent, R6 route_after_save
current_jrxml str generate, generate_skeleton, refine_layout, map_fields, modify_jrxml, correct_jrxml, handle_undo validate, save_session, finalize
user_input str process_input classify_intent, manage_context
user_modification_request str process_input modify_jrxml
conversation_history list process_input, finalize, handle_consult manage_context, classify_intent, modify_jrxml
full_conversation_history list process_input manage_context
compressed_history str manage_context modify_jrxml, handle_consult
retry_count int correct_jrxml, validate R7 route_after_correct
status str validate R7 route_after_validate, finalize
error_msg str validate explain_error, finalize
natural_explanation str explain_error correct_jrxml
final_jrxml str finalize (用户下载)
jrxml_versions list finalize (前端展示)
last_error_case dict correct_jrxml retrieve
pending_failure_context dict finalize process_input (下次)
layout_schema dict process_input R2 route_after_retrieve, generate_skeleton
ocr_elements list process_input refine_layout, generate_skeleton
ocr_extraction_result dict process_input map_fields, modify_jrxml, correct_jrxml
history_states list save_state_snapshot handle_undo
kb_id str process_input retrieve
kb_fields list retrieve generate_skeleton
uploaded_template_jrxml str process_input generate, generate_skeleton, modify_jrxml, correct_jrxml

LLM 调用统计

场景 classify 生成节点 窗口数 修正循环 总计(最小~最大)
1-shot 生成 1 generate=1 - 0~5×2 2 ~ 12
3 阶段生成 1 skeleton+refine×N N 0~5×2 2+N ~ 12+N
修改报表 1 modify=1 - 0~5×2 2 ~ 12
预览/导出 1 - - - 1
咨询 1 consult=1 - - 2
撤销 1 - - - 1
重置 1 - - - 1

N = band 窗口数。销售单.jrxml (73k 字符) 拆解后 N≈17。


修正循环流程

validate ──fail──→ explain_error ──→ correct_jrxml
    ▲                                      │
    │         retry_count < MAX_RETRY(5)    │
    └──────────────────────────────────────┘
                     │
                     │ retry_count >= 5
                     ▼
                  finalize (放弃, 记录pending_failure_context)

修正轮次推进:

  1. validate 失败 → status="fail", error_msg 有值
  2. explain_error → LLM 翻译错误 → natural_explanation 有值
  3. correct_jrxml → LLM 修正 → retry_count += 1。去重检测:输入输出相同 → retry_count += 2
  4. route_after_correct → retry<5 → 回到 validate; retry>=5 → finalize

失败上下文 (pending_failure_context): 重试耗尽后记录 {error_msg, bad_jrxml, retry_count, ts},下次用户消息时 process_input 自动注入到 prompt。


边定义索引(graph.py 全部边)

类型 源节点 目标节点 位置
固定边 load_session process_input line 198
固定边 process_input manage_context line 199
固定边 manage_context save_state_snapshot line 200
固定边 save_state_snapshot classify_intent line 201
条件边 classify_intent retrieve / modify_jrxml / save_session / handle_consult / handle_undo / handle_reset lines 204-215
条件边 retrieve generate / generate_skeleton lines 218-224
条件边 generate save_session lines 227-231
固定边 generate_skeleton refine_layout line 233
固定边 refine_layout map_fields line 234
条件边 map_fields save_session lines 235-239
条件边 modify_jrxml save_session lines 242-246
条件边 handle_undo save_session lines 249-253
条件边 save_session validate / finalize lines 256-260
条件边 validate finalize / explain_error lines 263-267
条件边 explain_error correct_jrxml lines 268-272
条件边 correct_jrxml validate / finalize lines 273-277
固定边 handle_consult finalize line 280
固定边 handle_reset finalize line 281
固定边 finalize END line 284