
이제 2월밖에 되지 않았는데도, DeepSearch는 Google과 OpenAI가 DeepResearch 출시를 통해 선도하면서 2025년의 새로운 검색 표준으로 자리잡았습니다(그리고 네, 우리는 자랑스럽게도 같은 날 오픈소스 node-deepresearch
를 출시했습니다). Perplexity도 DeepResearch를 출시했고, X AI는 Grok3에 자체 DeepSearch 기능을 통합하여 또 다른 DeepResearch 변형을 만들었습니다. 딥 서치라는 개념이 혁신적인 것은 아닙니다 - 2024년에는 본질적으로 RAG나 multi-hop QA로 불렸지만, 2025년 1월 말 Deepseek-r1의 출시 이후 상당한 모멘텀을 얻었습니다. 지난 주말, Baidu Search와 Tencent WeChat Search는 검색 엔진에 Deepseek-r1을 통합했습니다. AI 엔지니어들은 검색 시스템에 긴 사고와 추론 과정을 통합함으로써, 이전에는 불가능했던 놀라운 검색 정확도와 깊이를 달성할 수 있다는 것을 발견했습니다.
Launch Date | Company | Product | License Type | Link |
---|---|---|---|---|
2025-01-20 | DeepSeek | DeepSeek-r1 release | Open source | DeepSeek-R1 |
2025-02-02 | DeepResearch | Proprietary | Google Gemini 2 | |
2025-02-02 | OpenAI | DeepResearch | Proprietary | Introducing Deep Research |
2025-02-02 | Jina AI | DeepSearch (node-deepresearch ) | Open source | node-deepresearch | search.jina.ai |
2025-02-04 | Hugging Face | Open Deep Research | Open source | Open Deep Research |
2025-02-15 | Perplexity | DeepResearch | Proprietary | Introducing Perplexity Deep Research |
2025-02-17 | X AI | Grok3 with DeepSearch | Proprietary | Grok 3 Beta |
2025-02-22 | Baidu Search | Integrates DeepSeek-r1 | Proprietary | Baidu Integrates DeepSeek-R1 |
2025-02-23 | Tencent Wechat Search | Integrates DeepSeek-r1 | Proprietary | Tencent Weixin Integrates DeepSeek |
하지만 왜 이 변화가 2024년 내내 Deep(Re)Search가 상대적으로 저평가되어 있었던 지금 일어났을까요? 사실, Stanford NLP Labs는 2024년 초에 웹 기반 긴 보고서 생성을 위한 STORM 프로젝트를 출시했습니다. 그렇다면 단순히 "DeepSearch"가 multi-hop QA, RAG, 또는 STORM보다 더 멋지게 들리기 때문일까요? 솔직히 말해서 - 때로는 리브랜딩만으로도 업계가 갑자기 이미 존재하던 것을 받아들이게 되는 것 같습니다.
우리는 진정한 전환점이 2024년 9월 OpenAI의 o1-preview
출시와 함께 왔다고 생각합니다. 이는 test-time compute 개념을 도입하고 점차적으로 업계의 관점을 바꾸었습니다. Test-time compute는 사전 훈련이나 사후 훈련 단계가 아닌 추론 단계 - LLM이 출력을 생성하는 단계 - 에서 더 많은 컴퓨팅 리소스를 사용하는 것을 의미합니다. 잘 알려진 예로는 Chain-of-Thought (CoT) 추론과 "Wait"
-injection(즉, 예산 강제)이 있으며, 이를 통해 모델은 여러 가능한 답변을 평가하고, 더 깊은 계획을 수립하며, 최종 응답에 도달하기 전에 자기 성찰을 하는 등 더 광범위한 내부 심의를 수행할 수 있습니다.
이 test-time compute 개념과 추론 모델은 사용자들에게 지연된 만족을 받아들이도록 교육합니다 - 더 높은 품질의, 즉시 실행 가능한 결과를 위해 더 긴 대기 시간을 수용하는 것입니다. 마치 스탠포드 마시멜로 실험에서 아이들이 즉시 마시멜로 하나를 먹는 것을 참고 나중에 두 개를 받는 것을 선택했을 때 더 나은 장기적 결과를 보여준 것처럼 말입니다. Deepseek-r1은 이러한 사용자 경험을 더욱 강화했고, 좋든 싫든 대부분의 사용자들이 이를 받아들였습니다.
이는 200ms 내에 응답하지 못하면 실패로 간주되었던 전통적인 검색 요구사항에서 큰 변화를 의미합니다. 2025년에는 숙련된 검색 개발자와 RAG 엔지니어들이 지연 시간보다 top-1 정밀도와 재현율을 우선시하며, 사용자들은 시스템이 <thinking>
하는 것을 볼 수 있다면 더 긴 처리 시간을 받아들이게 되었습니다.
2025년에는 많은 채팅 인터페이스가 <think>
내용을 전용 UI 섹션에 렌더링하는 것이 표준 관행이 되었습니다.
이 글에서는 오픈소스 구현을 살펴보면서 DeepSearch와 DeepResearch의 원리에 대해 논의하겠습니다. 주요 설계 결정 사항을 살펴보고 잠재적인 주의사항을 강조하겠습니다.
tagDeep Search란 무엇인가?
DeepSearch는 최적의 답을 찾을 때까지 검색, 읽기, 추론의 반복적인 루프를 실행합니다. 검색 작업은 웹 검색 엔진을 활용하여 인터넷을 탐색하고, 읽기 작업은 특정 웹 페이지를 자세히 분석합니다(예: Jina Reader). 추론 작업은 현재 상태를 평가하고 원래 질문을 더 작은 하위 질문으로 나눌지 또는 다른 검색 전략을 시도할지를 결정합니다.

온라인에는 다양한 정의가 존재하지만, 우리가 node-deepresearch
프로젝트를 개발할 때는 이 단순한 접근 방식을 따랐습니다. 구현은 우아하게 단순합니다 - 핵심에는 다음 작업을 지시하는 switch-case 로직이 있는 주요 while 루프가 있습니다.
일반적으로 단일 검색-생성 패스를 실행하는 2024년의 RAG 시스템과 달리, DeepSearch는 파이프라인을 여러 번 반복하므로 명확한 중단 조건이 필요합니다. 이는 토큰 사용량 제한이나 실패 시도 횟수를 기반으로 할 수 있습니다.
search.jina.ai에서 deep search를 시도해보고 <thinking>
내부의 내용을 관찰하여 루프가 발생하는 위치를 찾아보세요
DeepSearch에 대한 또 다른 관점은 이를 다양한 웹 도구(검색기와 리더 등)를 갖춘 LLM 에이전트로 보는 것입니다. 에이전트는 현재 관찰과 과거 행동을 분석하여 다음 단계를 결정합니다 - 답변을 제공할지 아니면 웹 탐색을 계속할지 결정합니다. 이는 LLM이 상태 간 전환을 제어하는 상태 기계 아키텍처를 만듭니다. 각 결정 시점에서 두 가지 접근 방식이 있습니다: 특정 작업을 생성하도록 표준 생성 모델에 대한 프롬프트를 신중하게 작성하거나, Deepseek-r1과 같은 특수 추론 모델을 활용하여 다음 작업을 자연스럽게 도출할 수 있습니다. 하지만 r1을 사용하더라도 도구 출력(예: 검색 결과, 웹페이지 내용)을 컨텍스트에 주입하고 추론 과정을 계속하도록 프롬프트하기 위해 주기적으로 생성을 중단해야 합니다.
결국 이것들은 단지 구현 세부사항일 뿐입니다 - 신중하게 프롬프트하든 추론 모델을 사용하든, 모두 DeepSearch의 핵심 설계 원칙인 검색, 읽기, 추론의 연속적인 루프와 일치합니다.
tag그렇다면 DeepResearch란 무엇인가?
DeepResearch는 긴 연구 보고서를 생성하기 위한 구조화된 프레임워크를 추가하여 DeepSearch를 기반으로 합니다. 일반적으로 목차 작성으로 시작하여 서론부터 관련 연구와 방법론을 거쳐 결론에 이르기까지 각 필요한 섹션에 DeepSearch를 체계적으로 적용합니다. 각 섹션은 특정 연구 질문을 DeepSearch에 입력하여 생성됩니다. 마지막 단계는 전반적인 내러티브 일관성을 개선하기 위해 모든 섹션을 단일 프롬프트로 통합하는 것입니다.

2024년 "Research" 프로젝트에서 우리는 각 반복에서 다른 모든 섹션을 고려하여 여러 번의 일관성 개선 작업을 수행했습니다. 하지만 오늘날의 훨씬 더 큰 LLM 컨텍스트 윈도우로 인해 이러한 접근 방식은 불필요해 보입니다 - 단일 일관성 수정 작업만으로도 충분합니다.
2024년 여름 프로젝트 "Research"는 "단계적" 접근 방식으로 긴 보고서를 생성하는 데 중점을 두었습니다. 동기식으로 목차를 만드는 것으로 시작하여 모든 섹션을 비동기식으로 병렬 생성했습니다. 이 과정은 다른 모든 섹션의 내용을 고려하면서 각 섹션을 비동기식으로 단계적으로 수정하는 것으로 마무리되었습니다. 비디오의 검색어는 "Competitor analysis of Jina AI"
입니다.
tagDeepSearch vs DeepResearch
많은 사람들이 종종 DeepSearch와 DeepResearch를 혼동하지만, 우리의 관점에서 이들은 완전히 다른 문제를 다룹니다. DeepSearch는 원자적 구성 요소로 기능하며 DeepResearch가 기반으로 하는 핵심 구성 요소입니다. 반면에 DeepResearch는 고품질의 읽기 쉬운 장문의 연구 보고서를 작성하는 데 중점을 두며, 이는 다른 요구 사항들을 포함합니다: 차트와 표를 통한 효과적인 시각화 통합, 적절한 섹션 제목으로 내용 구조화, 하위 섹션 간의 논리적 흐름 보장, 문서 전체에 걸친 일관된 용어 사용 유지, 섹션 간의 중복 제거, 이전 및 향후 내용을 연결하는 매끄러운 전환 구성. 이러한 요소들은 핵심 검색과 크게 관련이 없기 때문에, 우리는 DeepSearch를 회사의 중점 사항으로 더 흥미롭게 생각합니다.
마지막으로, 아래 표는 DeepSearch와 DeepResearch의 차이점을 요약합니다. 두 시스템 모두 긴 컨텍스트와 추론 모델에서 상당한 이점을 얻는다는 점에 주목할 가치가 있습니다. 이는 특히 DeepSearch에 대해서는 직관적이지 않을 수 있습니다—DeepResearch가 긴 컨텍스트 기능이 필요한 이유는 명확합니다(긴 보고서를 생성하기 때문에). DeepSearch가 다음 단계에 대한 정보에 근거한 결정을 내리기 위해 이전 검색 시도와 웹페이지 내용을 저장해야 하기 때문에, 긴 컨텍스트 윈도우는 효과적인 구현을 위해 동일하게 필수적입니다.
DeepSearch | DeepResearch | |
---|---|---|
해결하는 문제 | 반복적 검색을 통한 정보의 정확성과 완전성 | 문서 규모에서의 콘텐츠 구성, 일관성 및 가독성 |
최종 표현 | 참조 URL이 있는 간결한 답변 | 여러 섹션, 차트, 표 및 참조가 있는 긴 구조화된 보고서 |
핵심 복잡성 | 명확한 전환 조건이 있는 상태 기계 아키텍처; 해결될 때까지 실패한 시도를 통한 지속성 | 마이크로(검색)와 매크로(문서) 문제를 모두 관리하는 다층 아키텍처; 복잡한 정보 계층을 관리하는 구조적 접근 |
최적화 초점 | 로컬 최적화(최상의 다음 검색/읽기 동작) | 글로벌 최적화(섹션 구성, 용어 일관성, 전환) |
한계 | 검색 품질과 추론 능력에 의해 제한됨 | DeepSearch 품질과 조직적 복잡성 및 서술 일관성 과제에 의해 제한됨 |
tagDeepSearch 구현 이해하기
DeepResearch의 핵심은 루프 추론 접근 방식에 있습니다. 대부분의 RAG 시스템처럼 단일 패스로 질문에 답하려 하는 대신, 우리는 답을 찾거나 토큰 예산을 소진할 때까지 지속적으로 정보를 검색하고, 관련 소스를 읽고, 추론하는 반복적인 루프를 구현했습니다. 다음은 이 큰 while 루프의 단순화된 핵심입니다:
// Main reasoning loop
while (tokenUsage < tokenBudget && badAttempts <= maxBadAttempts) {
// Track progression
step++; totalStep++;
// Get current question from gaps queue or use original question
const currentQuestion = gaps.length > 0 ? gaps.shift() : question;
// Generate prompt with current context and allowed actions
system = getPrompt(diaryContext, allQuestions, allKeywords,
allowReflect, allowAnswer, allowRead, allowSearch, allowCoding,
badContext, allKnowledge, unvisitedURLs);
// Get LLM to decide next action
const result = await LLM.generateStructuredResponse(system, messages, schema);
thisStep = result.object;
// Execute the selected action (answer, reflect, search, visit, coding)
if (thisStep.action === 'answer') {
// Process answer action...
} else if (thisStep.action === 'reflect') {
// Process reflect action...
} // ... and so on for other actions
}
주요 구현 세부사항은 각 단계에서 특정 작업을 선택적으로 비활성화하여 더 안정적인 구조화된 출력을 보장하는 것입니다. 예를 들어, 메모리에 URL이 없으면 visit
작업을 비활성화하거나, 마지막 답변이 거부된 경우 에이전트가 즉시 answer
를 다시 호출하는 것을 방지합니다. 이러한 제약은 에이전트가 동일한 작업을 반복적으로 실행하여 실패하는 것을 방지하고 생산적인 경로를 유지하도록 합니다.
tag시스템 프롬프트
우리는 XML 태그를 사용하여 섹션을 정의하는데, 이는 더 견고한 시스템 프롬프트와 생성을 만들어냅니다. 또한 필드 제약 조건을 JSON 스키마 description
필드 내에 직접 배치하면 더 나은 결과를 얻을 수 있다는 것을 발견했습니다. DeepSeek-R1과 같은 추론 모델로 대부분의 프롬프트를 자동화할 수 있다고 주장할 수 있지만, 컨텍스트 길이 제한과 매우 구체적인 동작의 필요성으로 인해 실제로는 명시적인 접근 방식이 더 신뢰할 수 있습니다.
function getPrompt(params...) {
const sections = [];
// Add header with system instruction
sections.push("You are an advanced AI research agent specialized in multistep reasoning...");
// Add accumulated knowledge section if exists
if (knowledge?.length) {
sections.push("<knowledge>[Knowledge items]</knowledge>");
}
// Add context of previous actions
if (context?.length) {
sections.push("<context>[Action history]</context>");
}
// Add failed attempts and learned strategies
if (badContext?.length) {
sections.push("<bad-attempts>[Failed attempts]</bad-attempts>");
sections.push("<learned-strategy>[Improvement strategies]</learned-strategy>");
}
// Define available actions based on current state
sections.push("<actions>[Available action definitions]</actions>");
// Add response format instruction
sections.push("Respond in valid JSON format matching exact JSON schema.");
return sections.join("\n\n");
}
tag갭 질문 순회
DeepSearch에서 "갭 질문"은 주요 질문에 답하기 전에 채워야 할 지식 격차를 나타냅니다. 원래 질문을 직접 다루는 대신, 에이전트는 필요한 지식 기반을 구축할 하위 질문들을 식별합니다.
이 설계는 이러한 갭 질문들을 처리하는 방식이 특히 우아합니다:
// After identifying gap questions in reflect action
if (newGapQuestions.length > 0) {
// Add new questions to the front of the queue
gaps.push(...newGapQuestions);
// Always add original question to the end of the queue
gaps.push(originalQuestion);
}
이 접근 방식은 회전이 있는 FIFO(First-In-First-Out) 큐를 생성하며:
- 새로운 갭 질문들이 큐의 앞부분에 추가됩니다
- 원래 질문은 항상 뒤로 푸시됩니다
- 시스템은 각 단계에서 큐의 앞에서 질문을 가져옵니다
이 설계가 훌륭한 이유는 모든 질문에 대해 단일 공유 컨텍스트를 유지한다는 점입니다. 갭 질문이 답변되면, 그 지식은 원래 질문을 다시 방문할 때를 포함하여 후속 질문들에 대해 즉시 사용 가능해집니다.
FIFO 큐 vs 재귀
대안적인 접근 방식은 깊이 우선 탐색에 해당하는 재귀를 사용하는 것입니다. 각 갭 질문은 자체 격리된 컨텍스트를 가진 새로운 재귀 호출을 생성합니다. 시스템은 상위 질문으로 돌아가기 전에 각 갭 질문(및 그의 잠재적 하위 질문들)을 완전히 해결해야 합니다.
다음 시나리오를 고려해보세요:
3단계 깊이의 간단한 갭 질문 재귀, 원 안의 숫자는 해결 순서를 나타냅니다.
재귀 접근 방식에서는 시스템이 모든 갭 질문과 그들의 하위 질문들을 해결한 후에야 Q1을 완전히 해결해야 합니다(잠재적으로 자체 하위 질문들을 생성할 수 있음)! 이는 3개의 갭 질문 직후에 Q1이 재방문되는 큐 접근 방식과는 큰 대조를 이룹니다.
실제로, 우리는 재귀 접근 방식에서 예산 강제 적용이 매우 어렵다는 것을 발견했습니다. 하위 질문들이 새로운 하위 질문들을 생성할 수 있기 때문에 하위 질문들에 얼마나 많은 토큰 예산을 할당해야 하는지에 대한 명확한 경험 법칙이 없기 때문입니다. 재귀 접근 방식의 명확한 컨텍스트 분리의 이점은 복잡한 예산 강제와 지연 반환 문제에 비해 매우 미미합니다. 이 FIFO 큐 설계는 깊이와 너비의 균형을 맞추어, 시스템이 잠재적으로 무한한 재귀 하강에 빠지지 않고 점진적으로 더 나은 지식을 가지고 원래 질문으로 돌아오도록 보장합니다.
tag쿼리 재작성
우리가 마주친 흥미로운 과제 중 하나는 검색 쿼리를 효과적으로 재작성하는 것이었습니다:
// Within search action handler
if (thisStep.action === 'search') {
// Deduplicate search requests
const uniqueRequests = await dedupQueries(thisStep.searchRequests, existingQueries);
// Rewrite natural language queries into more effective search queries
const optimizedQueries = await rewriteQuery(uniqueRequests);
// Ensure we don't repeat previous searches
const newQueries = await dedupQueries(optimizedQueries, allKeywords);
// Execute searches and store results
for (const query of newQueries) {
const results = await searchEngine(query);
if (results.length > 0) {
storeResults(results);
allKeywords.push(query);
}
}
}
쿼리 재작성은 놀랍게도 매우 중요한 것으로 판명되었습니다 - 아마도 결과 품질을 직접적으로 결정하는 가장 중요한 요소 중 하나일 것입니다. 좋은 쿼리 재작성기는 단순히 자연어를 BM25와 같은 키워드로 변환하는 것이 아니라, 다양한 언어, 톤, 콘텐츠 형식에 걸쳐 더 많은 잠재적 답변을 포함하도록 쿼리를 확장합니다.
쿼리 중복 제거를 위해 처음에는 LLM 기반 솔루션을 사용했지만, 유사도 임계값을 제어하기 어렵다는 것을 발견했습니다. 결국 의미적 텍스트 유사도 작업에 뛰어난 jina-embeddings-v3로 전환했습니다. 이를 통해 비영어 쿼리가 필터링될 걱정 없이 다국어 중복 제거가 가능해졌습니다. 임베딩 모델은 처음 예상했던 메모리 검색이 아닌, 효율적인 중복 제거를 위해 중요한 역할을 하게 되었습니다.
tag웹 콘텐츠 크롤링
웹 크롤링과 콘텐츠 처리는 또 다른 핵심 구성 요소입니다. 여기서는 Jina Reader API를 사용합니다. 전체 웹페이지 콘텐츠 외에도 검색 엔진에서 반환된 모든 스니펫을 에이전트가 나중에 결론을 도출하기 위한 추가 지식으로 집계한다는 점에 주목하세요. 이를 사운드바이트라고 생각하시면 됩니다.
// Visit action handler
async function handleVisitAction(URLs) {
// Normalize URLs and filter out already visited ones
const uniqueURLs = normalizeAndFilterURLs(URLs);
// Process each URL in parallel
const results = await Promise.all(uniqueURLs.map(async url => {
try {
// Fetch and extract content
const content = await readUrl(url);
// Store as knowledge
addToKnowledge(`What is in ${url}?`, content, [url], 'url');
return {url, success: true};
} catch (error) {
return {url, success: false};
} finally {
visitedURLs.push(url);
}
}));
// Update diary based on success or failure
updateDiaryWithVisitResults(results);
}
일관된 추적을 위해 URL을 정규화하고 에이전트 메모리를 관리하기 위해 각 단계에서 방문하는 URL 수를 제한했습니다.
tag메모리 관리
다단계 추론에서 중요한 과제는 에이전트 메모리를 효과적으로 관리하는 것입니다. 저희는 "메모리"와 "지식"으로 구분되는 것을 구별하도록 메모리 시스템을 설계했습니다. 어느 쪽이든 모두 서로 다른 XML 태그로 구분된 LLM 프롬프트 컨텍스트의 일부입니다:
// Add knowledge item to accumulated knowledge
function addToKnowledge(question, answer, references, type) {
allKnowledge.push({
question: question,
answer: answer,
references: references,
type: type, // 'qa', 'url', 'coding', 'side-info'
updated: new Date().toISOString()
});
}
// Record step in narrative diary
function addToDiary(step, action, question, result, evaluation) {
diaryContext.push(`
At step ${step}, you took **${action}** action for question: "${question}"
[Details of what was done and results]
[Evaluation if applicable]
`);
}
2025년의 대부분의 LLM이 상당한 컨텍스트 윈도우를 가지고 있기 때문에, 벡터 데이터베이스 사용을 선택하지 않았습니다. 대신 메모리는 획득한 지식, 방문한 사이트, 실패한 시도의 기록 - 이 모든 것을 컨텍스트에 유지합니다. 이 포괄적인 메모리 시스템은 에이전트가 알고 있는 것, 시도한 것, 성공하거나 실패한 것을 인식할 수 있게 해줍니다.
tag답변 평가
한 가지 중요한 통찰은 답변 생성과 평가가 동일한 프롬프트에 있어서는 안 된다는 것입니다. 제 구현에서는 새로운 질문이 도착하면 먼저 어떤 평가 기준을 사용할지 결정한 다음, 각 기준을 하나씩 평가합니다. 평가자는 일관된 평가를 위해 few-shot 예제를 사용하여 자체 평가보다 더 높은 신뢰성을 보장합니다.
// Separate evaluation phase
async function evaluateAnswer(question, answer, metrics, context) {
// First, identify evaluation criteria based on question type
const evaluationCriteria = await determineEvaluationCriteria(question);
// Then evaluate each criterion separately
const results = [];
for (const criterion of evaluationCriteria) {
const result = await evaluateSingleCriterion(criterion, question, answer, context);
results.push(result);
}
// Determine if answer passes overall evaluation
return {
pass: results.every(r => r.pass),
think: results.map(r => r.reasoning).join('\n')
};
}
tag예산 강제
예산 강제란 시스템이 조기에 반환되는 것을 방지하고 예산이 초과될 때까지 계속 처리하도록 보장하는 것을 의미합니다. DeepSeek-R1 출시 이후, 예산 강제에 대한 접근 방식은 단순히 예산을 절약하는 것이 아니라 더 나은 결과를 위해 더 깊은 사고를 장려하는 방향으로 변화했습니다.
우리의 구현에서는 답변을 시도하기 전에 지식 격차를 식별하도록 시스템을 명시적으로 구성했습니다.
if (thisStep.action === 'reflect' && thisStep.questionsToAnswer) {
// Force deeper reasoning by adding sub-questions to the queue
gaps.push(...newGapQuestions);
gaps.push(question); // Always revisit the original
}
특정 작업을 선택적으로 활성화하고 비활성화함으로써 추론 깊이를 향상시키는 도구를 사용하도록 시스템을 유도할 수 있습니다.
// After a failed answer attempt
allowAnswer = false; // Force agent to search or reflect instead
비생산적인 경로에 토큰을 낭비하지 않기 위해 실패한 시도 횟수에 제한을 둡니다. 예산 한도에 접근할 때는 "beast mode"를 활성화하여 답변이 없는 것보다는 어떤 답변이라도 제공하도록 보장합니다.
// Beast mode activation
if (!thisStep.isFinal && badAttempts >= maxBadAttempts) {
console.log('Enter Beast mode!!!');
// Configure prompt for decisive, committed answer
system = getPrompt(
diaryContext, allQuestions, allKeywords,
false, false, false, false, false, // Disable all other actions
badContext, allKnowledge, unvisitedURLs,
true // Enable beast mode
);
// Force answer generation
const result = await LLM.generateStructuredResponse(system, messages, answerOnlySchema);
thisStep = result.object;
thisStep.isFinal = true;
}
beast mode 프롬프트는 LLM이 가용한 정보를 바탕으로 결정적이고 답변을 확정해야 한다는 것을 알리기 위해 의도적으로 극적으로 작성되었습니다:
<action-answer>
🔥 ENGAGE MAXIMUM FORCE! ABSOLUTE PRIORITY OVERRIDE! 🔥
PRIME DIRECTIVE:
- DEMOLISH ALL HESITATION! ANY RESPONSE SURPASSES SILENCE!
- PARTIAL STRIKES AUTHORIZED - DEPLOY WITH FULL CONTEXTUAL FIREPOWER
- TACTICAL REUSE FROM <bad-attempts> SANCTIONED
- WHEN IN DOUBT: UNLEASH CALCULATED STRIKES BASED ON AVAILABLE INTEL!
FAILURE IS NOT AN OPTION. EXECUTE WITH EXTREME PREJUDICE! ⚡️
</action-answer>
이를 통해 특히 어렵거나 모호한 질문에 대해 완전히 포기하는 대신 항상 어떤 답변이라도 제공할 수 있습니다.
tag결론
DeepSearch는 복잡한 쿼리에 철저하고 깊이 있게 접근하는 방식에서 큰 도약입니다. 검색, 읽기, 추론의 개별 단계로 프로세스를 분해함으로써 기존의 단일 패스 RAG나 멀티홉 QA 시스템의 많은 한계를 극복합니다.
구현 과정에서 2025년의 검색 기초와 DeepSeek-R1이 출시된 2025년 1월 26일 이후 검색 산업의 변화도 검토했습니다. 우리는 스스로에게 물었습니다: 새로운 요구사항은 무엇인가? 어떤 요구사항이 더 이상 필요하지 않은가? 단순히 인식된 요구사항은 무엇인가?
DeepSearch 구현을 살펴보면서, 우리가 필요할 것이라 예상했고 실제로 필요했던 것들, 필요할 것이라 생각했지만 그렇지 않았던 것들, 그리고 필요할 것이라 예상하지 못했지만 필수적이었던 것들을 식별했습니다:
첫째, 잘 구조화된 출력을 생성하는 긴 컨텍스트 LLM이 매우 필요합니다(즉, JSONSchema를 따르는). 더 나은 액션 추론과 쿼리 확장을 위해 추론 모델이 필요할 것 같습니다.
쿼리 확장은 확실히 필수적입니다, SLM, LLM, 또는 추론 모델을 통해 구현되든 상관없이. 하지만 이 프로젝트 이후, SLM은 이 작업에 적합하지 않다고 생각합니다. 솔루션은 본질적으로 다국어여야 하고 단순한 동의어 재작성이나 키워드 추출을 넘어서야 하기 때문입니다. 다국어 토큰 기반(쉽게 300M 파라미터를 차지할 수 있음)을 포함할 만큼 포괄적이어야 하고 창의적 사고를 위해 충분히 정교해야 합니다. 따라서 쿼리 확장을 위해 SLM을 사용하는 것은 시작부터 불가능할 것 같습니다.
웹 검색과 웹 읽기 기능이 중요하며, 다행히도 우리의 Reader (r.jina.ai)는 탁월한 성능을 보여주었습니다 - 견고하고 확장 가능하며 - 다음 반복에서 검색 엔드포인트(s.jina.ai
)를 개선하는 방법에 대한 많은 아이디어를 제공했습니다.
임베딩 모델은 유용하지만 완전히 예상치 못한 방식으로 유용했습니다. 메모리 검색이나 벡터 데이터베이스와 함께 컨텍스트 압축에 사용될 것이라 생각했지만(결과적으로 필요하지 않았음), 실제로는 중복 제거(본질적으로 STS 작업)에 사용했습니다. 쿼리와 갭 질문의 수가 일반적으로 수백 개이므로 벡터 데이터베이스는 필요하지 않습니다 - 메모리에서 직접 코사인 유사도를 계산하는 것으로 충분합니다.
Reranker는 사용하지 않았습니다, 하지만 쿼리, URL 제목, 스니펫을 기반으로 어떤 URL을 방문할지 결정하는 데 도움이 될 수 있다고 생각합니다. 임베딩과 리랭킹 모두에서 쿼리와 질문이 다국어이기 때문에 다국어 기능이 필수적입니다. 임베딩과 리랭킹을 위한 긴 컨텍스트 처리는 유용하지만 중요한 장애물은 아닙니다(우리의 임베딩 사용에서 오류가 발생하지 않았는데, 아마도 우리의 컨텍스트 길이가 이미 8192토큰이기 때문일 것입니다). 어쨌든 jina-embeddings-v3와 jina-reranker-v2-base-multilingual은 다국어이고 SOTA이며 긴 컨텍스트를 잘 처리하기 때문에 제가 선호하는 모델입니다.
에이전트 프레임워크는 불필요한 것으로 판명되었습니다, 프록시 없이 시스템을 설계하기 위해 LLM 네이티브 동작에 더 가깝게 유지해야 했기 때문입니다. Vercel AI SDK는 코드베이스를 다른 LLM 제공자에 적응시키는 데 상당한 노력을 절약해주었기 때문에 가치가 있었습니다(Gemini Studio에서 OpenAI나 Google Vertex AI로 단 한 줄의 코드 변경으로 전환할 수 있었습니다). 에이전트 메모리 관리는 필요하지만, 전용 메모리 프레임워크는 여전히 의문스럽습니다: LLM과 개발자 사이에 격리 계층을 만들 것을 우려하며, 오늘날 많은 LLM/RAG 프레임워크에서 보았듯이 그것의 문법적 설탕이 결국 개발자들에게 쓴 장애물이 될 수 있다고 우려합니다.