본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 26. 04:41

자동화된 콘텐츠 파이프라인에서 GPT 환각(Hallucination) 방지하기: 데이터 주입을 활용한 Make.com 워크플로우 구조화 방법

요약

Make.com을 활용한 자동화 콘텐츠 파이프라인에서 발생하는 GPT 환각 문제를 구조적 데이터 주입(Data Injection)으로 해결하는 방법을 다룹니다. 프롬프트 수정 대신 데이터 검증 및 구조화된 사실 블록 구축 단계를 추가하여 모델이 학습 데이터가 아닌 제공된 사실에만 기반하도록 설계합니다.

핵심 포인트

  • 환각의 근본 원인은 모델 성능이 아닌 데이터 부재에 있음
  • 프롬프트 트릭보다 구조적인 데이터 주입 패턴이 더 효과적임
  • 데이터 검증 및 구조화된 사실 블록 구축 모듈 추가 권장
  • LLM이 제공된 사실 범위를 벗어나지 못하도록 제약 조건 설정

저는 매일 스포츠 베팅 기사를 생성하는 Make.com 파이프라인을 운영하고 있습니다. Odds API 입력, API-Football 입력, 중간 단계의 데이터 집계, 작성을 위한 GPT-4o, 그리고 Google Docs 출력으로 구성됩니다. 다이어그램상으로는 완벽해 보였고, 테스트 단계에서도 아주 잘 작동했습니다.

하지만 실제 배포가 이루어졌습니다. 그리고 일주일도 채 되지 않아, 스페인 2부 리그 팀을 "현 챔피언스 리그 우승팀"이라고 확신하며 독자들에게 말하거나, 38세의 공격수가 "방금 첫 프로 계약을 체결했다"고 하거나, 제가 개인적으로 가장 좋아했던 사례인 토요일로 예정된 경기가 "지난 화요일에 시작되었다"고 말하는 기사들이 등장했습니다.

모든 사실은 기술적으로는 그럴듯했습니다. 하지만 모든 사실이 완전히 틀렸습니다.

이 포스트는 그런 일이 발생하지 않도록 제가 무엇을 변경했는지에 대한 내용입니다. 영리한 프롬프트(Prompt) 트릭이 아닙니다. 모델을 교체한 것도 아닙니다. LLM 호출로 데이터가 흘러 들어가는 방식의 구조적인 변화입니다.

원인은 거의 항상 "모델" 때문이 아닙니다

GPT가 사실을 지어낼 때 본능적으로 모델을 탓하게 됩니다. 더 큰 모델, 더 나은 프롬프트, 더 낮은 온도(Temperature) 설정 등을 생각하죠. 하지만 대부분의 경우 그것은 틀렸습니다. 파이프라인에서 환각(Hallucination)은 거의 항상 GPT에게 알 방법이 없는 것을 알라고 요구하는 프롬프트 때문에 발생합니다.

저의 원래 시스템 프롬프트(System Prompt)는 다음과 같았습니다:

당신은 스포츠 베팅 기자입니다. 다가오는 경기에 대한 600단어 분량의 경기 프리뷰를 작성하세요. 팀의 최근 흐름, 상대 전적(H2H), 주요 선수, 그리고 베팅 관점을 다루어야 합니다. 권위 있고 자신감 있게 작성하세요.

그다음 사용자 프롬프트(User Prompt)에는 두 팀의 이름과 경기 날짜가 포함되어 있었습니다.

이 부분을 주의 깊게 읽어보세요. 저는 GPT에게 팀의 흐름, 상대 전적, 주요 선수에 대해 권위 있게 작성하라고 명령했지만, 정작 그 정보들을 전혀 제공하지 않았습니다. 그래서 GPT는 데이터가 없는 상태에서 자신감 있는 질문을 받았을 때 LLM이 하는 행동을 정확히 수행했습니다. 즉, 몇 달 또는 몇 년 전의 오래된 학습 데이터에서 가져온, 스포츠 저널리즘처럼 "들리는" 무언가를 생성하며 그 공백을 환각으로 채워버린 것입니다.

해결책은 "GPT에게 환각을 일으키지 말라고 말하는 것"이 아닙니다. 그런 지시사항은 거의 아무런 효과가 없습니다. 해결책은 GPT에게 필요한 모든 사실을 제공하고, 그 사실을 벗어나는 것을 금지함으로써 환각이 구조적으로 불가능하게 만드는 것입니다.

이것이 사람들이 "데이터 주입 (Data Injection)"이라고 말할 때 의미하는 바입니다. 이것은 전문 용어가 아니라, 하나의 제약 패턴 (Constraint Pattern)입니다.

워크플로우에서 변경된 점

모듈 수준에서의 전후 차이는 다음과 같습니다.

변경 전:

Odds API → API-Football → Aggregator → GPT-4o → Google Docs

Aggregator 모듈은 GPT에 깨끗한 객체 (Object)를 전달했습니다. 프롬프트는 GPT에게 프리뷰를 작성하도록 요청했습니다. "여기 두 팀의 이름이 있습니다"와 "600단어의 자신감 있는 콘텐츠" 사이의 모든 내용은 GPT가 그럴듯하게 들리는 헛소리로 채워 넣는 블랙박스 (Black Box)였습니다.

변경 후:

Odds API → API-Football → Aggregator → Data Validator →
  Structured Fact Block Builder → GPT-4o (constrained) →
  Output Validator → Google Docs (or Error Queue)

세 개의 모듈이 추가되었습니다. 이 중 어느 것도 AI가 아닙니다. 이 모듈들은 모두 GPT가 오직 검증된 사실만을 보도록 하고, 구조적으로 더 많은 내용을 지어내는 것을 방지하기 위해 존재합니다.

하나씩 살펴보겠습니다.

1. Data Validator: 데이터가 환각이 되기 전에 누락된 데이터를 포착하기

환각은 종종 Null 필드에서 발생합니다. 만약 API가 5년 동안 경기를 치르지 않은 두 팀에 대해 상대 전적 (Head-to-head) 데이터를 반환하지 않고, Aggregator가 GPT에 h2h: null을 전달한다면, 프롬프트는 여전히 GPT에게 "상대 전적 역사를 다루라"고 요청할 것입니다. GPT가 그 상황에서 어떻게 행동할지 예상해 보십시오.

Data Validator는 기사가 참조할 모든 필드를 확인하는 Make.com의 라우터 (Router)입니다. 필수 필드가 누락된 경우, 워크플로우는 분기됩니다. 기사를 완전히 건너뛰거나, 대체 데이터 (Fallback)를 가져오거나, 혹은 진행하되 해당 필드를 "데이터 사용 불가"로 표시합니다. 이 표시된 정보는 나중에 프롬프트에서 엄격한 제약 조건 (Hard Constraint)으로 처리됩니다.

예시: 저는 각 팀의 최근 5경기를 요구합니다. 만약 API가 어느 한 쪽이라도 5경기 미만의 데이터를 반환한다면, 워크플로우는 실패하는 대신 form_data_complete: false 플래그 (Flag)를 설정하며, 이후 단계의 프롬프트 빌더 (Prompt Builder)가 이를 다르게 처리합니다.

코드와 유사한 관점에서 설명하자면 (Make.com 설정에서는 Router + 필터(filters)를 사용하겠지만, 로직은 다음과 같습니다):

const required = {
  home_team_name: data.home_team?.name,
  away_team_name: data.away_team?.name,
...

이 단일 단계는 그 어떤 프롬프트 변경보다 더 많은 환각 (Hallucination)을 제거했습니다. 초기 단계에서 발생한 대부분의 잘못된 기사들은, 근본적인 데이터가 불완전했기 때문에 GPT가 애초에 작성하도록 요청해서는 안 되었던 기사들로부터 비롯되었습니다.

2. 구조화된 사실 블록 (Structured Fact Block): GPT에게 진실로 가득 찬 컨텍스트 윈도우 (Context Window) 제공

이것이 데이터 주입 (Data Injection) 패턴의 핵심입니다. GPT에게 학습 데이터 (Training Data)를 사용하여 팀에 대해 쓰라고 요청하는 대신, 프롬프트 빌더 (Prompt Builder)가 검증된 API 데이터로부터 결정론적이고 구조화된 사실 블록을 구축합니다. 그리고 시스템 프롬프트 (System Prompt)는 _아래 블록에 있는 사실만을 사용할 것_이라고 다시 작성됩니다.

주입되는 내용은 다음과 같습니다:

=== 경기 사실 (이 내용만 사용하십시오) ===

경기 일정 (FIXTURE)
...

블록에 포함되지 않은 내용에 주목하십시오. 추측성 내용, "최근 흐름 (Form Trends)"에 관한 내용, 선수 서사에 관한 내용은 아무것도 없습니다. 오직 API가 확인한 데이터 포인트 (Data Points)뿐입니다.

이것을 구축하는 Make.com 모듈은 템플릿 변수 (Template Variables)를 사용하는 텍스트 애그리게이터 (Text Aggregator)일 뿐이며, AI는 관여하지 않습니다. 이는 결정론적 (Deterministic)이며 감사 가능 (Auditable)합니다. 만약 기사에 "솔란케(Solanke)가 지난 주말 두 골을 넣었다"라고 적혀 있다면, 저는 그것이 정확히 어떤 API 필드에서 왔는지 추적할 수 있습니다. 만약 추적이 불가능하다면, 그것은 환각 (Hallucination)입니다.

3. 제약된 시스템 프롬프트 (Constrained System Prompt): 명시적인 가드레일 (Guardrails)

사실 블록을 소비하는 시스템 프롬프트는 데이터 주입 패턴의 두 번째 절반입니다. 이것은 "훌륭한 스포츠 작가가 되어라"가 아닙니다. 이것은 계약 (Contract)입니다.

당신은 스포츠 베팅 콘텐츠 작가입니다. 당신은 MATCH FACTS 블록에 제공된 사실만을 사용하여
600단어 분량의 경기 프리뷰를 작성합니다.

...

여기에는 두 가지가 중요합니다. 첫째, 제약 조건 #6 — "학습 데이터보다 사실을 신뢰하라"입니다. 이 문구는 모델이 오래된 학습 기억을 사용하여 현재 데이터를 "수정"하는 것을 방지하는 경계선 역할을 합니다. 이 조건이 없다면, GPT는 학습 데이터에 스타 선수로 기록되어 있다는 이유만으로 6개월 전에 은퇴한 선수에 대해 친절하게 글을 쓰는 상황을 보게 될 것입니다.

둘째, 제약 조건 #2 — "최근 데이터를 사용할 수 없음"이라고 작성할 수 있는 명시적 허용입니다. 이 조건이 없다면, GPT는 침묵을 메우기 위해 허구(Invention)를 만들어낼 것입니다. 이 조건이 있다면, GPT는 기꺼이 특정 주제를 건너뛸 것이며, 기사는 자신만만하게 틀린 내용이 아닌 적절하게 절제된 형태로 읽히게 됩니다.

Temperature(온도)는 낮게(0.3-0.4) 유지합니다. 0은 아닙니다. 여전히 읽기 좋은 산문(Prose)을 원하기 때문입니다. 하지만 모델이 창의적인 완성(Creative completions)을 시도하지 않을 만큼 충분히 낮아야 합니다.

4. 출력 검증기(The Output Validator): 누락된 오류 포착하기

프롬프트가 아무리 엄격하더라도 가끔씩 환각(Hallucination)이 빠져나갈 수 있습니다. 출력 검증기(Output Validator)는 생성된 기사를 정규 표현식(Regex) 및 사실 블록(Fact block)에 대한 조회(Lookup) 체크 세트를 통해 실행하는 Make.com의 마지막 단계입니다.

제가 실행하는 체크 항목은 다음과 같습니다:

  • 날짜 체크 (Date check): 기사 내의 모든 날짜를 정규 표현식(Regex)으로 추출하여 match_date_iso와 교차 참조합니다. 기사의 날짜가 일치하지 않으면 수동 검토(Manual review) 단계로 보냅니다.
  • 선수 이름 체크 (Player name check): 선수 이름처럼 보이는 고유 명사를 추출하여 KEY PLAYERS 목록 및 API의 스쿼드 데이터와 교차 참조합니다. 알 수 없는 이름은 플래그(Flag)를 지정합니다.
  • 점수 체크 (Score check): 기사 내의 모든 "X-Y" 패턴을 양 팀의 last_5_results 데이터와 대조합니다. 이것이 실제 최근 결과도 아니고 예측으로 프레임화되지도 않았다면 플래그를 지정합니다.
  • 금지 문구 (Forbidden phrases): 확신을 나타내는 언어("반드시 승리할 것이다", "보장된", "이번 주의 확실한 승부")에 대한 작은 블랙리스트입니다. 이는 베팅 콘텐츠의 준수(Compliance)를 위한 순수한 위생 관리 목적이지만, 자신만만하게 틀린 기사는 자신만만하고 확신에 찬 언어를 사용하는 경향이 있기 때문에 환각 탐지기(Hallucination tripwire) 역할도 겸합니다.

어떤 체크 항목이라도 통과하지 못한 기사는 "검토 필요(Needs Review)\

대략 매주 기사의 5~8% 정도가 플래그(flag)를 발생시킵니다. 그중 약 3분의 1은 실제 환각 (Hallucination)이며, 나머지는 정규 표현식 (Regex)에 의한 오탐 (False Positive)입니다 (예: 일반적인 단어와 이름이 겹치는 선수 등). 저는 잘못된 사실을 배포하느니 차라리 오탐을 검토하는 쪽을 택하겠습니다.

이를 통해 배운 점

우선순위에 따른 세 가지입니다.

첫째: 환각은 모델의 문제가 아니라 데이터의 문제입니다. 이 방식을 도입하기 전 프롬프트 엔지니어링 (Prompt Engineering)에 쏟았던 매 시간은 거의 영향이 없었습니다. 하지만 데이터 검증기 (Data Validator)와 사실 확인 블록 (Fact Block)을 구축하는 데 보낸 한 시간은 10배의 영향력을 발휘했습니다. 만약 여러분의 파이프라인이 자신만만하게 틀린 결과물을 내놓는다면, 모델 자체를 살펴보기 전에 모델에게 무엇을 _제공하지 않고 있는지_를 먼저 살펴보십시오.

둘째: LLM 주변의 결정론적 모듈 (Deterministic Modules)은 타협할 수 없는 필수 요소입니다. Make.com (또는 n8n, 혹은 여러분이 사용하는 무엇이든)은 바로 이 작업, 즉 예측 불가능한 LLM 호출 주변에 지루하고 예측 가능하며 감사 가능한 (Auditable) 스캐폴딩 (Scaffolding)을 구축하는 데 매우 탁월합니다. 만약 여러분의 흐름이 "API → GPT → 출력" 방식이라면, 여러분에게는 스캐폴딩이 없는 것입니다. LLM이 하지 말아야 할 일까지 수행하고 있는 것입니다.

셋째: 우아하게 실패할 수 있는 명시적인 허용은 수행해야 한다는 암묵적인 압박보다 낫습니다. 모델에게 "훌륭한 미리보기를 작성하라"고 말하는 것은 데이터가 부족할 때 이야기를 지어내야 한다는 압박을 만듭니다. 반면 "데이터가 누락되었다면, 데이터가 누락되었다고 작성하라"고 말하는 것은 그 압박을 해소합니다. 직관과는 반대로, 모델에게 "최근 데이터를 사용할 수 없음"이라고 말할 수 있는 권한을 준 후 기사의 품질이 _상승_했습니다. 왜냐하면 그 대안은 항상 '지어내기'였고, 지어낸 내용은 정직한 공백보다 항상 품질이 낮았기 때문입니다.

콘텐츠 파이프라인을 구축하면서 환각 문제에 부딪히고 있다면, 데이터 검증기 (Data Validator)부터 시작하십시오. 이는 가장 적은 비용으로 가장 큰 효과를 볼 수 있는 변화이며, 여러분이 미처 알지 못했던 상류 (Upstream) 데이터 문제들을 많이 드러내 줄 것입니다.

다음 포스팅에서는 동일한 패턴의 n8n 버전을 작성하겠습니다. 논리는 동일하며, 스캐폴딩만 다를 것입니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
1

댓글

0