Jina의 마지막 긴 클래식 글인 "The딥서치/딥리서치 설계 및 구현'라는 질문에 대한 답을 찾았다면 답변의 품질을 획기적으로 향상시킬 수 있는 세부 사항을 좀 더 자세히 살펴볼 필요가 있습니다. 이번에는 두 가지 세부 사항에 중점을 두겠습니다:
긴 웹 페이지에서 최적의 텍스트 세그먼트 추출하기긴 웹 콘텐츠에서 가장 관련성이 높은 정보 조각을 선택하기 위해 후기 청킹 알고리즘을 사용하는 방법. 수집된 URL 재배치리랭커를 사용하여 LLM 에이전트가 수백 개의 URL 중에서 크롤링할 URL을 지능적으로 선택하도록 하는 방법은 무엇인가요?
이전 게시물에서 "딥서치에서 임베딩 모델은 STS(의미론적 텍스트 유사도)와 같은 작업의 쿼리 중복 제거에만 적합하며, 리랭커는 원래 딥서치 프로그래밍 구현에도 없었다"는 결론을 기억하시는 분도 계실 것입니다.
돌이켜보면 두 가지 유형의 리콜 모델 모두 우리가 일반적으로 생각하는 방식이 아닐 뿐 여전히 그 가치가 있습니다. 저희는 검색에서 항상 '80-20' 원칙을 지켜왔으며, 감성적인 가치나 임베딩 및 리랭크 제공업체로서의 시장 입지를 증명하기 위해 무리하게 모델을 구축하지 않습니다. 우리는 매우 실용적인 80-20 원칙을 고수합니다.검색 시스템의 가장 필수적인 요구사항에만 신경을 쓸 정도로 실용적입니다.
그래서 몇 주에 걸친 시험과 반복 끝에 딥서치/딥리서치 시스템에서 임베딩과 리랭커의 색다르지만 매우 효과적인 적용 방법을 발견했습니다. 이러한 방법을 사용한 결과, Jina DeepSearch의 품질이 크게 향상되었습니다(직접 체험해 보시기 바랍니다). 또한 이러한 경험을 이 분야에서 함께 일하고 있는 동료들과 공유하고 싶습니다.
긴 텍스트에서 최적의 텍스트 세그먼트 선택
문제는 바로 이것입니다. 지나 리더 웹 페이지의 콘텐츠를 읽은 후에는 이를 에이전트가 추론할 수 있는 지식으로 에이전트의 컨텍스트에 넣어야 합니다. 전체 콘텐츠를 한꺼번에 LLM의 컨텍스트에 넣는 것이 가장 번거로운 방법이지만, 이 작업을 수행하는 데는 토큰 비용과 생성 속도를 고려할 때 이것이 최선의 선택은 아닙니다. 실제로는 콘텐츠에서 문제와 가장 관련성이 높은 부분을 식별하고 해당 부분만 상담원의 컨텍스트에 지식으로 추가해야 합니다.
💡 여기서는 Jina Reader를 사용하여 콘텐츠를 깔끔한 마크다운으로 정리한 후에도 여전히 너무 긴 경우에 대해 이야기하고 있습니다. 예를 들어 GitHub 이슈, Reddit 게시물, 포럼 토론 및 블로그 게시물과 같은 긴 페이지가 이에 해당합니다.
LLM 기반 심사 방식도 비용과 지연 시간 문제는 동일하므로 소규모 모델 솔루션이 있는지 알아봐야 합니다:여러 언어를 지원하면서도 더 작고 저렴한 모델이 필요합니다.질문이나 문서가 항상 중국어로 작성된다는 보장이 없기 때문에 이는 매우 중요한 요소입니다.
한쪽에는 질문(원래 쿼리 또는 "정보 부족" 질문)이 있고 다른 한쪽에는 많은 마크다운 콘텐츠가 있는데, 그 중 상당수는 관련성이 없습니다. 질문과 가장 관련성이 높은 부분을 골라내야 합니다. 이는 다음과 비슷합니다. RAG 2023년부터 커뮤니티가 해결하려고 노력해 온 청크 문제 - 리트리버 모델을 사용하여 관련성 있는 청크만 검색하고 이를 컨텍스트 창에 넣어 요약하는 문제입니다.
하지만 우리 상황에는 두 가지 중요한 차이점이 있습니다:
한정된 수의 문서에 한정된 수의 텍스트 블록이 있습니다.
각 블록에 약 500개의 토큰이 있다고 가정하면, 일반적인 긴 웹 문서에는 약 200,000~1,000,000개의 토큰(99번째 백분위수)이 있습니다. 저희는 Jina Reader를 사용하여 한 번에 4~5개의 URL을 크롤링하며 수백 개의 텍스트 블록을 생성합니다. 이는 수백 개의 벡터와 수백 개의 코사인 유사성을 의미합니다. 이 작업은 JavaScript를 사용하여 메모리에서 쉽게 처리할 수 있으며 벡터 데이터베이스가 필요하지 않습니다.
지식을 효과적으로 요약하려면 연속적인 텍스트 블록이 필요합니다.
1-2, 6-7, 9, 14, 17, ...]와 같은 요약은 허용되지 않습니다. 이와 같이 흩어져 있는 문장으로 구성된 요약은 허용되지 않습니다. 더 유용한 지식 요약은 [3-15, 17-24, ...]와 같은 것입니다. 이렇게 하면 텍스트의 일관성을 더 잘 유지할 수 있습니다. 이렇게 하면 LLM이 지식 소스에서 복사 및 인용하기가 더 쉬워지고 '착각'의 수도 줄어들 것입니다.
벡터 모델이 너무 긴 컨텍스트를 처리할 수 없기 때문에 각 텍스트 블록이 너무 길어서는 안 된다는 점, 청킹으로 인해 컨텍스트가 손실되고 각 텍스트 블록의 벡터가 독립적으로 동일하게 분산된다는 점, 가독성과 의미를 모두 보존하는 최적의 경계를 어떻게 찾을 수 있다는 점 등 개발자들이 불평하는 것과 동일한 주의 사항들이 있습니다. 이러한 문제에 대해 잘 알고 계신다면, RAG 구현에서도 이러한 문제로 고민해 보셨을 가능성이 높습니다.
하지만 간단히 말해서 jina-embeddings-v3
(명목식 형태로 사용됨) 늦은 청킹이 세 가지 문제를 모두 완벽하게 해결합니다. '후기 분할'은 각 블록의 컨텍스트 정보를 보존하고, 경계에 민감하지 않으며, 다음과 같은 문제를 해결합니다. jina-embeddings-v3
자체는 비대칭 다국어 검색 작업에서 최첨단 기술입니다. 관심 있는 독자는 Jina의 블로그 게시물 또는 논문에서 전반적인 구현에 대한 자세한 내용을 확인할 수 있습니다.
🔗 https://arxiv.org/pdf/2409.04701

후기 점수를 사용한 조각 선택 순서도
이 그림은 1차원 컨볼루션(Conv1D)과 유사하게 작동하는 요약 선택 알고리즘을 보여줍니다. 이 프로세스는 먼저 긴 문서를 고정 길이의 청크로 분할한 다음, 후기 분할이 활성화된 jina-embeddings-v3
텍스트 블록을 벡터화합니다. 각 블록과 문제 사이의 유사도 점수를 계산한 후 슬라이딩 창이 이 유사도 점수 위로 이동하여 평균이 가장 높은 창을 찾습니다.
'1D 컨볼루션'과 유사한 후기 분할과 평균 풀링을 사용하여 문제와 가장 관련성이 높은 구절을 골라내는 개략적인 코드는 다음과 같습니다.
function cherryPick(question, longContext, options) {
if (longContext.length < options.snippetLength * options.numSnippets)
return longContext;
const chunks = splitIntoChunks(longContext, options.chunkSize);
const chunkEmbeddings = getEmbeddings(chunks, "retrieval.passage");
const questionEmbedding = getEmbeddings([question], "retrieval.query")[0];
const similarities = chunkEmbeddings.map(embed =>
cosineSimilarity(questionEmbedding, embed));
const chunksPerSnippet = Math.ceil(options.snippetLength / options.chunkSize);
const snippets = [];
const similaritiesCopy = [...similarities];
for (let i = 0; i < options.numSnippets; i++) {
let bestStartIndex = 0;
let bestScore = -Infinity;
for (let j = 0; j <= similarities.length - chunksPerSnippet; j++) {
const windowScores = similaritiesCopy.slice(j, j + chunksPerSnippet);
const windowScore = average(windowScores);
if (windowScore > bestScore) {
bestScore = windowScore;
bestStartIndex = j;
}
}
const startIndex = bestStartIndex * options.chunkSize;
const endIndex = Math.min(startIndex + options.snippetLength, longContext.length);
snippets.push(longContext.substring(startIndex, endIndex));
for (let k = bestStartIndex; k < bestStartIndex + chunksPerSnippet; k++)
similaritiesCopy[k] = -Infinity;
}
return snippets.join("\n\n");
}
Jina 임베딩 API를 호출할 때는 반드시 task
검색으로 설정하고 late_chunking
(수학.) 속truncate
또한 다음과 같이 설정합니다:
await axios.post(
'https://api.jina.ai/v1/embeddings',
{
model: "jina-embeddings-v3",
task: "retrieval.passage",
late_chunking: true,
input: chunks,
truncate: true
},
{ headers });
문제를 벡터화하려면 다음을 입력해야 합니다. task
(STH)를 (STH 다른) 교환 retrieval.query
그런 다음 전원을 끕니다. late_chunking
.
전체 구현 코드는 GitHub에서 확인할 수 있습니다:https://github.com/jina-ai/node-DeepResearch/blob/main/src/tools/jina-latechunk.ts
"다음 읽기"를 위한 URL 정렬
문제는 다음과 같습니다. 모든 딥서치 긴 프로세스에서 검색 엔진 결과 페이지(SERP)에서 수많은 URL을 수집할 수 있으며, 웹 페이지를 열 때마다 새로운 링크를 많이 발견할 수 있으며, 강조 표시를 제거한 후에도 여전히 수백 개의 URL이 쉽게 발견될 수 있다는 것입니다. 마찬가지로, 이 모든 URL로 LLM을 채우는 것은 효과가 없을 뿐만 아니라 귀중한 문맥 길이를 낭비하는 일이며, 더 큰 문제는 LLM이 기본적으로 무작위로 선택된다는 사실입니다. 그래서 우리는 LLM이 답을 포함할 가능성이 가장 높은 URL을 선택하도록 안내하는 방법을 찾아야 했습니다.
curl https://r.jina.ai/https://example.com \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "X-Retain-Images: none" \
-H "X-Md-Link-Style: discarded" \
-H "X-Timeout: 20" \
-H "X-With-Links-Summary: all"
이것은 딥서치에서 페이지를 크롤링하도록 Jina Reader를 구성하는 가장 좋은 방법입니다. 페이지의 모든 링크를 골라내어 링크 필드에 넣고 콘텐츠 필드에서 삭제합니다.
단일 세션에서 수백 개의 URL을 점수화한다는 점을 제외하면 이를 '컨텍스트 내 페이지 순위'라고 생각할 수 있습니다.
마지막 업데이트 시간, 도메인 이름의 발생 빈도, 페이지 경로의 구조, 그리고 가장 중요한 질문과의 의미적 관련성 등 여러 요소를 고려하여 종합 점수를 계산합니다. 그러나 URL을 클릭하기 전에 사용할 수 있는 정보만 사용할 수 있습니다.

1. 주파수 신호URL이 여러 소스에 여러 번 표시되면 더 많은 가중치가 부여됩니다. 또한 도메인 이름이 검색 결과에 자주 표시되는 경우 해당 도메인의 URL에 가산점이 부여됩니다. 일반적으로 인기 있는 도메인은 더 권위 있는 콘텐츠를 포함하는 경향이 있기 때문입니다.
2. 경로 구조URL의 경로 구조를 분석하여 어떤 콘텐츠가 서로 클러스터링되어 있는지 확인합니다. 여러 URL이 모두 동일한 경로 계층 구조에 속하면 점수가 높아지지만, 경로가 깊을수록 점수 보너스는 점차 감소합니다.
3. 의미적 관련성사용 jina-reranker-v2-base-multilingual
를 사용하여 질문의 의미적 관련성과 각 URL의 텍스트 정보(예: 제목 및 초록)를 평가하는데, 이는 일반적인 재정렬 문제입니다. 각 URL의 텍스트 정보는 여러 곳에서 가져옵니다:
검색 엔진 결과 페이지(SERP) API는 제목과 요약을 반환합니다(https://s.jina.ai/ 이 인터페이스는 'X-Respond-With': 'no-content'를 사용하면 특정 콘텐츠가 아닌 제목과 요약만 반환합니다). 페이지의 URL에 대한 앵커 텍스트(https://r.jina.ai 인터페이스 사용 및 'X-With-Links-Summary' 설정: '모두'는 페이지 내의 모든 링크에 대한 요약 정보 또는 앵커 텍스트를 반환합니다).
4. 마지막 업데이트: 딥서치 쿼리 중 일부는 시의성 요구사항이 높기 때문에 일반적으로 URL이 최신일수록 값이 높아집니다. 그러나 Google의 대규모 인덱싱 기능이 없으면 웹 페이지의 마지막 업데이트 시간을 정확하게 파악하기 어렵습니다. Google은 필요할 때 최신 콘텐츠를 우선적으로 표시할 수 있도록 다음 신호의 조합을 사용하여 신뢰도 점수와 함께 타임스탬프를 부여합니다:
SERP API에서 제공하는 필터링 기능(예: 시간별로 필터링할 수 있는 s.jina.ai의 tbs 매개변수). HTTP 헤더 정보 분석(마지막 수정 및 ETag 필드 등). 메타데이터 추출(예: 메타 태그 및 Schema.org 타임스탬프). 콘텐츠 패턴 인식(HTML에 표시된 날짜 인식). 특정 CMS 플랫폼(예: 워드프레스, 드루팔, 고스트 등)에 대한 지표
5. 제한된 콘텐츠일부 소셜 미디어 플랫폼에는 접근이 제한되거나 결제가 필요한 콘텐츠가 있습니다. 로그인하지 않고는 이러한 콘텐츠에 합법적으로 액세스할 수 있는 방법이 없습니다. 따라서 이러한 문제가 있는 URL과 도메인의 블랙리스트를 적극적으로 관리하여 순위를 낮추고 접근이 불가능한 콘텐츠에 컴퓨팅 리소스를 낭비하지 않도록 하고 있습니다.
6. 도메인 이름 다양성간혹 상위 URL이 모두 동일한 도메인에 있는 경우가 있는데, 이로 인해 딥서치가 '로컬 최적'에 빠지게 되어 최종 결과의 품질에 영향을 미칠 수 있습니다. 앞서 언급했듯이 상위 URL은 모두 StackOverflow의 것이므로 결과의 다양성을 높이기 위해 각 도메인에서 상위 K개의 URL을 선택하는 "탐색 및 익스플로잇" 전략을 사용할 수 있습니다.
URL 정렬의 전체 코드 구현은 깃허브(https://github.com/jina-ai/node-DeepResearch/blob/main/src/utils/url-tools.ts#L192)에서 확인할 수 있습니다.
<action-visit>
- Crawl and read full content from URLs, you can get the fulltext, last updated datetime etc of any URL.
- Must check URLs mentioned in <question> if any
- Choose and visit relevant URLs below for more knowledge. higher weight suggests more relevant:
<url-list>
+ weight: 0.20 "https://huggingface.co/docs/datasets/en/loading": "Load - Hugging FaceThis saves time because instead of waiting for the Dataset builder download to time out, Datasets will look directly in the cache. Set the environment ...Some datasets may have more than one version based on Git tags, branches, or commits. Use the revision parameter to specify the dataset version you want to load ..."
+ weight: 0.20 "https://huggingface.co/docs/datasets/en/index": "Datasets - Hugging Face🤗 Datasets is a library for easily accessing and sharing datasets for Audio, Computer Vision, and Natural Language Processing (NLP) tasks. Load a dataset in a ..."
+ weight: 0.17 "https://github.com/huggingface/datasets/issues/7175": "[FSTimeoutError] load_dataset · Issue #7175 · huggingface/datasetsWhen using load_dataset to load HuggingFaceM4/VQAv2, I am getting FSTimeoutError. Error TimeoutError: The above exception was the direct cause of the following ..."
+ weight: 0.15 "https://github.com/huggingface/datasets/issues/6465": "`load_dataset` uses out-of-date cache instead of re-downloading a ...When a dataset is updated on the hub, using load_dataset will load the locally cached dataset instead of re-downloading the updated dataset."
+ weight: 0.12 "https://stackoverflow.com/questions/76923802/hugging-face-http-request-on-data-from-parquet-format-when-the-only-way-to-get-i": "Hugging face HTTP request on data from parquet format when the ...I've had to get the data from their data viewer using the parquet option. But when I try to run it, there is some sort of HTTP error. I've tried downloading ..."
</url-list>
</action-visit>
요약
2025년 2월 2일 Jina의 딥검색이 출시된 이후, 품질을 획기적으로 개선할 수 있는 두 가지 엔지니어링 세부 사항이 발견되었습니다.흥미로운 점은 두 세부 사항 모두 '상황에 맞는 창' 방식으로 다국어 임베딩 및 리랭커 모델을 활용한다는 점입니다.이러한 모델에 일반적으로 필요한 방대한 사전 계산 인덱스에 비하면 아무것도 아닙니다.
이는 향후 검색 기술의 양극화를 예고하는 것일 수 있습니다. 이러한 추세를 이해하기 위해 카네만의 이중 프로세스 이론을 빌려볼 수 있습니다:
빠르게 생각하세요. 빠르게 생각하기 (grep, BM25, SQL): 최소한의 계산으로 빠른 규칙 기반 패턴 매칭이 가능합니다. 천천히 생각하기 느리게 생각하기 (LLM): 맥락에 대한 깊은 이해를 바탕으로 포괄적인 추론이 필요하지만 계산 집약적입니다. medium밴드 중간 생각 (임베딩, 리랭커 외 리콜 모델): 단순한 패턴 매칭보다는 어느 정도 의미론적 이해가 가능하지만, LLM보다는 추론이 훨씬 적습니다.
한 가지 가능성이 있습니다:2계층 검색 아키텍처의 인기가 점점 높아지고 있습니다.가볍고 효율적인 SQL/BM25는 검색의 입력을 처리한 다음 결과를 LLM에 직접 공급하여 출력을 검색합니다. 그런 다음 중간 계층 모델의 잔여 값은 필터링, 중복 제거, 정렬 등과 같은 특정 컨텍스트 창 내의 작업으로 이동합니다. 이러한 시나리오에서는 LLM으로 완전한 추론을 수행하는 것이 비효율적일 수 있습니다.
하지만 어쨌든.키 클립 선택 노래로 응답 URL 정렬 여전히 딥서치/딥리서치 시스템의 품질에 직접적인 영향을 미치는 근본적인 측면입니다. 이번 조사 결과가 여러분의 시스템을 개선하는 데 도움이 되기를 바랍니다.
쿼리 확장은 품질을 결정하는 또 다른 핵심 요소입니다.야채.단순한 프롬프트 기반 재작성부터 소규모 모델, 추론 기반 접근 방식에 이르기까지 다양한 접근 방식을 적극적으로 검토하고 있습니다. 이 방향에 대한 후속 연구 결과도 기대해 주세요!
© 저작권 정책
기사 저작권 AI 공유 서클 모두 무단 복제하지 마세요.
관련 문서
댓글 없음...