AI 업무 활용

문서에 대해 AI에게 질문하고 줄 번호 인용을 검증하는 방법

문서 기반 AI 답변은 각 주장에 줄 번호 인용을 붙이면 훨씬 쉽게 검증할 수 있습니다. 작은 합성 문서를 사용해 AI에게 인용이 포함된 답변을 요청하고, Python으로 각 인용 범위를 자동 확인하는 방법을 설명합니다.

목차 보기

이런 분께보고서, 메모, 규정, 기타 텍스트 문서에 대해 AI 채팅 도우미에게 질문하고 답변의 근거를 쉽게 검증하고 싶은 사람을 위한 내용입니다.

준비사항
  • Python 3.12
  • 검토 중 줄 번호가 바뀌지 않도록 유지할 수 있는 일반 텍스트 문서

01줄 번호 인용을 요구해야 하는 이유

AI 채팅 도우미가 문서에 대해 답할 때 중요한 것은 답변이 자연스럽게 들리는지가 아닙니다. 핵심은 각 주장이 제공된 문서에 실제로 근거하고 있는지 확인하는 것입니다. 줄 번호 인용을 요구하면 검토자가 문서 전체를 다시 검색하지 않고도 바로 원문으로 돌아갈 수 있습니다.

실용적인 검토 흐름은 두 단계로 나눌 수 있습니다. 먼저 프로그램으로 모든 인용이 실제 줄 번호를 가리키는지 확인하고 해당 줄을 다시 추출합니다. 그다음 사람이 인용된 문장이 실제 주장과 의미상 일치하는지 확인합니다. 자동 검증은 잘못되거나 존재하지 않는 참조를 찾는 데 유용하지만, 그것만으로 주장에 대한 의미적 근거가 충분하다는 사실까지 증명하지는 못합니다.

02작은 합성 문서 만들기

다음과 같은 합성 프로젝트 메모를 사용해 보겠습니다. 아래 7개 줄을 표시된 그대로 project_note.txt 파일에 저장합니다. 예제를 의도적으로 작게 만들었기 때문에 모든 답변과 인용을 손으로도 직접 확인할 수 있습니다.

내용
L1Project Atlas weekly note
L2Prototype test moved from 12 Sep to 16 Sep.
L3Reason: replacement sensor arrived late.
L4Analysis owner: Mina.
L5Draft results due 18 Sep.
L6Final review meeting scheduled 21 Sep.
L7Budget status unchanged.

질문이 '일정에서 무엇이 변경되었고, 왜 변경되었으며, 이후 마감 일정은 무엇인가?'라고 가정합니다. 원문에는 프로토타입 시험이 12 Sep에서 16 Sep로 변경되었고, 이유는 교체 센서가 늦게 도착했기 때문이며, 초안 결과는 18 Sep까지 제출하고 최종 검토 회의는 21 Sep에 예정되어 있다고 명시되어 있습니다.

03프롬프트에서 인용 형식을 엄격하게 지정하기

단순히 문서를 인용하라고 요청하지 말고, 나중에 스크립트로 분석할 수 있도록 정확한 형식을 지정하는 것이 좋습니다. 이 예제에서는 모든 사실 문장 끝에 [L2-L3] 또는 [L5] 같은 형식의 인용을 붙이도록 합니다.

  1. 제공한 문서만 사용하라고 지정합니다.
  2. 인용 형식을 정확히 [Lx] 또는 [Lx-Ly]로 제한합니다.
  3. 앞선 주장에 직접 근거가 되지 않는 줄은 인용하지 말라고 요청합니다.
  4. 문서에 근거가 없으면 답을 만들어내지 말고 문서에서 확인할 수 없다고 말하도록 지정합니다.
  5. 줄 번호가 유지되도록 검토 중에는 원본 파일을 수정하지 않습니다.

04일반적인 인용 답변 확인하기

적절한 답변은 다음과 같을 수 있습니다. 프로토타입 시험은 12 Sep에서 16 Sep로 변경되었으며, 교체 센서가 늦게 도착한 것이 이유입니다. [L2-L3] 초안 결과 제출 기한은 18 Sep이고, 이후 최종 검토 회의가 21 Sep에 예정되어 있습니다. [L5-L6]

이 답변에는 두 개의 인용 범위가 있습니다. 첫 번째 인용은 2번과 3번 줄로 연결되어야 하고, 두 번째 인용은 5번과 6번 줄로 연결되어야 합니다. 답변을 신뢰하기 전에 검증 스크립트로 해당 범위가 실제로 존재하는지 확인하고, 각 인용에 해당하는 원문을 그대로 출력해 볼 수 있습니다.

05Python으로 모든 인용 자동 검증하기

AI 답변을 ai_answer.txt에 저장합니다. 아래 스크립트는 project_note.txt와 ai_answer.txt를 읽고, 정규식으로 인용을 추출하고, 잘못된 범위를 거부하며, 검토 결과를 outputs/citation_check.txt에 저장합니다. 원본 문서와 AI 답변은 수정하지 않으며, 출력 보고서가 이미 존재하면 중단하도록 설계되어 있습니다.

python
import re
from pathlib import Path

DOCUMENT_FILE = Path("project_note.txt")
ANSWER_FILE = Path("ai_answer.txt")
OUTPUT_DIR = Path("outputs")
OUTPUT_FILE = OUTPUT_DIR / "citation_check.txt"

CITATION_RE = re.compile(r"\[L(\d+)(?:-L?(\d+))?\]")


def main():
    if not DOCUMENT_FILE.is_file():
        raise SystemExit(f"Document not found: {DOCUMENT_FILE}")

    if not ANSWER_FILE.is_file():
        raise SystemExit(f"AI answer not found: {ANSWER_FILE}")

    if OUTPUT_FILE.exists():
        raise SystemExit(f"Output already exists: {OUTPUT_FILE}")

    lines = DOCUMENT_FILE.read_text(encoding="utf-8").splitlines()
    answer = ANSWER_FILE.read_text(encoding="utf-8")
    matches = list(CITATION_RE.finditer(answer))

    if not matches:
        raise SystemExit("No citations found in AI answer.")

    report = []
    report.append(f"document_lines={len(lines)}")
    report.append(f"citations_found={len(matches)}")
    report.append("")

    invalid_count = 0

    for index, match in enumerate(matches, start=1):
        start = int(match.group(1))
        end = int(match.group(2)) if match.group(2) else start
        citation = match.group(0)

        valid = 1 <= start <= end <= len(lines)
        report.append(f"citation_{index}={citation}")
        report.append(f"valid_range={valid}")

        if not valid:
            invalid_count += 1
            report.append("source=INVALID LINE RANGE")
            report.append("")
            continue

        for line_number in range(start, end + 1):
            source_text = lines[line_number - 1]
            report.append(f"L{line_number}: {source_text}")

        report.append("")

    report.append(f"invalid_citations={invalid_count}")

    OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
    OUTPUT_FILE.write_text("\n".join(report) + "\n", encoding="utf-8")
    print(f"Wrote: {OUTPUT_FILE}")


if __name__ == "__main__":
    main()

06각 주장과 추출된 원문 비교하기

이 합성 답변에서는 인용 2개가 발견되고 잘못된 인용 범위는 0개여야 합니다. [L2-L3]은 시험 날짜 변경과 교체 센서 지연을 가리키고, [L5-L6]은 초안 결과 마감일과 최종 검토 회의 일정을 가리킵니다.

인용예상 추출 줄수동 검토 결론
[L2-L3]L2와 L3변경된 시험 날짜와 명시된 이유를 뒷받침합니다.
[L5-L6]L5와 L6초안 마감일과 최종 검토 회의 날짜를 뒷받침합니다.

이번에는 [L8] 같은 잘못된 인용을 의도적으로 넣어 볼 수 있습니다. 합성 문서는 7줄뿐이므로 스크립트는 해당 범위를 잘못된 인용으로 표시해야 합니다. 이렇게 하면 문장 안에서는 그럴듯해 보여도 실제 원문에는 존재하지 않는 인용을 찾을 수 있습니다.

07흔한 실수와 한계 이해하기

  • AI 답변을 생성한 뒤 원본 문서를 수정하지 마세요. 줄이 추가되거나 삭제되면 줄 번호가 바뀝니다.
  • 검증 스크립트가 [Lx] 형식을 기대한다면 페이지 이름이나 모호한 문구 같은 다른 인용 방식을 그대로 허용하지 마세요.
  • 인용된 줄이 실제로 존재한다고 해서 그 줄이 자동으로 주장을 증명한다고 생각하지 마세요.
  • AI가 문서의 서로 다른 위치에서 두 사실을 결합했는데 한 부분만 인용하지 않았는지 확인하세요.
  • 문서에 근거가 없을 때 일반 지식으로 빈 부분을 채우지 말고 근거가 없다고 명시하도록 요구하세요.
  • 긴 문서에서는 이후 검토자가 같은 줄 번호를 재현할 수 있도록 고정된 원본 사본을 보관하세요.

이 방법은 일반 텍스트 검토에는 잘 맞지만 PDF, 스프레드시트, OCR 결과, 동적으로 생성되는 문서에서는 줄 경계가 안정적이지 않을 수 있습니다. 이런 경우에는 페이지 번호, 문단 ID, 섹션 ID, 표 셀처럼 더 안정적인 인용 단위를 사용하는 편이 적절할 수 있습니다.

따라서 전체 흐름은 두 단계입니다. 먼저 인용 형식과 줄 범위를 자동 검증하고, 이후 사람이 각 인용 문장이 실제 답변을 뒷받침하는지 확인합니다. 두 단계를 함께 사용하면 출처 없이 생성된 AI 답변보다 문서 질의 결과를 훨씬 쉽게 감사하고 재검토할 수 있습니다.

실행·검증 기록

2026-09-21 · hand-checked example · Python 3.12

  • 합성 문서는 글에 제시된 그대로 정확히 7개 줄로 구성되어 있습니다.
  • 예시 답변에는 정확히 2개의 인용 범위 [L2-L3]과 [L5-L6]이 포함되어 있습니다.
  • [L2-L3]은 합성 원문과 손으로 대조했으며 변경된 프로토타입 시험 날짜와 명시된 이유를 포함합니다.
  • [L5-L6]은 합성 원문과 손으로 대조했으며 초안 결과 마감일과 최종 검토 회의 날짜를 포함합니다.
  • [L8]은 7줄짜리 문서 범위를 벗어나므로 제시된 로직에서는 잘못된 인용 범위로 처리되어야 합니다.
  • 정규식은 의도한 형식인 [L2], [L2-L3], [L2-3]을 허용합니다.
검증 범위의 한계
  • 이 글의 Python 코드는 직접 실행하지 않았으며 동작은 수동으로 검토했습니다.
  • 스크립트는 인용 형식과 참조된 줄 범위의 존재 여부를 검증할 뿐, 해당 줄이 주장에 의미적으로 충분한 근거인지 판단하지 않습니다.
  • 이 예제는 줄 번호가 고정된 일반 텍스트를 전제로 하며 OCR 오류, PDF 레이아웃 변경, 표, 검토 단계 사이에서 줄이 재배치되는 문서는 다루지 않습니다.

사이트 전체의 작성·검증 원칙

참고 출처

설명과 예제는 직접 작성했습니다. 관련 동작과 개념은 아래 공식 자료에서 확인할 수 있습니다.