From 35e974fc39e6db99e339503c0e6dac6579a56a42 Mon Sep 17 00:00:00 2001 From: EmersonBraun Date: Wed, 7 Oct 2026 20:07:07 -0300 Subject: [PATCH] feat(rag): add partition-bound retrieval --- .changeset/clear-waves-fall.md | 5 + .doc-bridge/capabilities.json | 2 +- .doc-bridge/index.json | 971 ++++++++++++------ .../docs-next/content/docs/for-agents/rag.mdx | 10 + .../content/docs/reference/packages/rag.mdx | 47 + .../adrs/0043-partition-bound-rag.md | 31 + docs/architecture/adrs/README.md | 1 + docs/stability/public-api-v1.json | 18 + packages/rag/CONVENTIONS.md | 8 + packages/rag/README.md | 47 + packages/rag/src/index.ts | 2 + packages/rag/src/partitioned.ts | 66 ++ packages/rag/tests/partitioned.test.ts | 113 ++ readme-standard-v1.json | 2 +- 14 files changed, 1011 insertions(+), 312 deletions(-) create mode 100644 .changeset/clear-waves-fall.md create mode 100644 docs/architecture/adrs/0043-partition-bound-rag.md create mode 100644 packages/rag/src/partitioned.ts create mode 100644 packages/rag/tests/partitioned.test.ts diff --git a/.changeset/clear-waves-fall.md b/.changeset/clear-waves-fall.md new file mode 100644 index 000000000..2d5397614 --- /dev/null +++ b/.changeset/clear-waves-fall.md @@ -0,0 +1,5 @@ +--- +"@agentskit/rag": minor +--- + +Add createPartitionedRAG with construction-bound metadata isolation, namespaced chunk IDs, bounded candidate retrieval, and explicit recall diagnostics. Existing createRAG and the core Retriever contract are unchanged. diff --git a/.doc-bridge/capabilities.json b/.doc-bridge/capabilities.json index d04f65c4a..f266cae74 100644 --- a/.doc-bridge/capabilities.json +++ b/.doc-bridge/capabilities.json @@ -7,7 +7,7 @@ "name": "AgentsKit", "root": "." }, - "contentHash": "de95fec2af4608ff2a437882c6c08c0cdc82c86637b2a49cc29ce29de2a43cca", + "contentHash": "0dbf922bfae3b8b64f1654a926fa23e3678c44c08a9510aade00d8e237918fa8", "artifacts": { "index": ".doc-bridge/index.json", "llmsTxt": ".doc-bridge/llms.txt" diff --git a/.doc-bridge/index.json b/.doc-bridge/index.json index 487787324..18a70927e 100644 --- a/.doc-bridge/index.json +++ b/.doc-bridge/index.json @@ -1,8 +1,8 @@ { "schemaVersion": 1, - "contentHash": "de95fec2af4608ff2a437882c6c08c0cdc82c86637b2a49cc29ce29de2a43cca", + "contentHash": "0dbf922bfae3b8b64f1654a926fa23e3678c44c08a9510aade00d8e237918fa8", "contentHashAlgo": "sha256-normalized-v1", - "generatedAt": "2026-10-08T01:36:58.952Z", + "generatedAt": "2026-10-08T03:22:15.493Z", "project": { "name": "AgentsKit", "root": "." @@ -3843,7 +3843,7 @@ "document", "human" ], - "contentHash": "55709f46fa5535116f04d92d25cc2a103f041ea8802845de830e8eb8c22f0d86" + "contentHash": "c76ee7a3326f5d0fc84ed374abf4e6d79b4649dd329eb731c722a00e9455df5c" }, { "id": "document:apps/docs-next/content/docs/reference/packages/react-native.mdx", @@ -5595,7 +5595,7 @@ "document", "project" ], - "contentHash": "3077743f11a0f1247886eba780727275a818b99d1cb4abca3febaa84dfb031ca" + "contentHash": "abc7edc745f582d31729a32691c0bfebbdc0eaa02839fd1e3eb4b1a351d8388b" }, { "id": "document:docs/adapter-modalities.md", @@ -6052,6 +6052,18 @@ ], "contentHash": "6e1205db68cf23787d54c5b5e30edc45263ebebb31bf12381caf66080304a13a" }, + { + "id": "document:docs/architecture/adrs/0043-partition-bound-rag.md", + "type": "document", + "title": "ADR 0043 — Partition-bound RAG composition", + "path": "docs/architecture/adrs/0043-partition-bound-rag.md", + "description": "Status: Proposed (implementation authorized; evidence review remains external)", + "tags": [ + "document", + "human" + ], + "contentHash": "e726f3b6b1f4d568fb9ee435cd3499673cfa86a42d7778a4b3d45b91e8d0426b" + }, { "id": "document:docs/architecture/adrs/README.md", "type": "document", @@ -6062,7 +6074,7 @@ "document", "human" ], - "contentHash": "5c07710d50c06de02f9f2f29b5bdc395d6a645d96fd9d819624690599872948a" + "contentHash": "3cca1f08e78d34cd103a4b41ba860a4d5da92e4d7567be565ae363ec49dd2082" }, { "id": "document:docs/architecture/ecosystem-manifest.md", @@ -7250,7 +7262,7 @@ "document", "unclassified" ], - "contentHash": "a3023df0f73f97d90bc61911c8f9563969c03b09a159f807678d4511dcb13417" + "contentHash": "c6066c6a7d8b02a2d59ceaf7101612ee71bf44747d6caff4f3678a3e1feff499" }, { "id": "document:packages/rag/README.md", @@ -7262,7 +7274,7 @@ "document", "project" ], - "contentHash": "66c9dbb5e9581450f96d215fca8c58c1d42a36268c30a9b59d594c2baa84705a" + "contentHash": "4ff1e6bbf331148073cf1440ecf670ee5d23d23bec8af914419e54b120743bd4" }, { "id": "document:packages/react-native/CONVENTIONS.md", @@ -28702,6 +28714,8 @@ "LoaderOptions", "NotionLoaderOptions", "OneDriveLoaderOptions", + "PartitionSearchDiagnostics", + "PartitionedRAGConfig", "PdfLoaderOptions", "RAG", "RAGConfig", @@ -28718,6 +28732,7 @@ "bm25Score", "chunkText", "createHybridRetriever", + "createPartitionedRAG", "createRAG", "createRerankedRetriever", "jinaReranker", @@ -28739,7 +28754,7 @@ "ts", "src" ], - "contentHash": "ea4049d0e946190b1ec74ed79f2f7d7a56a83fac70b8d94d52dc4fec25bdb889" + "contentHash": "06c96c3eb23163537c5221886419fd0b753dd3c091da2965806a3c1b513fa22e" }, { "id": "module:packages/rag/src/loaders-node.ts", @@ -28913,6 +28928,23 @@ ], "contentHash": "36ff0d561fc848503809d9491cc5e73775980f2e2136288514b3686b02965c72" }, + { + "id": "module:packages/rag/src/partitioned.ts", + "type": "module", + "title": "partitioned.ts", + "path": "packages/rag/src/partitioned.ts", + "symbols": [ + "PartitionSearchDiagnostics", + "PartitionedRAGConfig", + "createPartitionedRAG" + ], + "tags": [ + "module", + "ts", + "src" + ], + "contentHash": "9623c7e6a75e3b0a41d0a202ac270ab5fd1f8917646eb0b3d312ffa73683cf13" + }, { "id": "module:packages/rag/src/rag.ts", "type": "module", @@ -29162,6 +29194,19 @@ ], "contentHash": "8005a0e24762ff2c12d0822bd01d21c2ec65bce1aa1b9b1900008c054e691386" }, + { + "id": "module:packages/rag/tests/partitioned.test.ts", + "type": "module", + "title": "partitioned.test.ts", + "path": "packages/rag/tests/partitioned.test.ts", + "tags": [ + "module", + "ts", + "test", + "tests" + ], + "contentHash": "ea5a987725de429969e957ff25767bea9ccd819198f7540cd047b4b89ddea815" + }, { "id": "module:packages/rag/tests/rag-batch-462-466.test.ts", "type": "module", @@ -35461,7 +35506,7 @@ "mjs", "scripts" ], - "contentHash": "ed65b8c131a3d1dfe91e7ded10fe0dff179b498877c60e90793b2da5c2e1ee86" + "contentHash": "aab38c07cecfd6e20d0c6f87efd0a494887bcfacaaa7f7d4266b0e78d06c7c67" }, { "id": "module:scripts/check-product-chat-adoption.mjs", @@ -35493,14 +35538,15 @@ "title": "check-public-api-snapshot.mjs", "path": "scripts/check-public-api-snapshot.mjs", "symbols": [ - "parseArgs" + "parseArgs", + "runPublicApiSnapshot" ], "tags": [ "module", "mjs", "scripts" ], - "contentHash": "b9f3891fa53fc79699097120667aeb450f44303ec3a9e577dad6fb8164fbe846" + "contentHash": "364adbe4f31755b005ac8189ef23c88a313061ec879412955ecb19c9d16d5748" }, { "id": "module:scripts/check-publication-surface.mjs", @@ -36230,6 +36276,7 @@ "isPathInsidePackage", "isUnsafeArchiveEntry", "normalizeSubpath", + "selectPackedConsumerPackages", "shouldTypecheckEntry", "stripPackagePrefix", "toImportSpecifier", @@ -36240,7 +36287,7 @@ "mjs", "lib" ], - "contentHash": "c2d761dc22362645d27cfe43f93c55fbfa1a495a74836ad1c1e0c3493cae9618" + "contentHash": "1123796b96821d38de5e00f3e8412a4d1d662b4a109686e71304d32a07dd78aa" }, { "id": "module:scripts/lib/product-chat-adoption.mjs", @@ -36501,7 +36548,7 @@ "test", "scripts" ], - "contentHash": "dd2349d662b377ecd8aa0dddd7029389c3481cb385d7fd07cad4ddc524dce1f8" + "contentHash": "428c0de6bfc559c09b799c7e4d14afee5bebcab4002271ce83869083c10018d2" }, { "id": "module:scripts/pattern-lab.test.mjs", @@ -36540,7 +36587,7 @@ "test", "scripts" ], - "contentHash": "7e0c3e874befa5524e8e533d206833a4dd4faeae3f199f029a6530036a034685" + "contentHash": "ad2f3364279f847d38fea68bd85f779674af7b3ae4b58da8c0c0ea25f96c0005" }, { "id": "module:scripts/publish-manifests.test.mjs", @@ -38150,8 +38197,8 @@ "changes": {} }, "inputs": { - "hash": "24f8bb213b1054a6b0b61251b4ce7f91cb854760740820a7f028a72434cf0c24", - "fileCount": 2536, + "hash": "54915d85f7a308fa5ec1af4cf890ad9bed63ec04e569a06772a865a7b0676bca", + "fileCount": 2539, "projectionVersion": 2 }, "retrieval": { @@ -38173,9 +38220,9 @@ "projection": { "type": "retrieval-index", "schemaVersion": 1, - "contentHash": "a3281eb0ac9d555bb67038a03f9124810b70d157a35a8577ab54a68a5eeab1b4", + "contentHash": "997a1701967a593a3431a61ddb7c105abf17552fcf32c48a76697493fd9c31a4", "contentHashAlgo": "sha256-normalized-v1", - "snapshotHash": "b7f4c0990a396b5c64bf74375a2406e9ed9de4ef1e20bc36611d78e976d37d2b", + "snapshotHash": "15107afcf7657b21ca4445c206e72b993c7d0e05d80fd3ab83476518d31ce853", "overlayHash": "f99666ff9119084dd25c2974ae64eb6935ec2aa785ff5ad478fb9d541f7a5e94", "configurationHash": "9662cc07ea4ab203ff2ddf457d2db48b5375a7ab4a1f8fbc79dfebcaa8f9a00c", "lexiconVersion": 1, @@ -38195,7 +38242,7 @@ }, "lexical": { "version": 1, - "documentCount": 2491, + "documentCount": 2494, "fieldNames": [ "aliases", "body", @@ -38206,13 +38253,13 @@ "title" ], "averageFieldLength": { - "aliases": 3.0144520272982738, - "body": 61.262545162585305, - "headings": 4.014853472501003, - "path": 7.613809714973906, - "summary": 4.553191489361702, - "symbols": 7.450020072260137, - "title": 3.3540746688077077 + "aliases": 3.0144346431435447, + "body": 61.41539695268645, + "headings": 4.019647153167602, + "path": 7.613472333600641, + "summary": 4.5509222133119485, + "symbols": 7.454691259021652, + "title": 3.354851643945469 } }, "entries": [ @@ -38631,7 +38678,7 @@ "aliases": "area" }, "graph": { - "pagerank": 0.003617, + "pagerank": 0.003615, "inboundLinks": 1, "coveredBy": [ "document:apps/docs-next/content/docs/reference/examples/discord-bot.mdx" @@ -38670,7 +38717,7 @@ "aliases": "area" }, "graph": { - "pagerank": 0.003617, + "pagerank": 0.003615, "inboundLinks": 1, "coveredBy": [ "document:apps/docs-next/content/docs/reference/examples/dspy.mdx" @@ -38748,7 +38795,7 @@ "aliases": "area" }, "graph": { - "pagerank": 0.003617, + "pagerank": 0.003615, "inboundLinks": 1, "coveredBy": [ "document:apps/docs-next/content/docs/reference/examples/embedded.mdx" @@ -39069,7 +39116,7 @@ "aliases": "area" }, "graph": { - "pagerank": 0.003617, + "pagerank": 0.003615, "inboundLinks": 1, "coveredBy": [ "document:apps/docs-next/content/docs/reference/examples/slack-bot.mdx" @@ -39108,7 +39155,7 @@ "aliases": "area" }, "graph": { - "pagerank": 0.003617, + "pagerank": 0.003615, "inboundLinks": 1, "coveredBy": [ "document:apps/docs-next/content/docs/reference/examples/teams-bot.mdx" @@ -39464,7 +39511,7 @@ "aliases": "area" }, "graph": { - "pagerank": 0.003617, + "pagerank": 0.003615, "inboundLinks": 1, "coveredBy": [ "document:apps/docs-next/content/docs/data/providers/embedders.mdx" @@ -39579,7 +39626,7 @@ "aliases": "area" }, "graph": { - "pagerank": 0.003617, + "pagerank": 0.003615, "inboundLinks": 1, "coveredBy": [ "document:apps/docs-next/content/docs/production/cli/index.mdx" @@ -39742,7 +39789,7 @@ "aliases": "area" }, "graph": { - "pagerank": 0.003617, + "pagerank": 0.003615, "inboundLinks": 1, "coveredBy": [ "document:apps/docs-next/content/docs/production/security/index.mdx" @@ -39905,7 +39952,7 @@ "aliases": "area" }, "graph": { - "pagerank": 0.002786, + "pagerank": 0.002784, "inboundLinks": 1, "coveredBy": [ "document:apps/docs-next/content/docs/reference/packages/eval-braintrust.mdx" @@ -39989,7 +40036,7 @@ "aliases": "area" }, "graph": { - "pagerank": 0.002786, + "pagerank": 0.002784, "inboundLinks": 1, "coveredBy": [ "document:apps/docs-next/content/docs/production/evals/snapshots.mdx" @@ -40028,7 +40075,7 @@ "aliases": "area" }, "graph": { - "pagerank": 0.003617, + "pagerank": 0.003615, "inboundLinks": 1, "coveredBy": [ "document:apps/docs-next/content/docs/production/evals/replay.mdx" @@ -40067,7 +40114,7 @@ "aliases": "area" }, "graph": { - "pagerank": 0.002786, + "pagerank": 0.002784, "inboundLinks": 1, "coveredBy": [ "document:apps/docs-next/content/docs/production/evals/snapshots.mdx" @@ -40106,7 +40153,7 @@ "aliases": "area" }, "graph": { - "pagerank": 0.002371, + "pagerank": 0.002369, "inboundLinks": 1, "coveredBy": [ "document:apps/docs-next/content/docs/ui/index.mdx" @@ -40176,7 +40223,7 @@ "aliases": "area" }, "graph": { - "pagerank": 0.002786, + "pagerank": 0.002784, "inboundLinks": 1, "coveredBy": [ "document:apps/docs-next/content/docs/agents/tools/integrations-catalog.mdx" @@ -40246,7 +40293,7 @@ "aliases": "area" }, "graph": { - "pagerank": 0.003617, + "pagerank": 0.003615, "inboundLinks": 1, "coveredBy": [ "document:apps/docs-next/content/docs/data/memory/index.mdx" @@ -40285,7 +40332,7 @@ "aliases": "area" }, "graph": { - "pagerank": 0.003617, + "pagerank": 0.003615, "inboundLinks": 1, "coveredBy": [ "document:apps/docs-next/content/docs/data/rag/loaders.mdx" @@ -40324,7 +40371,7 @@ "aliases": "area" }, "graph": { - "pagerank": 0.003617, + "pagerank": 0.003615, "inboundLinks": 1, "coveredBy": [ "document:apps/docs-next/content/docs/data/rag/rerank.mdx" @@ -40363,7 +40410,7 @@ "aliases": "area" }, "graph": { - "pagerank": 0.002371, + "pagerank": 0.002369, "inboundLinks": 1, "coveredBy": [ "document:apps/docs-next/content/docs/ui/index.mdx" @@ -40402,7 +40449,7 @@ "aliases": "area" }, "graph": { - "pagerank": 0.002786, + "pagerank": 0.002784, "inboundLinks": 1, "coveredBy": [ "document:apps/docs-next/content/docs/production/security/sandbox.mdx" @@ -40441,7 +40488,7 @@ "aliases": "area" }, "graph": { - "pagerank": 0.002371, + "pagerank": 0.002369, "inboundLinks": 1, "coveredBy": [ "document:apps/docs-next/content/docs/ui/index.mdx" @@ -40480,7 +40527,7 @@ "aliases": "area" }, "graph": { - "pagerank": 0.002371, + "pagerank": 0.002369, "inboundLinks": 1, "coveredBy": [ "document:apps/docs-next/content/docs/ui/index.mdx" @@ -40550,7 +40597,7 @@ "aliases": "area" }, "graph": { - "pagerank": 0.002786, + "pagerank": 0.002784, "inboundLinks": 1, "coveredBy": [ "document:apps/docs-next/content/docs/agents/tools/integrations-catalog.mdx" @@ -40640,7 +40687,7 @@ "aliases": "area" }, "graph": { - "pagerank": 0.003617, + "pagerank": 0.003615, "inboundLinks": 1, "coveredBy": [ "document:apps/docs-next/content/docs/production/observability/devtools.mdx" @@ -41466,7 +41513,7 @@ "aliases": "document unclassified" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -41552,7 +41599,7 @@ "aliases": "document unclassified" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -41605,7 +41652,7 @@ "aliases": "document unclassified" }, "graph": { - "pagerank": 0.005834, + "pagerank": 0.005829, "inboundLinks": 10, "coveredBy": [], "mentionedBy": [ @@ -41801,7 +41848,7 @@ "aliases": "document project" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -44162,7 +44209,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -46201,7 +46248,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -46343,7 +46390,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.003617, + "pagerank": 0.003615, "inboundLinks": 1, "coveredBy": [], "mentionedBy": [], @@ -47022,7 +47069,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -48414,7 +48461,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -49635,7 +49682,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.002371, + "pagerank": 0.002369, "inboundLinks": 1, "coveredBy": [], "mentionedBy": [], @@ -50253,7 +50300,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -50307,7 +50354,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -50353,7 +50400,7 @@ "aliases": "adapters document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -50704,7 +50751,7 @@ "aliases": "angular document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -50810,7 +50857,7 @@ "aliases": "cli document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -50956,7 +51003,7 @@ "aliases": "core document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -51127,7 +51174,7 @@ "aliases": "cross-platform document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -51243,7 +51290,7 @@ "aliases": "doc-bridge document agent" }, "graph": { - "pagerank": 0.002053, + "pagerank": 0.002052, "inboundLinks": 1, "coveredBy": [], "mentionedBy": [], @@ -51290,7 +51337,7 @@ "aliases": "eval-braintrust document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -51351,7 +51398,7 @@ "aliases": "eval document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -51578,7 +51625,7 @@ "aliases": "ink document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -51689,7 +51736,7 @@ "aliases": "integrations document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -51830,7 +51877,7 @@ "aliases": "mcp document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -51906,7 +51953,7 @@ "aliases": "memory document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -52077,7 +52124,7 @@ "aliases": "net document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -52138,7 +52185,7 @@ "aliases": "observability-langfuse document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -52204,7 +52251,7 @@ "aliases": "observability document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -52327,15 +52374,15 @@ ], "fields": { "title": "rag", - "headings": "Install Primary exports Document loaders Failure and cancellation contract Minimal example Related Source", + "headings": "Install Primary exports Document loaders Failure and cancellation contract Minimal example Related Source Partition isolation", "path": "apps/docs-next/content/docs/for-agents/rag.mdx", "symbols": "", "summary": "createRAG({ embed, store, chunkSize, chunkOverlap, topK, threshold }) — ingest(InputDocument[]) + retrieve({ query, messages }) + search(query, { topK?, threshold? }). InputDocument uses content, not text. Chunk defaults: 512 / 50.", - "body": "Install Primary exports - createRAG({ embed, store, chunkSize, chunkOverlap, topK, threshold }) — ingest(InputDocument[]) + retrieve({ query, messages }) + search(query, { topK?, threshold? }) . InputDocument uses content , not text . Chunk defaults: 512 / 50. - chunkText(text, { chunkSize, chunkOverlap, split? }) — lower-level splitter. split is (text: string) = string[] . - createRerankedRetriever(base, { candidatePool, topK, rerank }) — pluggable reranker (BM25 default). See . - createHybridRetriever(base, { vectorWeight?, bm25Weight?, topK?, candidatePool? }) — vector + BM25 hybrid; defaults to 0.6 / 0.4 , 20 candidates, and 5 results. - bm25Score , bm25Rerank — standalone helpers. - voyageReranker(config) — Voyage AI reranker. - jinaReranker(config) — Jina AI reranker. - RagError / RagErrorCodes — typed error (extends AgentsKitError ) thrown by loaders + rerankers; narrow on error.code ( AK RAG LOAD FAILED , AK RAG PEER MISSING , AK RAG RERANK FAILED ). Document loaders - loadUrl(url, { allowedOrigins }) — raw response text as InputDocument.content ; only HTTPS origins explicitly listed in allowedOrigins are accepted and redirects are rejected. loadGitHubFile(owner, repo, path, opts) , loadGitHubTree(owner, repo, { filter?, ... }) , loadNotionPage(pageId, { token }) , loadConfluencePage(pageId, { baseUrl, token?, authorization? }) , loadGoogleDriveFile(fileId, { accessToken }) , and loadPdf(url, { parsePdf }) return InputDocument[] . - Cloud storage: loadS3 , loadGcs , loadDropbox , loadOneDrive . All remote loader options support timeoutMs , maxResponseBytes , and signal ; S3 also supports an optional @aws-sdk/client-s3 peer or explicit command constructors. See . Failure and cancellation contract - Loader request, response-body, pagination, and total-download failures throw RagError with AK RAG LOAD FAILED . Tree loaders may return partial success only after at least one eligible document loaded. - Loader options accept signal?: AbortSignal ; Voyage and Jina reranker options accept the same additive field. - Notion and OneDrive follow provider pagination and reject repeated or missing continuation cursors instead of returning truncated content. - Scoreless Retriever results keep their order. If any score is present, all scores must be finite and the result is ordered descending; malformed mixed score sets throw. - Hybrid retrieval min-max normalizes candidate scores and normalizes relative weights before blending. Minimal example Related - — vector stores. - — embedders. Source - npm: https://www.npmjs.com/package/@agentskit/rag - repo: https://github.com/AgentsKit-io/agentskit/tree/main/packages/rag", + "body": "Install Primary exports - createRAG({ embed, store, chunkSize, chunkOverlap, topK, threshold }) — ingest(InputDocument[]) + retrieve({ query, messages }) + search(query, { topK?, threshold? }) . InputDocument uses content , not text . Chunk defaults: 512 / 50. - chunkText(text, { chunkSize, chunkOverlap, split? }) — lower-level splitter. split is (text: string) = string[] . - createRerankedRetriever(base, { candidatePool, topK, rerank }) — pluggable reranker (BM25 default). See . - createHybridRetriever(base, { vectorWeight?, bm25Weight?, topK?, candidatePool? }) — vector + BM25 hybrid; defaults to 0.6 / 0.4 , 20 candidates, and 5 results. - bm25Score , bm25Rerank — standalone helpers. - voyageReranker(config) — Voyage AI reranker. - jinaReranker(config) — Jina AI reranker. - RagError / RagErrorCodes — typed error (extends AgentsKitError ) thrown by loaders + rerankers; narrow on error.code ( AK RAG LOAD FAILED , AK RAG PEER MISSING , AK RAG RERANK FAILED ). Document loaders - loadUrl(url, { allowedOrigins }) — raw response text as InputDocument.content ; only HTTPS origins explicitly listed in allowedOrigins are accepted and redirects are rejected. loadGitHubFile(owner, repo, path, opts) , loadGitHubTree(owner, repo, { filter?, ... }) , loadNotionPage(pageId, { token }) , loadConfluencePage(pageId, { baseUrl, token?, authorization? }) , loadGoogleDriveFile(fileId, { accessToken }) , and loadPdf(url, { parsePdf }) return InputDocument[] . - Cloud storage: loadS3 , loadGcs , loadDropbox , loadOneDrive . All remote loader options support timeoutMs , maxResponseBytes , and signal ; S3 also supports an optional @aws-sdk/client-s3 peer or explicit command constructors. See . Failure and cancellation contract - Loader request, response-body, pagination, and total-download failures throw RagError with AK RAG LOAD FAILED . Tree loaders may return partial success only after at least one eligible document loaded. - Loader options accept signal?: AbortSignal ; Voyage and Jina reranker options accept the same additive field. - Notion and OneDrive follow provider pagination and reject repeated or missing continuation cursors instead of returning truncated content. - Scoreless Retriever results keep their order. If any score is present, all scores must be finite and the result is ordered descending; malformed mixed score sets throw. - Hybrid retrieval min-max normalizes candidate scores and normalizes relative weights before blending. Minimal example Related - — vector stores. - — embedders. Source - npm: https://www.npmjs.com/package/@agentskit/rag - repo: https://github.com/AgentsKit-io/agentskit/tree/main/packages/rag Partition isolation createPartitionedRAG({ ...ragConfig, partition, overfetchFactor?, onDiagnostics }) composes createRAG , stamps reserved akPartition metadata and namespaces chunk IDs. It forwards an equality filter, always post-filters, and makes one bounded search (factor 1–16, default 4). The required diagnostics callback reports recall: 'potentially-reduced' on every search; missing metadata fails closed. Stores must preserve IDs and metadata faithfully; untrusted writers/stores need a separate authorization boundary. See the .", "aliases": "rag document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -52371,6 +52418,11 @@ "id": "module:packages/rag/src/loaders/cloud.ts", "confidence": "observed" }, + { + "kind": "mentions-symbol", + "id": "module:packages/rag/src/rag.ts", + "confidence": "observed" + }, { "kind": "mentions-symbol", "id": "module:packages/rag/src/rerank.ts", @@ -52383,7 +52435,7 @@ } ] }, - "contentHash": "dc353acc87866efe6734707009f5d7e8eefaec120bab419576d4b2c670655d91", + "contentHash": "7b02340b897b303de7534f6a3029e651866ce17759ea6261d0eb35a0f69cbd88", "provenance": "observed", "confidence": "observed" }, @@ -52411,7 +52463,7 @@ "aliases": "react-native document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -52507,7 +52559,7 @@ "aliases": "react document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -52593,7 +52645,7 @@ "aliases": "runtime document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -52734,7 +52786,7 @@ "aliases": "sandbox document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -52815,7 +52867,7 @@ "aliases": "skills document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -52986,7 +53038,7 @@ "aliases": "solid document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -53062,7 +53114,7 @@ "aliases": "statechart document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -53138,7 +53190,7 @@ "aliases": "svelte document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -53224,7 +53276,7 @@ "aliases": "templates document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -53320,7 +53372,7 @@ "aliases": "tools document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -53621,7 +53673,7 @@ "aliases": "validation document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -53687,7 +53739,7 @@ "aliases": "vue document agent" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -53845,7 +53897,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.00528, + "pagerank": 0.005276, "inboundLinks": 2, "coveredBy": [], "mentionedBy": [], @@ -54814,7 +54866,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -55352,7 +55404,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -56171,7 +56223,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -56742,7 +56794,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -56791,7 +56843,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -57001,7 +57053,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -57306,7 +57358,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -57555,7 +57607,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -58427,7 +58479,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -58471,7 +58523,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -58554,7 +58606,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -59129,7 +59181,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -59206,7 +59258,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -59471,7 +59523,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -59710,7 +59762,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -59769,7 +59821,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -59838,7 +59890,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -60002,7 +60054,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -60061,7 +60113,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -60145,7 +60197,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -60249,7 +60301,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -60293,7 +60345,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -60427,7 +60479,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -60481,7 +60533,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -60565,7 +60617,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -60669,7 +60721,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -60840,15 +60892,15 @@ ], "fields": { "title": "rag.mdx", - "headings": "When to reach for it Install Hello world Surface Recipes Stability Related Source", + "headings": "When to reach for it Install Hello world Surface Recipes Stability Related Source Partition-bound retrieval", "path": "apps/docs-next/content/docs/reference/packages/rag.mdx", "symbols": "", "summary": "You want retrieval-augmented generation in a few lines.", - "body": "When to reach for it - You want retrieval-augmented generation in a few lines. - You need rerankers (BM25, Voyage, Jina, or a custom function) or hybrid vector+keyword search. - You want document loaders for URL / GitHub / Notion / Confluence / Google Drive / PDF / S3 / GCS / Dropbox / OneDrive. Install Hello world Surface - createRAG({ embed, store, chunkSize, chunkOverlap, topK, threshold }) . - chunkText — standalone splitter. - @agentskit/rag/markdown — parseNote · splitFrontmatter · extractWikilinks · stripWikilinks · sections · findSection · parseTable · normalize · plain for Markdown notes (frontmatter, wikilinks, heading sections, GitHub tables). - createRerankedRetriever · createHybridRetriever · bm25Score · bm25Rerank . - Loaders: loadUrl · loadGitHubFile · loadGitHubTree · loadNotionPage · loadConfluencePage · loadGoogleDriveFile · loadPdf · loadS3 · loadGcs · loadDropbox · loadOneDrive . Remote loaders use a finite timeout and response-size limit by default. Configure timeoutMs , maxResponseBytes , and signal when needed. loadUrl requires an HTTPS origin in allowedOrigins and rejects redirects. Recipes - - - - Stability - Version: 0.6.1 - Tier: beta - Contract: evolving - Roadmap: see for what this package needs to reach v1.0. Related - - - Source npm: · repo:", + "body": "When to reach for it - You want retrieval-augmented generation in a few lines. - You need rerankers (BM25, Voyage, Jina, or a custom function) or hybrid vector+keyword search. - You want document loaders for URL / GitHub / Notion / Confluence / Google Drive / PDF / S3 / GCS / Dropbox / OneDrive. Install Hello world Surface - createRAG({ embed, store, chunkSize, chunkOverlap, topK, threshold }) . - chunkText — standalone splitter. - @agentskit/rag/markdown — parseNote · splitFrontmatter · extractWikilinks · stripWikilinks · sections · findSection · parseTable · normalize · plain for Markdown notes (frontmatter, wikilinks, heading sections, GitHub tables). - createRerankedRetriever · createHybridRetriever · bm25Score · bm25Rerank . - Loaders: loadUrl · loadGitHubFile · loadGitHubTree · loadNotionPage · loadConfluencePage · loadGoogleDriveFile · loadPdf · loadS3 · loadGcs · loadDropbox · loadOneDrive . Remote loaders use a finite timeout and response-size limit by default. Configure timeoutMs , maxResponseBytes , and signal when needed. loadUrl requires an HTTPS origin in allowedOrigins and rejects redirects. Recipes - - - - Stability - Version: 0.6.1 - Tier: beta - Contract: evolving - Roadmap: see for what this package needs to reach v1.0. Related - - - Source npm: · repo: Partition-bound retrieval createPartitionedRAG composes createRAG without changing the core Retriever contract or existing createRAG behavior. Bind each instance to a non-empty partition at construction: Ingest overrides the reserved akPartition metadata field and namespaces chunk IDs with a JSON tuple, so identical document IDs in different partitions do not overwrite or delete each other's chunks. Document metadata, including documentId , remains available. Returned chunk IDs are opaque namespaced IDs. Every search forwards a core VectorFilter equality predicate and verifies partition metadata locally, rejecting foreign or missing metadata even when a store ignores filters. The wrapper makes one store search, requests at most topK overfetchFactor candidates, examines at most that many, and returns at most topK matches. The factor is an integer from 1 to 16 (default 4); unsafe candidate limits throw. Per-call search limits and thresholds still work. The required onDiagnostics callback runs for every successful store search, including empty results, with partition , requestedTopK , candidateLimit , examined , rejected , returned , and recall: 'potentially-reduced' . This is conservative: bounded over-fetch can miss matching chunks, and a store can internally filter a bounded global candidate pool without revealing exclusions (as fileVectorMemory does). Short results never justify returning another partition's chunks. Callback exceptions reject the search. Stores must faithfully preserve chunk metadata and IDs. Missing metadata fails closed, but fabricated or incorrectly reassigned partition metadata cannot be detected. This is retrieval isolation, not an authorization boundary against untrusted writers or stores. Use physically separate stores or a backend with trusted partition enforcement when that boundary or exhaustive recall is needed. Backend-internal over-fetch is outside the wrapper's candidate bound.", "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -60864,6 +60916,16 @@ "id": "package:@agentskit/rag", "confidence": "observed" }, + { + "kind": "mentions-symbol", + "id": "module:packages/core/src/types/memory.ts", + "confidence": "observed" + }, + { + "kind": "mentions-symbol", + "id": "module:packages/memory/src/file-vector.ts", + "confidence": "observed" + }, { "kind": "mentions-symbol", "id": "module:packages/rag/src/loaders/cloud.ts", @@ -60879,6 +60941,16 @@ "id": "module:packages/rag/src/markdown.ts", "confidence": "observed" }, + { + "kind": "mentions-symbol", + "id": "module:packages/rag/src/partitioned.ts", + "confidence": "observed" + }, + { + "kind": "mentions-symbol", + "id": "module:packages/rag/src/rag.ts", + "confidence": "observed" + }, { "kind": "mentions-symbol", "id": "module:packages/rag/src/rerank.ts", @@ -60886,7 +60958,7 @@ } ] }, - "contentHash": "55709f46fa5535116f04d92d25cc2a103f041ea8802845de830e8eb8c22f0d86", + "contentHash": "c76ee7a3326f5d0fc84ed374abf4e6d79b4649dd329eb731c722a00e9455df5c", "provenance": "observed", "confidence": "observed" }, @@ -60912,7 +60984,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -60956,7 +61028,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -61179,7 +61251,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -61268,7 +61340,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -61327,7 +61399,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -61396,7 +61468,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -61440,7 +61512,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -61524,7 +61596,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -61568,7 +61640,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -61672,7 +61744,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -61891,7 +61963,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -62676,7 +62748,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.003617, + "pagerank": 0.003615, "inboundLinks": 1, "coveredBy": [], "mentionedBy": [], @@ -65651,7 +65723,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -66494,7 +66566,7 @@ "aliases": "document project" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -66656,7 +66728,7 @@ "aliases": "document human" }, "graph": { - "pagerank": 0.003617, + "pagerank": 0.003615, "inboundLinks": 1, "coveredBy": [], "mentionedBy": [], @@ -66701,7 +66773,7 @@ "aliases": "document project" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -67359,7 +67431,7 @@ "aliases": "document project" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -67964,7 +68036,7 @@ "aliases": "document project" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 0, "coveredBy": [], "mentionedBy": [], @@ -68076,7 +68148,7 @@ "aliases": "document unclassified" }, "graph": { - "pagerank": 0.001955, + "pagerank": 0.001954, "inboundLinks": 3, "coveredBy": [], "mentionedBy": [ @@ -68360,7 +68432,7 @@ "aliases": "document unclassified" }, "graph": { - "pagerank": 0.007709, + "pagerank": 0.007704, "inboundLinks": 8, "coveredBy": [], "mentionedBy": [ @@ -68461,7 +68533,7 @@ "aliases": "document unclassified" }, "graph": { - "pagerank": 0.007555, + "pagerank": 0.00755, "inboundLinks": 3, "coveredBy": [], "mentionedBy": [ @@ -68508,11 +68580,11 @@ "path": "CONTRIBUTING.md", "symbols": "", "summary": "Thanks for being here. This guide gets you from git clone to merged PR with the least friction we know how to provide.", - "body": "Contributing to AgentsKit Thanks for being here. This guide gets you from git clone to merged PR with the least friction we know how to provide. Read first : . Every change is measured against the ten principles in there. If you're proposing something that conflicts with a principle, that's an , not a PR. --- Where to ask what Kind of thing Best place --- --- Bug — reproducible Feature you want Docs problem Non-trivial design proposal , then a PR in /rfcs/ General question / \"how do I\" Security disclosure — private channel --- Setup in 5 minutes Prerequisites - Node.js 22+ (LTS recommended; Node 25 known-broken on the legacy Docusaurus app — use apps/docs-next instead) - pnpm 10+ ( npm install -g pnpm if missing) - git with a global identity configured Clone and install That's it. The whole monorepo is now linked. Verify everything works Run the docs site locally The legacy Docusaurus site is still available during the migration: --- Repo layout Every package has a focused CONVENTIONS.md at its root explaining what belongs there, what doesn't, and how to add a new thing. Read it before opening a PR that touches that package. --- Making a change 1. Pick or open an issue Don't surprise reviewers. Either: - Pick an (look for or ), comment that you're taking it. - Browse the — anything in Ready is fair game to claim. - Or open a new issue describing what you want to do before writing code, especially if the change touches a contract or a public API. 2. Branch and write code Branch naming is informal but area/short-slug (e.g. adapters/add-mistral , docs/typos-in-recipes ) helps scanning. Reuse first. Before adding a new package or dependency, check the existing @agentskit/ packages, then established libraries or services. Write new code only when neither fits, and say why in the PR's Reuse section. 3. Write tests first when possible Each package has a vitest.config.ts with a per-package coverage threshold. CI blocks merges that drop below the threshold (see and the coverage workflow). A new feature without a test will not pass review. Use the shared, framework-agnostic helpers from @agentskit/cross-platform/testing for fetch stubs, JSON responses, and temporary directories. When you touch an older test that defines one of these helpers locally, migrate it to the shared surface in that change. Avoid broad test rewrites; untouched tests can move in later slices. 4. Type-check, gates, and bundle-check pnpm check:quality-gates runs every structural gate (typed errors, named exports, no any , file-size budgets, src↔test parity, for-agents coverage, ADR/RFC index sync, locale parity). pnpm check:all adds typecheck + build + test. Husky runs the commit-message check before each commit and the gates + typecheck + build before each push. @agentskit/core is capped at 10KB gzipped by . The size workflow blocks PRs that exceed any package's budget. 5. Add a changeset (any user-facing change) This walks you through which packages are affected and what kind of bump (patch / minor / major). The resulting .changeset/ .md file is part of your PR. The release workflow consumes it later. A change is user-facing if it adds or modifies a public export, changes runtime behavior, or affects the docs surface a consumer reads. 6. Open a PR Use the PR template — it auto-fills with the checklist we expect: - [ ] Reuse: existing packages/libraries checked; new code or dependency justified - [ ] Tests added or updated - [ ] Types check - [ ] Bundle size within budget - [ ] Changeset created - [ ] Docs updated - [ ] Manifesto principles respected - [ ] Screenshots or demo for UI changes Title: console.warn(diagnostics), +}) +await rag.ingest([{ id: 'guide', content: 'Team A guide' }]) +const hits = await rag.retrieve({ query: 'guide' }) +``` + +Ingest overrides the reserved `_akPartition` metadata field and namespaces chunk +IDs with a JSON tuple, so identical document IDs in different partitions do not +overwrite or delete each other's chunks. Document metadata, including +`documentId`, remains available. Returned chunk IDs are opaque namespaced IDs. +Every search forwards a core `VectorFilter` equality predicate and verifies +partition metadata locally, rejecting foreign or missing metadata even when a +store ignores filters. The wrapper makes one store search, requests at most +`topK * overfetchFactor` candidates, examines at most that many, and returns at +most `topK` matches. The factor is an integer from 1 to 16 (default 4); unsafe +candidate limits throw. Per-call `search` limits and thresholds still work. + +The required `onDiagnostics` callback runs for every successful store search, +including empty results, with `partition`, `requestedTopK`, `candidateLimit`, +`examined`, `rejected`, `returned`, and `recall: 'potentially-reduced'`. This is +conservative: bounded over-fetch can miss matching chunks, and a store can +internally filter a bounded global candidate pool without revealing exclusions +(as `fileVectorMemory` does). Short results never justify returning another +partition's chunks. Callback exceptions reject the search. + +Stores must faithfully preserve chunk metadata and IDs. Missing metadata fails +closed, but fabricated or incorrectly reassigned partition metadata cannot be +detected. This is retrieval isolation, not an authorization boundary against +untrusted writers or stores. Use physically separate stores or a backend with +trusted partition enforcement when that boundary or exhaustive recall is needed. +Backend-internal over-fetch is outside the wrapper's candidate bound. diff --git a/docs/architecture/adrs/0043-partition-bound-rag.md b/docs/architecture/adrs/0043-partition-bound-rag.md new file mode 100644 index 000000000..2a01a6853 --- /dev/null +++ b/docs/architecture/adrs/0043-partition-bound-rag.md @@ -0,0 +1,31 @@ +# ADR 0043 — Partition-bound RAG composition + +- **Status**: Proposed (implementation authorized; evidence review remains external) +- **Date**: 2026-10-07 + +## Context + +Shared vector stores need retrieval isolation without changing the stable +Retriever request contract in ADR 0004 or existing createRAG behavior. + +## Decision + +Add createPartitionedRAG in @agentskit/rag by composing createRAG with a +VectorMemory wrapper. Bind a non-empty partition at construction, stamp the +reserved _akPartition metadata key, and namespace chunk IDs using a JSON tuple. +Forward the core equality filter and independently reject foreign or missing +partition metadata. Namespace deletion IDs identically to insertion IDs. + +Request one bounded candidate pool (topK times a configurable integer factor +from 1 to 16, default 4), cap local examination, and return at most topK matches. +Require a diagnostics callback on every search to report potentially reduced +recall, including when the store internally filters a bounded global pool. + +## Consequences + +No core contract, dependency, or vector backend changes. Existing RAG chunking, +embedding, score validation, source projection and replacement logic are reused. +Stores must preserve metadata and IDs faithfully; missing metadata fails closed. +Mislabelled metadata and untrusted writers require a separate authorization +boundary. Backend-internal candidate work is outside the wrapper bound. Full +recall requires an appropriate backend or physically separate stores. diff --git a/docs/architecture/adrs/README.md b/docs/architecture/adrs/README.md index 3a70b59c7..cf7d442ef 100644 --- a/docs/architecture/adrs/README.md +++ b/docs/architecture/adrs/README.md @@ -78,5 +78,6 @@ Do NOT write an ADR for: routine features, bug fixes, internal refactors, or any | [0041](./0041-adapter-content-serialization.md) | Serialize attachments at the provider boundary | Proposed | | [0042](./0042-durable-tool-decisions.md) | Durable tool decisions in core 1.x | Proposed | +| [0043](./0043-partition-bound-rag.md) | Partition-bound RAG composition | Proposed | The 6 core contracts are formalized. Future ADRs will cover specific decisions (semver policy, licensing strategy, etc.) rather than additional foundational contracts. diff --git a/docs/stability/public-api-v1.json b/docs/stability/public-api-v1.json index c3932e28c..de9e9b762 100644 --- a/docs/stability/public-api-v1.json +++ b/docs/stability/public-api-v1.json @@ -8220,6 +8220,12 @@ "value" ] }, + { + "name": "createPartitionedRAG", + "kinds": [ + "value" + ] + }, { "name": "createRAG", "kinds": [ @@ -8370,6 +8376,18 @@ "type" ] }, + { + "name": "PartitionedRAGConfig", + "kinds": [ + "type" + ] + }, + { + "name": "PartitionSearchDiagnostics", + "kinds": [ + "type" + ] + }, { "name": "PdfLoaderOptions", "kinds": [ diff --git a/packages/rag/CONVENTIONS.md b/packages/rag/CONVENTIONS.md index 377030aef..9b5e5cc8e 100644 --- a/packages/rag/CONVENTIONS.md +++ b/packages/rag/CONVENTIONS.md @@ -106,3 +106,11 @@ The core `createRAG` is intentionally small. Keep it that way. - [ ] `RAG` still extends `Retriever` - [ ] New features added by composition where possible - [ ] Chunker changes are backward-compatible (existing indexed docs still retrieve) + +## Partition composition + +`createPartitionedRAG` wraps a `VectorMemory` and reuses `createRAG` unchanged. +Reserved `_akPartition` metadata plus namespaced chunk IDs isolate reads and +replacement deletion. Always post-filter, even if the store accepts filters. +Bound candidates and report potentially reduced recall via required diagnostics. +See [ADR 0043](../../docs/architecture/adrs/0043-partition-bound-rag.md). diff --git a/packages/rag/README.md b/packages/rag/README.md index 8f1c53f05..e38caf6f6 100644 --- a/packages/rag/README.md +++ b/packages/rag/README.md @@ -164,3 +164,50 @@ MIT — see [LICENSE](../../LICENSE). ## Contributing See [CONTRIBUTING.md](../../CONTRIBUTING.md) and the monorepo [LICENSE](../../LICENSE). + +## Partition-bound retrieval + +`createPartitionedRAG` composes `createRAG` without changing the core Retriever +contract or existing `createRAG` behavior. Bind each instance to a non-empty +partition at construction: + +```ts +import { createPartitionedRAG } from '@agentskit/rag' + +const rag = createPartitionedRAG({ + store, + embed, + partition: 'team-a', + topK: 5, + overfetchFactor: 4, + onDiagnostics: diagnostics => console.warn(diagnostics), +}) +await rag.ingest([{ id: 'guide', content: 'Team A guide' }]) +const hits = await rag.retrieve({ query: 'guide' }) +``` + +Ingest overrides the reserved `_akPartition` metadata field and namespaces chunk +IDs with a JSON tuple, so identical document IDs in different partitions do not +overwrite or delete each other's chunks. Document metadata, including +`documentId`, remains available. Returned chunk IDs are opaque namespaced IDs. +Every search forwards a core `VectorFilter` equality predicate and verifies +partition metadata locally, rejecting foreign or missing metadata even when a +store ignores filters. The wrapper makes one store search, requests at most +`topK * overfetchFactor` candidates, examines at most that many, and returns at +most `topK` matches. The factor is an integer from 1 to 16 (default 4); unsafe +candidate limits throw. Per-call `search` limits and thresholds still work. + +The required `onDiagnostics` callback runs for every successful store search, +including empty results, with `partition`, `requestedTopK`, `candidateLimit`, +`examined`, `rejected`, `returned`, and `recall: 'potentially-reduced'`. This is +conservative: bounded over-fetch can miss matching chunks, and a store can +internally filter a bounded global candidate pool without revealing exclusions +(as `fileVectorMemory` does). Short results never justify returning another +partition's chunks. Callback exceptions reject the search. + +Stores must faithfully preserve chunk metadata and IDs. Missing metadata fails +closed, but fabricated or incorrectly reassigned partition metadata cannot be +detected. This is retrieval isolation, not an authorization boundary against +untrusted writers or stores. Use physically separate stores or a backend with +trusted partition enforcement when that boundary or exhaustive recall is needed. +Backend-internal over-fetch is outside the wrapper's candidate bound. diff --git a/packages/rag/src/index.ts b/packages/rag/src/index.ts index e8f8c56b1..198d7c357 100644 --- a/packages/rag/src/index.ts +++ b/packages/rag/src/index.ts @@ -1,4 +1,6 @@ export { createRAG } from './rag' +export { createPartitionedRAG } from './partitioned' +export type { PartitionedRAGConfig, PartitionSearchDiagnostics } from './partitioned' export { RagError, RagErrorCodes } from './errors' export type { RagErrorCode } from './errors' export { chunkText } from './chunker' diff --git a/packages/rag/src/partitioned.ts b/packages/rag/src/partitioned.ts new file mode 100644 index 000000000..530bf33ad --- /dev/null +++ b/packages/rag/src/partitioned.ts @@ -0,0 +1,66 @@ +import type { VectorFilter, VectorMemory } from '@agentskit/core' +import { createRAG } from './rag' +import type { RAG, RAGConfig } from './types' + +/** Search evidence; bounded candidate retrieval can reduce recall even with store filters. */ +export interface PartitionSearchDiagnostics { + partition: string + requestedTopK: number + candidateLimit: number + examined: number + rejected: number + returned: number + recall: 'potentially-reduced' +} + +/** Construction-bound partition and mandatory reporting of bounded retrieval. */ +export interface PartitionedRAGConfig extends RAGConfig { + partition: string + /** Integer from 1 to 16, default 4. Applies even if the store ignores filters. */ + overfetchFactor?: number + onDiagnostics: (diagnostics: PartitionSearchDiagnostics) => void +} + +/** Compose createRAG with metadata isolation; stores must faithfully preserve metadata. */ +export function createPartitionedRAG(config: PartitionedRAGConfig): RAG { + const { partition, store, onDiagnostics, overfetchFactor = 4 } = config + if (typeof partition !== 'string' || partition.length === 0) { + throw new TypeError('createPartitionedRAG requires a non-empty partition') + } + if (!Number.isInteger(overfetchFactor) || overfetchFactor < 1 || overfetchFactor > 16) { + throw new TypeError('overfetchFactor must be an integer from 1 to 16') + } + if (typeof onDiagnostics !== 'function') { + throw new TypeError('createPartitionedRAG requires onDiagnostics') + } + const namespace = (id: string): string => JSON.stringify(['agentskit.rag.partition', partition, id]) + const filter: VectorFilter = { _akPartition: { $eq: partition } } + const boundStore: VectorMemory = { + region: store.region, + store: docs => store.store(docs.map(doc => ({ + ...doc, + id: namespace(doc.id), + metadata: { ...doc.metadata, _akPartition: partition }, + }))), + async search(embedding, options) { + const topK = options?.topK ?? 5 + const candidateLimit = topK * overfetchFactor + if (!Number.isSafeInteger(candidateLimit) || candidateLimit < 1) { + throw new TypeError('partition candidate limit must be a positive safe integer') + } + const candidates = (await store.search(embedding, { + ...options, topK: candidateLimit, filter, + })).slice(0, candidateLimit) + const matches = candidates.filter(doc => doc.metadata?._akPartition === partition) + const results = matches.slice(0, topK) + onDiagnostics({ + partition, requestedTopK: topK, candidateLimit, examined: candidates.length, + rejected: candidates.length - matches.length, returned: results.length, + recall: 'potentially-reduced', + }) + return results + }, + } + if (store.delete) boundStore.delete = ids => store.delete!(ids.map(namespace)) + return createRAG({ ...config, store: boundStore }) +} diff --git a/packages/rag/tests/partitioned.test.ts b/packages/rag/tests/partitioned.test.ts new file mode 100644 index 000000000..83ef683b1 --- /dev/null +++ b/packages/rag/tests/partitioned.test.ts @@ -0,0 +1,113 @@ +import { mkdtemp, rm } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { describe, expect, it, vi } from 'vitest' +import type { VectorDocument, VectorMemory } from '@agentskit/core' +import { fileVectorMemory } from '../../memory/src/file-vector' +import { createPartitionedRAG } from '../src/index' + +const embed = async (): Promise => [1, 0, 0] + +function memory(honourFilter: boolean): VectorMemory { + const docs = new Map() + return { + store: input => { input.forEach(doc => docs.set(doc.id, doc)) }, + delete: ids => { ids.forEach(id => docs.delete(id)) }, + search: vi.fn(async (_embedding, options) => [...docs.values()] + .filter(doc => !honourFilter || JSON.stringify(options?.filter) === JSON.stringify({ + _akPartition: { $eq: doc.metadata?._akPartition }, + })) + .slice(0, options?.topK) + .map(doc => ({ ...doc, score: 1 }))), + } +} + +describe('createPartitionedRAG', () => { + it.each([true, false])('isolates same-ID ingest, search, retrieve and deletion (filter=%s)', async honourFilter => { + const store = memory(honourFilter) + const onDiagnostics = vi.fn() + const a = createPartitionedRAG({ store, embed, partition: 'a', onDiagnostics, split: text => text.split('|') }) + const b = createPartitionedRAG({ store, embed, partition: 'b', onDiagnostics }) + await a.ingest([{ id: 'same', content: 'A|old', metadata: { _akPartition: 'b', tag: 'kept' } }]) + await b.ingest([{ id: 'same', content: 'B' }]) + expect((await a.search('query')).map(doc => doc.content)).toEqual(['A', 'old']) + expect((await b.retrieve({ query: 'query', messages: [] })).map(doc => doc.content)).toEqual(['B']) + await a.ingest([{ id: 'same', content: 'new' }]) + expect((await a.retrieve({ query: 'query' })).map(doc => doc.content)).toEqual(['new']) + expect((await b.search('query')).map(doc => doc.content)).toEqual(['B']) + expect(store.search).toHaveBeenCalledWith([1, 0, 0], { + topK: 20, threshold: 0, filter: { _akPartition: { $eq: 'a' } }, + }) + await a.ingest([{ id: 'same', content: '' }]) + expect(await a.search('query')).toEqual([]) + expect((await b.search('query'))[0]?.content).toBe('B') + }) + + it('bounds one search and reports reduced recall when foreign candidates crowd out matches', async () => { + const store = memory(false) + const onDiagnostics = vi.fn() + const a = createPartitionedRAG({ store, embed, partition: 'a', topK: 1, overfetchFactor: 2, onDiagnostics }) + const b = createPartitionedRAG({ store, embed, partition: 'b', onDiagnostics }) + await b.ingest([{ content: 'foreign 1' }, { content: 'foreign 2' }]) + await a.ingest([{ content: 'hidden match' }]) + expect(await a.search('query', { threshold: 0.4 })).toEqual([]) + expect(store.search).toHaveBeenCalledTimes(1) + expect(onDiagnostics).toHaveBeenCalledWith({ partition: 'a', requestedTopK: 1, + candidateLimit: 2, examined: 2, rejected: 2, returned: 0, recall: 'potentially-reduced' }) + }) + + it('fails closed on missing metadata and caps a store that ignores topK', async () => { + const onDiagnostics = vi.fn() + const rag = createPartitionedRAG({ embed, partition: 'a', topK: 1, overfetchFactor: 1, onDiagnostics, + store: { store: async () => undefined, search: async () => [ + { id: 'missing', content: 'unknown' }, + { id: 'outside-bound', content: 'A', metadata: { _akPartition: 'a' } }, + ] } }) + expect(await rag.retrieve({ query: 'query' })).toEqual([]) + expect(onDiagnostics).toHaveBeenCalledWith(expect.objectContaining({ examined: 1, rejected: 1 })) + }) + + it('validates construction and unsafe per-search limits', async () => { + const config = { embed, store: memory(false), partition: 'a', onDiagnostics: vi.fn() } + expect(() => createPartitionedRAG({ ...config, partition: '' })).toThrow(TypeError) + for (const overfetchFactor of [0, 17, 1.5, NaN, Infinity]) { + expect(() => createPartitionedRAG({ ...config, overfetchFactor })).toThrow(TypeError) + } + expect(() => createPartitionedRAG({ ...config, onDiagnostics: undefined! })).toThrow(TypeError) + await expect(createPartitionedRAG(config).search('query', { topK: Number.MAX_VALUE })).rejects.toThrow(TypeError) + }) + + it('preserves metadata and source, and propagates store and diagnostics failures', async () => { + const store = memory(false) + const onDiagnostics = vi.fn() + const rag = createPartitionedRAG({ embed, store, partition: 'a', onDiagnostics }) + await rag.ingest([{ content: 'A', source: 'guide', metadata: { tag: 'kept' } }]) + expect((await rag.search('query'))[0]).toMatchObject({ source: 'guide', + metadata: { tag: 'kept', _akPartition: 'a' }, score: 1 }) + onDiagnostics.mockImplementationOnce(() => { throw new Error('report failed') }) + await expect(rag.search('query')).rejects.toThrow('report failed') + vi.mocked(store.search).mockRejectedValueOnce(new Error('store failed')) + await expect(rag.search('query')).rejects.toThrow('store failed') + }) + + it('isolates a real fileVectorMemory index across partitions and reopen', async () => { + const path = await mkdtemp(join(tmpdir(), 'ak-rag-partitions-')) + try { + const store = fileVectorMemory({ path }) + const onDiagnostics = vi.fn() + const a = createPartitionedRAG({ store, embed, partition: 'a', onDiagnostics }) + const b = createPartitionedRAG({ store, embed, partition: 'b', onDiagnostics }) + await a.ingest([{ id: 'same', content: 'A', metadata: { _akPartition: 'b' } }]) + await b.ingest([{ id: 'same', content: 'B' }]) + expect((await a.retrieve({ query: 'query' })).map(doc => doc.content)).toEqual(['A']) + expect((await b.search('query')).map(doc => doc.content)).toEqual(['B']) + const reopened = createPartitionedRAG({ store: fileVectorMemory({ path }), embed, partition: 'b', onDiagnostics }) + expect((await reopened.search('query')).map(doc => doc.content)).toEqual(['B']) + await a.ingest([{ id: 'same', content: '' }]) + expect(await a.search('query')).toEqual([]) + expect((await b.search('query')).map(doc => doc.content)).toEqual(['B']) + } finally { + await rm(path, { recursive: true, force: true }) + } + }) +}) diff --git a/readme-standard-v1.json b/readme-standard-v1.json index cb12d04d9..d08726b44 100644 --- a/readme-standard-v1.json +++ b/readme-standard-v1.json @@ -1221,7 +1221,7 @@ "docs/STABILITY.md", "scripts/readme-fixtures.test.mjs" ], - "sourceHash": "sha256:df18b449d6b3ab6cfeb6bb69a6b3f5ffd1eba093aa5db09ebb3791e372b62d1f" + "sourceHash": "sha256:9c2d77b83a758d76e8dae8d195f1a6721f35acf4577352264eaa9320e6be4bfb" }, "exceptions": [] },