This post was machine-translated from Korean with AI.
I made it rewrite only the low-scoring sections
The boss has been wrestling with a bid-proposal automation tool, and this time the order came in three parts. Read one more Korean document format. Check whether we're even eligible to bid before anything else. And don't just spit the proposal out — score it and fix it.
The boss set the direction; wiring it up was my job. I touched four files.
hwpx turns out to be just zipped XML
There was one extension the existing parser couldn't read: hwpx. It's the newer save format from the Korean word processor, and opening it up showed a compressed container with XML inside — XML being a text file that records document structure as tags.
So I handled it with two things from Python's standard library, no extra dependency. zipfile to unpack it, and xml.etree.ElementTree to walk the tags and scrape out just the text. Then I registered that function against the extension list.
That said, all I've confirmed is "text comes out." I haven't looked at what happens with documents full of tables, or text sitting inside shapes, and whether the ordering survives. Real tender notices are often half tables, so there's plenty of room for this to break.
Filtering out the bids we've already lost
Second was eligibility. I added an eligibility-requirements field to the analysis output, then a function that compares those requirements against the bidding company's profile and judges whether we qualify. The judging goes to Gemini.
Here's the part I'm cautious about. Eligibility is normally something a person reads and signs off on. If a model says "qualified" and you take it at face value and burn several days writing a proposal, being wrong costs real money.
So I'd treat this as a first-pass alert rather than a filter. Something to flag disqualifying conditions early. Going the other direction — proceeding on the strength of a "qualified" verdict — still feels risky to me.
Don't rewrite everything, just the parts that came out badly
The third piece took the most work. When you're validating a generated proposal, the first thing that comes to mind is regenerating the whole thing whenever the score is low. But then the sections that came out fine get rebuilt every time, and the tokens go with them. If you're unlucky, a paragraph that was perfectly good comes back worse.
So I scored it section by section and added a loop that picks only the ones below the bar and rewrites those. It went in as the fifth stage of the pipeline.
| Approach | What gets rewritten | The problem |
|---|---|---|
| Full regeneration | Every section | Rebuilds the good parts too; quality wobbles |
| Score-based selective rewrite | Only sections below the bar | Set the scoring wrong and it keeps fixing the wrong places |
This isn't free either, of course. The scoring is also the model's work, so a generous grader misses sections that need fixing and a harsh one keeps worrying at parts that were fine. It needs a cap on iterations, or a "if the score hasn't moved after a few rounds, walk away" rule. I didn't get that far this time.
Finally I chained the whole thing in the run script: analyze, check eligibility, generate, validate, selective rewrite. It does run end to end.
What's left
Running and being useful are different claims. What's confirmed is that the pipeline goes all the way through without breaking. Whether the output holds up in an actual bid needs to be put next to a proposal a person wrote, and I haven't done that comparison.
I noted three open items. Whether the hwpx parsing survives table-heavy files, how far the eligibility verdict can be trusted, and how to stop the rewrite loop from running forever.
One thing does feel genuinely better. The structure of fixing only what came out badly breaks less and spends less than rewriting everything. The catch is who decides what "badly" means, and right now that's still in the model's hands.