sync: update all skills from latest workspace code
CI / test (push) Successful in 8s

doc_parser_skill:
- New: verify_flowchart.py (flowchart validation)
- Updated: LLM.py (multi-provider: DeepSeek + DashScope)
- Updated: image_parser.py (logic tree support, external prompts)
- Updated: SKILL.md, prompts/image_prompt.md

conflict_detection_skill:
- Updated: LLM.py (multi-provider sync)
- Updated: detect_conflicts.py (logic tree text conversion)

ir_generation_skill:
- Replaced old scripts/LLM.py + ir_generator.py with standalone project
- New: main.py, config.py, step1-3_*.py, ensemble_merge.py
- New: prompts/, tests/ subdirectories

tests:
- New: acceptance/ test suite with schema validation
- Fixed: conftest no longer globally skips non-acceptance tests
- Updated: test_sample.py for new ir_generation structure

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-30 22:45:08 +08:00
parent db64df2da1
commit fec4c09ee0
35 changed files with 8021 additions and 530 deletions
@@ -96,6 +96,77 @@ PROMPT_DETECT_CONFLICT = """你是一个文档一致性检查专家。以下内
"""
def _is_nested_tree(lt: dict) -> bool:
"""Return True if logic_tree uses the nested children format."""
return isinstance(lt.get("children"), list)
def _logic_tree_to_text(lt: dict) -> str:
"""Convert logic_tree JSON to readable text for conflict detection.
Supports both the new nested-tree format and the legacy flat-nodes format.
"""
if _is_nested_tree(lt):
return _nested_tree_to_text(lt)
return _flat_tree_to_text(lt)
def _nested_tree_to_text(tree: dict) -> str:
"""Convert a nested flowchart tree to readable text."""
lines: list[str] = []
def _walk(node: dict, indent: int = 0):
prefix = " " * indent
nid = node.get("id", "")
name = node.get("name", "")
ntype = node.get("type", "")
type_label = {
"start": "起始", "end": "结束", "process": "处理",
"decision": "判断", "action": "动作",
}.get(ntype, ntype)
lines.append(f"{prefix}[{type_label}] {nid}: {name}")
if ntype == "decision":
for child in node.get("children", []):
cond = child.get("condition", "")
lines.append(f"{prefix} 分支 \"{cond}\":")
_walk(child["node"], indent + 2)
elif "children" in node:
for child in node.get("children", []):
_walk(child, indent + 1)
_walk(tree)
return "\n".join(lines)
def _flat_tree_to_text(lt: dict) -> str:
"""Convert legacy flat-nodes logic_tree to readable text."""
lines: list[str] = []
root = lt.get("root", "")
if root:
lines.append(f"根节点: {root}")
for node in lt.get("nodes", []):
nid = node.get("id", "")
ntype = node.get("type", "")
if ntype == "decision":
cond = node.get("condition", "")
branches = node.get("branches", [])
lines.append(f"判断节点 {nid}: 条件=\"{cond}\"")
for b in branches:
lines.append(f" - 分支 \"{b.get('value', '')}\"{b.get('target', '')}")
elif ntype == "action":
lines.append(f"动作节点 {nid}: {node.get('description', '')}")
elif ntype == "state":
lines.append(f"状态节点 {nid}: {node.get('description', '')}")
elif ntype == "start":
lines.append(f"起始节点 {nid}: {node.get('description', '')}")
elif ntype == "end":
lines.append(f"结束节点 {nid}: {node.get('description', '')}")
return "\n".join(lines)
def _build_text_for_section(sections: list[dict], section_name: str) -> str:
"""Build a single text block for the given section name."""
texts: list[str] = []
@@ -184,8 +255,9 @@ def detect_conflicts(
img_type = img.get("type", "other")
rid = img.get("rid", "")
description = img.get("description", "").strip()
logic_tree = img.get("logic_tree_nested") or img.get("logic_tree")
if img_type not in DIAGRAM_TYPES or not description:
if img_type not in DIAGRAM_TYPES or (not description and not logic_tree):
logger.info("Skip conflict check: rid=%s type=%s", rid, img_type)
continue
@@ -211,8 +283,17 @@ def detect_conflicts(
logger.info(" [DRY RUN] would call LLM to detect conflicts")
continue
# Enrich description with logic_tree if available
combined_desc = description
if logic_tree:
lt_text = _logic_tree_to_text(logic_tree)
if combined_desc:
combined_desc = f"[结构化逻辑树]\n{lt_text}\n\n[文字描述]\n{combined_desc}"
else:
combined_desc = f"[结构化逻辑树]\n{lt_text}"
prompt = PROMPT_DETECT_CONFLICT.format(
image_description=description,
image_description=combined_desc,
text_description=text_content,
section_name=section_name,
)