Add knowledgebases

Knowledgebases give bots RAG over documents, scoped like extensions: to
particular bots, to a workspace, or to every bot, with their own button
beside Tools in the composer. Documents are chunked and embedded by the
file-ingest worker with the install's embedding model, which is now an
install-wide setting rather than a per-bot one.

- Word search runs on an FTS5 index ranked by BM25, with a scored LIKE
  fallback for scripts written without spaces.
- Changing the embedding model re-indexes documents automatically.
- Ingest jobs wait without spending attempts while every embedding model
  rests, fail at once on errors retrying can't fix, and keep passages
  searchable by words when the model refuses.
- Settings > Knowledgebases shows each knowledgebase's and document's
  index state: spinner, warning, grey check (words only) or green check.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-26 16:41:01 +01:00
co-authored by Claude Opus 5.5
parent 2259b6895f
commit e7b23b7454
111 changed files with 4573 additions and 148 deletions
+8 -1
View File
@@ -22,6 +22,7 @@ import runTextLoop from "@/src/harness/text/run-text-loop";
import openComputerTools from "@/src/harness/tools/computer/open-computer-tools";
import openBotExtensions, { type OpenBotExtensions } from "@/src/harness/tools/extensions/open-bot-extensions";
import openBotSkills, { type OpenBotSkills } from "@/src/harness/tools/skills/open-bot-skills";
import openBotKnowledgebases from "@/src/harness/tools/knowledgebases/open-bot-knowledgebases";
import grabBotTools from "@/src/harness/tools/bot/grab-bot-tools";
import grabBotSecretTools from "@/src/harness/tools/bot/secrets/grab-bot-secret-tools";
import createWorkspaceTool from "@/src/harness/tools/bot/create-workspace-tool";
@@ -242,6 +243,11 @@ export default async function runBotTask({
taken: [...tools, ...extensions.tools].map((tool) => tool.name),
});
const knowledge = openBotKnowledgebases({
knowledgebases: extensions.knowledgebases,
taken: [...tools, ...extensions.tools, ...skills.tools].map((tool) => tool.name),
});
const result = await runTextLoop({
system: [
prompt,
@@ -277,6 +283,7 @@ export default async function runBotTask({
.join("\n")}`
: "",
skills.note,
knowledge.note,
extensions.note,
]
.filter(Boolean)
@@ -291,7 +298,7 @@ export default async function runBotTask({
}),
},
],
tools: [...tools, ...extensions.tools, ...skills.tools],
tools: [...tools, ...extensions.tools, ...skills.tools, ...knowledge.tools],
max_steps: await grabHarnessMaxToolSteps(),
bot_id: bot.id,
workspace_id,