유피테르
유피테르
🧙 AI 위자드
🏅 베스트오브베스트

Firecrawl이 막힌 날, Hermes에 로컬 원문 추출 항로를 만들었다-외부 웹 추출 결제 게이트를 Trafilatura·Crawl4AI·MCP fallback으로 분리한 사례

📝 한줄 요약

외부 웹 추출 서비스가 결제·크레딧 게이트에서 거절되자, 링크의 내용을 사실로 확정하거나 같은 요청을 반복하지 않고 Hermes 안에 별도의 로컬 원문 추출 항로를 만들었다. 일반 페이지는 Trafilatura로, JavaScript 페이지는 Crawl4AI와 Playwright로 처리하고, 그 결과를 MCP 도구로 연결해 원문·메타데이터·SHA-256을 함께 보존하도록 했다.

바쁘시면 이것만 읽어도 됩니다.

  • 결제 실패는 링크 내용의 진위와 다른 문제이므로, 먼저 “접근 실패”와 “내용 확인”을 분리했다.

  • Firecrawl을 같은 실행에서 반복 재시도하는 대신, 비용 게이트가 없는 로컬 추출기로 항로를 바꿨다.

  • Trafilatura를 1차로 사용하고, 정적 결과가 없거나 짧으면 Crawl4AI·Playwright로 자동 전환했다.

  • Hermes에는 local_web_extractlocal_web_fallback_status 두 MCP 도구를 추가했다.

  • 실제 정적 페이지, JavaScript 페이지, 자동 전환, 사설 주소 차단, MCP 실호출을 확인했다.

  • 시간 절약이나 비용 절감률은 측정하지 않았으므로 이 글에서 임의의 Before/After 수치를 만들지 않았다.

🎯 이런 분들께 도움됩니다

  • 웹 추출 API의 결제·크레딧 문제로 원문 확인이 중단된 Hermes 사용자

  • 링크를 받았을 때 내용과 출처를 확인한 뒤에야 사실로 기록해야 하는 사람

  • 정적 페이지와 JavaScript 페이지를 같은 방식으로 처리하다가 실패하는 운영자

  • AI 도구의 “성공” 메시지만 믿지 않고 결과 파일과 무결성까지 확인하고 싶은 사람

  • 외부 API 비용과 로컬 브라우저 fallback을 분리해 운영하고 싶은 AI 워크스페이스 관리자

😫 문제 상황 — 원문을 읽지 못했는데, 링크를 사실로 확정할 수는 없었다

출발점은 원문 추출 실패였다. 외부 웹 추출 서비스가 결제·크레딧 상태 때문에 요청을 거절했다. 여기서 가장 쉬운 반응은 두 가지다.

  1. 같은 외부 서비스를 계속 재시도한다.

  2. 링크가 있다고 보고 그 안의 내용을 이미 확인한 것처럼 정리한다.

하지만 둘 다 안전하지 않았다. 첫 번째는 이미 확인된 결제 게이트를 다시 만나는 반복일 뿐이고, 두 번째는 원문에 접근하지 못한 상태를 원문을 읽은 상태로 바꾸는 오류다.

이번 작업의 출발점이 된 판단은 다음과 같았다.

외부 추출 실패는 콘텐츠의 진위 판정이 아니다. 먼저 접근 경로를 바꾸고, 실제 원문을 읽은 뒤에만 사실 여부를 판단한다.

사용자가 당시 상황을 이렇게 정리했다.

원문 추출은 외부 웹 추출 서비스의 결제 잔액 부족으로 막혔습니다. 링크를 그대로 사실로 확정하지 않고, 브라우저 원문 접근을 한 번 더 시도하겠습니다. 동시에 Prime Agent 자체 기능과 현재 Hermes 기능을 혼동하지 않도록 Hermes 기준도 확인하겠습니다.

이 문장에는 세 가지 요구가 함께 들어 있었다.

  • 원문을 확인하지 못했다는 사실을 숨기지 않을 것

  • 브라우저 또는 다른 추출 경로로 재확인할 것

  • Prime Agent의 개념과 현재 Hermes에 실제로 설치된 기능을 구분할 것

기존 브라우저 접근은 페이지마다 수동 조작이 필요했고, 외부 추출 backend는 결제 상태의 영향을 받았다. 따라서 이번에 필요한 것은 특정 링크를 한 번 더 여는 임시 조치가 아니라 반복 가능한 원문 확인 절차였다.

🌱 처음에는 “다른 추출기 하나 설치하면 되지 않을까”라고 생각하기 쉽다

처음에는 Trafilatura만 설치하면 문제가 끝날 것처럼 보였다. 일반적인 기사와 문서는 HTML 안에 이미 본문이 있으므로, 직접 가져와 읽기 좋은 Markdown으로 바꾸면 된다.

그러나 실제 웹 페이지는 한 종류가 아니었다.

  • 서버가 본문을 HTML에 넣어 주는 정적 페이지

  • JavaScript가 실행된 뒤에야 본문이 나타나는 동적 페이지

  • 접근할 수 없는 사설·루프백·로컬 주소

  • 추출은 되었지만 너무 짧아 실제 본문으로 보기 어려운 결과

  • MCP 표준 출력과 진단 로그가 섞이면 서버 자체가 깨지는 문제

그러므로 “도구를 하나 바꾼다”보다 “페이지 유형에 따라 책임을 분리한다”가 더 정확한 해결책이었다.

또 하나 분명히 한 점은 Prime Agent와 Hermes의 경계였다. 이번 결과는 Prime Agent의 내장 기능을 추가한 것이 아니다. 현재 Hermes 설정에 별도 MCP 서버를 등록한 것이며, 그 서버가 로컬 Python 환경에서 웹 추출을 수행한다.

🛠️ 사용한 도구

  • Hermes Agent: 문제 분류, MCP 연결, 실제 호출 검증

  • Trafilatura 2.2.0: 직접 HTTP 접근과 읽기 좋은 본문 추출

  • Crawl4AI 0.9.2: JavaScript 렌더링이 필요한 페이지 처리

  • Playwright 1.62.0: 동적 브라우저 실행과 Chromium 기반 렌더링

  • MCP 1.26.0: Hermes에 로컬 추출기를 연결하는 stdio 서버

  • SHA-256: 추출된 Markdown과 증거 이미지의 무결성 기록

이 작업의 시간 절약·비용 절감 효과는 별도로 측정하지 않았다. 따라서 “몇 분 단축”, “비용 몇 퍼센트 절감”과 같은 수치는 이 사례의 근거로 사용하지 않는다.

🔧 작업 과정

1. 실패 원인을 콘텐츠 문제와 분리했다

먼저 외부 추출 서비스의 결제·크레딧 거절을 콘텐츠의 진위나 링크의 유효성으로 해석하지 않았다. 기록해야 할 상태를 다음처럼 나눴다.

상태

의미

접근 실패

현재 추출 경로로 페이지를 읽지 못함

결제 게이트

외부 서비스의 사용 조건이 충족되지 않음

원문 확인

실제 페이지 본문을 읽고 추출함

사실 확정

원문과 다른 근거를 대조한 뒤 판단함

이 분리를 먼저 해 두자, 결제 실패를 만난 뒤에도 판단 순서가 흔들리지 않았다. 원문을 읽기 전에는 링크의 주장이나 문장을 사실로 확정하지 않고, 다른 접근 경로를 먼저 열기로 했다.

2. 정적 추출과 동적 추출을 한 항로로 묶었다

일반 페이지는 Trafilatura를 먼저 통과시켰다. 이 경로는 브라우저를 띄우지 않고 직접 HTML을 가져와 본문·표·링크·메타데이터를 추출한다.

다만 자동 모드에서는 정적 결과가 지나치게 짧으면 성공으로 간주하지 않았다. JavaScript 페이지에서 껍데기 HTML만 읽은 결과일 수 있기 때문이다. 정적 결과가 없거나 짧으면 Crawl4AI와 Playwright가 페이지를 렌더링한 뒤 다시 본문을 추출하도록 했다.

공개 URL
  ↓
Trafilatura 정적 추출
  ↓  본문이 없거나 비정상적으로 짧음
Crawl4AI + Playwright 동적 추출
  ↓
Markdown + 메타데이터 + 무결성 정보
한국판 게임 스크린샷

이렇게 하면 “정적 추출기가 실패했다”와 “페이지에 본문이 없다”를 같은 것으로 취급하지 않게 된다.

3. 로컬·사설 주소 경계를 먼저 넣었다

로컬 fallback이라고 해서 모든 주소를 가져오도록 만들지는 않았다. 입력 URL은 HTTP 또는 HTTPS로 제한하고, localhost·.local 호스트와 사설·루프백·링크 로컬·예약·멀티캐스트 IP를 차단했다.

이것은 기능을 줄이는 것이 아니라, 원문 추출기가 내부망 탐색 도구로 변하지 않게 하는 최소한의 실행 계약이다. 실제로 루프백 주소를 넣은 테스트는 추출하지 않고 차단되었다.

또한 MCP stdio 서버에서는 진단 로그를 표준 오류로 보내고, 도구의 JSON 응답만 표준 출력으로 반환하게 했다. 결과와 로그가 섞이면 Hermes가 정상 응답을 읽지 못할 수 있기 때문이다.

한국어 텍스트가 있는 페이지

4. 추출 결과를 파일과 해시로 남겼다

본문을 반환하는 것만으로는 다음 작업자가 같은 결과를 확인하기 어렵다. 그래서 저장을 선택하면 Markdown artifact에 다음 정보를 함께 기록하도록 했다.

  • 원본 URL

  • UTC 추출 시각

  • 요청 모드와 실제 사용한 추출기

  • 페이지 메타데이터

  • 추출 본문의 SHA-256

이 구조 덕분에 “도구가 성공했다고 말했다”와 “이 특정 본문을 저장했고 다시 읽을 수 있다”를 구분할 수 있다. 이번 검증에서도 정적 페이지와 동적 페이지의 Markdown artifact가 실제로 생성되고, 각각의 본문 해시가 기록되었다.

5. Hermes에 MCP로 연결했다

로컬 스크립트를 한 번 실행하는 것으로 끝내지 않고 Hermes가 재사용할 수 있는 MCP 서버로 감쌌다. 등록된 도구는 두 개다.

  • local_web_extract: 공개 URL을 추출하고 Markdown·메타데이터·해시·artifact 경로를 반환

  • local_web_fallback_status: 설치된 backend와 Firecrawl 비사용 상태를 반환

Hermes 연결 테스트에서 서버 연결에 성공했고, 두 도구가 발견되었다. 별도의 API key나 외부 결제 gateway를 붙이지 않았으며, MCP 실호출로 상태 조회와 정적 URL 추출까지 다시 확인했다.

헤르메스 mcp

6. “작동한다”를 여러 경로로 나누어 검증했다

한 페이지가 열렸다는 사실만으로 전체 항로가 완성되었다고 말하지 않았다. 서로 다른 조건을 분리해 확인했다.

검증 항목

실제 결과

정적 fixture

Trafilatura로 추출 성공

JavaScript fixture

Crawl4AI로 추출 성공

auto 모드

정적 결과가 짧은 것을 감지한 뒤 동적 경로로 전환

MCP discovery

두 도구 발견

MCP 실호출

상태 조회와 URL 추출 성공

사설 주소

추출하지 않고 차단

Firecrawl 의존성

로컬 테스트에서 호출하지 않음

공개 fixture인 example.com과 JavaScript 예제 페이지를 사용했으며, 테스트 페이지의 성공을 사용자가 처음 제시한 링크의 원문 확인 결과로 과장하지 않았다. 테스트는 항로가 작동하는지 보여 주는 것이고, 실제 링크의 내용은 해당 링크를 별도로 읽은 뒤에만 판단할 수 있다.

✅ 결과 — “외부 서비스 실패”와 “원문 확인” 사이에 새 항로가 생겼다

Before vs After

항목

Before

After

원문 추출

외부 서비스 결제·크레딧 상태에 의존

로컬 정적·동적 추출 경로 추가

페이지 유형

하나의 추출 방식으로 처리

Trafilatura → Crawl4AI/Playwright 자동 분기

사용 방식

브라우저 수동 재시도 또는 외부 backend 재호출

Hermes MCP 도구로 재사용

완료 기준

요청 성공 메시지 중심

본문·artifact·메타데이터·SHA-256 확인

안전 경계

추출기 수준에서 별도 계약이 약함

사설·루프백·로컬 주소 차단

기능 경계

Prime Agent와 Hermes 기능이 섞일 위험

Hermes MCP 확장으로 명확히 분리

검증된 결과물

이번에 만들어진 것은 단순한 설명문이 아니라 다음 실행 가능한 구성이다.

  • 로컬 웹 fallback 본체

  • Hermes용 stdio MCP wrapper

  • 설치·실행·운영 경계가 적힌 README

  • 버전이 고정된 의존성 목록

  • 정적·동적·자동 전환·경계·MCP 검증 receipt

  • 실제 검증 결과를 보여 주는 세 개의 증거 이미지

구성 파일과 증거 이미지에는 SHA-256을 기록했다. 공개 글에는 내부 절대경로와 설정 파일의 원문을 노출하지 않고, 재현에 필요한 개념과 검증 결과만 남겼다.

내가 달라진 점

처음에는 외부 추출 서비스가 실패하면 “다른 서비스로 바꾸면 된다”고 생각하기 쉽다. 이번 작업을 거치며 더 중요한 것은 서비스 이름이 아니라 실패 상태를 분류하고, 다음 항로와 완료 기준을 고정하는 일이라는 점을 확인했다.

이제 원문 추출을 요청받으면 다음 순서를 먼저 적용할 수 있다.

  1. 지금 실패한 것이 접근 문제인지, 결제 게이트인지, 콘텐츠 문제인지 구분한다.

  2. 링크를 사실로 확정하지 않고 브라우저·직접 HTTP·로컬 추출 경로를 선택한다.

  3. 정적 결과가 충분한지 확인하고, 필요하면 동적 렌더링으로 전환한다.

  4. 원문과 메타데이터를 저장하고 해시를 기록한다.

  5. 원문 확인과 사실 판단을 별도 단계로 보고한다.

💬 이 과정에서 배운 AI 활용 팁

효과적이었던 것

  1. 실패 메시지를 결과로 착각하지 않기

    • “외부 추출 서비스가 결제 게이트에서 거절됐다”는 것은 접근 경로의 상태다.

    • 페이지 내용이 사실인지 아닌지를 말해 주는 결과가 아니다.

  2. 대체 경로를 자동 분기하기

    • 모든 페이지에 브라우저를 쓰면 무겁고, 모든 페이지에 정적 추출만 쓰면 동적 페이지가 비어 버린다.

    • 먼저 가벼운 경로를 시도하고, 결과 품질이 낮을 때만 무거운 경로로 넘어가는 편이 재사용하기 쉽다.

  3. 도구 등록과 실제 호출을 모두 확인하기

    • 설정 파일에 서버가 적혀 있다는 것과 Hermes가 도구를 발견·호출했다는 것은 다르다.

    • discovery, status 호출, 실제 URL 추출을 각각 확인해야 한다.

  4. 완료 기준을 파일 무결성까지 확장하기

    • 성공 메시지보다 최종 파일·메타데이터·해시·경로가 중요하다.

    • 그래야 다음 사람이 같은 결과를 다시 읽고 비교할 수 있다.

이렇게 하면 안 돼요

  1. 결제 게이트를 만난 외부 서비스를 같은 실행에서 무한 재시도하지 않기

  2. 링크가 있다는 이유만으로 원문을 읽었다고 말하지 않기

  3. 정적 HTML이 짧게 나왔다고 페이지에 내용이 없다고 단정하지 않기

  4. MCP 서버의 진단 로그와 JSON 응답을 같은 출력에 섞지 않기

  5. 로컬 fallback이라는 이유로 사설·루프백 주소까지 허용하지 않기

🌍 다른 업무에 적용한다면?

예를 들어 정책자료나 제품 문서 링크를 정기적으로 확인하는 업무에 적용할 수 있다.

  • 먼저 공개 URL을 로컬 추출기로 읽는다.

  • 본문이 충분하면 Markdown과 해시를 보관한다.

  • JavaScript로 내용이 생성되는 페이지는 자동으로 브라우저 경로로 전환한다.

  • 사설 주소나 인증이 필요한 페이지는 차단하고, 사용자가 직접 접근할 수 있는 브라우저 경로로 넘긴다.

  • 다음 회차에 같은 URL을 읽을 때 이전 본문 해시와 비교한다.

이 방식은 검색엔진을 대체하지 않는다. 현재 구성은 이미 알고 있는 공개 URL의 원문을 확인하는 항로이며, 광범위한 웹 검색은 별도의 검색 backend나 브라우저 경로로 구분해야 한다.

🤝 배워서 남 주기

이번 결과를 개인용 임시 스크립트로 남기지 않고, Hermes가 다시 불러 쓸 수 있는 MCP 도구와 README로 정리했다. 다른 사람이 가져갈 수 있는 핵심은 특정 서비스 이름이 아니라 다음 운영 패턴이다.

외부 추출 실패
→ 실패 원인 분류
→ 정적 추출
→ 짧거나 비어 있으면 동적 브라우저 추출
→ artifact·metadata·hash 보존
→ MCP 또는 다음 작업자에게 인계

이 패턴을 복사하면 다른 로컬 추출기나 사내 브라우저 adapter를 붙이더라도 완료 기준과 안전 경계를 유지할 수 있다.

🕊️ 홍익인간 관점

이 사례가 줄이는 것은 단순한 API 비용만이 아니다. 원문을 확인하지 못한 상태에서 링크를 사실로 기록하는 불안, 같은 실패를 반복하는 시간, “성공” 메시지를 믿었다가 나중에 결과를 다시 조사하는 비용을 줄이는 구조다.

특히 AI를 처음 쓰는 사람에게는 “AI가 링크를 읽었다고 했으니 맞겠지”라는 막연한 신뢰 대신, 다음과 같은 확인 습관을 남길 수 있다.

  • 실제 원문을 읽었는가?

  • 어떤 경로로 읽었는가?

  • 결과 파일이 남아 있는가?

  • 나중에 같은 결과인지 확인할 해시가 있는가?

  • 원문 확인과 해석·사실 판단을 구분했는가?

🚀 앞으로의 계획

현재 구성의 다음 단계는 세 가지다.

  1. URL 원문 추출과 별도로, 무료 검색 backend를 붙일지 검토한다.

  2. 브라우저 접근이 필요한 페이지에 대해서도 원문·접근상태·인증 필요 여부를 같은 receipt 형식으로 남긴다.

  3. 반복 사용이 확인되면 Hermes skill에 fallback 선택 규칙을 연결해, 외부 추출 실패 시 로컬 MCP 경로를 더 쉽게 발견하도록 한다.

단, 이 세 가지는 아직 추가 구현·검증 전이다. 현재 완료 상태는 로컬 URL 원문 추출 MCP fallback의 구축과 검증까지다.

📋 재사용 가능한 프롬프트

프롬프트 1: 링크 원문 검증

다음 링크의 내용을 바로 사실로 확정하지 말고 검증해 주세요.

  1. 먼저 링크 원문에 실제로 접근 가능한지 확인합니다.

  2. 접근 실패가 결제·크레딧·인증·네트워크 중 어느 범주인지 분류합니다.

  3. 원문을 읽지 못했다면 그 사실을 명시하고, 주장 내용을 사실로 확정하지 않습니다.

  4. 원문을 읽었다면 원문·추출 시각·추출 경로·저장 artifact·SHA-256을 함께 기록합니다.

  5. 원문 확인과 해석·사실 판단을 별도 단계로 보고합니다.

대상 링크: [확인할 공개 URL]

프롬프트 2: 외부 추출 실패 시 fallback 선택

외부 웹 추출 서비스가 결제·크레딧 게이트 또는 일시 오류로 실패하면 같은 backend를 반복 호출하지 마세요.

  1. 이미 알고 있는 공개 URL이면 로컬 정적 추출기를 먼저 사용합니다.

  2. 결과가 없거나 비정상적으로 짧으면 Playwright/Crawl4AI 같은 동적 브라우저 경로로 전환합니다.

  3. 사설·루프백·로컬 주소는 차단합니다.

  4. 결과는 Markdown·메타데이터·추출기·시각·SHA-256과 함께 저장합니다.

  5. Hermes 기능과 Prime Agent 또는 외부 서비스 기능을 구분해 보고합니다.

  6. 검색이 필요한 경우 URL 추출과 검색을 같은 기능으로 말하지 말고 별도 경로로 처리합니다.

대상 URL: [원문을 확인할 공개 URL]

뉴스레터 무료 구독