데이터 추출의 해답이 AI 에이전트라는 것을 깨닫기 전까지 일주일 동안 정규표현식(Regex)에 매달렸던 경험
요약
비정형 이메일 데이터에서 구조화된 정보를 추출하기 위해 정규표현식, NLP, ML 모델을 시도했으나 실패한 경험을 다룹니다. 결국 함수 호출(Function Calling) 기능을 갖춘 AI 에이전트를 활용하여 복잡한 데이터 추출 문제를 해결하는 과정을 설명합니다.
핵심 포인트
- 정규표현식은 자유 형식의 비정형 데이터 처리에 한계가 있음
- spaCy와 같은 NLP 모델은 상대적 날짜나 복잡한 문맥 파악에 어려움이 있음
- 소규모 데이터셋에 대한 ML 미세 조정은 과도한 비용이 발생할 수 있음
- AI 에이전트의 함수 호출 기능이 비정형 데이터 구조화의 효과적인 대안임
데이터 추출의 해답이 AI 에이전트라는 것을 깨닫기 전까지 일주일 동안 정규표현식(Regex)에 매달렸던 경험
몇 달 전, 저는 사용자 이메일을 파싱(Parsing)하여 이름, 날짜, 금액 및 일부 사용자 정의 필드와 같은 구조화된 데이터(Structured data)를 추출해야 하는 작은 내부 도구를 만들고 있었습니다. 이메일은 정해진 양식이 아니었습니다. "저기, $500 인보이스에 대해 논의하기 위해 다음 주 화요일 오후 3시에 회의를 잡을 수 있을까요?"와 같은 자유 형식(Free-form)의 요청이었습니다.
처음에는 "정규표현식(Regex)이면 충분해, 이건 그냥 패턴 매칭(Pattern matching)일 뿐이니까."라고 생각했습니다. 저는 틀렸습니다. 아주 크게 틀렸습니다.
문제에 대한 상세 내용
저는 다음 항목들을 추출해야 했습니다:
- 날짜 (Date) ("다음 주 화요일", "3월 5일", "내일", "이틀 뒤" 등)
- 숫자 금액 (Numeric amount) (때로는 $가 붙고, 때로는 붙지 않음)
- 사람 이름 (Person name) (종종 철자가 틀리거나 "Dr."와 같은 호칭이 붙음)
- 목적 (Purpose) ("인보이스 논의" 또는 "프로젝트 업데이트"와 같은 자유 텍스트)
입력값은 이메일 본문이었습니다. 표준 구조도, 템플릿도 없었습니다. 사람들은 말하는 방식 그대로 글을 씁니다.
시도했지만 실패했던 것들
1. 정규표현식 (Regex) (첫 번째 함정)
저는 Python의 re 모듈로 시작했습니다. 금액을 위해 r"\$?\d+(\.\d{2})?"와 같은 패턴을 작성하고, 날짜를 위해 r"(next|this) (Monday|Tuesday|...)"와 같은 패턴을 작성했습니다. 테스트 케이스에서는 작동했지만, 실제 데이터에서는 실패했습니다:
- "I owe you 500" ($ 기호 없음)
- "Let's meet on the 5th"
- "We should discuss the 1.2% fee" (금액이 퍼센트와 혼동됨)
정규표현식(Regex)은 취약합니다(Brittle). 모든 예외 케이스(Edge case)마다 새로운 패턴이 필요했습니다. 50줄의 정규표현식을 작성한 후에도 여전히 추출 작업의 절반을 놓치고 있었습니다.
2. spaCy NLP 파이프라인 (spaCy NLP pipeline)
저는 똑똑하게 spaCy의 개체명 인식 (NER, Named Entity Recognition)을 사용해야겠다고 생각했습니다. en_core_web_lg 모델을 로드하여 각 이메일에 적용했습니다. 날짜와 돈 관련 개체(Entities)는 꽤 잘 찾아냈지만, 다음과 같은 문제가 있었습니다:
- 상대적 날짜(Relative dates)를 처리하지 못함 ("next Tuesday" → spaCy는 "Tuesday"를 날짜로 태깅했지만 "next"는 해결하지 못함)
- "$500 invoice"를 MONEY + ORG로 태깅함 ("invoice"라는 단어가 ORG 태그를 유발함)
- 사용자 정의 필드(프로젝트 코드와 같은)는 완전히 놓침
결국 spaCy 위에 후처리 규칙 (post-processing rules)을 작성하게 되었습니다. 그것은 또 다른 미궁 (rabbit hole)이었습니다.
3. 커스텀 ML 분류기 (ML classifier) 구축
심지어 200개의 주석이 달린 이메일 데이터셋으로 작은 BERT 모델을 미세 조정 (fine-tuning)하는 시도까지 했습니다. 그것은 과잉 (overkill)이었습니다. 훈련하는 데 몇 시간이 걸렸고, 데이터셋이 너무 작고 다양했기 때문에 결과는 spaCy보다 크게 나아지지 않았습니다. 이틀 만에 그 시도를 포기했습니다.
결국 효과를 본 방법: 함수 호출 (function calling) 기능을 갖춘 AI 에이전트
여러 번의 막다른 길에 부딪힌 후, 저는 한 걸음 물러나 질문했습니다: "자유 형식의 텍스트 (free text)에서 구조화된 데이터 (structured data)를 추출하는 가장 유연한 방법은 무엇인가?" 정답은 지시 사항을 따르고 JSON을 출력할 수 있는 AI 언어 모델 (language model)이었습니다.
저는 원문 텍스트와 스키마 정의 (schema definition)를 입력받은 다음, LLM (저는 OpenAI의 API를 사용했지만, 함수 호출 기능이 있는 어떤 모델이든 사용할 수 있습니다)을 호출하여 필드를 추출하는 가벼운 **에이전트 (agent)**를 구축했습니다. 핵심은 **함수 호출 (function calling)**이었습니다. 저는 모델이 추출된 파라미터 (parameters)와 함께 "호출"할 수 있는 함수를 정의했습니다.
핵심 접근 방식은 다음과 같습니다:
- 출력 스키마를 위한 Pydantic 모델 정의
- 추출 규칙을 설명하는 시스템 프롬프트 (system prompt) 작성
- 추출된 데이터와 함께 함수를 호출하도록 모델에 요청
- 함수 호출 인자 (arguments)를 JSON으로 파싱 (parse)
코드 예시 (Python)
import openai
from pydantic import BaseModel, Field
from typing import Optional
...
이 접근 방식의 묘미는 스키마를 변경하면 모델이 그에 맞춰 적응한다는 점입니다. 새로운 필드를 추가하고 싶나요? 그냥 Pydantic 모델에 추가하기만 하면 됩니다. 정규표현식 (regex)을 다시 쓸 필요도, 파이프라인 (pipeline)을 변경할 필요도 없습니다.
호스팅 환경
내부 도구용으로, 저는 OpenAI API를 호출하는 작은 Flask 앱을 배포했습니다. 또한 Ollama를 통해 로컬 모델 (예: llama3)로도 테스트해 보았지만, 추출 정확도가 더 낮았습니다. 프로토타이핑 (prototyping)에는 충분하지만, 프로덕션 (production) 환경에는 적합하지 않았습니다. 유사한 엔드포인트 (endpoint)를 시도해보고 싶다면, 구조화된 추출 엔드포인트를 제공하는 https://ai.interwestinfo.com/와 같은 서비스들이 있습니다 (저는 LLM 호출 부담을 줄이기 위해 호스팅된 서비스를 사용했습니다). 하지만 기술 자체는 제공업체와 상관없이 동일합니다.
배운 점과 트레이드오프 (Trade-offs)
장점 (Pros)
- 유연성 (Flexibility): 스키마 (Schema)를 변경하는 데 몇 초밖에 걸리지 않습니다.
- 정확도 (Accuracy): LLM이 문맥을 이해합니다 (예: "다음 주 화요일"의 해석).
- 유지보수성 (Maintainability): 100개의 정규표현식 (Regex) 패턴 대신, 하나의 프롬프트 (Prompt)와 하나의 모델 정의만 있으면 됩니다.
단점 (Cons)
- 지연 시간 (Latency): LLM 호출에는 1~3초가 소요됩니다. 실시간 스트리밍 (Real-time streaming)에는 적합하지 않습니다.
- 비용 (Cost): OpenAI API는 토큰 (Token)당 비용이 발생합니다. 처리량이 많을 경우 최적화가 필요합니다 (예: 반복되는 패턴 캐싱, 더 작은 모델 사용).
- 결정론적 특성 (Determinism): 모델이 매번 약간씩 다른 JSON을 반환할 수 있습니다. 어느 정도의 변동은 괜찮지만, 결정론적인 출력이 필요한 경우에는 정규표현식 (Regex)이 더 나을 수 있습니다.
- 환각 (Hallucinations): 날짜가 언급되지 않았음에도 날짜를 지어낼 수 있습니다. 필드를 null로 남겨두도록 주의 깊게 프롬프트 (Prompt)를 작성해야 합니다.
이 방식을 사용하지 말아야 할 때
- 패턴의 집합이 매우 제한적이고 엄격한 성능 요구 사항이 있는 경우 (예: 표준화된 형식에서 주문 번호 추출).
- 트랜잭션당 발생하는 비용을 감당할 수 없는 경우.
- 외부 API 호출 없이 오프라인 처리가 필요한 경우 (단, 오픈 소스 모델들이 빠르게 발전하고 있습니다).
다음에 다시 한다면 다르게 할 점
다음에 다시 한다면, 첫날부터 AI 에이전트 (AI agent) 방식으로 시작하되, 하이브리드 (Hybrid) 방식을 구축할 것입니다. 즉, 이메일 주소와 같이 쉽고 신뢰도가 높은 패턴에는 정규표현식 (Regex)을 사용하고, 모호한 추출 (Fuzzy extraction)에는 LLM을 사용하는 방식입니다. 또한, 명백한 LLM 오류(예: 범위를 벗어난 날짜)를 잡아내기 위한 검증 레이어 (Validation layer)를 추가할 것입니다.
또한, 시스템 프롬프트 (System prompt)를 정교하게 만드는 데 더 많은 시간을 할애할 것입니다. 좋은 프롬프트는 환각 (Hallucination)을 줄이고 정확도를 극적으로 향상시킵니다.
여러분의 의견은 어떠신가요?
데이터 추출을 위해 LLM을 사용해 보신 적이 있나요? 아니면 여전히 정규표현식 (Regex)을 고수하시나요? 여러분의 경험을 듣고 싶습니다. 특히 이 작업에서 GPT-4에 필적하는 좋은 오픈 소스 모델을 찾으셨다면 꼭 공유해 주세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기