Skip to main content

LLM 기반 국책과제계획서·제안서·보고서(HWP/HWPX) CLI 자동 작성 파이프라인

Project description

ReportGen

A CLI pipeline for AI-assisted drafting of Korean national R&D project plans, proposals, and technical reports (HWP/HWPX) — from form analysis to a finished document, in partnership with an external LLM agent.

Python PyPI License Platform Tests 5 Table Types


Overview

ReportGen is a command-line tool, not a hosted service or a model. You give it an HWPX-based form — a national R&D project plan, a proposal, a technical report, any similarly structured Korean institutional document — plus reference material (PDF/TXT/HWPX), and it does two things:

  1. Analyzes the form's structure and emits a mapping-request artifact that any external LLM agent can read.
  2. Applies the agent-authored mapping back onto the form to produce a finished HWPX file.

The pipeline itself never calls an LLM API. Content generation is delegated entirely to whichever external agent drives the CLI — Claude Code, OpenCode, Codex, or any other coding agent/harness. This means no API-key management and no lock-in to a single model provider inside the tool, and it's why the same pipeline works unmodified whether you're filling out a dementia-research R&D plan, a public-works proposal, or a mid-term technical report. ReportGen ships through three channels — PyPI, standalone binaries, and a Claude Code plugin — so it installs cleanly in any environment.


Features

  • 🔍 Automatic HWPX structure analysis — TOC detection, 5-way table classification (DATA/INSTR/SKIP/SCHED/IDENT), and paragraph placeholder detection in one pass.
  • 🤝 Mapping-request artifact generation — emits request.json + prompt.md for any external LLM agent to read. Zero internal LLM calls.
  • ✍️ FieldMapping apply engine — takes the agent-authored JSON mapping and writes it into the HWPX XML via three precise actions: replace / fill_cell / insert_after.
  • 🎯 Adaptive fill — no per-form hardcoding — table cells can be targeted by structural intent instead of raw coordinates: a label field resolves to whatever cell sits next to that label text, and a schedule field expands a Gantt-chart task into the right monthly cells — both computed fresh from each document's own structure, so the same code works on any form of that shape.
  • 🔤 Font consistency & line-wrap safety — automatically applies a 12pt non-bold Malgun Gothic charPr, and strips stale <hp:linesegarray> caches so Hangul Word doesn't render overlapping text after reflow.
  • 📎 Reference-material ingestion — PDF/TXT files are parsed automatically and folded into the mapping-request payload.
  • 🛡️ Structural verification gate — checks for table loss, coordinate anomalies, and paragraph fill rate right after the pipeline runs, and measures accurate fill rate excluding SKIP/SCHED tables.
  • 🔁 HWP → HWPX conversion — converts binary HWP to ZIP-based HWPX via a Java fat JAR.
  • 📦 Three distribution channels — PyPI (update-notification), standalone binary (true self-update), and the Claude Code plugin marketplace. See Installation for details on each.

Quick Start

pip install reportgen-pipeline

# Phase 1 — analyze the form & emit a mapping request (no LLM call)
reportgen \
  --template  input/project-plan-form.hwpx \
  --reference input/project-summary.txt input/rfp.pdf \
  --emit-mapping-request request.json

# (an external LLM agent reads request.json and writes mappings.json)

# Phase 2 — apply the mapping & produce the finished HWPX
reportgen \
  --template input/project-plan-form.hwpx \
  --mappings mappings.json \
  --output   output/result.hwpx \
  --measure

How It Works

  1. Form analysis (Phase 1)StructureAnalyzer parses the HWPX form's table/paragraph structure, auto-classifies every table into one of 5 types (DATA/INSTR/SKIP/SCHED/IDENT), detects fill targets, and emits request.json (full detail) plus a compact digest from digest.py (a few dozen KB, via --emit-digest) and an optional prompt.md. This phase never calls an LLM API.
  2. An external LLM agent fills it in — Claude Code, OpenCode, or any other agent designs the fill plan from the compact digest (not the full request.json, especially for large forms), targets only the specific elements it needs from request.json, and writes mappings.json following the FieldMapping[] contract. Swapping models or providers never requires touching the pipeline code.
  3. Mapping apply (Phase 2)SmartFillEngine writes mappings.json into the HWPX XML: replace (paragraphs, with stale linesegarray cleared), fill_cell (table cells, coordinates verified against the real document — or resolved dynamically from a label/schedule intent), insert_after (new paragraphs, 12pt Malgun Gothic applied).
  4. Verify + measureverify_output gates on table loss and paragraph fill rate, and measure_fill_rate computes an accurate fill rate against the original template, excluding SKIP/SCHED tables. The finished output.hwpx opens directly in Hangul Word Processor.

Core design principles

Principle What it means
Form-invariant Instruction tables ("delete before submission"), diagram-like tables, and identity tables are preserved as-is; only genuine data tables get filled
Line-wrap safety Stale <hp:linesegarray> caches are stripped so Hangul Word reflows cleanly, with no overlapping text
Dynamic classification, never hardcoded Gantt charts, org charts, and empty grids are identified by structural pattern, not by memorizing any one form's layout
Content stays external Content generation is delegated to an external agent — model and provider stay fully swappable
Accurate measurement Fill rate is computed against the original template, excluding SKIP/SCHED tables
Digest-first analysis The design phase reads a compact digest instead of the full request.json — measured example: a 29-table form went from a 1.5MB request.json to a 45KB digest

LLM Allocation — how model tiers are assigned

ReportGen's own code contains no LLM calls anywhere. It is a deterministic CLI: Phase 1 and Phase 2 are pure structural analysis and XML editing, and --measure is a pure scoring script. Every piece of authored content comes from whatever external agent invokes the CLI — which means you (or your coding agent) decide which model tier does which job. The tool is designed around a specific 3-stage division of labor so that model spend tracks task difficulty instead of being uniform:

Stage Tier required Input it should read Why
① Form analysis & fill design High-tier (e.g. Opus-class, GPT-5.6-class "high"/"solve" reasoning, or an equivalent top-tier reasoning mode) The compact digest (--emit-digest), not the full request.json This is the step that decides what goes in each field, how much content each section needs, and where a large table should be preserved vs. filled — the single highest-leverage decision in the whole run, so it deserves the strongest available model. Reading the full request.json here (which can be hundreds of KB to >1MB on large forms) burns tokens without improving the decision, since the digest already carries everything structural: element indices, previews, table classifications, and fill coordinates.
② Field content generation Mid-tier (e.g. Sonnet-class, GPT-5.6 standard, or a comparable general-purpose coding-agent model) The Phase ① design plan, plus targeted lookups into request.json by element_index only when exact source text or merge structure is needed Once the design is set, writing the actual sentence/number for each target is comparatively mechanical — a mid-tier model handles it reliably and far more cheaply.
③ Verification & fill-rate measurement None — deterministic code --measure output This is fully computed by measure_fill_rate.py; no model judgment is involved. Only re-invoke a mid-tier model here if the measured fill rate looks abnormally low or the output content looks suspicious — a high-tier model is never needed for this step.

A few corollaries that follow directly from this design:

  • Never use a lightweight/mini-tier model at any stage. The whole pipeline is calibrated around "one strong analysis pass, one competent fill pass" — a cheap model at either stage tends to produce sparse content or mis-scoped table decisions, which is exactly the failure mode the digest/design split is meant to prevent.
  • Simple forms don't need the two-stage split. A form with a handful of tables and under ~20 paragraph targets is fine end-to-end in a single mid-tier session — reserve the high-tier design pass for large forms (10+ tables, multi-year tables, complex row/merge structures), where it earns its cost.
  • Cache your research. If you're filling out many proposals in the same domain, save web-research findings (e.g. input/research_facts.md) and reuse them — re-searching the same domain facts on every run is wasted spend, independent of which model tier you're using.
  • Prefer intent over coordinates when authoring mappings.json. Use label/schedule fields (see FieldMapping schema) instead of hand-counting table_row/table_col — this is what makes the same mapping-authoring approach transfer across different forms without new per-form logic.

CLI

--template   <HWPX form path>              (required)
--output     <output HWPX path>            (required for Phase 2)
--reference  <file1> <file2> ...           PDF/TXT/HWPX reference files
--data       <user_data.json>              additional structured data (optional)
--instructions "extra instructions"        additional instructions for the agent (optional)
--emit-mapping-request <request.json>      Phase 1: emit the mapping request
--emit-digest <digest.json>                Phase 1: emit the compact structural digest (recommended primary input for the design stage)
--prompt-output <prompt.md>                Phase 1: emit a Markdown prompt (optional)
--mappings   <mappings.json>               Phase 2: apply the mapping
--analysis-output <analysis.json>          save the structure analysis result (optional)
--measure                                  print the fill rate on completion
--version                                  show version
--upgrade                                  standalone-binary channel: self-upgrade immediately
--no-update-check                          skip the startup update check
--verbose

After converting HWP → HWPX

java -jar java/hwp2hwpx-fat.jar input/form.hwp input/form.hwpx
reportgen --template input/form.hwpx --emit-mapping-request request.json

Measuring fill rate separately

python scripts/measure_fill_rate.py --file output/result.hwpx --template input/form.hwpx
=======================================================
Fill Rate Report: result.hwpx  [template]
=======================================================
TABLE CELLS (DATA tables only, SKIP/SCHED excluded):
  Total data cells  : 86
  Filled cells      : 86
  Fill rate         : 100.0%  [##################################################]

PARAGRAPH TARGETS (from template):
  Total targets     : 66
  Filled            : 60
  Fill rate         : 90.9%  [#############################################-----]
=======================================================

Installation

Channel Command Notes
PyPI (recommended) pip install reportgen-pipeline Public distribution; checks for a new version once a day at runtime and prints a pip install -U notice
Standalone binary Download reportgen-<os>-<arch> from Releases For internal/collaborator use (private repo, requires auth); true self-update in the OpenCode style
Source (for development) git clone then pip install -e . Includes the reportgen CLI, editable install

Prerequisites

  • Python 3.10+
  • Java 11+ / Maven (only needed for HWP → HWPX conversion)

PyPI channel details

pip install reportgen-pipeline
pip install -U reportgen-pipeline   # upgrade
reportgen --version

pip deliberately never reinstalls itself automatically (this is correct, safe behavior). The CLI only prints a "a new version is available — run pip install -U" notice, the same way gh or npm do (disable with REPORTGEN_NO_UPDATE_CHECK=1). This repository is private, but PyPI is a separate public registry, so this channel is reachable by anyone.

Standalone binary channel details

gh release download v0.3.0 --repo kaismin82/ReportGen-pipeline \
  --pattern "reportgen-<os>-<arch>*"
./reportgen-windows-x86_64.exe --template ... --emit-mapping-request request.json

⚠️ Because this repository is private, this channel only works for people with repo access (anonymous requests get a 404 from the GitHub API). Authentication comes from gh auth token or the GITHUB_TOKEN/GH_TOKEN environment variable. Use the PyPI channel if you need public distribution.

Unlike the pip package, a standalone binary owns nothing but itself (no shared venv or pinned-version environment to break), so on every run, if it detects a newer version it replaces itself and immediately re-executes the same command — the same mechanism OpenCode uses for opencode upgrade. Force it immediately with reportgen --upgrade, or disable it with REPORTGEN_NO_AUTOUPDATE=1.

Source install & conda caveat

git clone https://github.com/kaismin82/ReportGen-pipeline.git
cd ReportGen-pipeline
pip install -r requirements.txt      # dependencies only
pip install -e .                     # or an editable install (includes the reportgen CLI)
bash setup.sh --java                 # (optional) build the HWP→HWPX converter

conda environments may already have a different package also named hwpx installed (pip install hwpx ❌ ≠ pip install python-hwpx ✅):

pip install python-hwpx --force-reinstall

Architecture

Module Role
universal_pipeline.py 📌 Main CLI entry point (the reportgen command)
document_model.py Data classes (DocumentElement, TableInfo, FieldMapping)
structure_analyzer.py HWPX structure analysis · table/paragraph classification
dynamic_template_analyzer.py Analysis orchestrator (no LLM calls)
smart_fill_engine.py HWPX XML edit engine (replace/fill_cell/insert_after)
mapping_resolve.py Resolves label/schedule intent mappings into concrete fill_cell mappings
schedule_resolve.py Expands a Gantt-chart task intent into the right monthly cells, from the table's own structure
label_resolve.py Resolves a label intent to its value cell by text match + spatial rule
identity_fill.py Auto-fills IDENT (identity/institution) tables from user_data, with zero fabrication
table_resolve.py Nested-table resolver (passes through layout frames)
toc_detection.py TOC detection · table-classification helpers · blank-detection regex
reference_reader.py PDF/TXT reference-file parser
schedule_handler.py Gantt-chart metadata extraction
verify_output.py Output structural verification gate
measure_fill_rate.py Fill-rate measurement (accurate, computed against the template)
digest.py Compact structural digest generation (computed from elements alone; primary input for the design stage)
hwpx_compat.py / hwpx_utils.py HWPX-package compatibility layer · shared utilities
update_check.py PyPI latest-version check/notice (pip channel)
self_update.py Standalone-binary self-update (private repo, requires auth)
bump_version.py Bulk version bump · release · CI consistency check
ReportGen-pipeline/
├── scripts/            # all modules listed above + archive/ (legacy scripts)
├── tests/               # pytest suite (103+ passing)
├── java/                # HWP→HWPX converter (Convert.java, hwp2hwpx-fat.jar)
├── .claude-plugin/      # Claude Code marketplace catalog
├── reportgen/           # Claude Code plugin (distribution copy: plugin.json + SKILL.md)
├── .claude/skills/      # project-scoped skill (plugin mirror)
├── .github/workflows/   # ci.yml · publish.yml (PyPI) · build-binaries.yml
├── docs/                # design proposals
├── pyproject.toml       # package metadata · build config
└── requirements.txt

Table classification scheme

Class Criteria Handling
DATA Ordinary data table Filled by the agent (fill_cell)
INSTR Contains authoring instructions ("delete before submission") Preserved (never touched)
SKIP Org chart, WBS, empty grid Preserved as-is
SCHED Gantt chart (task rows + monthly columns) Filled via a schedule intent mapping
IDENT PI/institution identity table Auto-filled from user_data if present, otherwise preserved

Claude Code Skill

ReportGen ships as a CLI-driven Claude Code skill — invoking /reportgen doesn't run a hosted service; it drives the same reportgen CLI described above from your local shell, with an external agent (Claude Code itself, in this case) doing the analysis and fill-content authoring per the LLM Allocation model. You can use it two ways:

Option 1 — Plugin marketplace (recommended, auto-upgrading)

/plugin marketplace add kaismin82/ReportGen-pipeline
/plugin install reportgen@reportgen-marketplace

New releases are detected and downloaded automatically in the background, and activate automatically the next session (restart) with no command needed (run /reload-plugins once to pick it up immediately in the current session). ⚠️ Since this repository is private, this channel is also collaborator-only.

Option 2 — Directly from the repository Cloning the repository makes .claude/skills/reportgen/SKILL.md recognized immediately as a project-scoped skill. Update with git pull.

Use the /reportgen skill to draft a project plan matching this form.
- template: input/project-plan-form.hwpx
- reference files: input/project-summary.txt input/rfp.pdf
- output file: output/result.hwpx

The skill runs Phase 1 automatically → drafts mappings.json from reference material and web search → runs Phase 2 → confirms fill rate with --measure.


Versioning & Release Automation

Version strings (pyproject.toml, universal_pipeline.py, plugin.json) are managed as a single source of truth by scripts/bump_version.py:

python scripts/bump_version.py 0.3.0               # bump the version everywhere (incl. SKILL.md mirror sync)
python scripts/bump_version.py 0.3.0 --release      # + commit · tag (v & reportgen--v) · push · GitHub release
python scripts/bump_version.py --check              # CI consistency check (run automatically by ci.yml)

Once a GitHub Release is published, two workflows follow automatically:

  • publish.yml → publishes sdist/wheel to PyPI via Trusted Publishing (OIDC) — no token needed (requires a one-time PyPI pending-publisher + GitHub pypi environment setup)
  • build-binaries.yml → builds, verifies, and attaches standalone binaries for the Linux/macOS/Windows matrix as release assets

FAQ

Q: I get ImportError: cannot import name 'HwpxPackage' from 'hwpx'. A conflicting, unrelated package also named hwpx is installed in your conda environment. Run pip install python-hwpx --force-reinstall.

Q: After Phase 1, which files do I hand to the external agent? Hand it request.json (from --emit-mapping-request), the compact digest (from --emit-digest), and prompt.md (from --prompt-output) if you generated one. The agent should design the fill plan from the digest, target only the specific elements it needs from request.json, and write a FieldMapping[] JSON array to mappings.json.

Q: The analysis stage burns too many tokens on a large, table-heavy form. Generate the compact digest with --emit-digest and feed that to the design-stage model instead of request.json. The digest doesn't repeat each element's full text — it carries only indices, previews, table classifications, and fill coordinates, compressing a 29-table form's request.json from 1.5MB down to 45KB in one measured case. Only look up request.json by element_index when you need a specific element's exact source text.

Q: My paragraph fill rate comes out low. Always pass --template alongside --measure. Fill quality itself depends on the external agent's model and prompt quality — see LLM Allocation.

Q: An instruction table got filled in. structure_analyzer.py's _is_instruction_table() treats a "delete before submission" marker as a strong signal. If your form doesn't carry that marker, add another instruction signal to _DELETE_RE.

Q: The Gantt chart / schedule table gets filled oddly. Prefer a schedule intent mapping over hand-picked coordinates — it lets schedule_resolve.py detect the month-header row and task blocks from the table's own structure. Structural metadata is also exposed via schedule_handler.detect_schedule_meta() in request.json if you need to inspect it directly.

Q: Why do the pip channel and the standalone-binary channel self-update differently? A pip package lives inside a shared venv / pinned-version environment, so silently bumping its own version could break a user's lockfile — hence "notify only." A standalone binary owns nothing but itself, so a true self-replace is safe.


Development

pip install -e ".[test]"
pytest tests/ -v                                                       # full suite
pytest tests/test_output_qa.py tests/test_smart_fill_engine_p0.py -v   # core invariants only
python scripts/bump_version.py --check                                 # version consistency check
Test file Verifies
test_output_qa.py Absence of stale linesegarray · cell placement · resolver invariants
test_smart_fill_engine_p0.py replace/fill_cell/linesegarray stripping
test_structure_analyzer_p1.py TOC detection · header extraction · instruction tables
test_mapping_artifact_handoff.py Phase 1/2 CLI round-trip
test_self_update.py Platform → asset-name mapping, version comparison, asset-selection logic
test_digest.py Table-classification precedence, fill-target extraction, large-table coordinate-omission threshold
test_e2e_pipeline.py Full flow from structure analysis through SmartFillEngine
test_korean_edge_cases.py Full-width brackets, ZWSP, full-width-space blank detection
test_schedule_table.py Gantt-table detection · metadata extraction
test_mapping_resolve.py label/schedule intent dispatch and fail-soft skipping
test_schedule_resolve.py Dynamic month/task-block detection across varied Gantt shapes
test_label_resolve.py Label text matching + spatial value-cell resolution
test_identity_fill.py Zero-fabrication identity auto-fill, duplicate-label safety
test_quality_gates.py Section-tier classification and content-length quality gates

License & Credits

MIT — see LICENSE. Free to use, modify, and distribute.

Project Role License
python-hwpx HWPX file parsing/packaging (HwpxPackage) MIT
hwp2hwpx by neolord0 HWP → HWPX binary conversion (Java) Apache 2.0
@ohah/hwpjs by ohah HWP → JSON/Markdown/HTML (Node.js) MIT
lxml HWPX XML parsing and manipulation BSD
python-dotenv .env environment-variable loading BSD
pypdf PDF text extraction BSD

Reference & inspiration: hwp-pipeline (Yoojin-nam) — the Claude Code skill whose early design informed this project · the HWP/HWPX format specification (Hancom HWPML 2011/2016) · Claude Code by Anthropic

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

reportgen_pipeline-0.3.0.tar.gz (143.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

reportgen_pipeline-0.3.0-py3-none-any.whl (96.9 kB view details)

Uploaded Python 3

File details

Details for the file reportgen_pipeline-0.3.0.tar.gz.

File metadata

  • Download URL: reportgen_pipeline-0.3.0.tar.gz
  • Upload date:
  • Size: 143.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for reportgen_pipeline-0.3.0.tar.gz
Algorithm Hash digest
SHA256 94f0b7512d042223130a7185b1581ce8f41b2410de2910f2fc97b3ff7362c173
MD5 88af48dbc3cba60a14a6d0247684dfd9
BLAKE2b-256 9181205f972798291f0f003fe2949f0a825784dea21b10729a06599c8d709dd6

See more details on using hashes here.

Provenance

The following attestation bundles were made for reportgen_pipeline-0.3.0.tar.gz:

Publisher: publish.yml on kaismin82/ReportGen-pipeline

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file reportgen_pipeline-0.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for reportgen_pipeline-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6ba66dc78ccfc92f28a4f1f0bb6acbdda7d02a3fc8cb0cd02586ed67ce14eaa1
MD5 e25d3b4b9c4a766b4f62b45f1bac3c03
BLAKE2b-256 38115c631c1a05efda0541c5a422aa63511eb8f37f1dbdc094393f1d845b4e41

See more details on using hashes here.

Provenance

The following attestation bundles were made for reportgen_pipeline-0.3.0-py3-none-any.whl:

Publisher: publish.yml on kaismin82/ReportGen-pipeline

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page