mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-11 03:12:55 +00:00
feat(settings): graph retrieval options in a graph source's retrieval settings
Exposes the three per-source graph options in the source's retrieval settings, shown only when the retriever is graphrag: where the walk starts (entities or relationships), whether passages join the walk, and whether vector hits are blended in. Defaults match the backend's measured-best configuration and are filled in for sources saved before the options existed. A note points at the "search tool" exposure, which is what offers the graph to an agent.
This commit is contained in:
1 parent
6b9b193337
commit
5e67f8c927
10 files changed
+256
No files matched your search
@@ -268,6 +268,19 @@
|
||||
},
|
||||
"exposureHint": "Lade diese Quelle vorab in den Prompt oder lass den Agenten sie bei Bedarf als Werkzeug durchsuchen."
|
||||
},
|
||||
"graphRetrieval": {
|
||||
"title": "Graph-Abruf",
|
||||
"tag": "ohne Neuimport",
|
||||
"seedStrategy": "Suche beginnt bei",
|
||||
"seedStrategyHint": "Entitäten eignen sich für die meisten Dokumente. Beziehungen erreichen auch Entitäten, die in der Frage nicht vorkommen – ideal für Inhalte, die beschreiben, wie Dinge zusammenhängen.",
|
||||
"seedEntities": "Entitäten (empfohlen)",
|
||||
"seedRelationships": "Beziehungen",
|
||||
"passageNodes": "Textabschnitte in die Suche einbeziehen",
|
||||
"passageNodesHint": "Ein Abschnitt wird gefunden, wenn er zur Frage passt oder mit etwas Passendem verbunden ist. Am besten bei Graphen, die mit dieser Version erstellt wurden.",
|
||||
"blendVector": "Mit Vektorsuche kombinieren",
|
||||
"blendVectorHint": "Ergänzt Ergebnisse der Vektorsuche, damit kein Abschnitt verloren geht, den der Graph übersieht.",
|
||||
"agentToolHint": "Agenten können diesen Beziehungen auch selbst folgen, wenn die Bereitstellung dieser Quelle „Suchwerkzeug auf Abruf“ ist oder ein agentischer Agent sie nutzt."
|
||||
},
|
||||
"prescreen": {
|
||||
"enable": "LLM-Vorfilterung aktivieren",
|
||||
"warning": "Ruft eine größere Kandidatenmenge ab und filtert sie mit einem LLM. Das erhöht Latenz und Kosten pro Anfrage.",
|
||||
|
||||
@@ -272,6 +272,19 @@
|
||||
},
|
||||
"exposureHint": "Pre-fetch this source into the prompt, or let the agent search it on demand as a tool."
|
||||
},
|
||||
"graphRetrieval": {
|
||||
"title": "Graph retrieval",
|
||||
"tag": "no re-ingest",
|
||||
"seedStrategy": "Start the walk from",
|
||||
"seedStrategyHint": "Entities suit most documents. Relationships can reach an entity the question never names, and suit content that describes how things connect.",
|
||||
"seedEntities": "Entities (recommended)",
|
||||
"seedRelationships": "Relationships",
|
||||
"passageNodes": "Include passages in the walk",
|
||||
"passageNodesHint": "Lets a passage be found both by matching the question and by being connected to what does. Works best on graphs built with this version.",
|
||||
"blendVector": "Blend with vector search",
|
||||
"blendVectorHint": "Adds plain vector search results, so a passage the graph misses is not lost.",
|
||||
"agentToolHint": "Agents can also follow these relationships themselves when this source's exposure is “On-demand search tool”, or when an agentic agent uses it."
|
||||
},
|
||||
"prescreen": {
|
||||
"enable": "Enable LLM prescreen",
|
||||
"warning": "Fetches a larger candidate set and uses an LLM to filter it. This adds query-time latency and cost.",
|
||||
|
||||
@@ -268,6 +268,19 @@
|
||||
},
|
||||
"exposureHint": "Precarga esta fuente en el prompt, o deja que el agente la busque bajo demanda como herramienta."
|
||||
},
|
||||
"graphRetrieval": {
|
||||
"title": "Recuperación por grafo",
|
||||
"tag": "sin reingesta",
|
||||
"seedStrategy": "Iniciar el recorrido desde",
|
||||
"seedStrategyHint": "Las entidades funcionan para la mayoría de documentos. Las relaciones pueden llegar a una entidad que la pregunta no menciona; son ideales para contenido que describe cómo se conectan las cosas.",
|
||||
"seedEntities": "Entidades (recomendado)",
|
||||
"seedRelationships": "Relaciones",
|
||||
"passageNodes": "Incluir fragmentos en el recorrido",
|
||||
"passageNodesHint": "Un fragmento puede encontrarse por coincidir con la pregunta o por estar conectado con lo que coincide. Funciona mejor en grafos creados con esta versión.",
|
||||
"blendVector": "Combinar con búsqueda vectorial",
|
||||
"blendVectorHint": "Añade resultados de la búsqueda vectorial para no perder fragmentos que el grafo pase por alto.",
|
||||
"agentToolHint": "Los agentes también pueden seguir estas relaciones por sí mismos cuando la exposición de esta fuente es «Herramienta de búsqueda bajo demanda» o cuando la usa un agente agéntico."
|
||||
},
|
||||
"prescreen": {
|
||||
"enable": "Habilitar preselección con LLM",
|
||||
"warning": "Obtiene un conjunto de candidatos más grande y usa un LLM para filtrarlo. Esto añade latencia y costo por consulta.",
|
||||
|
||||
@@ -268,6 +268,19 @@
|
||||
},
|
||||
"exposureHint": "このソースをプロンプトに事前取得するか、エージェントがツールとして必要に応じて検索できるようにします。"
|
||||
},
|
||||
"graphRetrieval": {
|
||||
"title": "グラフ検索",
|
||||
"tag": "再取り込み不要",
|
||||
"seedStrategy": "探索の開始点",
|
||||
"seedStrategyHint": "ほとんどのドキュメントにはエンティティが適しています。リレーションは質問に登場しないエンティティにも到達でき、物事のつながりを説明するコンテンツに向いています。",
|
||||
"seedEntities": "エンティティ(推奨)",
|
||||
"seedRelationships": "リレーション",
|
||||
"passageNodes": "パッセージを探索に含める",
|
||||
"passageNodesHint": "質問に一致するパッセージだけでなく、一致したものとつながるパッセージも見つけられます。このバージョン以降に構築したグラフで最も効果的です。",
|
||||
"blendVector": "ベクトル検索と組み合わせる",
|
||||
"blendVectorHint": "ベクトル検索の結果を加え、グラフが見落としたパッセージも失わないようにします。",
|
||||
"agentToolHint": "このソースの公開方法が「オンデマンド検索ツール」の場合、またはエージェント型エージェントが使用する場合、エージェントはこれらのリレーションを自ら辿ることもできます。"
|
||||
},
|
||||
"prescreen": {
|
||||
"enable": "LLMプリスクリーニングを有効にする",
|
||||
"warning": "より多くの候補を取得し、LLMでフィルタリングします。クエリ時のレイテンシとコストが増加します。",
|
||||
|
||||
@@ -268,6 +268,19 @@
|
||||
},
|
||||
"exposureHint": "Предзагружать этот источник в промпт или позволить агенту искать по нему по мере необходимости как по инструменту."
|
||||
},
|
||||
"graphRetrieval": {
|
||||
"title": "Поиск по графу",
|
||||
"tag": "без повторной загрузки",
|
||||
"seedStrategy": "Начинать обход с",
|
||||
"seedStrategyHint": "Сущности подходят для большинства документов. Связи позволяют дойти до сущности, которая не упоминается в вопросе, — хорошо для контента о том, как всё связано.",
|
||||
"seedEntities": "Сущностей (рекомендуется)",
|
||||
"seedRelationships": "Связей",
|
||||
"passageNodes": "Включать фрагменты в обход",
|
||||
"passageNodesHint": "Фрагмент находится, если он соответствует вопросу или связан с тем, что соответствует. Лучше всего работает на графах, построенных в этой версии.",
|
||||
"blendVector": "Сочетать с векторным поиском",
|
||||
"blendVectorHint": "Добавляет результаты векторного поиска, чтобы не терять фрагменты, пропущенные графом.",
|
||||
"agentToolHint": "Агенты также могут сами проходить по этим связям, если для источника выбран режим «Инструмент поиска по запросу» или его использует агентный агент."
|
||||
},
|
||||
"prescreen": {
|
||||
"enable": "Включить предварительный отбор LLM",
|
||||
"warning": "Извлекается расширенный набор кандидатов, который затем фильтруется с помощью LLM. Это увеличивает задержку и стоимость запроса.",
|
||||
|
||||
@@ -268,6 +268,19 @@
|
||||
},
|
||||
"exposureHint": "將此來源預先載入提示中,或讓代理以工具形式隨選搜尋。"
|
||||
},
|
||||
"graphRetrieval": {
|
||||
"title": "圖譜檢索",
|
||||
"tag": "無需重新匯入",
|
||||
"seedStrategy": "走訪起點",
|
||||
"seedStrategyHint": "實體適用於大多數文件。關係可以到達問題中未提及的實體,適合描述事物之間如何關聯的內容。",
|
||||
"seedEntities": "實體(建議)",
|
||||
"seedRelationships": "關係",
|
||||
"passageNodes": "將段落納入走訪",
|
||||
"passageNodesHint": "段落既可因符合問題而被找到,也可因與符合內容相連而被找到。在此版本之後建立的圖譜上效果最佳。",
|
||||
"blendVector": "與向量檢索結合",
|
||||
"blendVectorHint": "加入向量檢索結果,避免遺漏圖譜未找到的段落。",
|
||||
"agentToolHint": "當此來源的公開方式為「隨選搜尋工具」,或由代理型代理使用時,代理也可以自行沿著這些關係查找。"
|
||||
},
|
||||
"prescreen": {
|
||||
"enable": "啟用 LLM 預篩選",
|
||||
"warning": "會擷取較大的候選集合並使用 LLM 篩選。這將增加查詢延遲與成本。",
|
||||
|
||||
@@ -268,6 +268,19 @@
|
||||
},
|
||||
"exposureHint": "将此来源预取到提示词中,或让代理按需将其作为工具进行搜索。"
|
||||
},
|
||||
"graphRetrieval": {
|
||||
"title": "图谱检索",
|
||||
"tag": "无需重新导入",
|
||||
"seedStrategy": "遍历起点",
|
||||
"seedStrategyHint": "实体适用于大多数文档。关系可以到达问题中未提及的实体,适合描述事物之间如何关联的内容。",
|
||||
"seedEntities": "实体(推荐)",
|
||||
"seedRelationships": "关系",
|
||||
"passageNodes": "将段落纳入遍历",
|
||||
"passageNodesHint": "段落既可因匹配问题被找到,也可因与匹配内容相连而被找到。在此版本之后构建的图谱上效果最佳。",
|
||||
"blendVector": "与向量检索结合",
|
||||
"blendVectorHint": "加入向量检索结果,避免遗漏图谱未找到的段落。",
|
||||
"agentToolHint": "当此来源的公开方式为“按需搜索工具”,或由智能体型代理使用时,代理也可以自行沿这些关系查找。"
|
||||
},
|
||||
"prescreen": {
|
||||
"enable": "启用 LLM 预筛选",
|
||||
"warning": "会获取更大的候选集并使用 LLM 进行过滤。这会增加查询时的延迟和成本。",
|
||||
|
||||
@@ -32,6 +32,17 @@ export type SourcePrescreenConfig = {
|
||||
max_keep?: number; // default 8, <= candidate_k
|
||||
};
|
||||
|
||||
// Where the graph walk starts: matching entities, or matching relationships
|
||||
// ("A streams_to B"), which can reach an entity the question never names.
|
||||
export type GraphSeedStrategy = 'entities' | 'relationships';
|
||||
|
||||
// Query-time graph retrieval knobs (graphrag only; live, no re-ingest).
|
||||
export type SourceGraphRetrievalConfig = {
|
||||
seed_strategy?: GraphSeedStrategy; // default 'entities'
|
||||
passage_nodes?: boolean; // default true
|
||||
blend_vector?: boolean; // default true
|
||||
};
|
||||
|
||||
// Query-time retrieval knobs (live; no re-ingest needed).
|
||||
export type SourceRetrievalConfig = {
|
||||
retriever?: string; // default 'classic' (only option for now)
|
||||
@@ -40,6 +51,7 @@ export type SourceRetrievalConfig = {
|
||||
score_threshold?: number | null; // default null
|
||||
rephrase_query?: boolean; // default true
|
||||
prescreen?: SourcePrescreenConfig | null; // null = off
|
||||
graph?: SourceGraphRetrievalConfig; // graphrag retriever only
|
||||
};
|
||||
|
||||
// Ingest-time GraphRAG extraction knobs (only used when kind === 'graphrag').
|
||||
|
||||
@@ -146,6 +146,11 @@ describe('round-trip configToOptions(optionsToConfig(x)) == x', () => {
|
||||
batch_size: 5,
|
||||
max_keep: 10,
|
||||
},
|
||||
graph: {
|
||||
seed_strategy: 'relationships',
|
||||
passage_nodes: false,
|
||||
blend_vector: false,
|
||||
},
|
||||
},
|
||||
graph: {
|
||||
extraction_model: null,
|
||||
@@ -157,6 +162,44 @@ describe('round-trip configToOptions(optionsToConfig(x)) == x', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('graph retrieval options', () => {
|
||||
it('defaults to the measured-best configuration', () => {
|
||||
expect(DEFAULT_RETRIEVAL_OPTIONS.retrieval.graph).toEqual({
|
||||
seed_strategy: 'entities',
|
||||
passage_nodes: true,
|
||||
blend_vector: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('fills the defaults for a source saved before the options existed', () => {
|
||||
const opts = configToOptions({ retrieval: { retriever: 'graphrag' } });
|
||||
expect(opts.retrieval.graph).toEqual(
|
||||
DEFAULT_RETRIEVAL_OPTIONS.retrieval.graph,
|
||||
);
|
||||
});
|
||||
|
||||
it('honors stored options and fills only the missing ones', () => {
|
||||
const opts = configToOptions({
|
||||
retrieval: { graph: { seed_strategy: 'relationships' } },
|
||||
});
|
||||
expect(opts.retrieval.graph).toEqual({
|
||||
seed_strategy: 'relationships',
|
||||
passage_nodes: true,
|
||||
blend_vector: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('writes the options into the retrieval block', () => {
|
||||
const v = clone(DEFAULT_RETRIEVAL_OPTIONS);
|
||||
v.retrieval.graph.blend_vector = false;
|
||||
expect(optionsToConfig(v).retrieval?.graph).toEqual({
|
||||
seed_strategy: 'entities',
|
||||
passage_nodes: true,
|
||||
blend_vector: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isPrescreenConfigValid', () => {
|
||||
const withPrescreen = (
|
||||
chunks: number,
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
import { Switch } from '../../components/ui/switch';
|
||||
import type {
|
||||
ChunkingStrategy,
|
||||
GraphSeedStrategy,
|
||||
RetrievalExposure,
|
||||
SourceConfig,
|
||||
} from '../../models/misc';
|
||||
@@ -59,6 +60,11 @@ export type RetrievalOptionsValue = {
|
||||
batch_size: number;
|
||||
max_keep: number;
|
||||
};
|
||||
graph: {
|
||||
seed_strategy: GraphSeedStrategy;
|
||||
passage_nodes: boolean;
|
||||
blend_vector: boolean;
|
||||
};
|
||||
};
|
||||
graph: {
|
||||
extraction_model: string | null;
|
||||
@@ -85,6 +91,12 @@ export const DEFAULT_RETRIEVAL_OPTIONS: RetrievalOptionsValue = {
|
||||
enabled: false,
|
||||
...DEFAULT_PRESCREEN,
|
||||
},
|
||||
// The configuration that measured best across the corpora tested.
|
||||
graph: {
|
||||
seed_strategy: 'entities',
|
||||
passage_nodes: true,
|
||||
blend_vector: true,
|
||||
},
|
||||
},
|
||||
graph: {
|
||||
extraction_model: null,
|
||||
@@ -204,6 +216,7 @@ export function configToOptions(config?: SourceConfig): RetrievalOptionsValue {
|
||||
const chunking = config?.chunking ?? {};
|
||||
const retrieval = config?.retrieval ?? {};
|
||||
const prescreen = retrieval.prescreen ?? null;
|
||||
const retrievalGraph = retrieval.graph ?? {};
|
||||
const graph = config?.graph ?? {};
|
||||
const d = DEFAULT_RETRIEVAL_OPTIONS;
|
||||
return {
|
||||
@@ -228,6 +241,14 @@ export function configToOptions(config?: SourceConfig): RetrievalOptionsValue {
|
||||
batch_size: prescreen?.batch_size ?? DEFAULT_PRESCREEN.batch_size,
|
||||
max_keep: prescreen?.max_keep ?? DEFAULT_PRESCREEN.max_keep,
|
||||
},
|
||||
graph: {
|
||||
seed_strategy:
|
||||
retrievalGraph.seed_strategy ?? d.retrieval.graph.seed_strategy,
|
||||
passage_nodes:
|
||||
retrievalGraph.passage_nodes ?? d.retrieval.graph.passage_nodes,
|
||||
blend_vector:
|
||||
retrievalGraph.blend_vector ?? d.retrieval.graph.blend_vector,
|
||||
},
|
||||
},
|
||||
graph: {
|
||||
extraction_model: graph.extraction_model ?? d.graph.extraction_model,
|
||||
@@ -271,6 +292,11 @@ export function optionsToConfig(value: RetrievalOptionsValue): SourceConfig {
|
||||
max_keep: ps.max_keep,
|
||||
}
|
||||
: null,
|
||||
graph: {
|
||||
seed_strategy: value.retrieval.graph.seed_strategy,
|
||||
passage_nodes: value.retrieval.graph.passage_nodes,
|
||||
blend_vector: value.retrieval.graph.blend_vector,
|
||||
},
|
||||
},
|
||||
graph: {
|
||||
extraction_model: value.graph.extraction_model?.trim()
|
||||
@@ -399,6 +425,12 @@ export default function RetrievalOptions({
|
||||
});
|
||||
};
|
||||
|
||||
const setGraphRetrieval = (
|
||||
patch: Partial<RetrievalOptionsValue['retrieval']['graph']>,
|
||||
) => {
|
||||
setRetrieval({ graph: { ...value.retrieval.graph, ...patch } });
|
||||
};
|
||||
|
||||
const modelOptions = useMemo(() => {
|
||||
const builtin: Model[] = [];
|
||||
const user: Model[] = [];
|
||||
@@ -611,6 +643,84 @@ export default function RetrievalOptions({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Graph retrieval group (graphrag only; live, so shown when testing too) */}
|
||||
{isGraphRAG && (
|
||||
<div className="flex flex-col gap-3">
|
||||
<GroupHeader
|
||||
title={tr('graphRetrieval.title')}
|
||||
tag={tr('graphRetrieval.tag')}
|
||||
/>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{tr('graphRetrieval.agentToolHint')}
|
||||
</p>
|
||||
|
||||
<div className="divide-border/50 divide-y">
|
||||
<SettingRow
|
||||
label={tr('graphRetrieval.seedStrategy')}
|
||||
htmlFor="graph-seed-strategy"
|
||||
description={tr('graphRetrieval.seedStrategyHint')}
|
||||
alignStart
|
||||
>
|
||||
<Select
|
||||
value={value.retrieval.graph.seed_strategy}
|
||||
disabled={disabled}
|
||||
onValueChange={(v) =>
|
||||
setGraphRetrieval({ seed_strategy: v as GraphSeedStrategy })
|
||||
}
|
||||
>
|
||||
<SelectTrigger
|
||||
id="graph-seed-strategy"
|
||||
className="w-52 rounded-md"
|
||||
size="lg"
|
||||
>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="entities">
|
||||
{tr('graphRetrieval.seedEntities')}
|
||||
</SelectItem>
|
||||
<SelectItem value="relationships">
|
||||
{tr('graphRetrieval.seedRelationships')}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</SettingRow>
|
||||
|
||||
<SettingRow
|
||||
label={tr('graphRetrieval.passageNodes')}
|
||||
htmlFor="graph-passage-nodes"
|
||||
description={tr('graphRetrieval.passageNodesHint')}
|
||||
alignStart
|
||||
>
|
||||
<Switch
|
||||
id="graph-passage-nodes"
|
||||
checked={value.retrieval.graph.passage_nodes}
|
||||
disabled={disabled}
|
||||
onCheckedChange={(checked) =>
|
||||
setGraphRetrieval({ passage_nodes: checked })
|
||||
}
|
||||
/>
|
||||
</SettingRow>
|
||||
|
||||
<SettingRow
|
||||
label={tr('graphRetrieval.blendVector')}
|
||||
htmlFor="graph-blend-vector"
|
||||
description={tr('graphRetrieval.blendVectorHint')}
|
||||
alignStart
|
||||
>
|
||||
<Switch
|
||||
id="graph-blend-vector"
|
||||
checked={value.retrieval.graph.blend_vector}
|
||||
disabled={disabled}
|
||||
onCheckedChange={(checked) =>
|
||||
setGraphRetrieval({ blend_vector: checked })
|
||||
}
|
||||
/>
|
||||
</SettingRow>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Graph extraction group (graphrag only; re-ingest required to apply) */}
|
||||
{isGraphRAG && !queryOnly && (
|
||||
<div className="flex flex-col gap-3">
|
||||
|
||||
Reference in new issue
Block a user