프로덕션을 생존하는 LangChain YouTube 스크립트 로더

모든 LangChain YouTube 스크립트 튜토리얼은 동일한 3줄을 보여줍니다: YoutubeLoader.from_youtube_url(), 로드, 완료. 그다음 클라우드 박스에 배포하면 조용히 아무것도 반환하지 않습니다. 아래는 대신 호스팅된 API에서 적절한 Document 개체를 반환하는 로더입니다. 약 20줄과 끝에서 끝까지 작동함을 증명하는 요약 체인입니다.
빌트인 YoutubeLoader가 서버에서 사망하는 이유
YoutubeLoader는 공식 API를 호출하지 않습니다. 코드가 실행되는 모든 기계에서 YouTube의 내부 캡션 엔드포인트를 스크래핑합니다. 주거용 IP에서 대부분 작동합니다. AWS, GCP 또는 Hetzner IP에서 차단되거나 빈 응답을 제공하기 때문에 YouTube는 데이터센터 트래픽을 봇 트래픽으로 취급합니다. YouTube 스크립트 스크래핑이 클라우드 IP에서 작동이 중단되는 이유의 세부 정보를 작성했습니다. 짧은 버전은 실패가 환경이므로 모든 로컬 테스트를 통과하고 중요한 정확히 곳에서 사망합니다.
youtube2text.org는 측면에서 가져오고 JSON을 전달하여 로더를 일반 HTTP 호출로 변환합니다.
로더
import requests
from langchain_core.documents import Document
API_KEY = "yt_your_key_here"
def load_youtube_transcript(url: str, max_chars: int = 150000) -> Document:
resp = requests.get(
"https://youtube2text.org/api/transcribe",
params={"url": url, "maxChars": max_chars},
headers={"x-api-key": API_KEY},
timeout=90,
)
resp.raise_for_status()
r = resp.json()["result"]
return Document(
page_content=r["content"],
metadata={
"videoId": r["videoId"],
"title": r["title"],
"pubDate": r["pubDate"],
"source": url,
},
)
API는 videoId, title, pubDate, content, contentSize 및 truncated 플래그를 반환합니다. 스크립트 텍스트는 page_content로, 다른 모든 것은 메타데이터로 들어갑니다. 정확히 다운스트림 체인과 벡터 저장소가 예상하는 모양입니다.
아직 키가 없으신가요? 공유 데모 키를 가져오세요:
curl https://youtube2text.org/api/demo-key
IP당 월 5개 비디오로 제한되므로 로더를 테스트하기에 충분합니다.
요약 체인
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain.chains.combine_documents import create_stuff_documents_chain
llm = ChatOpenAI(model="gpt-4o-mini")
prompt = ChatPromptTemplate.from_template(
"Summarize this video transcript in five bullet points:\n\n{context}"
)
chain = create_stuff_documents_chain(llm, prompt)
doc = load_youtube_transcript("https://www.youtube.com/watch?v=dQw4w9WgXcQ")
print(chain.invoke({"context": [doc]}))
create_stuff_documents_chain는 전체 스크립트를 하나의 프롬프트에 넣습니다. 기본적으로 maxChars가 페이로드를 150,000자로 제한하기 때문에 작동합니다. 작은 컨텍스트 모델에서 긴 팟캐스트의 경우 로더에 더 낮은 maxChars를 전달하거나 맵-감소 패턴으로 전환하세요.
실제 코드에서 처리할 것
2개의 실패 모드는 명시적으로 캐치할 가치가 있습니다. 캡션이 비활성화된 비디오는 오류 코드 TRANSCRIPT_UNAVAILABLE로 HTTP 404를 반환합니다. 재시도 또는 라이브러리 스왑이 이를 수정하지 못하고, 텍스트는 단순히 존재하지 않습니다. RATE_LIMIT_EXCEEDED (429)는 오류 JSON에 retryAfterSeconds를 포함하므로 읽고 대신 백오프하세요:
if resp.status_code == 429:
wait = resp.json()["error"].get("retryAfterSeconds", 60)
한 가지 정직한 제한: content는 타임스탬프 없는 평문입니다. 요약 및 검색에는 좋지만 "12:34로 이동" 딥 링크가 필요하면 메타데이터의 videoId를 통해 비디오 수준 속성으로 정산해야 합니다.
여기서 어디로 갈 것인가
동일한 Document 개체는 벡터 저장소로 직접 떨어집니다. 전체 YouTube 채널 위의 RAG에서 전체 파이프라인을 살펴봅니다. 스택이 LangChain이 아닌 LlamaIndex이면 동등한 설정도 짧습니다.
데모 키의 5개 비디오가 충분하지 않을 때 자신의 키를 가져오세요. 무료 계층 포함, $5.99 월 50개 비디오부터 유료 계획.