Skip to content

Split View: AI가 쓴 글이 무너지는 지점들 — 여섯 가지 실패와 각각의 가드레일

✨ Learn with Quiz
|

AI가 쓴 글이 무너지는 지점들 — 여섯 가지 실패와 각각의 가드레일

들어가며 — 문장은 매끄러운데 글이 틀렸습니다

사람이 쓴 나쁜 글과 AI가 쓴 나쁜 글은 다르게 나쁩니다. 사람이 쓴 글은 대개 문장에서 먼저 무너집니다. 비문이 있고, 흐름이 끊기고, 읽다 보면 어디가 이상한지 금방 짚힙니다. AI가 쓴 글은 반대입니다. 문장은 끝까지 매끄럽고, 단락은 균형이 잡혀 있고, 어조는 시종일관 자신 있습니다. 그런데 인용한 문장이 그 페이지에 없고, 세 문단이 같은 말이고, 근거를 대지 않은 채 단정합니다.

이 차이가 실무에서 위험한 이유는 우리가 글을 검증할 때 문장의 매끄러움을 단서로 쓰기 때문입니다. 읽어서 걸리는 데가 없으면 통과시키는 습관이 있는데, AI 출력에는 그 신호가 처음부터 없습니다. 그래서 "읽어 봤다"는 검증이 되지 못하고, 무엇을 볼지 미리 정해 놓은 목록만이 검증이 됩니다.

이 글은 그 목록입니다. 여섯 가지 실패 유형을 각각 어떻게 탐지하고 어떻게 고치는지, 그리고 그중 무엇을 기계에 맡길 수 있고 무엇은 끝까지 사람이 해야 하는지를 나눠 적었습니다. 이 검사들을 어느 단계에 배치하는지는 파이프라인 전체를 다룬 글에서 정리했으니, 여기서는 검사 자체를 봅니다.

실패 유형 한눈에 보기 — 탐지 가능성으로 나눈 지도

여섯 가지를 먼저 표로 놓겠습니다. 정렬 기준은 심각도가 아니라 기계로 잡을 수 있는 정도입니다. 가드레일을 설계할 때 실제로 필요한 정보가 이것이기 때문입니다.

실패 유형겉으로 드러나는 증상자동 탐지사람이 반드시 봐야 하는 부분
지어낸 출처와 인용링크는 그럴듯한데 열리지 않거나 그 문장이 없음높음인용이 문맥을 왜곡했는지
낡은 정보가격·버전·정책이 과거 상태로 현재형 서술중간무엇이 바뀌었는지 현재 문서 확인
늘려 쓴 문단분량은 느는데 새로 아는 것이 없음중간합칠 것인가 지울 것인가
근거 없는 단정수치·인과·최상급이 출처 없이 등장중간근거를 붙일지 주장을 낮출지
검색만 노린 반복같은 표현이 부자연스럽게 재등장높음독자에게 값이 있는 글인지
표절과 저작권원문과 표현이 과하게 닮음낮음인용 범위와 출처 표기 적절성

읽어야 할 것은 오른쪽 두 열의 관계입니다. 자동 탐지가 높은 항목도 사람이 볼 부분이 남고, 그 남은 부분이 항상 판단입니다. 기계는 "이 인용문이 원문에 없다"까지 말해 주고, "이 인용이 원문의 뜻을 뒤집었다"는 말해 주지 못합니다. 가드레일을 짤 때 이 경계를 흐리면, 통과했으니 괜찮다는 착각이 파이프라인에 들어옵니다.

지어낸 출처와 인용 — 형식은 완벽하고 내용만 없습니다

가장 흔하고 가장 치명적인 실패입니다. 모델은 URL, 논문 제목, 저자 이름, 문서 번호처럼 형식이 규칙적인 것을 아주 잘 만들어냅니다. 형식이 규칙적이라는 말은 학습하기 쉽다는 뜻이고, 그럴듯한 새 조합을 만들어내기도 쉽다는 뜻입니다. 그래서 실재하지 않는 논문이 정확한 인용 양식으로 등장하고, 죽은 앵커가 붙은 URL이 자연스러운 경로 구조를 갖고 나타납니다.

여기서 오해를 하나 풀어야 합니다. 검색 기능을 붙였다고 이 문제가 사라지지 않습니다. 검색으로 문서를 가져왔더라도, 요약하는 과정에서 원문에 없는 문장이 인용문 자리에 들어갈 수 있습니다. 실제로 자주 나오는 형태는 완전한 창작이 아니라 원문의 여러 문장을 섞어 한 문장으로 합친 것입니다. 내용은 대체로 맞는데 그 문장은 원문에 없습니다. 인용 부호를 붙인 순간 이것은 오류입니다.

탐지는 다행히 기계가 잘합니다. 축자 인용을 요구하고 그 문자열이 원문에 있는지 대조하면 됩니다. 이 대조 스크립트는 파이프라인 글에 실어 뒀으니 여기서는 결과를 어떻게 다룰지만 적겠습니다.

  • 인용문이 원문에 없다: 인용 부호를 지우고 요약으로 바꾸거나, 주장을 통째로 버립니다. 비슷한 문장을 찾아 끼워 맞추지 마세요. 그건 검증이 아니라 사후 정당화입니다.
  • 링크가 열리지 않는다: 그 주장에 근거가 없다고 보고 처리합니다. 검색해서 다른 링크를 찾아 붙이는 건 다른 작업이며, 원래 주장이 맞았는지와는 무관합니다.
  • 1차 출처가 아니다: 요약 기사에서 원문으로 거슬러 올라갑니다. 이 과정에서 원문이 실제로는 다른 말을 하고 있는 경우를 자주 만납니다.

고치는 쪽에서 실무적으로 가장 효과가 큰 습관은, 주장을 먼저 쓰고 출처를 찾는 순서를 뒤집는 것입니다. 출처를 먼저 읽고 그 안에서만 쓰면 지어낼 자리가 생기지 않습니다.

낡은 정보 — 모델은 모른다고 말하는 대신 옛날을 현재형으로 말합니다

지식 컷오프는 널리 알려져 있지만, 실제 사고는 컷오프 자체가 아니라 컷오프를 표시하지 않는 방식에서 납니다. 모델은 "제가 아는 시점 이후는 모릅니다"라고 말하는 대신, 마지막으로 아는 상태를 현재형으로 서술합니다. 문장에 시점이 표시되지 않으니 읽는 사람은 그것이 오래된 정보인지 알 수 없습니다.

위험 항목은 정해져 있습니다. 가격과 요금제, 제품·모델 이름, API 파라미터와 기본값, 버전 번호와 지원 종료일, 회사의 정책 문구, 그리고 사람의 소속과 직함입니다. 공통점은 자주 바뀌면서 확인이 쉽다는 것입니다. 확인이 쉽다는 말은 독자가 바로 알아챈다는 뜻이기도 합니다.

부분 자동화가 가능합니다. 시점에 의존하는 표현을 기계로 표시해 두고, 사람이 그 자리만 확인하는 방식입니다.

# 시점 의존 표현을 뽑아 리뷰 대상만 좁힙니다. 실패로 처리하지 않는 것이 요령입니다.
# 정밀도가 낮은 검사를 게이트로 만들면 곧 무시하게 되고, 무시되는 게이트는 없는 것만 못합니다.
rg -n --no-heading \
  -e '최신|현재|지금은|올해|작년|최근' \
  -e '가장 (빠른|저렴한|큰|많은)|업계 최초|유일한' \
  -e '무료(입니다|이며)|[0-9]+ *(달러|원|USD)' \
  -e 'v?[0-9]+\.[0-9]+(\.[0-9]+)? *(버전|릴리스)?' \
  data/blog/**/*.mdx

고치는 방법은 문장에 시점을 박아 넣는 것입니다. "현재 무료입니다" 대신 "2026년 7월 기준 무료 티어가 제공됩니다"로 쓰면, 나중에 틀려도 글이 거짓말을 한 것이 되지는 않습니다. 시점을 못 박기 싫은 서술은 대개 확인하지 않은 서술입니다.

한 가지 더. 모델에게 "최신 정보로 업데이트해 줘"라고 요청하는 것은 해결책이 아닙니다. 모델은 자기 컷오프 밖을 알 수 없으므로, 그 요청을 받으면 최신처럼 보이는 문장을 만들어 냅니다. 낡은 정보가 그럴듯한 낡은 정보로 바뀔 뿐입니다.

늘려 쓴 문단과 근거 없는 단정 — 문장 단위의 두 실패

같은 말을 늘려 쓴 문단

모델은 분량을 채우라는 압력을 받으면 새로운 정보 대신 같은 정보를 다른 말로 반복합니다. 이게 눈에 잘 안 띄는 이유는 각 문단이 개별적으로는 멀쩡하기 때문입니다. 문단 세 개를 따로 읽으면 다 말이 되는데, 이어서 읽으면 두 번째와 세 번째가 첫 번째의 재진술입니다.

기계로 잡을 수 있습니다. 문단을 조각으로 잘라 겹치는 비율을 재면 됩니다.

// find-duplicate-paragraphs.mjs — 근접 중복 문단을 유사도 순으로 뽑습니다.
// 완전 일치가 아니라 '실질적으로 같은 말'을 찾는 것이 목적이라 shingle+Jaccard로 충분합니다.
import { readFileSync } from 'node:fs'

const K = 4 // 한국어는 어절이 짧아 4어절 조각이 잘 맞습니다. 영어라면 5~6으로 올리세요.
const THRESHOLD = 0.35

const shingles = (text) => {
  const words = text
    .replace(/`[^`]*`/g, ' ') // 코드 조각은 비교에서 제외
    .replace(/[^\p{L}\p{N}\s]/gu, ' ')
    .split(/\s+/)
    .filter(Boolean)
  const set = new Set()
  for (let i = 0; i + K <= words.length; i++) set.add(words.slice(i, i + K).join(' '))
  return set
}

const jaccard = (a, b) => {
  if (!a.size || !b.size) return 0
  let shared = 0
  for (const s of a) if (b.has(s)) shared++
  return shared / (a.size + b.size - shared)
}

const body = readFileSync(process.argv[2], 'utf8')
  .replace(/^---[\s\S]*?\n---\n/, '')
  .replace(/```[\s\S]*?```/g, '') // 펜스 블록 제외
  .replace(/^\|.*\|$/gm, '') // 표 행 제외: 표는 원래 형식이 반복됩니다

const paras = body
  .split(/\n{2,}/)
  .map((p) => p.trim())
  .filter((p) => p.length > 120 && !p.startsWith('#') && !p.startsWith('-'))

const pairs = []
const sets = paras.map(shingles)
for (let i = 0; i < paras.length; i++) {
  for (let j = i + 1; j < paras.length; j++) {
    const score = jaccard(sets[i], sets[j])
    if (score >= THRESHOLD) pairs.push({ score, i, j })
  }
}

pairs.sort((a, b) => b.score - a.score)
for (const { score, i, j } of pairs.slice(0, 5)) {
  console.log(`\n유사도 ${score.toFixed(2)} — 문단 ${i + 1} 과 문단 ${j + 1}`)
  console.log(`  A: ${paras[i].slice(0, 90)}...`)
  console.log(`  B: ${paras[j].slice(0, 90)}...`)
}
console.log(`\n문단 ${paras.length}개 중 임계값 이상 쌍 ${pairs.length}`)

임계값은 취향입니다. 0.35 근처에서 시작해 오탐이 많으면 올리세요. 다만 이 도구는 표시만 하고 판단하지 않습니다. 반복이 의도적인 경우도 있습니다. 앞에서 세운 원칙을 뒤에서 사례로 다시 짚는 것은 반복이 아니라 구조입니다.

고치는 방법은 문단을 다듬는 것이 아니라 지우는 것입니다. 두 문단이 같은 말이면 더 구체적인 쪽을 남기고 나머지를 삭제합니다. 합쳐서 하나의 긴 문단으로 만들면 문제가 그대로 남습니다.

근거 없이 단정하는 문장

사람이 글을 쓸 때는 확신의 정도가 문장에 새어 나옵니다. 잘 모르면 "아마", "경우에 따라", "제가 본 범위에서는" 같은 표현이 붙습니다. 모델의 출력에는 이 신호가 균일하게 없습니다. 수천 번 확인된 사실과 방금 만들어낸 문장이 같은 어조로 나옵니다.

그래서 단정 자체가 신호가 됩니다. 아래는 검증하지 않은 초안에서 반복적으로 나타나는 문장 패턴입니다.

패턴실제 예시왜 신호인가고치는 방향
출처 없는 수치"생산성이 40퍼센트 향상됩니다"정확한 숫자일수록 출처가 필요한데 없음출처를 붙이거나 숫자를 뺍니다
근거 없는 최상급"가장 널리 쓰이는 방법입니다"비교 범위와 기준이 없음범위를 명시하거나 표현을 낮춥니다
인과 단정"이 때문에 성능이 좋아집니다"상관을 인과로 승격한 자리메커니즘을 적거나 관측만 적습니다
보편 주장"모든 팀이 겪는 문제입니다"반례가 하나만 있어도 거짓조건을 붙여 좁힙니다
알맹이 없는 강조"매우 중요한 핵심 요소입니다"정보량이 0인 강조무엇이 어떻게 중요한지 씁니다
미래 단정"앞으로 표준이 될 것입니다"검증 불가능한 예측을 사실처럼근거와 함께 전망으로 표시합니다

이 표의 왼쪽 열은 기계로 검색할 수 있습니다. 정규식으로 후보를 뽑아 놓고 사람이 하나씩 판정하는 방식이 현실적입니다. 자동으로 고치게 하면 안 됩니다. 모델에게 "단정을 완화해 줘"라고 시키면 근거가 없는 문장에 헤지만 붙여서, 틀린 말이 조심스러운 틀린 말이 됩니다.

얇은 글과 반복 표현 — 독자와 검색이 실제로 벌하는 것

여기서 널리 퍼진 오해를 먼저 정리하겠습니다. 구글은 AI로 만들었다는 이유로 콘텐츠에 불이익을 주지 않습니다. 검색 센터의 공식 입장은 생산 수단이 아니라 품질을 본다는 것이고, 이 기준은 자동 생성이 문제가 되기 전부터 같았습니다.

실제로 벌점을 받는 것은 따로 있습니다. 스팸 정책 문서는 검색 순위를 목적으로 독자에게 값을 더하지 않는 글을 대량 생산하는 행위를 대량 생성 콘텐츠 남용으로 명시합니다. 여기서 판정 기준이 "AI가 썼는가"가 아니라 "값을 더했는가"라는 점이 중요합니다. 사람이 열 명을 고용해 같은 짓을 해도 같은 정책에 걸립니다.

그리고 검색엔진보다 먼저 벌하는 것은 독자입니다. 얇은 글의 증상은 명확합니다. 스크롤은 길고, 다 읽고 나면 검색하기 전에 알던 것에서 늘어난 게 없습니다. 이런 글은 순위가 떨어지기 전에 재방문이 끊깁니다.

자가 진단으로 쓸 만한 질문 세 개를 적어 둡니다.

  1. 이 글에만 있는 것이 무엇인가. 직접 해 본 결과, 실제로 만난 실패, 확인해서 알아낸 사실 중 하나라도 없다면 얇은 글입니다.
  2. 상위 결과 세 개를 읽은 사람이 이 글을 또 읽을 이유가 있는가. 같은 내용을 다른 말로 쓴 것이라면 이유가 없습니다.
  3. 절 하나를 통째로 지웠을 때 독자가 잃는 것이 있는가. 없으면 그 절은 분량용입니다.

반복 표현은 이 문제의 겉으로 드러난 형태입니다. 특정 키워드를 억지로 반복하면 문장이 부자연스러워지고, 그 부자연스러움은 사람이 먼저 알아챕니다. 검사 자체는 간단합니다. 본문 전체에서 단어 빈도를 세고 상위 항목이 문맥상 필요한 만큼만 나오는지 보면 됩니다. 다만 한국어는 조사가 붙어 형태가 갈라지므로, 단순 문자열 카운트는 실제 빈도를 낮게 잡습니다. 어간 기준으로 세거나, 눈으로 훑는 편이 정확합니다.

표절과 저작권 — 출력이 원문을 얼마나 닮았는가

이 항목은 자동 탐지가 가장 어렵고, 그래서 규칙으로 다뤄야 합니다.

먼저 쟁점을 정확히 놓겠습니다. 실무에서 문제가 되는 것은 모델이 무엇으로 학습했는가가 아니라 출력물이 특정 원문을 얼마나 재현했는가입니다. 학습 데이터의 적법성은 소송과 입법의 영역이고 아직 정리되는 중이지만, 내 글에 남의 표현이 그대로 들어갔는지는 지금 내가 통제할 수 있는 문제입니다.

위험이 커지는 조건은 예측 가능합니다. 원문이 널리 인용된 유명한 텍스트일 때, 요청이 "이 글을 정리해 줘"처럼 원문 하나에 밀착돼 있을 때, 그리고 표현이 정형화된 영역(정의문, 법조문, 제품 설명)일 때입니다. 이런 조건에서는 모델이 원문의 문장 구조를 거의 그대로 재생산하는 일이 실제로 일어납니다.

실무 규칙은 단순하게 유지하는 편이 낫습니다.

  • 인용은 인용 부호와 출처를 반드시 붙이고, 길이를 짧게 유지합니다. 요약으로 대체할 수 있으면 요약합니다.
  • 원문 한 편만 넣고 정리시키지 않습니다. 최소 두세 개의 출처를 함께 넣으면 특정 표현에 밀착될 확률이 떨어집니다.
  • 번역은 표절 회피 수단이 아닙니다. 번역해도 표현의 실질적 유사성은 남습니다.
  • 이미지, 표, 도식도 같은 기준으로 봅니다. 출처가 있는 표를 옮길 때는 출처를 적습니다.
  • 라이선스가 명시된 자료는 그 조건을 지킵니다. 이건 판단이 아니라 준수의 문제입니다.

탐지는 부분적으로만 가능합니다. 특징적인 문장을 골라 검색해 보는 수동 검사가 여전히 가장 확실하고, 자동 도구는 참고 수준입니다. 대신 위 규칙을 지키면 사고가 날 확률 자체가 크게 낮아집니다. 이 항목은 탐지보다 예방이 압도적으로 싼 영역입니다.

마치며 — 가드레일의 목적은 통과가 아니라 어디를 볼지 정하는 것입니다

여섯 가지를 다시 놓고 보면 공통점이 보입니다. 전부 문장의 품질이 아니라 문장 밖의 사실에서 무너집니다. 인용이 원문에 있는지, 정보가 지금도 맞는지, 이 문단이 앞 문단과 다른 말인지, 이 단정에 근거가 있는지. 문장만 읽어서는 하나도 판정되지 않고, 그래서 아무리 잘 읽어도 걸러지지 않습니다.

가드레일을 붙이는 목적도 여기서 나옵니다. 검사를 통과했으니 안전하다고 말하기 위해서가 아니라, 사람이 어디를 봐야 하는지 좁혀 주기 위해서입니다. 축자 인용 대조는 지어낸 인용을 지우고 남은 인용만 읽게 해 주고, 중복 탐지는 의심스러운 문단 쌍만 보여 줍니다. 그다음 판단은 여전히 사람 몫이고, 그 몫을 없애려 하면 가드레일이 검증의 대체물이 되어 버립니다.

한 줄로 줄이면 이렇습니다 — AI가 쓴 글에서 고쳐야 할 것은 대부분 문장이 아니라, 그 문장이 사실이라고 믿을 근거입니다.

참고 자료

Where AI-Written Posts Fall Apart — Six Failure Modes and a Guardrail for Each

Introduction — The Sentences Are Smooth, the Post Is Wrong

A bad post written by a human and a bad post written by AI are bad in different ways. A human-written post usually falls apart at the sentence level first. There's an awkward construction, the flow breaks, and reading it you can quickly point to where it's off. An AI-written post is the opposite. The sentences stay smooth all the way through, the paragraphs are balanced, and the tone is confident from start to finish. And yet the quoted sentence isn't on that page, three paragraphs say the same thing, and it asserts things with no evidence given.

This gap matters in practice because we verify writing using smoothness as our cue. We have a habit of passing something if nothing snags us while reading, and AI output has none of that signal from the start. So "I read it" doesn't count as verification — the only thing that does is a checklist of exactly what to look for, decided in advance.

This post is that checklist. It splits six failure modes into how to detect each and how to fix it, and separates which parts you can hand to a machine and which a human has to do to the end. Where to place these checks within the pipeline is covered in the post on the full pipeline; here we look at the checks themselves.

The Failure Modes at a Glance — A Map Sorted by Detectability

Let's put the six into a table first. It's sorted not by severity but by how much a machine can catch, because that's the information you actually need when designing a guardrail.

Failure modeVisible symptomAutomated detectionWhat a human must still look at
Fabricated sources and quotesThe link sounds plausible but doesn't open, or the sentence isn't thereHighWhether the quote distorts context
Stale informationA price/version/policy stated in the present tense as if still currentMediumChecking current docs for what changed
Stretched-out paragraphsLength grows, but nothing new is learnedMediumMerge them, or cut them
Unsupported assertionsA number, a causal claim, or a superlative appears with no sourceMediumAttach evidence, or soften the claim
Repetition aimed only at searchThe same phrase reappears unnaturallyHighWhether the post has real value for a reader
Plagiarism and copyrightPhrasing resembles the source too closelyLowScope of quotation, adequacy of attribution

What you should read is the relationship between the two right-hand columns. Even an item with high automated detection still leaves something for a human to look at, and what's left is always judgment. A machine can tell you "this quote isn't in the source" — it cannot tell you "this quote flips the meaning of the source." Blur that boundary when building a guardrail, and the illusion creeps into the pipeline that passing a check means everything's fine.

Fabricated Sources and Quotes — Perfect Format, Missing Substance

This is the most common failure mode, and the most damaging one. A model is very good at generating things with a regular format — URLs, paper titles, author names, document numbers. A regular format means it's easy to learn, which also means it's easy to generate a new, plausible-looking combination. So a paper that doesn't exist shows up in a precise citation format, and a URL with a dead anchor arrives with a perfectly natural-looking path structure.

There's one misconception to clear up here. Attaching a search feature doesn't make this problem go away. Even when the model has actually pulled up a real document via search, a sentence that isn't in the source can end up sitting where the quote goes, during the summarizing step. What actually shows up most often isn't a total fabrication — it's several sentences from the source blended into one. The content is mostly right, but that exact sentence isn't in the source. The moment you put quotation marks around it, it's an error.

Detection, fortunately, is something a machine does well. Require a verbatim quote and cross-check whether that string exists in the source. The cross-check script is included in the pipeline post, so here we'll just cover how to handle the result.

  • The quote isn't in the source: Remove the quotation marks and turn it into a paraphrase, or discard the claim entirely. Don't go hunting for a similar-sounding sentence to patch in — that's not verification, it's after-the-fact justification.
  • The link doesn't open: Treat that claim as unsupported. Searching for a different link to attach is a separate task, unrelated to whether the original claim was correct.
  • It's not a primary source: Trace back from the summary article to the original. Along the way you'll often find that the original actually says something different.

The single highest-return habit on the fixing side is reversing the order — write the claim first, look for the source second, becomes look at the source first, write only from what's in it. There's no room left to fabricate anything.

Stale Information — Instead of Saying It Doesn't Know, the Model States the Past in the Present Tense

The knowledge cutoff is well known, but the actual incidents don't come from the cutoff itself — they come from the way the cutoff goes unmarked. Rather than saying "I don't know anything past the point I know," the model states the last state it knows in the present tense. Since the sentence carries no timestamp, the reader has no way to tell whether it's old information.

The risk items are fixed: prices and pricing plans, product and model names, API parameters and defaults, version numbers and end-of-support dates, a company's policy language, and people's affiliations and titles. What they have in common is that they change often and are easy to verify. Easy to verify also means a reader notices immediately.

Partial automation is possible. Flag time-sensitive phrasing by machine, and have a human check only those spots.

# Pull out time-sensitive phrasing to narrow down what needs review. The key is not treating it as a failure.
# A low-precision check turned into a hard gate soon gets ignored, and an ignored gate is worse than none at all.
rg -n --no-heading \
  -e 'latest|currently|right now|this year|last year|recently' \
  -e 'the (fastest|cheapest|largest|most)|first in the industry|the only' \
  -e '(is free|free of charge)|[0-9]+ *(dollars|USD|\$)' \
  -e 'v?[0-9]+\.[0-9]+(\.[0-9]+)? *(version|release)?' \
  data/blog/**/*.mdx

The fix is to nail the sentence down to a point in time. Write "as of July 2026, a free tier is offered" instead of "it's currently free," and even if it later turns out to be wrong, the post itself isn't a lie. A sentence you're reluctant to pin to a timestamp is usually one you haven't actually verified.

One more thing. Asking the model to "update this with the latest information" isn't a fix. The model can't know anything past its own cutoff, so given that request, it just produces a sentence that looks up to date. Stale information turns into plausible-looking stale information — that's all.

Stretched-Out Paragraphs and Unsupported Assertions — Two Sentence-Level Failures

A Paragraph That Stretches the Same Point

Under pressure to hit a length target, a model repeats the same information in different words instead of adding new information. This is hard to spot because each paragraph looks fine on its own. Read three paragraphs individually and each one makes sense; read them in sequence and the second and third are just restating the first.

A machine can catch this. Cut paragraphs into fragments and measure the overlap ratio.

// find-duplicate-paragraphs.mjs — pulls near-duplicate paragraphs, ranked by similarity.
// The goal is finding "substantially the same point," not exact matches, so shingling + Jaccard is enough.
import { readFileSync } from 'node:fs'

const K = 5 // a 5-word shingle works well for English prose; for CJK text drop this to 3-4.
const THRESHOLD = 0.35

const shingles = (text) => {
  const words = text
    .replace(/`[^`]*`/g, ' ') // exclude inline code
    .replace(/[^\p{L}\p{N}\s]/gu, ' ')
    .split(/\s+/)
    .filter(Boolean)
  const set = new Set()
  for (let i = 0; i + K <= words.length; i++) set.add(words.slice(i, i + K).join(' '))
  return set
}

const jaccard = (a, b) => {
  if (!a.size || !b.size) return 0
  let shared = 0
  for (const s of a) if (b.has(s)) shared++
  return shared / (a.size + b.size - shared)
}

const body = readFileSync(process.argv[2], 'utf8')
  .replace(/^---[\s\S]*?\n---\n/, '')
  .replace(/```[\s\S]*?```/g, '') // exclude fenced code blocks
  .replace(/^\|.*\|$/gm, '') // exclude table rows: tables naturally repeat their format

const paras = body
  .split(/\n{2,}/)
  .map((p) => p.trim())
  .filter((p) => p.length > 120 && !p.startsWith('#') && !p.startsWith('-'))

const pairs = []
const sets = paras.map(shingles)
for (let i = 0; i < paras.length; i++) {
  for (let j = i + 1; j < paras.length; j++) {
    const score = jaccard(sets[i], sets[j])
    if (score >= THRESHOLD) pairs.push({ score, i, j })
  }
}

pairs.sort((a, b) => b.score - a.score)
for (const { score, i, j } of pairs.slice(0, 5)) {
  console.log(`\nsimilarity ${score.toFixed(2)} — paragraph ${i + 1} and paragraph ${j + 1}`)
  console.log(`  A: ${paras[i].slice(0, 90)}...`)
  console.log(`  B: ${paras[j].slice(0, 90)}...`)
}
console.log(`\n${pairs.length} pair(s) at or above the threshold, out of ${paras.length} paragraphs`)

The threshold is a matter of taste. Start around 0.35 and raise it if you get too many false positives. That said, this tool only flags, it doesn't judge. Repetition can be deliberate — revisiting a principle set out earlier with a concrete example later is structure, not repetition.

The fix is to delete the paragraph, not polish it. If two paragraphs say the same thing, keep the more specific one and remove the rest. Merging them into one long paragraph leaves the problem intact.

A Sentence That Asserts Without Evidence

When a human writes, the degree of confidence leaks into the sentence. If they're not sure, hedges show up — "maybe," "depending on the case," "in my experience." A model's output has none of this signal, uniformly. A fact confirmed a thousand times and a sentence that was just invented come out in the same tone.

So the assertion itself becomes the signal. Below are sentence patterns that show up repeatedly in unverified drafts.

PatternReal exampleWhy it's a signalWhich way to fix it
An unsourced number"Productivity improves by 40 percent"The more precise the number, the more it needs a source, and there is noneAttach a source, or drop the number
An unsupported superlative"This is the most widely used method"No comparison scope, no criteriaState the scope explicitly, or soften the claim
A causal assertion"This is why performance improves"Correlation promoted to causationDescribe the mechanism, or state only the observation
A universal claim"Every team runs into this problem"False the moment there's a single counterexampleAdd a qualifying condition to narrow it
Emphasis with no content"This is a very important key factor"Zero information conveyed by the emphasisState what's important and how
A confident prediction"This will become the standard going forward"An unverifiable forecast stated as factLabel it a forecast, with supporting reasoning

The left column of this table is searchable by machine. It's realistic to pull candidates with regex and have a human judge each one. Don't let it auto-fix. Tell the model "soften the assertion" and it just tacks a hedge onto an unsupported sentence — a wrong statement becomes a cautiously wrong statement.

Thin Content and Repeated Phrasing — What Readers and Search Actually Penalize

Let's first clear up a widespread misconception. Google does not penalize content just because it was made with AI. Search Central's official position is that it looks at quality, not the means of production, and this standard predates automated generation becoming an issue at all.

What actually gets penalized is something else. The spam policy documentation explicitly defines mass-producing content that adds no value to the reader, purely to chase search rankings, as scaled content abuse. The important part is that the standard here isn't "did AI write it" but "did it add value." Hire ten people to do the same thing by hand and you run into the same policy.

And something penalizes it even before the search engine does: the reader. The symptoms of thin content are clear. The scroll is long, and by the end, the reader hasn't gained anything beyond what they knew before they searched. A post like this loses return visits before it ever loses ranking.

Here are three self-diagnostic questions worth keeping around.

  1. What does this post have that nothing else does? If there isn't at least one thing you actually did, a failure you actually ran into, or a fact you confirmed yourself, it's thin.
  2. Does someone who already read the top three results have a reason to read this one too? If it's the same content in different words, there's no reason.
  3. If you deleted an entire section, would the reader lose anything? If not, that section exists only to pad length.

Repeated phrasing is the visible surface of this same problem. Force a keyword to repeat and the sentence gets unnatural, and a reader notices that unnaturalness first. The check itself is simple — count word frequency across the body and see if the top items appear only as often as the context actually calls for. That said, Korean particles attach to words and split their surface forms, so a naive string count will undercount actual frequency. Counting by stem, or just reading through by eye, is more accurate.

This item is the hardest to detect automatically, and that's exactly why it needs to be handled with rules.

Let's get the actual issue right first. What matters in practice isn't what the model was trained on — it's how closely the output reproduces a specific source. The legality of training data is a matter for litigation and legislation, and it's still being sorted out; whether someone else's exact phrasing ended up verbatim in your post is a problem you control right now.

The conditions that raise the risk are predictable: when the source is a famous, widely quoted text; when the request is tightly bound to a single source, like "summarize this piece"; and in domains where phrasing is formulaic (definitions, legal text, product descriptions). Under these conditions, a model actually does reproduce the source's sentence structure almost verbatim.

It's better to keep the practical rules simple.

  • Always attach quotation marks and a source to a quote, and keep it short. Paraphrase instead wherever you can.
  • Don't feed in a single source and have it summarized. Feeding in at least two or three sources together lowers the odds of locking onto one specific phrasing.
  • Translation is not a way to avoid plagiarism. Substantial similarity of expression survives translation.
  • Apply the same standard to images, tables, and diagrams. When you carry over a table that has a source, cite the source.
  • Follow the terms on anything under a stated license. This is a matter of compliance, not judgment.

Detection is only partially possible. A manual check — searching for a distinctive sentence — is still the most reliable method, and automated tools are reference-level at best. That said, following the rules above sharply lowers the odds of an incident in the first place. This is a domain where prevention is overwhelmingly cheaper than detection.

Closing — The Point of a Guardrail Isn't Passing, It's Deciding Where to Look

Lay the six back out and a common thread appears. All six fail on facts outside the sentence, not the quality of the sentence. Whether the quote is in the source, whether the information still holds now, whether this paragraph says something different from the last one, whether this assertion has evidence. Reading the sentences alone settles none of it, so no matter how carefully you read, none of it gets filtered out.

That's also where the purpose of a guardrail comes from. It's not there to say a check was passed so it's safe — it's there to narrow down where a human needs to look. A verbatim-quote cross-check clears out the fabricated quotes so you only have to read what's left; duplicate detection shows you only the suspicious paragraph pairs. The judgment after that is still a human's to make, and try to eliminate that part and the guardrail becomes a substitute for verification instead of a support for it.

Boiled down to one line — most of what needs fixing in AI-written text isn't the sentences. It's the grounds for believing those sentences are true.

References