fix: per-node max_tokens + validation 502 guard + correct_jrxml output validity
- backend/llm.py: per-node max_tokens via get_llm(max_tokens=N), LLM_MAX_TOKENS env var (default 8192) - agent/nodes.py: 5 generation nodes use max_tokens=32768, generate_skeleton retries at 65536 - agent/nodes.py: fix ns:field regex (<field → <[\w:]*field) to handle namespace prefixes - agent/nodes.py: fix correct_jrxml never writing back to state["current_jrxml"] - agent/nodes.py: correct_jrxml rejects non-JRXML output (no <jasperReport tag) - agent/nodes.py: _strip_continuation_wrapper strips markdown/prefixes from continuation rounds - agent/nodes.py: _extract_jrxml iterates multiple markdown code blocks, skips fragments - agent/graph.py: route_after_validate skips correction loop when service_unavailable - agent/graph.py: route_after_save skips validation for empty JRXML - backend/validation.py: returns service_unavailable: True for ConnectError and HTTP 5xx - Docs: CLAUDE.md v14 changelog, README.md LLM_MAX_TOKENS, .env.example LLM_MAX_TOKENS
This commit is contained in:
@@ -547,3 +547,47 @@ GET /api/sessions/{session_id}/kb # 获取会话绑定的 KB
|
||||
| `tests/test_programmatic_map_fields.py` | 20 | 字段声明替换/引用替换/中文转义/坐标保留/部分映射/空字段跳过 |
|
||||
|
||||
完整测试套件(385 项)无回归。
|
||||
|
||||
## 更新 (v14 — 2026-05-24)
|
||||
|
||||
### max_tokens per-node + 修正循环死锁修复
|
||||
|
||||
**问题 A — max_tokens 自限**: `backend/llm.py` 硬编码 `max_tokens=8192`。MiniMax M2.7 的 reasoning token 吃光 8192 输出预算后骨架生成为空(0 个可见字符)。其他节点(correct_jrxml/modify_jrxml)输入 68K+ 字符时输出也被截断。
|
||||
|
||||
**问题 B — ns:field 命名空间前缀正则失配**: `_programmatic_map_fields()` 正则 `<field\b` 匹配不到 `<ns0:field name="field_1">`,导致字段声明保持占位符但引用被替换为 OCR 字段名,校验报"used in expressions but not declared"。
|
||||
|
||||
**问题 C — 验证服务 502 修正死循环**: 验证服务(port 8001)未启动时,`validate_jrxml()` 返回 502。错误消息被当作 JRXML 校验错误送入 `explain_error → correct_jrxml`,LLM 尝试"修复"网络错误产出 HTML/markdown 等垃圾,循环 5 轮直到 retry_count 耗尽。
|
||||
|
||||
**问题 D — correct_jrxml 从未写回 current_jrxml**: 修正后的 JRXML 只写入 `conversation_history`,从不更新 `state["current_jrxml"]`,导致每轮 validate 看到同一份原始 JRXML,修正完全无效。这是 5 轮 jrxml_length 始终 4441 不变的根本原因。
|
||||
|
||||
**修复方案**:
|
||||
|
||||
#### 1. per-node max_tokens(`backend/llm.py` + `agent/nodes.py`)
|
||||
- `get_llm(caller, max_tokens=None)` — 新增可选 `max_tokens` 参数,透传到 `_build_raw_llm`
|
||||
- `MiniMaxLLM.__init__()` — 存储 `self._max_tokens`
|
||||
- `LLM_MAX_TOKENS` 环境变量覆盖默认 8192
|
||||
- 5 个生成节点 max_tokens 提升到 32768:`generate`, `generate_skeleton`, `refine_layout`, `modify_jrxml`, `correct_jrxml`
|
||||
- `generate_skeleton` 空响应自动重试(max_tokens=65536)
|
||||
|
||||
#### 2. ns:field 正则修复(`agent/nodes.py:548`)
|
||||
- `<field\b` → `<[\w:]*field\b` 兼容 `<ns0:field>`, `<field>` 等所有命名空间前缀
|
||||
|
||||
#### 3. 验证服务不可用防护
|
||||
- `backend/validation.py` — 区分 ConnectError/HTTPStatusError(5xx):返回 `service_unavailable: True`
|
||||
- `agent/nodes.py:validate` — 透传 `state["service_unavailable"]`
|
||||
- `agent/graph.py:route_after_validate` — `service_unavailable` 时直接 `finalize`,不进入修正循环
|
||||
|
||||
#### 4. correct_jrxml 输出合法性守卫
|
||||
- 新增 JRXML 有效性检查:输出不含 `<jasperReport` 且不含 `<?xml` 时,回退到前一版本
|
||||
- **Bug 修复**: `state["current_jrxml"] = jrxml` 写回修正结果
|
||||
|
||||
#### 5. 连续输出提取增强
|
||||
- `_strip_continuation_wrapper()` — 剥离续写响应中 LLM 重新添加的 markdown 代码块和自然语言前缀
|
||||
- `_extract_jrxml()` — 逐一检查多个 markdown 代码块,跳过非 JRXML 片段
|
||||
- `_generate_with_continuation()` — 续写轮次自动应用 `_strip_continuation_wrapper`
|
||||
|
||||
#### 新增环境变量
|
||||
|
||||
| 变量 | 描述 | 默认值 |
|
||||
|------|------|--------|
|
||||
| `LLM_MAX_TOKENS` | 默认 max_tokens(各节点可覆盖) | 8192 |
|
||||
|
||||
Reference in New Issue
Block a user