メインコンテンツまでスキップ

Knowledge Graph Search

document から entity と relationship の graph を抽出し、根拠となる事実が複数 document に分散する質問に agent が回答できるようにします。

Knowledge Graph を使用する理由

vector search は、質問に最も似ている passage を取得します。1 つの passage に答えが含まれている場合は機能しますが、複数の passage から答えを組み立てる場合は機能しません。

company filing の knowledge base を考えてみましょう。「Aldous Corporation はどの drug を販売していますか?」という質問の答えは 2 つに分かれています。1 つの document には Aldous が Bfarma Labs を買収したこと、もう 1 つには Bfarma Labs が Zalofen を製造していることが記載されています。2 つ目の document には Aldous が一切登場しないため、質問との類似性はありません。ある程度の規模の corpus では、質問の文言を単に繰り返す document との top-k 競争に負けます。

knowledge graph は、共有する entity を通じて passage を結び付けます。

Aldous Corporation --[acquired]--> Bfarma Labs --[manufactures]--> Zalofen

graph search は質問に含まれる entity から開始し、relationship をたどり、到達したすべての entity の背後にある source passage を返します。document は質問に似ているからではなく、質問に接続されているから取得されます。

概要

graph を有効にすると、次の 2 つが変わります。

  1. Ingest 時。 各 chunk は追加の LLM pass を通過し、entity(person、organization、product、location)と、それらの関係を抽出します。複数 document に出現する entity は merge されるため、graph は単一 file ではなく knowledge base 全体にまたがります。
  2. Search 時。 新しい retrieval mode である searchMode: 'graph' は、vector search を使用して query から entity を seed し、その relationship を expand して、到達した entity を source chunk に map し、その result を標準 hybrid search と fuse します。hybrid search と比べて失われるものはありません。graph channel は passage を追加するものであり、置き換えるものではありません。

entity graph の上に、Squid は document node、entity graph から cluster 化された theme(topic)、context metadata から導出された facet tree で構成される concept layer を構築します。この layer は queryGraph() operation を支え、agent は passage を取得するだけでなく、「この knowledge base には何があるか」、「この topic に含まれる document は何か」、「この 2 つはどのように接続されているか」といった structural question を尋ねられます。

使用する場合

状況推奨
shared entity で接続された複数 document に答えがまたがる✅ Knowledge graph
「A は B とどのように接続されているか」「X に関係するものは何か」✅ Knowledge graph
agent が passage を quote するだけでなく corpus を説明・navigate する必要があるqueryGraph() を使用する ✅ Knowledge graph
error code、SKU、file name などの exact tokenKeyword search
1 つの passage が質問に回答できるdefault の hybrid search で十分

要件

  • knowledge base は 'mongoAtlas' vector store 上にある必要があります。store は作成後 immutable であるため、'postgres' 上で作成された knowledge base に後から graph を追加することはできません。deployment により異なる server default に依存せず、vectorDbType: 'mongoAtlas' を明示的に渡してください。既存 knowledge base に設定された値は getKnowledgeBase() で確認できます。Creating a Knowledge Baseを参照してください。
  • graphRag 自体は mutable であるため、既存の Atlas knowledge base で graph をいつでも有効・無効にできます。
  • graph extraction は ingest 時にすべての chunk で LLM を実行するため、embedding に加えて token cost が発生します。大規模 corpus で graph を有効にする前に budget を考慮し、実際の費用は getGraphStatus() で追跡してください。
  • graph API は TypeScript と Python の SDK で利用できます。この page の例は TypeScript を使用しています。Python client は squid.ai().knowledge_base(...)get_graph_status()rebuild_graph()query_graph()explore_graph()search_with_graph_context() と同じ surface を公開します。

クイックスタート

knowledge base の管理には API key が必要なため、backend code から実行し、executableを通じて frontend に公開してください。

Backend code
import { SquidService, executable } from '@squidcloud/backend';

export class KnowledgeGraphService extends SquidService {
@executable()
async createGraphKnowledgeBase(): Promise<void> {
await this.squid
.ai()
.knowledgeBase('filings-knowledgebase')
.upsertKnowledgeBase({
description: 'Company filings, acquisitions, and product lines',
embeddingModel: 'text-embedding-3-small',
chatModel: 'gpt-5.5',
metadataFields: [],
// Required: the graph is only built on the MongoDB Atlas vector store.
vectorDbType: 'mongoAtlas',
graphRag: {
enabled: true,
// Optional domain hint. Steers extraction toward the entity kinds you care about.
entityTypes: ['ORGANIZATION', 'PRODUCT', 'LOCATION'],
},
});
}

@executable()
async addFilings(): Promise<void> {
await this.squid
.ai()
.knowledgeBase('filings-knowledgebase')
.upsertContexts([
{
contextId: 'acquisition-2019',
type: 'text',
title: 'Acquisition',
text: 'Aldous Corporation acquired Bfarma Labs in 2019 for two billion dollars.',
},
{
contextId: 'product-line',
type: 'text',
title: 'Product line',
text: 'Bfarma Labs manufactures a migraine drug called Zalofen at its Dublin plant.',
},
]);
}
}

entity extraction は ingestion によって trigger され、request path 外で asynchronous に実行されます。そのため graph を query 可能にするための rebuild は不要ですが、upsertContexts() の resolve は graph の準備完了を意味しません。Monitoring the graphで示すように、graph retrieval に依存する前に、すべての context が index 化されるまで getGraphStatus() を poll してください。extraction は knowledge base ごとの limit までの context を対象とします。Graph indexing capを参照してください。

search に API key は不要であり、client から実行できます。

Client code
// 'graph' is the default mode on a graph-enabled knowledge base, so searchMode is optional here.
const chunks = await squid.ai().knowledgeBase('filings-knowledgebase').search({
prompt: 'Which drugs does Aldous Corporation sell?',
searchMode: 'graph',
});

traversal は Bfarma Labs を通じて Aldous Corporation から Zalofen に到達するため、Aldous Corporation に一切言及していない場合でも product-line chunk が取得されます。たどった chain は searchWithGraphContext() で確認できます。

2 document の knowledge base では search mode 間の違いは期待しないでください。競合するものがなければ、すべての mode がすべてを返します。graph が有用になるのは、connected ではあるものの類似しない passage が通常は除外されるほど、top-k を競う document が十分にある場合です。

Configuration

enabled を除く graphRag のすべての field は optional です。

FieldType説明
enabledbooleanextraction と searchMode: 'graph' を有効にします。必須です。
entityTypesstring[]たとえば ['ORGANIZATION', 'PRODUCT'] のように extraction prompt に inject する domain taxonomy hint。extraction を guide しますが、制限はしません。
extractionModelAiChatModelSelectionchunk ごとの extraction に使用する chat model。default は server の graph model です。
autoBuildDebounceMsnumberautomatic build の実行前の quiet window。positive integer で、default は server の 5 分 window です。
conceptsobjectconcept layer の configuration。concepts.facets は facet にする metadata field を選択します(default は 'auto'、disable するには [])。concepts.pathFacets は hierarchical path field から navigable tree を構築します。

path facet は folder path を graph 内の browse 可能な hierarchy に変換します。

Backend code
await this.squid
.ai()
.knowledgeBase('filings-knowledgebase')
.upsertKnowledgeBase({
description: 'Company filings, acquisitions, and product lines',
embeddingModel: 'text-embedding-3-small',
chatModel: 'gpt-5.5',
metadataFields: [],
vectorDbType: 'mongoAtlas',
graphRag: {
enabled: true,
concepts: {
// Build a folder tree from the folderPath metadata stamped by folder-aware uploads.
pathFacets: [{ field: 'folderPath', type: 'path' }],
},
},
});

graphRag の upsert は object 全体を置き換えるため、省略した field は失われます。単一 field を変更する場合は、getKnowledgeBase() で current configuration を読み取り、spread してください。

既存 Knowledge Base で Graph を有効にする

すでに content を持つ Atlas knowledge base で enabledtrue にすると、Squid が graph を backfill します。Squid は knowledge base が quiet になるまで待機してから、まだ graph index 化されていないすべての context を抽出し、その上に concept layer を構築する structural build を実行します。rebuildGraph() を呼び出す必要はありません。

Backend code
const kb = this.squid.ai().knowledgeBase('filings-knowledgebase');
const existing = await kb.getKnowledgeBase();

// Spread the current configuration so enabling the graph does not drop any other field.
await kb.upsertKnowledgeBase({ ...existing!, graphRag: { enabled: true } });

Automatic build

concept layer(theme と facet)は corpus 全体から導出されるため、document ごとではなく batch で rebuild されます。content の ingestion・deletion、または graph の有効化後に、build は自動で schedule されます。

  • build は最後の graph work が完了してから測定するquiet windowを待機します。進行中の ingestion は deadline を先送りし続けるため、長い bulk upload では document ごとではなく最後に 1 回だけ build が実行されます。
  • window の default は 5 分で、knowledge base ごとに graphRag.autoBuildDebounceMs で構成できます。
  • sweep は毎分、due になった knowledge base を確認するため、window が経過してからおおむね 1 分以内に検出されます。
  • build の保留中は、getGraphStatus()nextAutoBuildAt を報告します。activity が続く間は、この値も先に進みます。

entity extraction はこの schedule の一部ではありません。ingestion 自体によって trigger されるため、searchMode: 'graph' は concept layer の更新よりずっと前、extraction が完了すると新しく ingestion された document をカバーします。遅れるのは、queryGraph() が読み取る theme と facet structure です。

Build を強制する

quiet window を待ちたくない場合は、rebuildGraph() を呼び出します。default の 'structural' mode はほぼ常に適切です。以下の 'full' に関する warning を参照してください。

Backend code
// Runs a structural build (the default): folds newly ingested documents into topics and
// facets now, reusing the extracted entities. Same build the automatic schedule runs.
await this.squid.ai().knowledgeBase('filings-knowledgebase').rebuildGraph();
Mode再計算する内容Cost
'structural'(default)entity resolution、theme、facet。まだ graph index 化されていない context に対してのみ extraction を実行します。full rebuild のごく一部。
'full'すべて。すべての chunk の LLM re-extraction を含みます。re-extraction が大部分を占めます。initial ingest と同程度です。
警告

'full' rebuild が必要になることはまれで、destructive かつ低速です。既存 graph を最初に wipe してから、LLM ですべての chunk を再抽出するため、initial ingest と同程度の token と時間がかかります。job の完了まで、searchMode: 'graph'queryGraph() は incomplete graph に対して実行されます。extraction はすでに ingest 時に実行されるため、新規または更新済み document に full rebuild は不要です。structural build がそれらを取り込みます。entityTypes または extractionModel を変更した後など、extraction 自体を再実行する必要がある場合にのみ、mode: 'full' を明示的に request してください。

knowledge base ごとに、一度に実行できる rebuild は 1 つだけです。getGraphStatus().buildJob を通じて追跡してください。

Graph Indexing Cap

graph extraction は、knowledge base ごとの chunk cap で停止します。cap は knowledge base ごとの option ではなく deployment-level limit であり、value は変更される可能性があります。そのため固定数を前提に設計せず、以下の signal で検出してください。cap を超えて ingestion された context は通常どおり保存され、vector、hybrid、keyword mode で完全に search 可能です。graph coverage のみが存在しないため、graph traversal はそこに到達できず、queryGraph() も表示しません。

skip は silent ではなく報告されます。

  • upsertContexts() は影響を受けた context を graphIndexingSkippedContextIds に list 化し、単一 context の upsertContext()graphIndexingSkipped: true を設定します。
  • getGraphStatus()contextsIndexed < contextsTotal として継続する gap を表示します。
Backend code
const result = await this.squid
.ai()
.knowledgeBase('filings-knowledgebase')
.upsertContexts([
{
contextId: 'annual-report-2026',
type: 'text',
title: 'Annual report',
text: 'Aldous Corporation reported record revenue in 2026.',
},
]);

// The listed contexts are stored and fully searchable; they are only absent from the graph.
const skippedIds = result.graphIndexingSkippedContextIds ?? [];
if (skippedIds.length > 0) {
console.warn(`${skippedIds.length} contexts were stored without graph coverage`);
}

Console および CLI の bulk upload、connector-driven sync など、upsert result を受け取らない ingestion path では、getGraphStatus() の gap が唯一の signal です。

すでに graph に含まれている document を update しても、graph から追い出されることはありません。更新後 content が context の既存 chunk 数以下になる reupsert は cap の対象外であるため、同じ size の replacement は graph coverage を維持します。context が増加する replacement は新しい content として count され、cap が適用されます。

cap を超える corpus 全体で完全な graph coverage を維持するには、corpus を複数 knowledge base に分割してください。

searchMode: 'graph' は graph-enabled knowledge base の default mode です。そのため、searchMode を省略すると graph が使用されます。他の mode も引き続き利用でき、Searching a Knowledge Baseで document 化されています。

traversal は graphOptions で調整します。

OptionDefaultLimit説明
seedLimit825expansion 前に vector search で seed する entity 数。
maxHops23各 seed から traversal を expand する距離。
includeGraphContextfalsetraversal した subgraph を response に追加します。
Client code
const chunks = await squid
.ai()
.knowledgeBase('filings-knowledgebase')
.search({
prompt: 'How is Aldous Corporation connected to Dublin?',
searchMode: 'graph',
graphOptions: { seedLimit: 12, maxHops: 3 },
});

maxHops を増やすと範囲が広がり、弱い association も取得されるため、本当に multi-hop の質問にのみ増やしてください。

Search を concept に scope する

graphFilter は search を graph の 1 concept 配下の document に限定します。user がすでに subject を絞り込んでいる場合に有用です。これはすべての search mode および contextMetadataFilter と compose できるため、thematic scope と metadata filter を組み合わせられます。

Client code
const chunks = await squid
.ai()
.knowledgeBase('filings-knowledgebase')
.search({
prompt: 'What was the purchase price?',
// A theme name, a facet value, or a facet nodeId. Names are resolved server side.
graphFilter: { underConcept: 'Acquisitions' },
});

facet scope は exact metadata membership で filter します。theme scope は thematic であるため recall-safe ですが fuzzy です。theme の current membership 外にある document は、relevant であっても除外されます。resolve できない concept は CONCEPT_NOT_FOUND と最も近い matching name で failure します。

Traversal を確認する

search() は chunk のみを返します。それらを生成した entity と relationship を確認するには、includeGraphContext とともに searchWithGraphContext() を使用します。retrieval の debugging や、passage が返された理由を user に示す場合に役立ちます。

Client code
const response = await squid
.ai()
.knowledgeBase('filings-knowledgebase')
.searchWithGraphContext({
prompt: 'Which drugs does Aldous Corporation sell?',
searchMode: 'graph',
graphOptions: { includeGraphContext: true },
});

for (const entity of response.graphContext?.entities ?? []) {
console.log(`${entity.name} (${entity.type}): ${entity.description}`);
}
for (const rel of response.graphContext?.relationships ?? []) {
console.log(`${rel.source} --[${rel.type}]--> ${rel.target}`);
}

graphContext は、includeGraphContext: true を指定した graph mode の search でのみ存在します。

Graph を直接 Query する

queryGraph() は passage を取得するのではなく、graph の structure を読み取ります。各 operation は complete answer を返す single round trip です。

OpRequired inputReturns
overviewknowledge base の render 済み map: theme、facet、count。
resolverefdocument、entity、theme、facet 全体から name に一致する node。
describerefancestor と immediate neighbor を含む 1 つの node。
subtreerefdepth(default および cap は 3)までの document count を含む node subtree。
docsUnderrefnode closure 下の document。totalDocs は full size を報告します。
conceptsOfcontextId1 つの document が属する facet と theme。
neighborhoodrefentity 周辺の entity subgraph。hops(default 1、max 2)で expand されます。
pathBetweenrefrefB2 node を接続する relationship chain、または共有する concept。
globalSummaryquery質問に対する relevance で rank 付けされた knowledge base の theme summary。
Backend code
const kb = this.squid.ai().knowledgeBase('filings-knowledgebase');

// What is in this knowledge base?
const overview = await kb.queryGraph({ op: 'overview' });
console.log(overview.overview);

// How are two things connected?
const path = await kb.queryGraph({
op: 'pathBetween',
ref: 'Aldous Corporation',
refB: 'Zalofen',
});
if (path.pathBetween?.tier === 'entity') {
// An explanatory chain: Aldous Corporation acquired Bfarma Labs, which manufactures Zalofen.
for (const step of path.pathBetween.steps ?? []) {
console.log(`${step.from} --[${step.type}]--> ${step.to}`);
}
} else {
// No entity chain exists, so the answer falls back to the concepts both refs sit under.
console.log(path.pathBetween?.sharedConcepts);
}

ref は name または facet nodeId を受け取ります。name は Squid が server-side で exact match、alias、similarity の順に resolve します。永続化する価値がある stable handle は facet nodeId と document contextId のみです。theme および entity ID は rebuild のたびに変わるため、毎回 name で resolve してください。

Graph を探索する

exploreGraph() は visualization 用に entity graph の bounded slice を返します。degree が最も高い entity と、それらの relationship が対象です。

Backend code
const subgraph = await this.squid.ai().knowledgeBase('filings-knowledgebase').exploreGraph({
nodeLimit: 100, // Default 200, max 1000.
});

console.log(`${subgraph.nodes.length} nodes, ${subgraph.edges.length} edges`);
// topics maps each node's topicPath ids to names, so a visualization can group by theme.
console.log(subgraph.topics?.map((topic) => topic.name));

slice を 1 つの theme に限定するには、topicId を渡します。theme ID は getGraphStatus().topics または前の exploreGraph() response から取得でき、次の rebuild までのみ有効です。

Graph を Monitoring する

getGraphStatus() は build state、coverage、cost を報告します。

Backend code
const status = await this.squid.ai().knowledgeBase('filings-knowledgebase').getGraphStatus();

console.log(`${status.contextsIndexed}/${status.contextsTotal} contexts graph indexed`);
console.log(`${status.entityCount} entities, ${status.relationshipCount} relationships`);
console.log(`Graph ingest cost so far: $${status.ingestUsage?.estCostUsd ?? 0}`);

if (status.buildJob?.status === 'in_progress') {
console.log(`Build running since ${new Date(status.buildJob.startedAt!).toISOString()}`);
} else if (status.structureStale) {
console.log(`${status.staleDocCount} documents changed since the last build`);
}
if (status.nextAutoBuildAt) {
console.log(`Next automatic build around ${new Date(status.nextAutoBuildAt).toISOString()}`);
}

知っておくべき field:

Field意味
enabledこの knowledge base で graph が有効かどうか。
contextsIndexed / contextsTotalextraction coverage。value が等しい場合、すべての context が graph に含まれます。継続する gap は、graph indexing cap により context が skip されたことを示します。
entityCount / relationshipCountentity graph の size。
topics / facetsbuild 実行後に設定される concept layer。
structureStale / staleDocCount最後の concept build 以降に graph index 化された document。次の build まで theme は content に遅れます。
buildJob最新 build: failure 時の statusstartedAterror
lastStructureBuildAtbuild が最後に正常完了した時刻。これまでに存在しない場合はありません。
nextAutoBuildAt保留中 automatic build の予定時刻。schedule されていない場合はありません。
ingestUsage / lastRebuildUsagebyModel で model ごとに分けられた LLM token と estimated cost。

graph の完全な準備完了を待つには、contextsIndexed === contextsTotal かつ buildJob'in_progress' でなくなるまで poll します。graph indexing cap に達した knowledge base では、contextsIndexed は永続的に contextsTotal を下回ります。status には差し引くべき skipped count が含まれないため、wait に上限を設定してください。buildJob'in_progress' でない状態で poll 間の contextsIndexed の進行が停止したら、graph は settled と扱います。

Console から Graph を使用する

Squid Console では code なしで同じ feature を利用できます。

  • Vector StoremongoAtlas に設定してknowledge base を作成し、Knowledge Graph toggle を有効にします。store は作成後に変更できないため、Server default を含む他の store では toggle は disabled のままです。Server default で作成され Atlas に resolve された knowledge base は、編集により後で graph を有効にできます。
  • knowledge base page には Knowledge Graph card が表示され、最後の build time、それ以降に変更された document 数、次の automatic build の推定時刻、structural build をすぐに実行する Build now button が表示されます。

Agent Behavior

接続された agent は graph を自動的に取得します。graph-enabled knowledge base では以下のようになります。

  • knowledge base search tool は他の mode とともに 'graph' を提供し、これを default として扱います。そのため multi-hop question は特別な prompt なしで graph retrieval を使用します。
  • tool description には graph の compact overview が含まれるため、agent は search 前に corpus の theme と count を把握できます。
  • agent は上記 operationを基盤とする queryKnowledgeGraph tool を取得するため、passage を取得するだけでなく corpus structure を navigate できます。
  • agent は処理中に status update を broadcast します。queryKnowledgeGraph の実行時は Querying Knowledge Base Graph title が報告され、通常の knowledge base search の Accessing Knowledge Base title とは区別されます。そのため chat UI は graph navigation を独自の step として表示できます。

knowledge base を agent に接続する方法については、Agent abilitiesを参照してください。

Error Handling

Error原因修正
CONCEPT_NOT_FOUNDqueryGraph()ref が何にも match しません。message には最も近い name が list 化されます。最初に op: 'resolve' で name を resolve するか、suggest された name のいずれかを使用します。
TOPIC_NOT_FOUNDexploreGraph()topicId が不明です。通常は rebuild により ID が churn したためです。ID を永続化せず、rebuild のたびに getGraphStatus() から theme ID を再読み取りします。
JOB_ALREADY_EXISTSこの knowledge base ではすでに rebuild が実行中です。getGraphStatus().buildJob'in_progress' でなくなるまで待機します。
Graph call は成功するが空を返すknowledge base が mongoAtlas 上にない、または graphRag.enabled が false です。rebuildGraph() を除き、graph operation はそこで no-op です。getGraphStatus().enabled と knowledge base の vectorDbType を確認します。
buildJob.status === 'failed'build が buildJob.error に記録された error に到達しました。underlying cause を修正し、rebuildGraph() を再実行します。次の automatic build でも retry されます。
contextsIndexedcontextsTotal 未満で停止するknowledge base が graph indexing cap を超えています。upsertContexts() は skip された context を graphIndexingSkippedContextIds に list 化し、upsertContext()graphIndexingSkipped を設定します。failure ではありません。context は graph 外では完全に search 可能です。完全な graph coverage のために corpus を複数 knowledge base に分割します。

graphRag を disable すると graph search は利用できなくなりますが、抽出された graph data は保持されます。そのため再度 enable する cost は低くなります。structural build は、まだ index 化されていないものを取り込み、すでに抽出済みの data を再利用します。graph data を実際に削除するには、graphRag.enabled が false の状態で rebuildGraph() を呼び出します。これは disabled knowledge base にも作用する唯一の graph call であり、disabled 時には graph を構築する代わりに wipe します。knowledge base を delete した場合も graph は削除されます。

ベストプラクティス

  • knowledge base を作成する前に graph を使用するか決定し、vectorDbType を明示的に渡します。これは immutable であるため、'postgres' knowledge base に graph を追加することはできません。
  • domain に合う entityTypes を渡します。organization、person、location などの proper noun では、generic product category より extraction の信頼性が大幅に高くなります。
  • bulk ingestion 前に graph を有効にします。これにより、knowledge base が quiet になり automatic backfill build が完了するまで待つのではなく、ingestion に応じて graph を query 可能になります。後から有効にしても機能します。backfill は backlog に対して structural build を実行します。
  • maxHops は default のままにします。本当に複数 hop を必要とする質問でのみ増やし、その場合は result が緩くなることを想定してください。
  • まず小規模 corpus で ingestUsage.estCostUsd を確認し、全体で graph を有効にした場合の cost を見積もります。
  • continuous ingestion 中の knowledge base では autoBuildDebounceMs を広げ、build が moving target を追い続けないようにします。小規模 update 後に theme をすばやく refresh したい場合は短くします。
  • rebuild をまたいで theme ID ではなく theme name を resolve します。stable なのは facet nodeId と document contextId だけです。