botruntime
Данные

Базы знаний (RAG)

RAG-слой платформы: объявляете Knowledge и источники данных, поиск сам вплетается в агентный цикл, ответы несут обязательные цитаты, пассажи можно наполнять программно.

База знаний — индексированный корпус документов. Агент ищет по ней семантически: по смыслу, а не по подстроке.

Собирается в три приёма. Объявите базу примитивом Knowledge. Подключите источники (DataSource). Передайте базу в агентный цикл. Дальше платформа делает всё сама: даёт агенту инструмент поиска, подставляет найденные пассажи в контекст, требует ссылки на источники.

Модель ничего не «помнит» о ваших документах. Она обращается к базе на каждый запрос, где это уместно. Так знания (данные, которые меняются) отделяются от логики бота (код, который вы ревьюите).

Объявите базу знаний

Knowledge описывает именованный корпус и его источники. Держите объявление в src/knowledge/index.ts.

src/knowledge/index.ts
import { Knowledge, DataSource } from '@holocronlab/botruntime-runtime'

const guides = DataSource.Directory.fromPath('src/knowledge', {
  id: 'guides',
  filter: (filePath) => filePath.endsWith('.md') || filePath.endsWith('.pdf'),
})

export const HelpDesk = new Knowledge({
  name: 'help-desk',
  description: 'Регламенты поддержки, тарифы и ответы на частые вопросы.',
  sources: [guides],
})

Поля Knowledge:

ПолеТипОбязательноОписание
namestringдаУникальное имя базы в пределах бота. По нему агент выбирает, где искать.
descriptionstringнетОдна фраза «что здесь лежит». Попадает в подсказку инструменту поиска — пишите содержательно.
sourcesDataSource[]даОдин или несколько источников данных (см. ниже).

description — не украшение. Агент читает описания всех баз, чтобы решить, стоит ли вообще искать и в какой базе. Расплывчатое описание — это пропущенные обращения к знаниям.

Подключите источники

Источник (DataSource) — откуда платформа берёт документы и как их обновляет. Импортируйте DataSource из рантайма. Выбирайте фабрику под тип источника.

Каталог с файлами

DataSource.Directory.fromPath(path, options) индексирует локальные файлы проекта (.md, .pdf и другие). Путь резолвится относительно корня агент-проекта.

src/knowledge/index.ts
const policies = DataSource.Directory.fromPath('src/knowledge/policies', {
  id: 'policies',
  filter: (filePath) => filePath.endsWith('.md'),
})
ОпцияТипОписание
idstringСтабильный идентификатор источника внутри базы. По умолчанию выводится из пути.
filter(filePath: string) => booleanОтбор файлов. Вернувшие false не индексируются.

Каталог поддерживает только filter. Преобразование содержимого перед индексацией (transform) есть у источников-таблиц и сайтов, но не у каталога.

Каталоги индексируются на этапе разработки и деплоя, а не в рантайме. При brt dev и brt deploy --adk платформа сканирует папку, считает sha256 каждого файла и заливает только изменившиеся. Задеплоенный бот в production уже ищет по готовому индексу — сканировать локальную ФС ему нечем. Поменяли файлы знаний — повторите явный production deploy.

Сайт

DataSource.Website тянет содержимое веб-страниц. Выбирайте фабрику по способу обхода:

src/knowledge/index.ts
const docs = DataSource.Website.fromSitemap('https://example.com/sitemap.xml', {
  id: 'public-docs',
})
  • DataSource.Website.fromSitemap(sitemapUrl, options) — из карты сайта.
  • DataSource.Website.fromUrls(urls, options) — явный список URL.
  • DataSource.Website.fromLlmsTxt(llmsTxtUrl, options) — из файла llms.txt.

Опции обхода: maxPages (1–50000), maxDepth (1–20), filter (по context.url).

Таблица

DataSource.Table.fromTable(table, options) строит источник поверх вашей таблицы. Табличные записи попадают в семантический поиск.

src/knowledge/index.ts
import { FaqTable } from '../tables'

const faq = DataSource.Table.fromTable(FaqTable, {
  id: 'faq',
  transform: ({ row }) => `Вопрос: ${row.question}\nОтвет: ${row.answer}`,
})

transform превращает строку таблицы в текст для индексации. filter отсеивает строки.

Источник-таблица пока экспериментальный и не доведён до конца. Для продакшена опирайтесь на каталог и сайт. Таблицу держите как forward-looking паттерн.

Подключите базу к агенту

Передайте базы в execute({ knowledge }). Агентный цикл сам получит инструмент поиска. Регистрировать вручную ничего не нужно.

src/conversations/index.ts
import { Conversation } from '@holocronlab/botruntime-runtime'
import { HelpDesk } from '../knowledge'

export default new Conversation({
  channel: '*',
  handler: async ({ execute }) => {
    await execute({
      instructions:
        'Ты — оператор поддержки. Отвечай по-русски, кратко и по существу. ' +
        'Если ответа нет в базе знаний — честно скажи, что не знаешь.',
      knowledge: [HelpDesk],
    })
  },
})

Что платформа делает под капотом, когда в execute передан непустой knowledge:

Добавляет инструмент search_knowledge. В его описание попадают имена и description всех переданных баз, чтобы модель понимала, где что искать.

Дописывает инструкции. К вашим instructions добавляется блок с перечнем баз и указанием предпочитать знания из них общей эрудиции модели.

Возвращает пассажи с цитатами. Каждый найденный пассаж приходит модели обёрнутым в тег-источник (с file и title). Модель обязана проставить эти цитаты инлайн в ответе.

Ведёт себя честно при пустом результате. Если по запросу ничего не нашлось, инструмент прямо велит модели не выдумывать ответ, а сказать, что данных нет.

Цитаты — часть контракта поиска. Если пассажи найдены, модель добавляет ссылки на источники. Проверяемые факты должны опираться на цитату из документа.

Как устроено хранилище

При индексации документ режется на пассажи — короткие фрагменты с эмбеддингами. Поиск семантический. Запрос сравнивается с векторами пассажей, наверх всплывают близкие по смыслу, а не по словам. Пассажи и метаданные живут на стороне платформы. Бот обращается к ним через API.

Искать можно и прямо из кода бота — через client.searchFiles. Он ищет по всему файловому хранилищу бота и возвращает пассажи с метаданными для своей обработки:

src/actions/lookup.ts
import { client } from '@holocronlab/botruntime-runtime'

const { passages } = await client.searchFiles({
  query: 'как вернуть предоплату',
  limit: 10,
  contextDepth: 2,
})

for (const p of passages) {
  console.log(p.file.key, '→', p.content)
}

Каждый пассаж несёт content, score (близость), meta (type, pageNumber, position) и file (key, contentType, tags). tags в запросе фильтруют по плоским строковым тегам файлов. contextDepth (0–10) приклеивает соседние пассажи к каждому попаданию.

API базы знаний

Базы знаний и пассажи доступны и по admin-API — для программного наполнения и инспекции. Все эндпоинты bot-scoped. Авторизация api-ключом бота: Authorization: Bearer <api-key>.

Создать базу знаний

POST /v1/files/knowledge-bases

ПараметрТипОбязательноОписание
namestringдаИмя базы.
tagsobjectнетПлоская карта строковых тегов для фильтрации.
terminal
curl -X POST https://botruntime.ru/v1/files/knowledge-bases \
  -H "Authorization: Bearer $BOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "help-desk", "tags": { "env": "prod" } }'
ответ 200
{
  "knowledgeBase": {
    "id": "142",
    "name": "help-desk",
    "tags": { "env": "prod" }
  }
}

Список, обновление, удаление

Метод и путьНазначение
GET /v1/files/knowledge-basesСписок баз. Параметры: tags, pageSize, nextToken (keyset-пагинация).
PUT /v1/files/knowledge-bases/{id}Переименовать базу / заменить теги (name обязателен).
DELETE /v1/files/knowledge-bases/{id}Удалить базу.

Наполнить файл пассажами

PUT /v1/files/{id}/passages — задаёт пассажи файла явно (полная замена), в обход авто-нарезки. Полезно, когда фрагменты вы готовите сами.

terminal
curl -X PUT https://botruntime.ru/v1/files/kb%2Fdogovor.md/passages \
  -H "Authorization: Bearer $BOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "passages": [
      { "content": "Возврат можно оформить в течение 14 дней после получения.", "type": "paragraph" },
      { "content": "Условия возврата товара", "type": "title" }
    ]
  }'

type — один из title, subtitle, paragraph, blockquote, list, table, code, image (по умолчанию paragraph). Пустой content отклоняется с 400. Ключ файла в пути передаётся %2F-экранированным.

Прочитать пассажи файла: GET /v1/files/{id}/passages (постранично, pageSize + nextToken).

Семантический поиск по файлам

GET /v1/files/search — поиск пассажей по всему хранилищу файлов бота.

ПараметрТипОбязательноОписание
querystringдаПоисковый запрос.
limitnumberнетМаксимум пассажей в ответе.
contextDepthnumber (0–10)нетСколько соседних пассажей приклеить к каждому попаданию.
withContextbooleanнетВернуть соседние пассажи отдельными записями с меткой preceding/current/subsequent.
tagsobject (URL-encoded JSON)нетФильтр по тегам файлов. Поддержаны плоские строковые значения.
terminal
curl -G https://botruntime.ru/v1/files/search \
  -H "Authorization: Bearer $BOT_API_KEY" \
  --data-urlencode "query=возврат предоплаты" \
  --data-urlencode "contextDepth=2"

Часть опций контракта поиска сознательно не поддержана. Она возвращает 501, а не тихо игнорируется. consolidate=true и includeBreadcrumb=true требуют дерева заголовков, которого плоское хранилище пассажей не несёт. То же с tags-фильтром: OR-массивы и not-исключения дают 501. Это fail-loud: сервер не делает вид, что применил то, что не умеет.

Дальше

On this page