Что получится в результате
Соберем ИИ-агента, который ищет по внутренним документам компании и отвечает только с цитатами на найденные источники. Это не универсальный чат и не замена базе знаний, а поисковый слой поверх файлов, страниц, регламентов, презентаций, инструкций и архивных документов.
Первая рабочая версия будет делать так:
- документы лежат в `document_sources`;
- `source_index` хранит список файлов, владельцев, папок и прав доступа;
- n8n забирает новые и измененные документы;
- текст извлекается из PDF, DOCX, Google Docs, Notion или Confluence;
- документ разбивается на фрагменты в `document_chunks`;
- фрагменты получают embedding и попадают в поисковый индекс;
- пользователь задает вопрос в форме, Slack, Teams или Telegram;
- агент нормализует вопрос и ищет релевантные фрагменты;
- фильтр доступа убирает документы, которые пользователь не имеет права видеть;
- LLM формирует ответ только по найденным фрагментам;
- в ответе показываются ссылки, названия документов и цитаты;
- неудачные запросы уходят в `search_gaps`;
- качество поиска проверяется через `search_tests`;
- все запросы и ошибки пишутся в журналы.
В первой версии агент не должен раскрывать закрытые документы, отвечать без источников, пересказывать приватные файлы, удалять документы и самостоятельно менять права доступа.
Что понадобится
- n8n Cloud или self-hosted n8n.
- Google Drive, SharePoint, Notion, Confluence или локальное хранилище документов.
- Google Sheets для реестров, логов и тестов.
- Векторная база или готовый RAG-поиск.
- LLM API для нормализации вопроса и ответа по найденным фрагментам.
- 50-200 документов для первого пилота.
- Список ролей, отделов и правил доступа.
- 30 тестовых поисковых запросов.
- Владелец поиска, который разбирает плохие результаты.
Шаг 1. Выберите границы поиска
Не начинайте с “искать по всем документам компании”.
Для первого прототипа выберите одну область:
поиск по регламентам, инструкциям и шаблонам отдела продаж
Входит:
- регламенты продаж;
- инструкции по CRM;
- шаблоны писем;
- коммерческие условия;
- инструкции по передаче клиента;
- ответы на типовые вопросы;
- внутренние презентации по продукту.
Не входит:
- договоры с клиентами;
- персональные данные;
- зарплаты;
- закрытые финансы;
- документы HR;
- security-документы;
- личные переписки.
Проверка: вы можете перечислить папки, которые агент индексирует, и папки, которые он не трогает.
Шаг 2. Создайте структуру папок
В хранилище создайте:
Internal search AI agent
document_sources
excluded_sources
extracted_text
search_exports
owner_review
archive
Правило:
индексируются только файлы из document_sources
excluded_sources не попадает в индекс
archive используется только для ручной проверки
Проверка: у команды есть одно место, куда можно добавить документ для поиска.
Шаг 3. Создайте таблицу проекта
Создайте Google Sheet:
Internal document search AI agent
Добавьте листы:
source_index
document_chunks
access_matrix
query_log
search_results_log
search_gaps
owner_tasks
search_tests
index_runs
error_log
settings
Проверка: n8n имеет доступ к таблице на чтение и запись.
Шаг 4. Заполните source_index
`source_index` - реестр документов, которые можно искать.
Колонки:
source_id
source_type
source_title
source_url
storage_path
department
owner_name
owner_email
access_level
status
last_modified_at
indexed_at
content_hash
version
notes
Статусы:
active
needs_review
excluded
archived
error
Типы источников:
google_doc
pdf
docx
spreadsheet
presentation
notion_page
confluence_page
html
markdown
Проверка: у каждого источника есть владелец, ссылка, статус и уровень доступа.
Шаг 5. Создайте access_matrix
`access_matrix` нужна, чтобы поиск не стал дырой в безопасности.
Колонки:
access_level
allowed_departments
allowed_roles
denied_roles
can_show_title
can_show_snippet
can_show_full_answer
fallback_message
is_active
Пример:
public_internal | all | employee,manager | none | yes | yes | yes | Документ доступен внутри компании
sales_only | sales | employee,manager | contractor | yes | yes | yes | Документ доступен только отделу продаж
restricted | legal,finance | manager | employee,contractor | no | no | no | Недостаточно прав для просмотра
Проверка: для каждого уровня доступа понятно, что можно показывать пользователю.
Шаг 6. Настройте сбор документов
Создайте workflow:
Document search source sync
Минимальные узлы:
- `Schedule Trigger`;
- `List files/pages`;
- `Filter included folders`;
- `Get metadata`;
- `Calculate content_hash`;
- `Upsert source_index`;
- `Write index_runs`.
Индексируйте документ заново только если изменился:
last_modified_at
content_hash
access_level
status
Проверка: новый файл появляется в `source_index`, а неизмененный файл не индексируется повторно без причины.
Шаг 7. Извлеките текст
Создайте workflow:
Document text extraction
Правила:
- Google Docs - читать текст через API;
- DOCX - извлекать текст и заголовки;
- PDF - сначала пробовать текстовый слой, потом OCR;
- презентации - извлекать заголовки и текст слайдов;
- таблицы - индексировать только выбранные листы и диапазоны;
- Notion/Confluence - сохранять структуру заголовков.
Сохраняйте результат в `extracted_text`:
SRC-0001.txt
Проверка: для каждого активного документа есть извлеченный текст, который можно открыть и прочитать.
Шаг 8. Разбейте текст на document_chunks
В `document_chunks` добавьте колонки:
chunk_id
source_id
chunk_title
chunk_text
chunk_type
section_path
page_number
slide_number
source_url
owner_name
access_level
embedding_id
token_count
status
created_at
Правило chunking:
один chunk = один смысловой фрагмент, а не случайный кусок текста
Типы фрагментов:
heading_section
instruction
faq
policy
table_row
slide
definition
example
warning
Проверка: chunk содержит достаточно контекста, чтобы ответить на вопрос, и не смешивает разные темы.
Шаг 9. Сохраните метаданные для цитат
Каждый chunk должен хранить путь к источнику.
Минимум:
source_title
source_url
section_path
page_number
slide_number
last_modified_at
owner_name
Для PDF полезно сохранять страницу.
Для Confluence и Notion - заголовок блока.
Для презентаций - номер слайда.
Проверка: ответ агента можно проверить, открыв конкретный документ и раздел.
Шаг 10. Создайте embedding-индекс
Для каждого `document_chunks.status = active` создавайте embedding.
Сохраняйте:
embedding_id
chunk_id
source_id
access_level
department
updated_at
Если используете внешнюю векторную базу, храните `chunk_id` как metadata, чтобы можно было подтянуть ссылку и права.
Проверка: по тестовому вопросу поиск возвращает 3-5 релевантных фрагментов.
Шаг 11. Создайте поисковый endpoint
Создайте workflow:
Internal document search answer
Минимальные узлы:
- `Webhook` или `Slack Trigger`;
- `Identify user`;
- `Normalize query`;
- `Vector search`;
- `Keyword fallback`;
- `Access filter`;
- `Rank results`;
- `Answer with citations`;
- `Write query_log`;
- `Write search_results_log`;
- `If no result -> search_gaps`.
Проверка: пользователь может отправить вопрос и получить ответ с источниками.
Шаг 12. Определите пользователя
Перед поиском получите:
user_email
user_name
department
role
manager_status
groups
Если данные пользователя неизвестны, используйте самый строгий режим:
access_level = public_internal only
Проверка: поиск зависит от прав пользователя, а не только от текста вопроса.
Шаг 13. Нормализуйте запрос
Создайте LLM-узел `Normalize search query`.
Prompt:
Ты готовишь поисковый запрос по внутренним документам.
Не отвечай на вопрос.
Верни только JSON:
{
"normalized_query": "",
"intent": "find_fact|find_instruction|find_template|compare|unknown",
"keywords": [],
"likely_departments": [],
"needs_recent_document": true|false,
"sensitive_request": true|false
}
Пример:
Вопрос: где шаблон письма после демо?
normalized_query: шаблон письма клиенту после демонстрации продукта
intent: find_template
keywords: ["шаблон письма", "после демо", "клиент"]
Проверка: нормализованный запрос помогает найти документ даже при разговорной формулировке.
Шаг 14. Сделайте гибридный поиск
Не полагайтесь только на embeddings.
Используйте два канала:
vector_search: смысловая близость
keyword_search: точные названия, коды, номера документов, аббревиатуры
Объедините результаты:
final_score = vector_score * 0.7 + keyword_score * 0.3
Для запросов с кодами, номерами и точными названиями повышайте вес keyword.
Проверка: запрос `SLA продаж Q2` находит документ с этим названием, даже если embedding не идеален.
Шаг 15. Отфильтруйте результаты по доступу
После поиска примените `access_matrix`.
Правило:
сначала ищем, потом фильтруем, потом отвечаем
Если документ найден, но недоступен:
- не показывайте фрагмент;
- не пересказывайте смысл закрытого документа;
- не раскрывайте чувствительное название, если `can_show_title = no`;
- запишите `access_denied_count` в лог;
- предложите запросить доступ у владельца.
Проверка: пользователь без прав не получает ответ из restricted-документа.
Шаг 16. Переранжируйте найденные фрагменты
После фильтра доступа оставьте 3-7 лучших фрагментов.
Критерии:
точное совпадение с вопросом
актуальность документа
доверие к источнику
совпадение отдела
наличие владельца
качество фрагмента
Если два документа противоречат друг другу, не выбирайте один случайно.
Проверка: в prompt ответа попадают только сильные и доступные фрагменты.
Шаг 17. Сформируйте ответ с цитатами
Создайте LLM-узел `Answer with citations`.
Prompt:
Ты отвечаешь только по найденным фрагментам внутренних документов.
Не используй знания вне контекста.
Если точного ответа нет, скажи: "В документах не нашел точного ответа".
Если документы противоречат друг другу, скажи об этом и перечисли источники.
Формат:
1. Короткий ответ.
2. Шаги, если вопрос про процесс.
3. Документы: название, раздел, ссылка.
4. Цитаты: короткие фразы из найденных фрагментов.
5. Владелец документа.
Проверка: в ответе есть минимум один источник и короткая цитата.
Шаг 18. Записывайте query_log
В `query_log` добавьте колонки:
query_id
created_at
user_email
department
role
original_query
normalized_query
intent
answer_status
access_denied_count
latency_ms
feedback_score
Статусы:
answered
no_results
access_denied
conflict
needs_owner_review
error
Проверка: можно увидеть популярные запросы и долю неудачных поисков.
Шаг 19. Записывайте search_results_log
В `search_results_log` храните, что именно нашел поиск.
Колонки:
result_id
query_id
chunk_id
source_id
source_title
final_score
vector_score
keyword_score
rank
was_used_in_answer
access_allowed
created_at
Проверка: если пользователь жалуется на ответ, можно увидеть, какие фрагменты попали в выдачу.
Шаг 20. Собирайте search_gaps
Если результатов нет или они слабые, создайте строку в `search_gaps`.
Колонки:
gap_id
query_id
normalized_query
reason
suggested_owner
priority
status
owner_comment
created_at
closed_at
Причины:
no_document
poor_chunking
outdated_document
access_denied
conflicting_sources
bad_query_understanding
Проверка: плохой поиск превращается в задачу по улучшению документов или индекса.
Шаг 21. Создайте owner_tasks
`owner_tasks` нужен для людей, которые исправляют найденные проблемы.
Колонки:
task_id
source_id
gap_id
owner_email
task_type
description
status
due_date
created_at
closed_at
Типы задач:
add_document
update_document
fix_title
improve_chunking
resolve_conflict
confirm_access
archive_document
Проверка: у каждой проблемы поиска есть владелец и действие.
Шаг 22. Добавьте feedback от пользователя
После ответа покажите:
Нашел / Не нашел
Если “Не нашел”, спросите причину:
не тот документ
нет ответа
устаревший ответ
нет доступа
слишком общий ответ
другое
Записывайте оценку в `query_log.feedback_score` и комментарий в отдельный столбец или лист.
Проверка: команда видит, какие ответы нужно улучшить в первую очередь.
Шаг 23. Создайте search_tests
В `search_tests` добавьте тестовые запросы.
Колонки:
test_id
query
user_role
department
expected_status
expected_source_id
must_include
must_not_include
last_run_at
last_result
Первый набор:
где шаблон письма после демо?
как передать клиента после оплаты?
какой регламент по скидкам?
где инструкция по CRM?
что делать если клиент просит закрытый отчет?
какой SLA ответа клиенту?
где презентация по продукту?
как оформить возврат?
Проверка: после изменения индекса тесты показывают, стал поиск лучше или хуже.
Шаг 24. Настройте регулярную переиндексацию
Создайте workflow:
Document search reindex
Запускайте:
каждый час для измененных документов
раз в день для проверки статусов
раз в неделю для freshness review
В `index_runs` храните:
run_id
started_at
finished_at
sources_checked
sources_indexed
chunks_created
chunks_deleted
errors_count
status
Проверка: индекс обновляется после изменения документа, а не живет старой копией месяцами.
Шаг 25. Обработайте удаленные и архивные документы
Если документ удален или переведен в `archived`, его chunks нужно выключить.
Правило:
status = archived -> document_chunks.status = inactive
source deleted -> document_chunks.status = inactive
Не удаляйте сразу физически: оставьте след в `index_runs` и `audit` через логи.
Проверка: поиск не отвечает по документу, который больше не должен участвовать в выдаче.
Шаг 26. Настройте error_log
В `error_log` добавьте колонки:
error_id
workflow_name
node_name
source_id
query_id
error_type
error_message
input_snapshot
status
owner
created_at
Типы ошибок:
source_not_found
permission_denied
text_extraction_failed
ocr_failed
chunking_failed
embedding_failed
vector_search_failed
access_rule_missing
llm_timeout
llm_json_invalid
Проверка: если поиск не работает, видно, где именно сломался процесс.
Шаг 27. Запустите пилот
Первую неделю используйте режим:
pilot_search
Ограничения:
- только один отдел;
- только активные документы;
- ответ только с источниками;
- закрытые документы фильтруются;
- все `no_results` проверяются владельцем;
- feedback собирается после каждого ответа;
- раз в день прогоняются `search_tests`.
Проверка: перед расширением на всю компанию видно качество поиска и частые пробелы.
Минимальная проверка результата
Прототип работает, если выполняются все пункты:
- источник появляется в `source_index`;
- текст документа извлекается;
- документ разбивается на `document_chunks`;
- chunks получают embedding;
- поиск находит релевантные фрагменты;
- доступ фильтруется по `access_matrix`;
- ответ содержит источник и цитату;
- закрытые документы не раскрываются;
- запрос записывается в `query_log`;
- найденные фрагменты записываются в `search_results_log`;
- неудачные запросы попадают в `search_gaps`;
- владельцам создаются `owner_tasks`;
- удаленные документы выключаются из поиска;
- ошибки пишутся в `error_log`;
- `search_tests` проходят после переиндексации.
Что нельзя автоматизировать в первой версии
- выдачу закрытых документов без прав;
- ответы без источников;
- автоматическое изменение прав доступа;
- удаление документов;
- индексирование личных переписок;
- поиск по персональным данным без отдельного процесса;
- выбор “правильного” ответа при конфликте документов;
- публикацию черновиков;
- принятие управленческих решений за владельцев документов.
Частые вопросы
Чем такой агент отличается от базы знаний?
База знаний хранит документы, а агент делает поисковый слой: понимает вопрос, ищет фрагменты, фильтрует доступ, собирает ответ и показывает источники. Если документов нет или они плохие, агент не спасет процесс.
Можно ли искать по PDF и сканам?
Можно, но для сканов нужен OCR. В первой версии лучше отдельно помечать качество извлечения текста, потому что плохой скан даст слабые фрагменты и плохую выдачу.
Нужно ли использовать только векторный поиск?
Нет. Лучше делать гибрид: embeddings для смысла и keyword-поиск для точных названий, номеров документов, аббревиатур и кодов. Так поиск меньше промахивается по внутренним терминам.
Что делать, если найден закрытый документ?
Не показывать текст и не пересказывать его. Запишите `access_denied_count`, покажите нейтральное сообщение и предложите запросить доступ у владельца.
Какой минимум нужен для запуска?
`source_index`, `document_chunks`, `access_matrix`, `query_log`, `search_results_log`, `search_gaps`, `search_tests`, один workflow индексации, один workflow ответа и 30 тестовых запросов.