Langgraph 에이전트의 문서 처리 오류를 해결한 5가지 방법
My Langgraph agent kept making wrong decisions on documents. Five things i changed that helped
핵심 요약
문서 처리 오류를 겪던 Langgraph 에이전트의 성능을 파싱 개선과 구조 변경으로 획기적으로 개선한 경험담.
- 문서 파싱 개선 — PyPDFLoader 대신 PyMuPDF와 LlamaParse를 조합해 구조적 데이터 손실 방지함.
- 추출 단계 분리 — 에이전트가 추론하기 전 파싱된 데이터를 정형화된 스키마로 변환하여 정확도 향상함.
- 역할 분리 — 읽기 전용 에이전트와 실행 전용 에이전트로 나누어 디버깅과 휴먼 인 더 루프 구현함.
- 신뢰도 기반 게이팅 — 중요 작업 전 신뢰도를 검사해 낮은 경우 검토 큐로 보내 사고 방지함.
Langgraph 에이전트를 구축해 벤더 계약서와 재무 문서를 처리했습니다. 핵심 용어 추출, 이상 징후 표시, 승인 라우팅, 기록 업데이트 등을 수행하죠. 에이전트가 단순히 질문에 답하는 게 아니라 읽은 내용을 바탕으로 실제 행동을 취하는 방식입니다.
잘 작동했습니다. 작동하지 않기 전까지는요….
잘못된 조항 날짜, 오독된 결제 조건, 엉뚱한 승인 단계로 라우팅되는 계약서들. 프롬프트를 조정하고 모델을 바꾸고 추론 단계를 수정하며 몇 주를 보냈지만, 완전히 해결되지는 않았습니다.
결국 에이전트 탓을 멈추고 수집 계층부터 제대로 감사를 진행했습니다. 제가 발견한 것들을 영향력 순으로 정리했습니다. 순서가 의외라 놀랐거든요.
1. 에이전트가 실제로 무엇을 읽고 있는지 측정해 본 적이 없었습니다
뒤늦게 생각해보니 당연한 거였죠. 작업과 최종 출력은 기록했지만, 문서 수집 후 에이전트가 실제로 다루고 있는 중간 상태는 기록하지 않았거든요.
모든 노드에 로깅을 추가하고 알려진 정답(ground truth)이 있는 문서 150개로 테스트했습니다. 잘못된 필드 추출이 31%나 발생하고 있었습니다.
3개월 동안 모델 탓을 했지만, LLM은 멀쩡했습니다.
2. 문서가 파이프라인으로 들어오는 방식을 교체했습니다
솔직히 말하기 부끄럽네요….
모든 것에 PyPDFloader를 사용하고 있었습니다. 일반적인 산문에는 문제가 없지만, 구조화된 데이터(결제 일정, 조항 표, 재무 부록, 병합된 셀 등)에서는 레이아웃을 완전히 망가뜨리고 있었습니다. 표가 평문으로 나오고, 다단 섹션이 붕괴되고, 행/열 문맥이 중요한 숫자들은 그 문맥을 완전히 잃어버렸죠.
에이전트는 "결제 조건: 30"이라고 추출했지만, 실제 문서에는 "결제 조건 - 최종 마일스톤 인도 후 30일"이라고 적혀 있었습니다. 추출은 완벽해 보였지만, 파싱 과정에서 행의 나머지 부분이 잘려 나갔고 원본 자료는 이미 망가진 상태였습니다.
2단계 접근 방식으로 전환했습니다:
- PyMuPDF로 각 페이지를 분류하여 일반 텍스트인지 표 구조가 많은지 판단
- 실제 구조적 복잡성이 필요한 페이지에만 Llamaparse 사용
사전 필터링은 도구 교체만큼이나 중요했습니다. 90페이지짜리 계약서라도 실제 구조적 복잡성이 있는 건 12페이지뿐일 수 있거든요. 90페이지 전부를 보내는 건 낭비였습니다…. 사전 필터링으로 필요한 부분에만 정밀도를 집중했더니, 구조화된 문서의 잘못된 필드 추출 비율이 31%에서 8%로 줄었습니다!
3. 파싱과 에이전트 추론 사이에 전용 추출 단계를 추가했습니다
파싱을 수정한 후 에이전트는 깔끔한 마크다운을 받았지만, 여전히 모든 걸 한 번에 처리하고 있었습니다. 문서 읽기, 필드 추출, 결정 내리기를 같은 문맥에서 수행했죠. 그래서 에이전트가 손대기 전에 파싱된 마크다운을 타입이 지정된 스키마로 변환하는 전용 추출 노드를 추가했습니다. 에이전트는 구조화된 JSON을 받게 됩니다. 추론은 payment_terms.days: 30이라는 데이터를 가지고 수행되므로, 문단 중간에서 값을 찾으면서 동시에 무엇을 할지 고민할 필요가 없어 집중력이 유지됩니다.
또한 디버깅이 훨씬 쉬워졌습니다. 문제가 생기면 에이전트가 어떤 스키마를 받았는지, 오류가 추출 단계인지 추론 단계인지 즉시 알 수 있습니다. 이전에는 그런 구분이 없어서 우주 미아가 된 기분이었죠.
4. 읽기와 행동을 분리했습니다
하나의 에이전트가 둘 다 하는 건 감사하기도 어렵고 깔끔하게 중단하기도 불가능했습니다. 그래서 두 개의 노드로 분리했습니다:
- 리더 에이전트: 수집, 추출, 필드별 신뢰도를 포함한 구조화된 요약 생성
- 액션 에이전트: 요약을 전달받고 원본 문서는 보지 않음 + 하위 작업 실행
가장 큰 실질적 이득은 낮은 신뢰도의 추출 결과에 대해 액션 로직을 건드리지 않고도 두 노드 사이에 사람을 개입시킬 수 있게 된 점입니다. 문제가 생기면 읽기 문제인지 행동 문제인지 1분 안에 파악할 수 있습니다. 이전에는 그냥 추측뿐이었죠.


