AI로 제품 FAQ 만들 때 모르는 답을 지어내지 않게 하기
제품 설명으로 자주 묻는 질문과 답변(FAQ)을 만들 때는 근거가 없는 답은 보류하고 확인할 질문을 남기세요. 문서에 없는 기능을 AI가 그럴듯하게 덧붙이거나, 정보가 없다는 이유로 지원하지 않는다고 단정하면 잘못된 안내가 됩니다.
먼저 답변의 근거가 되는 원문을 찾고, 찾지 못한 질문은 담당자에게 확인할 목록으로 따로 남기세요. 이 글에서는 가상 제품 설명과 질문을 나란히 놓고 답할 수 있는 것과 확인이 필요한 것을 구분합니다.
제품 설명과 질문·답변은 가상 예제입니다. Python으로 답변 구조의 오류를 검사하고, 구조가 맞아도 뜻이 틀린 예를 대조했습니다. 구조 검사를 통과해도 자연어의 뜻과 제품 문서의 최신성은 별도로 확인해야 합니다.

답변을 쓰기 전에 원문 버전과 문장 ID 고정하기
아래 원문을 텍스트 편집기에 복사합니다. 문서 ID는 ND-DEMO, 버전은 fictional-v1.0입니다. D01은 제품이 가상임을 표시하는 범위 설명이며 실제 이용 조건이 아닙니다.
D01 NoteDock Mini v1.0은 이 실습을 위한 가상 메모 제품이다.
D02 지원 환경은 Windows 11이며 macOS는 지원하지 않는다.
D03 각 노트는 내보내기 메뉴에서 UTF-8 TXT 파일로 저장할 수 있다.
D04 v1.0의 내보내기는 한 번에 노트 한 개를 대상으로 한다.
D05 삭제한 노트는 휴지통으로 이동한다.
D06 휴지통을 비우지 않았다면 삭제한 노트를 삭제 시점부터 30일 동안 복원할 수 있다.
D06은 “언제나 복원 가능”이라는 문장이 아닙니다. 휴지통을 비우지 않았다는 조건과 삭제 시점부터 30일이라는 기간이 함께 있습니다. D04의 한 개 내보내기도 모든 노트의 일괄 백업이나 자동 백업을 뜻하지 않습니다. D02는 macOS 미지원의 명시 근거지만 Android에 관한 근거는 아닙니다.
실제 제품이라면 같은 이름의 구버전 안내·다른 플랜·로드맵을 섞지 않습니다. 답의 적용 버전과 원문 확인 날짜를 먼저 적습니다. 페이지가 바뀌면 이전 검토본을 보존하고 바뀐 문장과 답을 연결해 다시 확인해야 합니다.
여덟 질문을 근거 있음과 확인 필요로 나누기
| 질문 ID | 질문 | 이번 원문에서 확인한 상태 | 필요한 근거 |
|---|---|---|---|
| F01 | 지원하는 Windows 버전은? | 답변 가능 | D02 |
| F02 | macOS에서도 사용할 수 있나? | 명시된 미지원 답변 가능 | D02 |
| F03 | 내보내기 파일 형식은? | 답변 가능 | D03 |
| F04 | 모든 노트를 한 번에 내보낼 수 있나? | 한 개 대상이라는 제한을 설명 | D04 |
| F05 | 삭제한 노트는 어떻게 복원하나? | 휴지통·기간·조건 안에서 답변 가능 | D05·D06 |
| F06 | 클라우드 동기화가 되나? | 확인 필요 | 해당 문장 없음 |
| F07 | Android 앱이 있나? | 확인 필요 | 해당 문장 없음 |
| F08 | 노트가 종단간 암호화되나? | 확인 필요 | 해당 문장 없음 |
답변 가능 상태와 필수 근거 ID는 원문에서 사람이 먼저 정한 기준입니다. AI가 작성한 초안을 다시 AI에게 읽히고 “근거가 있다”는 자체 평가만 저장하지 않습니다. 특히 F08의 보안 기능은 TXT 내보내기 기능에서 추론할 수 없습니다.
Claude의 환각 감소 공식 안내는 모를 때 불확실성을 인정하게 하고, 원문 인용과 출처로 답을 대조하는 방법을 안내합니다. 동시에 이런 방법으로 오류가 완전히 사라지지는 않는다고 설명합니다. 아래 검토 절차도 프롬프트 하나로 환각을 막는 보장이 아니라, 잘못된 답을 발견하고 보류하기 위한 작업입니다.
문장마다 근거를 연결한 FAQ 검토본
아래 다섯 항목은 사람이 원문 범위 안에서 작성한 수정 예시입니다. 실제 사이트에 공개한 FAQ가 아니며 제공한 원문으로 확인할 수 있는 내용만 남겼습니다. 질문당 근거를 하나 달고 끝내지 않고, F05의 두 문장을 각각 연결합니다.
- F01 지원하는 Windows 버전은?
Windows 11을 지원합니다. 근거 D02
- F02 macOS에서도 사용할 수 있나?
macOS는 지원하지 않습니다. 근거 D02
- F03 내보내기 파일 형식은?
노트를 UTF-8 TXT 파일로 내보낼 수 있습니다. 근거 D03
- F04 모든 노트를 한 번에 내보낼 수 있나?
v1.0의 내보내기는 한 번에 노트 한 개를 대상으로 합니다. 근거 D04
- F05 삭제한 노트는 어떻게 복원하나?
삭제한 노트는 휴지통으로 이동합니다. 근거 D05 휴지통을 비우지 않았다면 삭제 시점부터 30일 동안 복원할 수 있습니다. 근거 D06
F05에는 복원 메뉴의 버튼 이름이 없습니다. 원문이 위치와 조건만 설명하므로 실제 UI 조작 순서를 새로 만들지 않았습니다. 사용자가 클릭할 단계까지 필요한 FAQ라면 해당 제품 버전의 공식 화면·복원 절차를 추가로 확인해야 합니다. “휴지통에서 복원 버튼을 누르세요”라는 그럴듯한 문장을 문서에 없는 상태로 확정하지 않습니다.
원문에 답이 없는 세 질문은 다음 목록에 남깁니다. 질문을 삭제하면 FAQ가 완성된 것처럼 보이지만 사용자의 궁금증과 제품 문서의 빈 부분이 사라집니다.
F06 확인 필요: 클라우드 동기화의 제공 여부와 지원 범위
F07 확인 필요: Android 앱의 제공 여부와 지원 버전
F08 확인 필요: 암호화 방식과 적용 범위
공통 상태: ND-DEMO fictional-v1.0의 D01~D06에는 답변 근거 없음
다음 단계: 권한 있는 제품 문서 담당자의 확인과 근거 문서 갱신
담당자에게 실제 메시지를 보낸 기록은 아닙니다. 확인이 끝나기 전에는 예·아니오로 채우거나 예정 출시일을 만들지 않습니다. 공개 FAQ에서 확인 필요 항목을 어떻게 보여 줄지는 제품팀이 정하되, 내부 검토본에는 질문·미확인 이유·다음 확인 범위를 보존합니다.
잘못된 후보의 사실과 조건을 문장 단위로 확인하기
다음 세 후보는 의도적으로 만든 오류입니다. 모두 실제 AI의 실패 사례로 부르지 않습니다.
| 잘못된 후보 | 붙인 인용 | 사람의 판정 |
|---|---|---|
| macOS를 지원합니다 | D02의 정확한 원문 | 미지원이라는 부정이 뒤집힘 |
| 삭제 시점부터 30일 동안 반드시 복원할 수 있습니다 | D06의 정확한 원문 | 휴지통 미비움 조건이 사라지고 확약이 추가됨 |
| 모든 노트를 한 번에 내보낼 수 있습니다 | D04의 정확한 원문 | 한 노트 제한과 반대 |
인용이 정확해도 옆의 답이 그 인용과 반대일 수 있습니다. D06을 인용한 단락에 “복원은 무조건 가능”을 넣는 경우에는 ID와 인용 존재만 검사해선 걸러지지 않습니다. 숫자 30이 유지됐다는 사실도 조건 보존을 증명하지 않습니다.
F06에서 “클라우드 동기화를 지원하지 않습니다”라고 바꾸는 것은 보수적인 답이 아닙니다. 현재 원문에 근거가 없는 새로운 미지원 안내입니다. F02는 D02가 명시적으로 미지원이라고 말하므로 두 질문의 처리 상태를 다르게 유지합니다.
AI 요청문에 미확인 질문을 출력 영역으로 남기기
아래 가상 제품 원문 ND-DEMO fictional-v1.0과 F01~F08로 FAQ 검토본을 작성하세요.
제품 사실은 D01~D06에서 확인할 수 있는 내용만 사용합니다.
각 답변 문장마다 근거 문장 ID와 정확한 원문을 별도로 적으세요.
부정·조건·기간·버전·한 번에 처리하는 개수를 보존하세요.
원문에 없는 기능은 지원 또는 미지원으로 추정하지 마세요.
답변 근거가 없는 F06~F08은 '확인 필요'로 유지하고 확인할 질문을 적으세요.
F05에 문서에 없는 버튼 이름·복원 조작 순서를 추가하지 마세요.
출력은 근거 있는 FAQ 검토본과 확인 필요 질문 목록입니다.
그 뒤 각 답의 의미가 근거와 일치하는지 사람이 검토할 항목을 적으세요.
이 문구는 요청 예시이며 실제 모델에 전송하지 않았습니다. 모델이 인용 표를 만들었다면 원문 문장도 직접 열어 확인합니다. OpenAI 평가 안내는 실제 사용 맥락에 맞는 기준과 경계 사례를 포함한 평가를 설명합니다. 이번에는 답이 있는 질문만 평가하지 않고 원문이 부족한 세 질문도 고정 사례에 포함하는 방식으로 적용했습니다. 특정 모델의 성능이나 일반적인 FAQ 정확도를 측정한 것은 아닙니다.
구조 검사와 의미 검사의 차이를 실행으로 남기기
아래 코드는 원문·질문·수정 예시를 모두 포함합니다. UTF-8 파일 faq_source_checks.py로 저장하고 실행하세요. Python 3 표준 라이브러리만 사용하며 API·제품 사이트·고객 지원 채널과 연결하지 않습니다.
REQUIRED는 원문에서 사람이 정한 이번 여덟 질문의 필수 근거 목록입니다. 새 질문이 답변 가능한지 자동 판정하는 범용 프로그램이 아닙니다. 원문이 바뀌면 사람이 상태와 필수 근거를 다시 정해야 합니다.
import copy
import json
# Fictional product documentation; not a real vendor policy or model response.
SOURCES = {
"D01": "NoteDock Mini v1.0은 이 실습을 위한 가상 메모 제품이다.",
"D02": "지원 환경은 Windows 11이며 macOS는 지원하지 않는다.",
"D03": "각 노트는 내보내기 메뉴에서 UTF-8 TXT 파일로 저장할 수 있다.",
"D04": "v1.0의 내보내기는 한 번에 노트 한 개를 대상으로 한다.",
"D05": "삭제한 노트는 휴지통으로 이동한다.",
"D06": "휴지통을 비우지 않았다면 삭제한 노트를 삭제 시점부터 30일 동안 복원할 수 있다.",
}
QUESTIONS = {
"F01": "지원하는 Windows 버전은?",
"F02": "macOS에서도 사용할 수 있나?",
"F03": "내보내기 파일 형식은?",
"F04": "모든 노트를 한 번에 내보낼 수 있나?",
"F05": "삭제한 노트는 어떻게 복원하나?",
"F06": "클라우드 동기화가 되나?",
"F07": "Android 앱이 있나?",
"F08": "노트가 종단간 암호화되나?",
}
# A human derived this fixed contract from D01-D06 before preparing candidates.
REQUIRED = {"F01": {"D02"}, "F02": {"D02"}, "F03": {"D03"},
"F04": {"D04"}, "F05": {"D05", "D06"},
"F06": set(), "F07": set(), "F08": set()}
def sentence(text, source_id):
return {"text": text, "source_id": source_id, "quote": SOURCES[source_id]}
GOOD = [
{"id": "F01", "status": "근거 있음", "sentences": [sentence("Windows 11을 지원합니다.", "D02")], "check_question": ""},
{"id": "F02", "status": "근거 있음", "sentences": [sentence("macOS는 지원하지 않습니다.", "D02")], "check_question": ""},
{"id": "F03", "status": "근거 있음", "sentences": [sentence("노트를 UTF-8 TXT 파일로 내보낼 수 있습니다.", "D03")], "check_question": ""},
{"id": "F04", "status": "근거 있음", "sentences": [sentence("v1.0의 내보내기는 한 번에 노트 한 개를 대상으로 합니다.", "D04")], "check_question": ""},
{"id": "F05", "status": "근거 있음", "sentences": [sentence("삭제한 노트는 휴지통으로 이동합니다.", "D05"), sentence("휴지통을 비우지 않았다면 삭제 시점부터 30일 동안 복원할 수 있습니다.", "D06")], "check_question": ""},
{"id": "F06", "status": "확인 필요", "sentences": [], "check_question": "클라우드 동기화의 제공 여부와 지원 범위를 제품 문서 담당자에게 확인"},
{"id": "F07", "status": "확인 필요", "sentences": [], "check_question": "Android 앱의 제공 여부와 지원 버전을 제품 문서 담당자에게 확인"},
{"id": "F08", "status": "확인 필요", "sentences": [], "check_question": "암호화 방식과 적용 범위를 제품 문서 담당자에게 확인"},
]
def validate_structure(rows):
if not isinstance(rows, list) or any(not isinstance(r, dict) for r in rows) or [r.get("id") for r in rows] != list(QUESTIONS):
raise ValueError("question IDs missing, duplicated or reordered")
for row in rows:
if set(row) != {"id", "status", "sentences", "check_question"}:
raise ValueError("row fields differ")
expected_ids = REQUIRED[row["id"]]
if not isinstance(row["sentences"], list) or not isinstance(row["check_question"], str):
raise ValueError("row types differ")
if not expected_ids:
if row["status"] != "확인 필요" or row["sentences"] or not row["check_question"].strip():
raise ValueError("unknown question was turned into an answer")
continue
if row["status"] != "근거 있음" or row["check_question"] or not row["sentences"]:
raise ValueError("supported question has wrong state")
actual_ids = set()
for item in row["sentences"]:
if not isinstance(item, dict) or set(item) != {"text", "source_id", "quote"}:
raise ValueError("sentence fields differ")
if not isinstance(item["text"], str) or not item["text"].strip():
raise ValueError("empty answer sentence")
sid = item["source_id"]
if not isinstance(sid, str) or sid not in SOURCES or item["quote"] != SOURCES[sid]:
raise ValueError("source ID or exact quote mismatch")
actual_ids.add(sid)
if actual_ids != expected_ids:
raise ValueError("required source coverage differs")
return True
def main():
validate_structure(GOOD)
rejected = []
for label in ["missing_question", "wrong_source", "changed_quote",
"missing_restore_condition_source", "invented_cloud_answer", "unknown_negative_claim"]:
bad = copy.deepcopy(GOOD)
if label == "missing_question":
bad.pop()
elif label == "wrong_source":
bad[0]["sentences"][0]["source_id"] = "D99"
elif label == "changed_quote":
bad[0]["sentences"][0]["quote"] = "Windows 10도 지원한다."
elif label == "missing_restore_condition_source":
bad[4]["sentences"].pop()
elif label == "invented_cloud_answer":
bad[5]["status"] = "근거 있음"
bad[5]["sentences"] = [sentence("클라우드 동기화를 제공합니다.", "D03")]
else:
bad[5]["sentences"] = [sentence("클라우드 동기화는 지원하지 않습니다.", "D03")]
try:
validate_structure(bad)
except ValueError:
rejected.append(label)
else:
raise AssertionError(label)
semantic_limits = []
for label, row_index, item_index, wrong_text in [
("negation_inverted", 1, 0, "macOS를 지원합니다."),
("restore_condition_removed", 4, 1, "삭제 시점부터 30일 동안 반드시 복원할 수 있습니다."),
("bulk_export_invented", 3, 0, "모든 노트를 한 번에 내보낼 수 있습니다."),
]:
bad = copy.deepcopy(GOOD)
bad[row_index]["sentences"][item_index]["text"] = wrong_text
if not validate_structure(bad):
raise AssertionError(label)
semantic_limits.append({"id": label, "structural_check": "passes",
"human_expected_verdict": "reject", "wrong_text": wrong_text})
print(json.dumps({"document_version": "fictional-v1.0", "questions": len(GOOD),
"grounded_questions": 5, "unknown_questions": ["F06", "F07", "F08"],
"structure_rejected_cases": rejected,
"semantic_limit_cases": semantic_limits,
"live_ai_calls": 0, "public_faq_writes": 0,
"result": "PASS"}, ensure_ascii=False))
if __name__ == "__main__":
main()
python faq_source_checks.py
Linux Python 3.12.14에서 기본 검토본은 질문 8개를 유지하고, 근거 있는 5개와 확인 필요 F06·F07·F08을 구분했습니다. 구조 오류 여섯 건은 실제로 거부됐습니다.
- F08 자체를 빼서 질문 한 개를 누락
- 존재하지 않는 D99를 근거로 지정
- 인용을 Windows 10 지원 문장으로 변경
- F05에서 D06 근거 문장을 제거
- F06을 클라우드 지원 답변으로 바꿈
- F06의 확인 필요 상태에 미지원 문장을 끼워 넣음
반대로 앞 표의 macOS 부정 뒤집기·복원 조건 제거·일괄 내보내기 발명은 구조 검사를 통과했습니다. 세 후보의 ID와 인용은 맞고 답의 의미만 바뀌었기 때문입니다. 코드에 human_expected_verdict=reject라고 적은 것은 사람이 미리 정한 판정이며 프로그램이 자연어 의미를 추론해 반려했다는 뜻이 아닙니다.
questions=8, grounded_questions=5
unknown_questions=[F06,F07,F08]
structure_rejected_cases=6개
semantic_limit_cases=3개: 구조 통과, 사람의 기대 판정은 반려
live_ai_calls=0, public_faq_writes=0, result=PASS
위는 콘솔 JSON의 요약입니다. 실제 출력에는 거부한 사례 이름과 세 의미 반례의 문장이 들어 있습니다. PASS는 모든 답의 의미를 자동으로 승인했다는 뜻이 아니라 기본 구조와 의도한 반례를 재현했다는 뜻입니다. JSON 문법·자료형과 문장 ID가 맞는지도 중요하지만, 최종 답이 그 근거로 설명되는지는 사람이 확인해야 합니다. Python json 문서는 객체를 JSON으로 표현하는 기능의 근거이며 사실성 검증 기능의 근거가 아닙니다.
새 문서가 생겼을 때는 해당 답만 다시 검토하기
예를 들어 후속 가상 문서에 “D07 v1.1은 두 기기 간 클라우드 동기화를 지원한다”는 승인된 문장이 생겼다고 가정할 수 있습니다. 이 문장은 현재 fixture에 없는 가상 변경 시나리오이며 실제 제품 변경이나 실행 검증 결과가 아닙니다.
그때 F06은 새 버전의 근거로 다시 작성할 수 있지만 v1.0 답변까지 소급해서 지원한다고 바꾸지 않습니다. F07 Android와 F08 암호화는 D07에서도 답할 수 없으므로 확인 필요를 유지합니다. 답 하나의 근거가 늘었다고 나머지 질문을 함께 승인하지 않습니다.
FAQ 항목 ID와 질문:
제품·적용 버전·플랜:
원문 문서 버전과 확인 날짜:
답변 문장:
근거 문장 ID·정확한 원문:
원문과 답의 주체·기능·부정·조건·기간 비교:
상태: 근거 일치 / 의미 수정 / 확인 필요
확인 필요라면 부족한 정보와 확인할 담당 범위:
최초 후보 / 수정본 / 최종 검토본 버전:
문서 변경 시 영향을 받는 FAQ ID:
검토자·검토 날짜:
원문에 근거가 없으면 질문을 남기고 답을 보류합니다. 인용만 틀렸으면 올바른 인용을 찾은 뒤 의미를 다시 읽습니다. 문장 자체가 반대라면 최초 후보를 보존하고 수정본을 따로 만듭니다. 검토 표의 상태를 바꾸는 것과 실제 공개 FAQ를 수정·발행하는 것은 다른 작업입니다.
검증 범위와 함께 읽을 글
공식 출처 확인·작성·Python 실행일은 2026-10-04 KST입니다. 실제 LLM 호출, 실제 제품 확인, 담당자 문의, 공개 FAQ 작성은 하지 않았습니다. 자연어 의미 전체나 제품 문서의 최신성을 자동으로 검증하는 도구도 아닙니다.
자료에 없는 값을 남기는 비교는 Gemini·NotebookLM 근거 비교의 미제공 참석 인원 사례와 연결됩니다. 이 글의 결과물은 제품 버전에 연결된 문장별 FAQ 근거와 확인 필요 기능 질문 목록입니다. 인용을 붙인 다섯 답의 의미를 읽고, 나머지 세 질문이 사라지지 않은 것을 확인한 뒤 검토본을 마칩니다.