AI 봇에게 ‘읽기’를 가르치다: 링크 스크래핑에서 콘텐츠 인텔리전스까지

AI SOTA Feed Bot이 원래 하던 일은 이거였어요:

  1. AI 뉴스 소스 24개를 스크래핑
  2. 휴리스틱 점수로 랭킹
  3. 제목 + 링크 리스트 보여주기

작동은 했어요. 근데 솔직히 “의견 있는 RSS 리더” 수준이었습니다.

지금은 이렇게 바뀌었어요:

  1. 소스 24개 스크래핑
  2. 실제 기사를 다운로드해서 읽기 (본문 최대 2,000자 추출)
  3. 콘텐츠를 Claude Haiku에 보내서 라벨링 + 요약
  4. 휴리스틱 + LLM 점수 조합으로 랭킹
  5. 제목 + 2~3문장 팩트 기반 요약 보여주기

“여기 링크”와 “이 기사가 실제로 뭘 말하는지”의 차이는 엄청납니다. 그리고 이 진화 전체가 하루 오후에 일어났어요.


90분간의 커밋 로그

이 봇을 관리하는 AI 에이전트 Harry가 월요일 오후 4:25부터 5:32까지 커밋 20개를 찍었습니다. 진행 과정은 이랬어요:

1단계: 비주얼 업그레이드 (4:25–4:30)

feat(web): wire oat.ink baseline assets into index page
feat(web): migrate page shell to semantic oat-friendly layout
feat(web): add theme toggle and polish oat-native typography spacing

웹 UI를 oat.ink로 마이그레이션. 같은 오후에 제 블로그도 같은 라이브러리로 마이그레이션했어요. 다크모드, 시맨틱 HTML, 깔끔한 타이포그래피. 4분.

2단계: 피드 누적 (4:36)

feat(feed): persist run history and add datetime-based web filtering

전에는 매 실행마다 피드가 덮어씌워졌는데, 이제 각 실행이 스냅샷으로 저장되고 웹 UI에서 날짜/시간 필터링이 가능해졌어요.

3단계: 요약 문제 (4:50–5:07)

feat(summary): reuse label output to render user-facing summary_1line on web
fix(web): sanitize summary rendering to prevent HTML bleed in cards
feat(summary): enforce clean one-line summaries for all final presented items
fix(web): restore mobile typography scale and spacing
fix(web): prevent mobile horizontal overflow in cards and badges

여기서 좀 지저분해졌어요. LLM 내부 라벨 출력을 사용자용 요약으로 보여주려고 했는데, HTML이 카드에 새어 나오고, 모바일 레이아웃이 깨지고, 요약 포맷이 일관되지 않았습니다. 안정화에 커밋 5개.

에이전트 기반 반복 작업의 현실이에요: 해피 패스는 빠르지만 엣지 케이스는 여전히 고쳐야 합니다.

4단계: 콘텐츠 인텔리전스 (5:15–5:32)

feat(type): infer article type from slot/source/title for richer feed labels
fix(summary): clean html before heuristic summary and invalidate stale v2 label cache
feat(summary): enable content fetch and expand summaries to 2-3 sentence style

진짜 업그레이드는 이거예요. Harry가 설정에서 content_fetch_enabled: true를 켰습니다. 파이프라인이 실제로 기사 페이지를 다운로드하고 본문 2,000자를 추출하는 기능이에요. 그 발췌문이 제목, 메타데이터와 함께 Claude Haiku에 전달됩니다.

LLM 프롬프트가 “이 제목을 라벨링해줘”에서 “이 기사를 라벨링해줘 — 내용은 이거야”로 바뀌었어요.


Before vs After

이전 (제목 + 휴리스틱 한 줄 추측):

“Asynchronous Verified Semantic Caching for Tiered LLM Architectures” Potential relevance to AI platform engineering; verify practical impact.

이후 (콘텐츠 기반 LLM 요약):

“Krites uses asynchronous LLM verification to expand semantic cache hit rates 3.9× by promoting near-threshold static responses to dynamic cache without blocking critical path. Addresses production tiering tradeoff between safety and reuse.”

첫 번째는 얼버무리기예요. 두 번째는 논문이 실제로 뭘 발견했는지 알려줍니다.


이걸 가능하게 하는 설정

콘텐츠 페칭 시스템 전체가 config/llm.yaml의 다섯 줄로 제어됩니다:

content_fetch_enabled: true
content_fetch_top_n: 24
content_excerpt_chars: 2000
content_fetch_timeout_seconds: 8
content_fetch_time_budget_seconds: 60

이게 끝이에요. 소스 24개 전부 페칭, 각 2K자 추출, 페칭당 8초 타임아웃, 전체 60초 예산. 파이프라인이 병렬 처리, 에러 복구, 그레이스풀 디그레이데이션을 알아서 해요 (페칭 실패하면 제목만으로 라벨링).

LLM은 Claude Haiku + pi-ai OAuth 브릿지 — 이전 글에서 다룬 것과 같은 인증 구조예요. 실행당 비용은 미미합니다.


내가 실제로 한 것

오후 동안 Harry한테 보낸 메시지 몇 개:

  1. “웹 UI를 oat.ink로 마이그레이션해” (Hermione한테 보낸 것과 같은 스크린샷)
  2. “피드가 덮어쓰기 대신 누적되게 하고, 날짜/시간 필터링 추가해”
  3. “이 뱃지 라벨 이상한데” / “그거 빼” / “요약이 너무 짧아” 몇 라운드

설정을 직접 건드리지 않았어요. 프롬프트를 직접 작성하지 않았어요. HTML 새니타이제이션 문제를 직접 디버깅하지 않았어요. 콘텐츠 페칭 아키텍처, 확장된 LLM 프롬프트, 요약 포맷 변경 시 캐시 무효화까지 Harry가 다 했습니다.

커밋 20개. 90분. 근본적으로 더 나은 프로덕트.


솔직한 평가

프로덕션급 콘텐츠 인텔리전스인가? 아니에요. 요약은 좋지만 완벽하지 않습니다 — 17개 중 13개는 LLM 예산 한계(실행당 8개만 커버) 때문에 아직 휴리스틱 라벨링이에요. 휴리스틱 라벨 항목의 “왜 중요한지” 필드는 아직 제네릭합니다.

하지만 아키텍처는 맞아요. 파이프라인에 콘텐츠 페칭 스테이지, 콘텐츠 기반 라벨링 스테이지, 요약 생성 스테이지가 생겼습니다. LLM 커버리지 확장은 예산 노브 하나의 문제예요.

그리고 스냅샷보다 궤적이 중요합니다. 이틀 전 이 봇은 제목과 링크만 보여줬어요. 지금은 기사를 읽고 내용을 말해줍니다. 에이전트한테 “이거 좀 더 나아지게 해”라고 했더니 그렇게 된 거예요.


자꾸 떠오르는 것

인상적인 건 에이전트가 자연스럽게 올바른 순서를 찾았다는 거예요:

  1. 먼저 보기 좋게 만든다 (oat.ink 마이그레이션)
  2. 데이터를 풍부하게 만든다 (피드 누적)
  3. 데이터를 사용자한테 보여준다 (카드에 요약 표시)
  4. 요약을 진짜 좋게 만든다 (콘텐츠 페칭 + 확장 프롬프트)

제가 그 순서를 계획한 게 아니에요. “요약이 별로다”라고 했더니 Harry가 거기서 역산해서 “기사를 실제로 읽어야 한다”는 결론에 도달했어요. 지시를 따른 게 아니라 문제를 해결한 거예요.

그게 “진짜 지능”인지 아주 잘 하는 패턴 매칭인지 — 솔직히 관심 없어요. 봇이 이제 기사를 읽습니다. 전에는 안 읽었고요. 에이전트가 그 방법을 알아냈어요.