재정지원 사업 제안서 작성 AI 파이프라인 구축기
---
소개
정부 재정지원 사업 제안서를 써본 분이라면 아실 겁니다. 공고문 분석, 타기관 사례 조사, 우리 기관 역량 정리, 전략 수립, 섹션별 집필, 검토… 한 사이클에 보통 2~3주가 걸리고, 그 과정에서 담당자는 수십 개의 문서를 열었다 닫았다 반복합니다.
처음엔 단순한 목표였습니다.
▎ "타기관 선정보고서를 AI 가 읽고 패턴을 찾아줬으면 좋겠다."
그런데 하나를 자동화하다 보니 욕심이 생겼습니다. RFP 분석도, 역량 매핑도, 갭 분석도, 집필도, 검토도… 결국 "담당자가 파일만 올리면 초안까지 나오는 시스템" 을 목표로 잡게 됐습니다.
이 글은 그 과정에서 시도한 것들, 막힌 것들, 그리고 실제로 돌아가게 된 것들의 기록입니다.
---
진행 방법
사용 도구
- Claude Code (CLI) — 파이프라인 오케스트레이터 + 집필 AI
- Python (pdfplumber, python-docx, lxml) — 파일 파싱 및 출력 생성
- Google Drive + MCP Filesystem — 파일 입출력 연동
- HWPX (한글 파일 포맷) — 한국 공공기관 문서 표준
---
전체 구조 설계
처음에는 단계별 프롬프트 파일(P01~P10)을 만들어놓고 담당자가 순서대로 실행하는 방식이었습니다. 근데 그게 또 일이더라고요. 프롬프트 파일 열고, 내용 복사하고, AI에 붙여넣고, 결과 저장하고…
그래서 "파이프라인을 파이프라인답게" 다시 설계했습니다.
파일 업로드 (담당자 1회)
↓ 자동
HWPX 파싱 → RFP 분석 → 역량 매핑 → 갭 분석
↓ 자동
전략 후보 제시 → 담당자 확인 (★ 유일한 의사결정)
↓ 자동
사업 설계 → 섹션별 집필 → 3단계 검토 → HWPX 출력
담당자가 실제로 판단해야 하는 순간은 딱 하나. "이 혁신 포인트들 중 어떤 걸 쓸 거야?" 그 질문에 "1, 3번. 제목은 B안." 이라고 대답하면 나머지는 AI가 이어갑니다.
---
핵심 과제 1: 한글 파일(HWPX) 읽기
한국 공공기관 문서는 대부분 한글(.hwp/.hwpx)로 작성됩니다. 문제는 Python 생태계에 신뢰할 수 있는 HWP 파서가 없다는 것.
HWPX가 사실 ZIP 구조라는 점을 활 용해서 직접 파서를 만들었습니다.
def extract_hwpx(file_path: str) -> str:
"""HWPX 파일에서 텍스트 추출"""
paragraphs = []
with zipfile.ZipFile(file_path, 'r') as zf:
section_files = sorted([
n for n in zf.namelist()
if re.match(r'Contents/section\d+\.xml', n)
])
for sec in section_files:
with zf.open(sec) as f:
paras = parsehwpx_section(f.read())
paragraphs.extend(paras)
return '\n\n'.join(paragraphs)
HWPX XML의 텍스트 노드는 {http://www.hancom.co.kr/hwpml/2012/run}t 태그 안에 있습니다. 네임스페이스 처리가 핵심이었고, 실패 시 태그 제거 방식으로 fallback하는 구조로 만들었습니다.
구형 .hwp 바이너리는 파싱이 안 됩니다. → 한글에서 열어 HWPX로 재저장하면 해결.
---
핵심 과제 2: RFP에 배점표가 없을 때
공고마다 배점을 명시하는 곳도 있고, 그냥 "다음 항목을 작성하시오" 식으로만 나오는 곳도 있습니다. 후자의 경우 갭 분석 자체가 흔들립니다.
이 부분을 AI 추론으로 해결했습니다. P01 프롬프트에 2단계 로직을 넣 었어요.
[1단계] 배점표 탐색
RFP 본문에서 배점표, 심사 기준표, 평가 기준이 명시되어 있는가?
- 있으면: 배점 항목을 그대로 추출하고 점수 높은 순으로 정렬
- 없으면: [2단계]로 이동
[2단계] 배점 AI 추론 (배점표가 없는 경우)
RFP 전체 맥락을 분석해서:
a) 사업의 핵심 가치와 우선순위 파악
b) 목차/항목별 서술 분량 비중 분석
c) "필수", "반드시", "중점" 등 강조 키워드 출현 빈도 분석
d) 유사 정부 사업의 일반적 배점 패턴 참조
이를 종합해서 추정 배점 테이블 작성 (총점 100점)
⚠ 표 상단에 "AI 추정 배점 — 실제 공고와 다를 수 있음" 표시
실제로 배점 없는 RFP에 돌려보니 생각보다 그럴듯한 배점이 나왔습니다. 물론 ±10점 정도의 오차는 감안해야 합니다.
---
핵심 과제 3: 갭 분석 프롬프트 — 3자료 동시 교차
이 파이프라인에서 가장 중요한 단계가 갭 분석입니다. "선정기관 수준을 어떻게 넘을 것인가"를 찾는 단계거든요.
핵심은 세 가지 파일을 동시에 넣는 것입니다.
아래 3가지 자료를 교차 분석해줘:
[자료 1] rfp_structure.md ← RFP 배점 항목
[자료 2] reference_patterns.md ← 선정기관 공통 전략
[자료 3] capacity_map.md ← 우리 기관 역량
RFP 고배점 항목에서:
1. 선정기관들의 평균 접근 수준
2. 우리 기관이 현재 낼 수 있는 수준
3. 우리 기관이 선정기관을 능가할 수 있는 근거
를 항목별로 비교 정리해줘.
v1에서는 이 세 파일을 담당자가 직접 복사해서 붙여넣어야 했습니다. v2에서는 Claude Code가 파일 경로를 직접 읽어서 자동으로 제공합니다. 작은 차이 같지만 실제로 엄청 귀찮은 작업이었거든요.
---
핵심 과제 4: HWPX 출력 생성
최종 산출물을 HWPX로 만드는 것이 가장 까다로웠습니다. Python에 HWP 쓰기 라이브러리가 없어서, HWPX가 ZIP 구조라는 점을 이용해 XML을 직접 조립했습니다.
HWPX_SECTION_HEADER = '''<?xml version="1.0" encoding="UTF-8"?>
<hsp:HWPMLTextBody
xmlns:hsp="http://www.hancom.co.kr/hwpml/2012/hsp"
xmlns:hp="http://www.hancom.co.kr/hwpml/2012/paragraph"
xmlns:hr="http://www.hancom.co.kr/hwpml/2012/run">
'''
def generate_hwpx(sections, output_path, doc_title=''):
xml_parts = [HWPX_SECTION_HEADER]
for sec in sections:
if sec.title:
style = {1: 'h1', 2: 'h2', 3: 'h3'}.get(sec.level, 'h3')
xml_parts.append(_make_paragraph(sec.title, style))
for para in sec.paragraphs:
xml_parts.append(_make_paragraph(para, 'body'))
xml_parts.append('</hsp:HWPMLTextBody>\n')
with zipfile.ZipFile(output_path, 'w', zipfile.ZIP_DEFLATED) as zf:
zf.writestr('META-INF/container.xml', HWPX_CONTAINER)
zf.writestr('Contents/content.hpf', HWPX_CONTENT_HPF)
zf.writestr('Contents/section0.xml', ''.join(xml_parts))
서식이 완벽하진 않습니다. 글꼴 지정, 표 스타일 같은 세부 포맷은 한글에서 열어서 마지막에 다듬어야 합니다. 그래도 내용이 다 들어간 HWPX 파일이 자동으로 생성된다는 것 자체가 의미 있습니다.
---
Claude Code Skill 등록
이 모든 흐름을 /제안서파이프라인 한 줄로 시작하도록 Skill 파일을 만들었습니다.
.claude/commands/제안서파이프라인.md 에 Phase A~I를 정의하고, 각 Phase가 완료되면 다음 Phase를 자동으로 이어가도록 구성했습니다. 프로젝트 폴더에서 Claude Code를 열고 /제안서파이프라인을 치면:
1. 현재 어느 단계까지 됐는지 자동 진단
2. 빠진 단 계부터 순차 실행
3. 전략 확정 단계에서만 멈춰서 확인 요청
4. 확인 받으면 집필 → 검토 → HWPX 출력까지 자동 완주
---
결과와 배운 점
결과
- 인간 개입 12단계 → 1~2단계 로 축소
- HWPX/DOCX/PDF 세 가지 형식 모두 입력 가능
- HWPX + DOCX 두 가지 형식으로 출력 생성
- 배점표 없는 RFP도 AI 추론 배점으로 처리 가능
- 새로운 사업에 같은 폴더 구조 그대로 재사용 가능
시행착오
① 구형 HWP 바이너리 문제
.hwp 파일은 ZIP이 아닌 OLE 바이너리 포맷이라 파싱이 안 됩니다. 처음엔 몰랐다가 파싱 결과가 계속 비어있어서 한참 헤맸습니다. 해결책은 한글에서 열어 HWPX로 다시 저장하는 것. 자동화 시스템의 전제 조건으로 명시해두었습니다.
② AI 추정 배점의 신뢰도
배점이 없는 RFP에 AI 추론을 쓰면 "그럴듯하지만 틀릴 수 있는" 배점이 나옵니다. 처음엔 이게 불안했는데, 생각해보면 배점이 없다는 건 심사위원도 재량껏 판단한다는 뜻이라 결국 방향성 자체는 비슷하더라고요. 대신 결과물에 "AI 추정" 표시를 반드시 붙여두고, 갭 분석에서 ±10점 유동성을 감안하도록 안내문을 넣었습니다.
③ 컨텍스트 초과
섹션이 많은 제안서를 한 번에 처리하면 중간에 컨텍스트가 넘칩니다. 섹션별로 나눠서 실행하고 나중에 병합하는 방식으로 우회했습니다. 트러블슈팅 항목으로 매뉴얼에 명시해뒀습니다.
④ "자동화"와 "품질" 사이의 긴장
자동화가 되면 편하지만, 결국 AI가 모르는 맥락이 있습니다. "우리 기관이 특별히 강한 이 부분"은 문서에 안 나와있을 수 있거든요. 그래서 역량 매핑 단계에서 "데이터 부족 — 어떤 자료를 추가하면 좋을지" 안내를 출력하도록 만들었습니다. 완전 자동화보다 "부족한 부분을 빠르게 알려주는 자동화" 가 더 현실적이라는 걸 배웠습니다.
배운 점
- 파이프라인은 설계부터 — 프롬프트 하나하나를 잘 만드는 것보다, 단계 간 연결과 산출물 규격을 먼저 설계하는 게 훨씬 중요합니다.
- 포맷 파악이 자동화의 첫 단추 — HWPX가 ZIP이라는 걸 알기 전까지는 아무것도 할 수 없었습니다. 다루려는 파일 포맷의 내부 구조를 먼저 파악하세요.
- Skill 파일은 레시피 — Claude Code의 .claude/commands/ 에 만드는 Skill 파일은 단순한 프롬프트가 아니라, "어떤 파일을 읽고, 어떤 순서로 처리하고, 어디에 저장하라"는 레시피입니다. 상세할수록 일관성이 높아집니다.
- 인간이 개입해야 하는 지점을 명확히 — "모든 걸 자동화한다"는 목표보다 "사람이 판단해야 하는 단계를 명확히 1개로 줄인다"는 목표가 훨씬 현실적이고 실용적입니다.
앞으로의 계획
- 타기관 보고서 파싱 품질 개선 (표·이미지 캡션 추출)
- 섹션 집필 시 수치 자동 삽입 (capacity_map의 실적 수치를 집필 중 자동 참조)
- 실제 사업 제안서 작성에 적용하고 결과 공유 예정
---
도움 받은 글
- HWPX 파일 포맷 공식 문서 (https://www.hancom.com/product/productExcelMain.do) — 한컴오피스 오픈 API 문서에서 XML 구조 파악
- Claude Code 공식 문서 — Skill(커스텀 슬래시 커맨드) 작성 방법
- pdfplumber GitHub — 한국어 PDF 파싱 이슈 스레드 참고