사실 확인 및 환각 방지
검색된 출처로 근거를 확보해 모든 주장을 검증하는 방법을 학습합니다.
사실 확인 및 환각 방지은(는) CoddyKit의 무료 AI Agents 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 AI Agents 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. AI Agents 강의에는 총 4개의 강의가 포함되어 있습니다.
리서치 에이전트의 환각 문제
LLM은 가져온 출처에 근거가 전혀 없는 그럴듯한 주장을 생성할 수 있습니다. 리서치 에이전트에서는 출력이 권위 있어 보이고 실제로는 주장을 뒷받침하지 않을 수도 있는 인용과 함께 제시되기 때문에 특히 위험합니다.
환각 방지는 최우선 고려 사항이어야 합니다.
근거 연결: 모든 주장은 출처로 추적 가능해야 함
근거 연결의 핵심 원칙은 최종 출력의 모든 사실 주장이 최소 하나의 검색된 문서로 추적 가능해야 한다는 것입니다. 추적할 수 없는 주장은 환각이거나 근거가 없는 주장 중 하나이며, 연구 보고서에서는 둘 다 허용되지 않습니다.
def check_grounding(claim: str, retrieved_docs: list[dict]) -> dict:
doc_texts = '\n\n'.join(
f'[DOC {i+1}] ({d["url"]})\n{d["text"][:800]}'
for i, d in enumerate(retrieved_docs[:5])
)
return {
'claim': claim,
'docs': doc_texts,
'grounded': None # to be filled by LLM verifier
}
if __name__ == '__main__':
docs = [{'url': 'https://example.com/geo', 'text': 'Paris is the capital of France.'}]
result = check_grounding('Paris is the capital of France.', docs)
print('Claim:', result['claim'])
print('Supporting docs used:')
print(result['docs'])
LLM 검증기 패턴
별도의 LLM 호출인 ‘검증기’를 사용하여 제공된 출처가 주장을 뒷받침하는지 판단합니다. 이렇게 하면 생성기와 검증기가 독립적으로 호출되는 견제와 균형 구조가 만들어집니다.
import openai, json
client = openai.OpenAI(api_key='YOUR_OPENAI_KEY')
def verify_claim(claim: str, source_texts: list[str]) -> dict:
sources_block = '\n---\n'.join(source_texts[:3])
prompt = (
f'Is the following claim directly supported by the provided sources?\n'
f'Claim: "{claim}"\n\n'
f'Sources:\n{sources_block}\n\n'
f'Return JSON: {{\n'
f' "supported": true/false,\n'
f' "confidence": 0.0-1.0,\n'
f' "reason": "one sentence explanation"\n'
f'}}'
)
resp = client.chat.completions.create(
model='gpt-4o',
messages=[{'role': 'user', 'content': prompt}],
response_format={'type': 'json_object'}
)
return json.loads(resp.choices[0].message.content)신뢰도 기반 출력 제어
검증기의 신뢰도 점수에 기준값을 설정합니다. 0.85를 초과하는 주장은 게시합니다. 0.5에서 0.85 사이라면 면책 고지와 함께 게시합니다. 0.5 미만인 주장은 제외합니다.
INCLUDE_THRESHOLD = 0.85
DISCLAIMER_THRESHOLD = 0.50
def gate_claim(claim: str, source_texts: list[str]) -> dict:
result = verify_claim(claim, source_texts)
conf = result.get('confidence', 0.0)
supported = result.get('supported', False)
if not supported or conf < DISCLAIMER_THRESHOLD:
return {'action': 'exclude', 'claim': claim, 'reason': result.get('reason')}
elif conf < INCLUDE_THRESHOLD:
return {
'action': 'include_with_disclaimer',
'claim': f'[LOW CONFIDENCE] {claim}',
'reason': result.get('reason')
}
else:
return {'action': 'include', 'claim': claim}특정 환각 패턴 포착
특정한 주장 패턴은 환각 위험이 높습니다. 예를 들어 정확한 통계 수치(백분율, 달러 금액), 구체적인 날짜, 실명으로 언급된 인물, 인과 관계 등이 있습니다. 이러한 주장에는 추가로 면밀한 검토를 적용합니다.
import re
def is_high_risk_claim(claim: str) -> bool:
patterns = [
r'\d+\.?\d*\s*%', # percentages: 9.1%
r'\$\s*\d+', # dollar amounts
r'\b(January|February|March|April|May|June|July|August|September|October|November|December)\s+\d{4}', # dates
r'\b[A-Z][a-z]+\s+[A-Z][a-z]+\s+(said|stated|argued|claimed)', # named quotes
r'(caused?|led to|resulted in)', # causal claims
]
return any(re.search(p, claim) for p in patterns)
print(is_high_risk_claim('Inflation hit 9.1% in June 2022')) # True
print(is_high_risk_claim('Inflation was elevated')) # False분해된 검증 루프
긴 섹션의 경우 텍스트를 개별 주장으로 분해하고, 각 주장을 독립적으로 검증한 다음, 검증된 주장만으로 섹션을 재구성합니다.
def decompose_into_claims(section_text: str) -> list[str]:
prompt = (
f'Break this text into individual verifiable factual claims.\n'
f'Each claim should be a single sentence containing exactly one fact.\n'
f'Return JSON: {{"claims": ["..."]}}\n\n'
f'TEXT:\n{section_text}'
)
resp = client.chat.completions.create(
model='gpt-4o-mini',
messages=[{'role': 'user', 'content': prompt}],
response_format={'type': 'json_object'}
)
return json.loads(resp.choices[0].message.content).get('claims', [])검증된 텍스트 재구성
각 주장을 선별한 후, 검증된 주장만으로 깔끔하고 일관된 섹션을 재구성합니다. 신뢰도가 낮은 주장은 제외하고 가독성을 높이도록 다시 작성합니다.
def reconstruct_section(verified_claims: list[str],
section_name: str) -> str:
claims_text = '\n'.join(f'- {c}' for c in verified_claims)
prompt = (
f'Rewrite these verified facts as a coherent {section_name} section.\n'
f'Do NOT add any new information not present in the claims.\n'
f'Only use the facts provided.\n\n'
f'VERIFIED CLAIMS:\n{claims_text}'
)
resp = client.chat.completions.create(
model='gpt-4o',
messages=[{'role': 'user', 'content': prompt}]
)
return resp.choices[0].message.content수치 변경 감지
LLM은 때때로 출처의 수치를 미묘하게 바꿉니다(예: 9.1%가 9.2%가 됨). 주장과 출처 텍스트에서 수치를 추출한 다음 명시적으로 비교합니다.
import re
def extract_numbers(text: str) -> list[float]:
matches = re.findall(r'[-+]?\d*\.?\d+', text)
return [float(m) for m in matches]
def check_numeric_drift(claim: str, source_text: str,
tolerance: float = 0.01) -> bool:
claim_nums = extract_numbers(claim)
source_nums = extract_numbers(source_text)
for cn in claim_nums:
found_match = any(abs(cn - sn) <= tolerance for sn in source_nums)
if not found_match:
return True # numeric drift detected
return False
print(check_numeric_drift(
'Inflation reached 9.2% in June 2022',
'The CPI rose 9.1 percent in June 2022'
)) # True — 9.2 vs 9.1출처 귀속 감사
전체 보고서를 생성한 후 출처 귀속 감사를 실행합니다. 보고서의 각 문장이 출처 중 하나에 귀속될 수 있는지 확인합니다. 일치하는 출처 발췌문이 없는 문장은 표시합니다.
def attribution_audit(report_text: str, sources: list[dict]) -> list[str]:
sentences = [s.strip() for s in report_text.split('.') if len(s.strip()) > 20]
unattributed = []
for sentence in sentences:
found = False
for src in sources:
if any(word in src.get('text', '') for word in sentence.split()[:5]):
found = True
break
if not found:
unattributed.append(sentence)
return unattributed
# Flag unattributed sentences for manual review or re-verification
if __name__ == '__main__':
report = ("Water boils at 100 degrees Celsius at sea level. "
"The moon is made primarily of green cheese according to this document.")
sources = [{'text': 'Water boils at 100C (212F) at standard atmospheric pressure.'}]
unattributed = attribution_audit(report, sources)
print('Unattributed sentences (need manual review):')
for s in unattributed:
print(' -', s)
불확실한 주장에 완화 표현 사용
모든 주장을 높은 신뢰도로 검증할 수 있는 것은 아닙니다. 경계선에 있는 주장을 완전히 제외하는 대신 ‘일부 분석가들은 ...라고 제안합니다’, ‘X에 따르면 ...’, ‘...라고 보고되었습니다’와 같은 완화 표현을 사용합니다. 이렇게 하면 불확실성을 알리면서도 정보를 보존할 수 있습니다.
def hedge_claim(claim: str, confidence: float) -> str:
if confidence >= 0.85:
return claim # state as fact
elif confidence >= 0.65:
hedges = ['Some sources suggest', 'According to available evidence',
'Analysts have noted']
return f'{hedges[hash(claim) % len(hedges)]}, {claim.lower()}'
else:
return f'It has been reported (with low confidence) that {claim.lower()}'
print(hedge_claim('Inflation peaked at 9.1%', 0.90))
print(hedge_claim('Supply chain disruptions contributed to inflation', 0.72))
print(hedge_claim('Specific policy caused inflation', 0.45))환각률 모니터링
시간이 지남에 따라 환각률을 추적합니다. 생성된 주장 중 검증에 실패하는 주장이 몇 퍼센트인지 확인합니다. 경고 기준값을 설정하고, 환각률이 급증하면 프롬프트 설계 또는 검색 품질이 저하된 것입니다.
verification_log = [] # In production: a database
def log_verification(claim: str, supported: bool, confidence: float):
verification_log.append({
'claim': claim[:100],
'supported': supported,
'confidence': confidence
})
def hallucination_rate() -> float:
if not verification_log:
return 0.0
failed = sum(1 for v in verification_log if not v['supported'])
return failed / len(verification_log)
def check_hallucination_alert(threshold: float = 0.15):
rate = hallucination_rate()
if rate > threshold:
print(f'ALERT: Hallucination rate {rate:.1%} exceeds threshold {threshold:.1%}')
return rate
if __name__ == '__main__':
log_verification('The sky is blue', True, 0.95)
log_verification('The moon is made of cheese', False, 0.2)
log_verification('Water boils at 100C at sea level', True, 0.99)
rate = check_hallucination_alert(threshold=0.15)
print(f'Hallucination rate so far: {rate:.1%}')
‘LLM 검증기’ 패턴의 주된 목적은 무엇입니까?
LLM 검증기는 환각 방지를 위한 핵심 아키텍처 패턴입니다. 무엇을 검증하며 왜 별도의 호출로 수행하는지 이해하는 것이 중요합니다.
환각 방지 요약
다음과 같은 방법으로 환각을 방지합니다. 근거 확인(모든 주장이 출처로 추적됨), LLM 검증기(별도의 호출로 근거 여부를 판단), 신뢰도 기준 선별(기준값 미만의 주장 제외), 수치 변경 감지, 경계선 사례를 위한 불확실성 완화 표현, 그리고 회귀 문제를 감지하기 위한 환각률 모니터링입니다.
자주 묻는 질문
“사실 확인 및 환각 방지” 강의는 무료인가요?
네 — “사실 확인 및 환각 방지” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 AI Agents 강의 전체를 잠금 해제할 수 있습니다. AI Agents 강의에는 총 4개의 강의가 포함되어 있습니다.
“사실 확인 및 환각 방지”에서 뭘 배우나요?
검색된 출처로 근거를 확보해 모든 주장을 검증하는 방법을 학습합니다. 브라우저에서 직접 실행하는 실습 코드로 AI Agents을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
AI Agents을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 AI Agents은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.
“사실 확인 및 환각 방지” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 AI Agents 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 AI Agents 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- 다단계 조사 루프 설계
- 출처 검증 및 인용
- 구조화된 보고서 생성
- 사실 확인 및 환각 방지