@besales/mcp 0.1.0 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +272 -17
- package/dist/auth/connection-store.d.ts +58 -0
- package/dist/auth/connection-store.js +208 -0
- package/dist/auth/connection-store.js.map +1 -0
- package/dist/auth/oauth-client.d.ts +27 -2
- package/dist/auth/oauth-client.js +62 -11
- package/dist/auth/oauth-client.js.map +1 -1
- package/dist/auth/session-workspace.d.ts +2 -0
- package/dist/auth/session-workspace.js +20 -0
- package/dist/auth/session-workspace.js.map +1 -0
- package/dist/auth/token-storage.d.ts +19 -5
- package/dist/auth/token-storage.js +11 -6
- package/dist/auth/token-storage.js.map +1 -1
- package/dist/cli.d.ts +2 -7
- package/dist/cli.js +111 -33
- package/dist/cli.js.map +1 -1
- package/dist/http/api-client.d.ts +4 -13
- package/dist/http/api-client.js +18 -18
- package/dist/http/api-client.js.map +1 -1
- package/dist/index.d.ts +8 -6
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/instructions/server-instructions.d.ts +15 -0
- package/dist/instructions/server-instructions.js +243 -0
- package/dist/instructions/server-instructions.js.map +1 -0
- package/dist/package-metadata.js +7 -1
- package/dist/package-metadata.js.map +1 -1
- package/dist/resources/concepts/feedback-sheets.md +77 -0
- package/dist/resources/concepts/sandbox.md +13 -0
- package/dist/resources/concepts/workbook-classification.md +241 -0
- package/dist/resources/docs/agent-behavior.md +393 -0
- package/dist/resources/docs/crm-integration.md +535 -0
- package/dist/resources/docs/files-and-uploads.md +295 -0
- package/dist/resources/docs/knowledge-base.md +521 -0
- package/dist/resources/docs/pipeline-builder.md +221 -0
- package/dist/resources/docs/pipeline-settings-deep.md +221 -0
- package/dist/resources/docs/platforms.md +513 -0
- package/dist/resources/docs/prompt-anatomy.md +298 -0
- package/dist/resources/docs/prompt-principles.md +289 -0
- package/dist/resources/registry.js +34 -12
- package/dist/resources/registry.js.map +1 -1
- package/dist/resources/workflows/compare-models.md +46 -0
- package/dist/resources/workflows/connect-crm-from-scratch.md +89 -0
- package/dist/resources/workflows/connect-datasource-from-scratch.md +92 -0
- package/dist/resources/workflows/extract-from-document.md +36 -0
- package/dist/resources/workflows/iterate-with-sandbox.md +31 -0
- package/dist/resources/workflows/platform-setup-from-scratch.md +113 -0
- package/dist/resources/workflows/production-readiness-check.md +41 -0
- package/dist/schemas/mcp-tools.json +2636 -182
- package/dist/server.js +2 -0
- package/dist/server.js.map +1 -1
- package/dist/tools/definitions/agent-design.d.ts +215 -0
- package/dist/tools/definitions/agent-design.js +643 -0
- package/dist/tools/definitions/agent-design.js.map +1 -0
- package/dist/tools/definitions/crm-platform.d.ts +211 -0
- package/dist/tools/definitions/crm-platform.js +1070 -0
- package/dist/tools/definitions/crm-platform.js.map +1 -0
- package/dist/tools/definitions/datasource.d.ts +40 -0
- package/dist/tools/definitions/datasource.js +196 -0
- package/dist/tools/definitions/datasource.js.map +1 -0
- package/dist/tools/definitions/knowledge.d.ts +215 -0
- package/dist/tools/definitions/knowledge.js +782 -0
- package/dist/tools/definitions/knowledge.js.map +1 -0
- package/dist/tools/definitions/model-comparison.d.ts +25 -0
- package/dist/tools/definitions/model-comparison.js +101 -0
- package/dist/tools/definitions/model-comparison.js.map +1 -0
- package/dist/tools/definitions/platform-setup.d.ts +412 -0
- package/dist/tools/definitions/platform-setup.js +738 -0
- package/dist/tools/definitions/platform-setup.js.map +1 -0
- package/dist/tools/definitions/session.d.ts +11 -0
- package/dist/tools/definitions/session.js +86 -0
- package/dist/tools/definitions/session.js.map +1 -0
- package/dist/tools/definitions/shared.d.ts +742 -0
- package/dist/tools/definitions/shared.js +773 -0
- package/dist/tools/definitions/shared.js.map +1 -0
- package/dist/tools/definitions.d.ts +873 -88
- package/dist/tools/definitions.js +14 -856
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/registry.d.ts +3 -1
- package/dist/tools/registry.js +90 -11
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/result.d.ts +1 -1
- package/dist/tools/result.js +12 -4
- package/dist/tools/result.js.map +1 -1
- package/dist/utils/logger.js +2 -1
- package/dist/utils/logger.js.map +1 -1
- package/docs/host-setup.md +34 -15
- package/package.json +2 -2
- package/scripts/install-claude-desktop.js +89 -11
- package/scripts/mock-api-server.js +1 -1
- package/scripts/mock-credentials.js +49 -6
- package/dist/types/api-contract.gen.d.ts +0 -6975
- package/dist/types/api-contract.gen.js +0 -6
- package/dist/types/api-contract.gen.js.map +0 -1
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
# 07. База знаний и RAG
|
|
2
|
+
|
|
3
|
+
> **Источник правды:** `ai-aniomaly/docs/ai-prompt-builder/07-knowledge-base.md` — копия для MCP doc-bridge (v1.5 Increment 6b). Синкать при изменении оригинала.
|
|
4
|
+
>
|
|
5
|
+
> ⚠️ **MCP-контекст:** это доменная справка, написанная для людей/админ-UI. В MCP-сессии ты выполняешь те же операции **через besales_* tools**, а не через UI (drag-n-drop, клики в админке). Где текст ниже говорит «клиент/админ делает X через UI» — это «что должно получиться»; механику бери из tool (напр. загрузка файла → `besales_file_upload_request`; документ в knowledge → `besales_knowledge_document_upload`; сайт → `besales_knowledge_website_add`) + из workflow-ресурсов. Внутренние ссылки `[NN-name.md]` — относительные пути исходной доки, через MCP НЕ резолвятся: ищи `besales://docs/<slug>` или concept-resource.
|
|
6
|
+
|
|
7
|
+
## 1. Назначение
|
|
8
|
+
|
|
9
|
+
База знаний нужна агенту, чтобы отвечать **по реальной информации** компании-клиента: документы, FAQ, прайсы, статьи, инструкции. Animaly использует Pinecone (vector DB) + локальную БД для хранения. Агент получает релевантные фрагменты во время каждого запроса (RAG — Retrieval Augmented Generation).
|
|
10
|
+
|
|
11
|
+
## 2. Архитектура
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
Workspace
|
|
15
|
+
└── Namespace (cleanNamespace + scopeType)
|
|
16
|
+
├── KnowledgeDocument[] → KnowledgeChunk[] (Pinecone: pineconeDoc)
|
|
17
|
+
├── Qa[] → QaAnswerChunk[] (Pinecone: pineconeQa)
|
|
18
|
+
├── WebsiteKnowledge[] (Pinecone: pineconeWeb)
|
|
19
|
+
└── KnowledgeTable[] (Pinecone: pineconeTable)
|
|
20
|
+
|
|
21
|
+
AgentChat
|
|
22
|
+
└── knowledgeNamespaces: string[] — массив cleanNamespace
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
При каждом запросе агента система:
|
|
26
|
+
1. Берёт `query` (сообщение пользователя или его переписанная версия из QUERY_REWRITE).
|
|
27
|
+
2. Делает vector-поиск в каждом из подключённых namespaces (по типам данных).
|
|
28
|
+
3. Объединяет результаты, отсекает по `ragMinScore`/`qaMinScore`, берёт top-K (`ragTopK`).
|
|
29
|
+
4. Подкладывает в промпт как `{knowledgeContext}`.
|
|
30
|
+
|
|
31
|
+
Для QA после поиска может запускаться `RERANKER` (`AgentSpecialized.RERANKER`), чтобы выбрать лучший QA-кандидат.
|
|
32
|
+
|
|
33
|
+
## 3. Namespace — JSON-схема
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"id": "uuid",
|
|
38
|
+
"cleanNamespace": "main_qa",
|
|
39
|
+
"displayName": "Main Q&A",
|
|
40
|
+
"scopeType": "PLATFORM",
|
|
41
|
+
"workspaceId": "<workspaceId>",
|
|
42
|
+
"platformId": "<platformId>",
|
|
43
|
+
|
|
44
|
+
"hasQa": true,
|
|
45
|
+
"hasDocuments": true,
|
|
46
|
+
"hasWebsites": false,
|
|
47
|
+
"hasTables": false,
|
|
48
|
+
|
|
49
|
+
"accessMode": "AUTO_QUERY",
|
|
50
|
+
|
|
51
|
+
"pineconeQa": "qa_main_qa_<workspaceId>_<platformId>",
|
|
52
|
+
"pineconeDoc": "doc_main_qa_<workspaceId>_<platformId>",
|
|
53
|
+
"pineconeWeb": null,
|
|
54
|
+
"pineconeTable": null
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
| Поле | Описание |
|
|
59
|
+
|---|---|
|
|
60
|
+
| `cleanNamespace` | Человеко-читаемое имя (например, `general`, `support`). Уникально в рамках платформы (или workspace для `WORKSPACE` scope). |
|
|
61
|
+
| `displayName` | Имя для UI. |
|
|
62
|
+
| `scopeType` | `GLOBAL` (системный) / `WORKSPACE` (общий для workspace) / `PLATFORM` (только для одной платформы). |
|
|
63
|
+
| `workspaceId`, `platformId` | Привязка по scope. |
|
|
64
|
+
| `hasQa`, `hasDocuments`, `hasWebsites`, `hasTables` | Флаги активных типов данных. |
|
|
65
|
+
| `accessMode` | `AUTO_QUERY` (поиск автоматически на каждом сообщении) или `TOOL_ONLY` (поиск только по явному вызову `read_namespaces`). |
|
|
66
|
+
| `pineconeQa/Doc/Web/Table` | Реальные имена namespace-ов в Pinecone (генерируются системой при создании). |
|
|
67
|
+
|
|
68
|
+
### `WORKSPACE` scope: привязка к нескольким платформам
|
|
69
|
+
|
|
70
|
+
`Namespace` с `scopeType=WORKSPACE` может быть привязан к нескольким `Platform` через `PlatformNamespace`:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"id": "uuid",
|
|
75
|
+
"platformId": "<platformId>",
|
|
76
|
+
"namespaceId": "<namespaceId>"
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Так одну общую базу знаний (например, общий FAQ) можно подключить к 3 разным каналам.
|
|
81
|
+
|
|
82
|
+
## 4. Назначение namespace агенту
|
|
83
|
+
|
|
84
|
+
Через `AgentChat.knowledgeNamespaces` (массив строк):
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"name": "main",
|
|
89
|
+
"knowledgeNamespaces": ["main_qa", "main_doc", "general_faq"]
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Имена в массиве — это `Namespace.cleanNamespace`. Агент получит данные из всех типов (qa/doc/web/table), доступных в этих namespaces.
|
|
94
|
+
|
|
95
|
+
## 5. KnowledgeDocument — документы
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"id": "uuid",
|
|
100
|
+
"filename": "price-list-2026.pdf",
|
|
101
|
+
"filepath": "s3://animaly/knowledge/price-list-2026.pdf",
|
|
102
|
+
"namespace": "doc_main_qa_<wsId>_<platformId>",
|
|
103
|
+
"namespaceRecordId": "<Namespace.id>",
|
|
104
|
+
"platformId": "<platformId>",
|
|
105
|
+
"workspaceId": "<workspaceId>",
|
|
106
|
+
|
|
107
|
+
"splitMode": "recursive",
|
|
108
|
+
"separator": null,
|
|
109
|
+
"chunkSize": 1000,
|
|
110
|
+
"chunkOverlap": 200,
|
|
111
|
+
"maxChunkSize": 2000,
|
|
112
|
+
|
|
113
|
+
"embeddingModel": "text-embedding-3-small",
|
|
114
|
+
"storageType": "s3",
|
|
115
|
+
"status": "COMPLETED"
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
| Поле | Описание |
|
|
120
|
+
|---|---|
|
|
121
|
+
| `splitMode` | Стратегия разбиения текста (`recursive`, `paragraph`, и т.п.). |
|
|
122
|
+
| `chunkSize`, `chunkOverlap`, `maxChunkSize` | Параметры разбиения. |
|
|
123
|
+
| `embeddingModel` | Модель эмбеддингов (по умолчанию OpenAI). |
|
|
124
|
+
| `status` | enum `DocumentStatus`: `LOADED` → `TEXT_EXTRACTED` → `SPLIT` → `EMBEDDINGS_GENERATED` → `VECTOR_DB_UPLOADED` → `COMPLETED`. |
|
|
125
|
+
|
|
126
|
+
Чанки хранятся в `KnowledgeChunk` (`text`, `embedding`, `chunkIndex`).
|
|
127
|
+
|
|
128
|
+
### 5.1. Заголовок документа НЕ попадает в эмбеддинг (contextual retrieval)
|
|
129
|
+
|
|
130
|
+
Эмбеддится **только сырой текст чанка**. `filename`/заголовок документа хранится в метаданных Pinecone, но **в вектор не входит** — чанк находится поиском только по словам, которые есть в нём самом. Чанк «Модуль 6. Колени» не содержит названия курса/темы, поэтому по запросу «какой курс от боли в коленях» матчится слабее, а в смешанном namespace путается с похожими темами других продуктов.
|
|
131
|
+
|
|
132
|
+
Приёмы:
|
|
133
|
+
- **Контекстный заголовок внутри чанка.** Начинай каждый смысловой блок строкой-контекстом, которая попадёт в эмбеддинг: `[Курс ДБО — суставы, позвоночник, боли] Модуль 6. Колени…`. Так вектор кодирует и тему, и принадлежность; LLM тоже видит, к чему относится фрагмент.
|
|
134
|
+
- **Контроль границ через `split_override`.** Чтобы «один смысловой юнит = один чанк» (и заголовок не отклеился), грузи документ с `split_mode=separator` + явным `separator` между юнитами (+ `max_chunk_size` с запасом). Иначе recursive-резка по `chunkSize` разорвёт блок, и второй кусок останется без заголовка.
|
|
135
|
+
|
|
136
|
+
## 6. Qa — пары «вопрос-ответ»
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"id": "uuid",
|
|
141
|
+
"question": "Какая стоимость доставки по Москве?",
|
|
142
|
+
"answer": "Доставка по Москве — 500 руб. в пределах МКАД. ...",
|
|
143
|
+
"alias": "delivery_moscow",
|
|
144
|
+
"namespace": "qa_main_qa_<wsId>_<platformId>",
|
|
145
|
+
"namespaceRecordId": "<Namespace.id>",
|
|
146
|
+
"platformId": "<platformId>",
|
|
147
|
+
"workspaceId": "<workspaceId>",
|
|
148
|
+
|
|
149
|
+
"mode": "SIMPLE",
|
|
150
|
+
"callOperator": false,
|
|
151
|
+
"chunkDelaySeconds": 10,
|
|
152
|
+
"isCallQaOnly": false,
|
|
153
|
+
|
|
154
|
+
"embeddingModel": "text-embedding-3-small",
|
|
155
|
+
"status": "COMPLETED",
|
|
156
|
+
|
|
157
|
+
"answerChunks": [
|
|
158
|
+
{ "orderIndex": 0, "text": "Доставка по Москве — 500 руб. ...", "buttons": [], "storedFileId": null },
|
|
159
|
+
{ "orderIndex": 1, "text": null, "buttons": [], "storedFileId": "<StoredFile.id>" }
|
|
160
|
+
]
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
| Поле | Описание |
|
|
165
|
+
|---|---|
|
|
166
|
+
| `question`, `answer` | Текст. `answer` используется как контекст; реально в чат уходит `answerChunks[]`. |
|
|
167
|
+
| `alias` | Уникальный псевдоним в `(workspaceId)`. Используется для `call_qa(alias_name)`. Опциональный, но обязательный, если `isCallQaOnly=true`. |
|
|
168
|
+
| `mode` | `SIMPLE` (RAG + QA), `STRICT` (только QA, дословно), `ONCE_STRICT` (первый раз STRICT, потом как контекст). |
|
|
169
|
+
| `callOperator` | Если `true` — после QA автоматически вызывается оператор. |
|
|
170
|
+
| `chunkDelaySeconds` | Задержка между отправкой чанков ответа (если их несколько). |
|
|
171
|
+
| `isCallQaOnly` | Если `true` — QA не векторизуется, доступна только через `call_qa(alias)`. |
|
|
172
|
+
|
|
173
|
+
### `QaAnswerChunk` — фрагменты ответа
|
|
174
|
+
|
|
175
|
+
```json
|
|
176
|
+
{
|
|
177
|
+
"orderIndex": 0,
|
|
178
|
+
"text": "Текст чанка",
|
|
179
|
+
"buttons": [
|
|
180
|
+
{ "name": "Подробнее", "url": "https://example.com/delivery" }
|
|
181
|
+
],
|
|
182
|
+
"storedFileId": "<StoredFile.id>"
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Чанк может быть текстовым (`text`), медийным (`storedFileId`) или иметь кнопки (`buttons`). Несколько чанков отправляются последовательно с задержкой `chunkDelaySeconds`.
|
|
187
|
+
|
|
188
|
+
> ⚠️ **Ссылки — в текст, не в кнопки (по умолчанию).** URL клади в `text` чанка/ответа, а НЕ в `buttons`. В режиме `SIMPLE` агент перегенерирует ответ из текста контекста, и URL, лежащий только в `buttons`, клиенту не доходит (агент его «не видит» и отвечает «не вижу ссылку»). Кнопки (`buttons`) добавляй ТОЛЬКО если пользователь явно попросил кнопку. То же при `besales_knowledge_qa_create` / `besales_knowledge_qa_patch`.
|
|
189
|
+
|
|
190
|
+
## 7. WebsiteKnowledge — скрапленные сайты
|
|
191
|
+
|
|
192
|
+
```json
|
|
193
|
+
{
|
|
194
|
+
"id": "uuid",
|
|
195
|
+
"url": "https://example.com",
|
|
196
|
+
"domain": "example.com",
|
|
197
|
+
"title": "Главная",
|
|
198
|
+
"description": "...",
|
|
199
|
+
"namespace": "web_main_qa_<wsId>_<platformId>",
|
|
200
|
+
"namespaceRecordId": "<Namespace.id>",
|
|
201
|
+
"status": "COMPLETED",
|
|
202
|
+
"totalPages": 142,
|
|
203
|
+
"totalChunks": 1530,
|
|
204
|
+
"chunkSize": 1000,
|
|
205
|
+
"chunkOverlap": 200,
|
|
206
|
+
"embeddingModel": "text-embedding-3-small",
|
|
207
|
+
"lastCrawledAt": "2026-04-25T10:00:00Z"
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Краулинг управляется отдельным сервисом (BullMQ-job). AI-сервис обычно не создаёт WebsiteKnowledge напрямую — это делает админ через UI.
|
|
212
|
+
|
|
213
|
+
## 8. KnowledgeTable — таблицы (Google Sheets / Notion / Airtable)
|
|
214
|
+
|
|
215
|
+
```json
|
|
216
|
+
{
|
|
217
|
+
"id": "uuid",
|
|
218
|
+
"name": "Прайс на услуги",
|
|
219
|
+
"type": "GOOGLE_SHEETS",
|
|
220
|
+
"url": "https://docs.google.com/spreadsheets/d/...",
|
|
221
|
+
"access": "PUBLIC",
|
|
222
|
+
"namespace": "table_main_qa_<wsId>_<platformId>",
|
|
223
|
+
"namespaceRecordId": "<Namespace.id>",
|
|
224
|
+
|
|
225
|
+
"headerRowIndex": 0,
|
|
226
|
+
"useVectorIndex": true,
|
|
227
|
+
"vectorEmbedMaxChars": 1000,
|
|
228
|
+
"vectorTopK": 5,
|
|
229
|
+
|
|
230
|
+
"isActive": true,
|
|
231
|
+
"errorCount": 0,
|
|
232
|
+
"lastSuccessAt": "2026-04-30T08:00:00Z"
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
| Поле | Описание |
|
|
237
|
+
|---|---|
|
|
238
|
+
| `type` | enum `KnowledgeSourceType`: `GOOGLE_SHEETS`, `NOTION`, `AIRTABLE`. |
|
|
239
|
+
| `access` | `PUBLIC` / `PRIVATE`. |
|
|
240
|
+
| `useVectorIndex` | Индексировать содержимое таблицы для vector-поиска. Если `false` — таблица читается «сырой» при каждом запросе (медленнее, но всегда актуально). |
|
|
241
|
+
| `vectorTopK` | Лимит результатов из таблицы. |
|
|
242
|
+
|
|
243
|
+
## 9. Параметры RAG на уровне агента
|
|
244
|
+
|
|
245
|
+
В `AgentChat`:
|
|
246
|
+
|
|
247
|
+
| Поле | Дефолт | Описание |
|
|
248
|
+
|---|---|---|
|
|
249
|
+
| `ragTopK` | 3 | Сколько top-K результатов брать из vector-поиска документов. |
|
|
250
|
+
| `ragMinScore` | 0.5 | Минимальная релевантность RAG-результата (0.0–1.0). |
|
|
251
|
+
| `qaMinScore` | 0.45 | Минимальная релевантность QA-совпадения (широкий recall; точность даёт LLM-reranker). |
|
|
252
|
+
| `qaContextLimit` | 3 | Лимит QA-кандидатов для контекста (1–6). |
|
|
253
|
+
|
|
254
|
+
> ⚠️ Значения в таблице — дефолты колонок `AgentChat` (что получает новый агент), они и действуют в рантайме. В коде есть вторичные fallback-константы (`DEFAULT_RAG_TOP_K=5`, `DEFAULT_RAG_MIN_SCORE=0.4`), которые срабатывают только если значение агента не задано — не путай их с дефолтом агента.
|
|
255
|
+
|
|
256
|
+
## 10. Поток обработки RAG
|
|
257
|
+
|
|
258
|
+
```
|
|
259
|
+
Сообщение пользователя
|
|
260
|
+
↓
|
|
261
|
+
QUERY_REWRITE (через AiUsageOperationKind=KNOWLEDGE_QUERY_REWRITE)
|
|
262
|
+
Опционально: LLM переписывает запрос для лучшего поиска (учитывая историю).
|
|
263
|
+
↓
|
|
264
|
+
Vector-поиск по namespaces агента
|
|
265
|
+
Параллельно во всех типах: qa, doc, web, table.
|
|
266
|
+
↓
|
|
267
|
+
Фильтрация по min-score
|
|
268
|
+
QA: qaMinScore (0.45)
|
|
269
|
+
Doc/Web/Table: ragMinScore (0.5)
|
|
270
|
+
↓
|
|
271
|
+
Reranker (AgentSpecialized.RERANKER)
|
|
272
|
+
Опционально: для QA выбирает лучший из top-K (если включён).
|
|
273
|
+
↓
|
|
274
|
+
Top-K результатов
|
|
275
|
+
ragTopK для doc/web, qaContextLimit для QA.
|
|
276
|
+
↓
|
|
277
|
+
Формирование {knowledgeContext} в промпте
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
**Приоритет QA над документами.** Поиск по типам идёт параллельно, но сборка ответа — QA-first: если найдена релевантная QA-пара (≥ `qaMinScore`, прошла reranker) — ответ строится в QA-режиме (QA + документный RAG как дополнение); если подходящей QA нет — чистый документный RAG (`llm-main.service.ts`, ветка «Если НЕ НАШЛИ QA → RAG»). Практический вывод: слишком «широкая» QA перехватывает запросы, которые ты хотел увести в документный RAG. Держи QA-вопросы конкретными (цена, программа, противопоказания), а свободные «симптомные» запросы оставляй документам.
|
|
281
|
+
|
|
282
|
+
## 11. Inline-пример: namespace для интернет-магазина
|
|
283
|
+
|
|
284
|
+
### Конфигурация
|
|
285
|
+
|
|
286
|
+
```json
|
|
287
|
+
{
|
|
288
|
+
"namespace": {
|
|
289
|
+
"cleanNamespace": "shop_main",
|
|
290
|
+
"displayName": "Интернет-магазин",
|
|
291
|
+
"scopeType": "PLATFORM",
|
|
292
|
+
"platformId": "<platformId>",
|
|
293
|
+
"workspaceId": "<workspaceId>",
|
|
294
|
+
"hasQa": true,
|
|
295
|
+
"hasDocuments": true,
|
|
296
|
+
"hasWebsites": true,
|
|
297
|
+
"hasTables": true,
|
|
298
|
+
"accessMode": "AUTO_QUERY"
|
|
299
|
+
},
|
|
300
|
+
"qaSamples": [
|
|
301
|
+
{
|
|
302
|
+
"question": "Какая стоимость доставки?",
|
|
303
|
+
"answer": "По Москве — 500 руб. в пределах МКАД. ...",
|
|
304
|
+
"alias": "delivery",
|
|
305
|
+
"mode": "SIMPLE"
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
"question": "Как работает гарантия?",
|
|
309
|
+
"answer": "Гарантия 1 год на все товары. ...",
|
|
310
|
+
"alias": "warranty",
|
|
311
|
+
"mode": "SIMPLE"
|
|
312
|
+
}
|
|
313
|
+
],
|
|
314
|
+
"documents": [
|
|
315
|
+
{ "filename": "price-list-2026.pdf", "splitMode": "recursive", "chunkSize": 1000 }
|
|
316
|
+
],
|
|
317
|
+
"websites": [
|
|
318
|
+
{ "url": "https://shop.example.com", "chunkSize": 1000 }
|
|
319
|
+
],
|
|
320
|
+
"tables": [
|
|
321
|
+
{
|
|
322
|
+
"name": "Каталог товаров",
|
|
323
|
+
"type": "GOOGLE_SHEETS",
|
|
324
|
+
"url": "https://docs.google.com/spreadsheets/d/...",
|
|
325
|
+
"useVectorIndex": true,
|
|
326
|
+
"vectorTopK": 5
|
|
327
|
+
}
|
|
328
|
+
],
|
|
329
|
+
"agent": {
|
|
330
|
+
"name": "main",
|
|
331
|
+
"knowledgeNamespaces": ["shop_main"],
|
|
332
|
+
"ragTopK": 3,
|
|
333
|
+
"ragMinScore": 0.5,
|
|
334
|
+
"qaMinScore": 0.78
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
## 12. Inline-пример: TOOL_ONLY namespace для бронирования услуг
|
|
340
|
+
|
|
341
|
+
Иногда не нужно искать в базе знаний на каждом сообщении — только когда AI явно решил уточнить (например, услуги/расписание):
|
|
342
|
+
|
|
343
|
+
```json
|
|
344
|
+
{
|
|
345
|
+
"namespace": {
|
|
346
|
+
"cleanNamespace": "services_catalog",
|
|
347
|
+
"scopeType": "PLATFORM",
|
|
348
|
+
"accessMode": "TOOL_ONLY",
|
|
349
|
+
"hasQa": false,
|
|
350
|
+
"hasTables": true
|
|
351
|
+
},
|
|
352
|
+
"agent": {
|
|
353
|
+
"knowledgeNamespaces": ["services_catalog"],
|
|
354
|
+
"tools": ["builtin-read_namespaces"]
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Агент получит инструмент `read_namespaces` и будет вызывать его осознанно.
|
|
360
|
+
|
|
361
|
+
## 13. Граничные случаи и валидация
|
|
362
|
+
|
|
363
|
+
| Правило | Что AI-сервису помнить |
|
|
364
|
+
|---|---|
|
|
365
|
+
| `cleanNamespace` уникален в `(workspaceId, ?platformId)` | Зависит от `scopeType`. |
|
|
366
|
+
| Pinecone-имена (`pineconeQa/Doc/...`) генерируются системой | Не пытаться задавать вручную. |
|
|
367
|
+
| Документы загружаются асинхронно | `status` пройдёт через несколько стадий, прежде чем `COMPLETED`. До этого RAG может не работать. |
|
|
368
|
+
| QA с `isCallQaOnly=true` не векторизуется | Доступна только через `call_qa(alias)`. Хорошо для готовых медиа-ответов. |
|
|
369
|
+
| `mode=STRICT` отдаёт answer дословно | LLM не перефразирует. Подходит для legal/юридических текстов. |
|
|
370
|
+
| QA `alias` уникален в `(workspaceId)` | Не дублировать. |
|
|
371
|
+
| `ragTopK` слишком большой → шум | Реалистично 3–5. |
|
|
372
|
+
| `qaMinScore` — баланс recall/точности | Дефолт **0.45** = широкий recall, точность даёт LLM-reranker. Без reranker (мало QA) держи выше (0.6–0.8), иначе ложные срабатывания. |
|
|
373
|
+
| `RERANKER` агент опционален | Если в namespace мало QA — можно без него. Если QA много (200+) — желательно. |
|
|
374
|
+
| `accessMode=TOOL_ONLY` без инструмента `read_namespaces` | Бесполезно — агент не сможет искать. |
|
|
375
|
+
| WebsiteKnowledge — отдельный воркфлоу | Краулинг через очередь, AI-сервис обычно не задаёт его — даёт URL администратору. |
|
|
376
|
+
| `KnowledgeTable.useVectorIndex=false` | Каждый запрос ходит во внешнюю Google Sheets API — медленно. Включать только для маленьких таблиц. |
|
|
377
|
+
| Cross-platform namespace | Workspace-namespace + `PlatformNamespace` для каждой платформы, которая должна видеть. |
|
|
378
|
+
|
|
379
|
+
## 14. Workbook ingestion — загрузка из xlsx/Google Sheets
|
|
380
|
+
|
|
381
|
+
Помимо обычной загрузки документов через UI (multipart upload), Animaly умеет автоматически препарировать **рабочие книги** (xlsx-файлы или Google Sheets с несколькими листами) в `KnowledgeDocument`/`Qa`/`KnowledgeTable` через workbook-adapter. Этот flow задумывался для AI-ассистента типа Claude Code: пользователь даёт ссылку на табличку, AI-ассистент классифицирует каждый лист и решает, что куда отправить.
|
|
382
|
+
|
|
383
|
+
### 14.1. Модель `SourceWorkbook`
|
|
384
|
+
|
|
385
|
+
```json
|
|
386
|
+
{
|
|
387
|
+
"id": "uuid",
|
|
388
|
+
"workspaceId": "<workspaceId>",
|
|
389
|
+
"kind": "XLSX_UPLOAD", // или "GOOGLE_SHEETS"
|
|
390
|
+
"storedFileId": "<StoredFile.id>", // для XLSX_UPLOAD
|
|
391
|
+
"googleSpreadsheetId": null, // для GOOGLE_SHEETS
|
|
392
|
+
"canonicalUrl": "https://docs.google.com/...",
|
|
393
|
+
"originalTitle": "Каталог 2026.xlsx",
|
|
394
|
+
"classification": [
|
|
395
|
+
{
|
|
396
|
+
"sheetTitle": "Prices",
|
|
397
|
+
"sheetGid": 0,
|
|
398
|
+
"detectedKind": "price_table",
|
|
399
|
+
"destination": "knowledge_table",
|
|
400
|
+
"mappedEntityId": "<KnowledgeTable.id>",
|
|
401
|
+
"notes": "Прайс на основные позиции — индексировать через vector"
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
"sheetTitle": "FAQ",
|
|
405
|
+
"sheetGid": 12345,
|
|
406
|
+
"detectedKind": "qa_pairs",
|
|
407
|
+
"destination": "qa",
|
|
408
|
+
"mappedEntityId": null,
|
|
409
|
+
"notes": "Q&A для импорта через besales_knowledge_qa_create"
|
|
410
|
+
}
|
|
411
|
+
],
|
|
412
|
+
"importedBy": "<userId or null>", // null если impersonated MCP key
|
|
413
|
+
"importedBySource": "MCP_KEY", // "USER" / "MCP_KEY"
|
|
414
|
+
"importedAt": "2026-05-27T10:00:00Z",
|
|
415
|
+
"archivedAt": null // soft delete
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
| Поле | Описание |
|
|
420
|
+
|---|---|
|
|
421
|
+
| `kind` | enum `SourceWorkbookKind`: `XLSX_UPLOAD` (multipart файл) или `GOOGLE_SHEETS` (по URL) |
|
|
422
|
+
| `classification` | Json-массив с per-sheet решениями AI-ассистента. Формат: `{sheetTitle, sheetGid, detectedKind, destination, mappedEntityId?, notes?}` |
|
|
423
|
+
| `importedBySource` | enum `ImportedBySource`: `USER` (через UI/auth user) или `MCP_KEY` (через impersonated MCP key — `importedBy` может быть `null`) |
|
|
424
|
+
| `archivedAt` | Soft delete. Записи остаются в БД для аудита |
|
|
425
|
+
|
|
426
|
+
### 14.2. Новые поля `KnowledgeDocument`
|
|
427
|
+
|
|
428
|
+
```json
|
|
429
|
+
{
|
|
430
|
+
"id": "uuid",
|
|
431
|
+
"filename": "FAQ_Prices.md",
|
|
432
|
+
"source": "AI_INGESTED", // или "USER_UPLOAD" (default)
|
|
433
|
+
"sourceWorkbookId": "<SourceWorkbook.id>", // null для USER_UPLOAD
|
|
434
|
+
"sourceSheetTitle": "Prices", // какой лист породил
|
|
435
|
+
...
|
|
436
|
+
}
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
| Поле | Тип | Дефолт | Описание |
|
|
440
|
+
|---|---|---|---|
|
|
441
|
+
| `source` | enum `DocumentSource` | `USER_UPLOAD` | `USER_UPLOAD` — обычный multipart upload через UI; `AI_INGESTED` — создан workbook-adapter'ом из листа `SourceWorkbook` |
|
|
442
|
+
| `sourceWorkbookId` | uuid? | `null` | FK на `SourceWorkbook.id` (`onDelete: SetNull`). Заполнено для `AI_INGESTED`-документов |
|
|
443
|
+
| `sourceSheetTitle` | string? | `null` | Имя листа, из которого извлечён документ. Partial unique index по `(workspaceId, sourceWorkbookId, sourceSheetTitle) WHERE source = 'AI_INGESTED'` — гарантирует идемпотентный re-ingest одного и того же листа |
|
|
444
|
+
|
|
445
|
+
### 14.3. Flow workbook ingestion
|
|
446
|
+
|
|
447
|
+
```
|
|
448
|
+
1. UI/CLI/MCP: upload xlsx → /api/v2/files → storedFileId
|
|
449
|
+
(или: pass Google Sheets URL целиком)
|
|
450
|
+
↓
|
|
451
|
+
2. POST /api/v2/workspaces/:wsId/workbooks/inspect
|
|
452
|
+
{ storedFileId? | googleSheetsUrl? }
|
|
453
|
+
→ server читает структуру всех листов (sample max 50 rows + hyperlink stats)
|
|
454
|
+
→ upsert SourceWorkbook (workspaceId, kind, …)
|
|
455
|
+
↓
|
|
456
|
+
3. AI-ассистент (Claude Code / GPT) локально классифицирует листы:
|
|
457
|
+
- price_table → knowledge_table
|
|
458
|
+
- qa_pairs → qa
|
|
459
|
+
- product_descriptions → knowledge_document
|
|
460
|
+
- amocrm_leads_export → POST /workbooks/:id/extract-amocrm-leads (вернёт массив leadId)
|
|
461
|
+
- dialogue_corpus → ICP analysis (см. spec icp)
|
|
462
|
+
- feedback_sheet → FeedbackSheet (см. 31-google-sheets-feedback.md)
|
|
463
|
+
↓
|
|
464
|
+
4. PATCH /workbooks/:id/classification { classification: [...] }
|
|
465
|
+
→ server сохраняет решение в SourceWorkbook.classification
|
|
466
|
+
↓
|
|
467
|
+
5. Per-sheet actions (по решению AI):
|
|
468
|
+
- POST /workbooks/:id/knowledge-document
|
|
469
|
+
→ создаёт KnowledgeDocument с source=AI_INGESTED + auto-detect splitMode
|
|
470
|
+
- besales_knowledge_qa_create (для qa-пар из листа)
|
|
471
|
+
- besales_knowledge_table_link (для таблиц)
|
|
472
|
+
↓
|
|
473
|
+
6. POST /workbooks/:id/archive — soft delete после полной обработки
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
### 14.4. Auto-detect splitMode при `POST /workbooks/:id/knowledge-document`
|
|
477
|
+
|
|
478
|
+
Workbook-adapter использует heuristic для выбора `splitMode` нового документа на основе структуры листа:
|
|
479
|
+
|
|
480
|
+
| Heuristic | splitMode выбирается |
|
|
481
|
+
|---|---|
|
|
482
|
+
| 2 колонки, первая короткая, вторая длинная | `qa` (Q&A pairs) |
|
|
483
|
+
| Заголовки строк `## ...` или `=== ...` | `heading` (по заголовкам) |
|
|
484
|
+
| Абзацы разделены пустыми строками | `paragraph` |
|
|
485
|
+
| Нумерованные пункты `1.`, `2.`, ... | `numbered` |
|
|
486
|
+
| Fallback (структура не распознана) | `fixed-size` (с дефолтным `chunkSize`) |
|
|
487
|
+
|
|
488
|
+
### 14.5. MCP tools для workbook ingestion
|
|
489
|
+
|
|
490
|
+
| Tool | Endpoint | Назначение |
|
|
491
|
+
|---|---|---|
|
|
492
|
+
| `besales_workbook_inspect` | POST `/workbooks/inspect` | Загрузить структуру + создать SourceWorkbook |
|
|
493
|
+
| `besales_workbook_extract_amocrm_leads` | POST `/workbooks/:id/extract-amocrm-leads` | Извлечь все leadId (regex `.amocrm.ru/leads/` + `/api/v4/`) — hard cap 10000 строк |
|
|
494
|
+
| `besales_workbook_to_knowledge_document` | POST `/workbooks/:id/knowledge-document` | Создать KnowledgeDocument из листа |
|
|
495
|
+
|
|
496
|
+
### 14.6. Ограничения
|
|
497
|
+
|
|
498
|
+
| Лимит | Значение |
|
|
499
|
+
|---|---|
|
|
500
|
+
| Sample строк при `inspect` | 50 |
|
|
501
|
+
| Hard cap строк при `extract-amocrm-leads` | 10 000 |
|
|
502
|
+
| Cross-tenant защита | `SourceWorkbook.workspaceId` фиксирован owner-ом; повторный ingest того же файла в другой workspace создаёт новую запись |
|
|
503
|
+
| Идемпотентность re-ingest | Partial unique index гарантирует, что повторный `to-knowledge-document` для того же `(workspaceId, sourceWorkbookId, sourceSheetTitle)` обновит существующий документ, а не создаст дубль |
|
|
504
|
+
|
|
505
|
+
### 14.7. Когда AI-сервис использует workbook ingestion
|
|
506
|
+
|
|
507
|
+
- Клиент даёт большую табличку с прайсом, FAQ, описаниями товаров — AI-сервис не пытается распарсить вручную, а вызывает `inspect` → классифицирует → `to-knowledge-document`.
|
|
508
|
+
- Клиент даёт Google Sheets с выгрузкой лидов AmoCRM (например, для прогрева через ICP-анализ) — AI вызывает `extract-amocrm-leads`, затем `besales_icp_import_dialogues` с полученными leadId.
|
|
509
|
+
- Клиент даёт лист с testing feedback — AI вызывает `besales_feedback_sheet_link` (см. [`31-google-sheets-feedback.md`](31-google-sheets-feedback.md)).
|
|
510
|
+
|
|
511
|
+
## 15. Связь с другими подсистемами
|
|
512
|
+
|
|
513
|
+
| Подсистема | Связь |
|
|
514
|
+
|---|---|
|
|
515
|
+
| **Агенты** ([`01-agents.md`](01-agents.md)) | `knowledgeNamespaces[]`, `ragTopK`, `ragMinScore`, `qaMinScore`. |
|
|
516
|
+
| **Промпт** ([`02-prompt-anatomy.md`](02-prompt-anatomy.md)) | `{knowledgeContext}`, `{knowledgeInstructions}`. |
|
|
517
|
+
| **Инструменты** ([`03-tools-catalog.md`](03-tools-catalog.md)) | `builtin-read_namespaces` для `TOOL_ONLY`, `builtin-call_qa` для QA-aliases. |
|
|
518
|
+
| **Триггеры** ([`04-triggers-and-actions.md`](04-triggers-and-actions.md)) | `READ_NAMESPACES` action — RAG-поиск через триггер. |
|
|
519
|
+
| **AgentSpecialized** ([`01-agents.md`](01-agents.md)) | `RERANKER` для QA, опционально. |
|
|
520
|
+
| **Файлы** ([`17-files-and-uploads.md`](17-files-and-uploads.md)) | `SourceWorkbook.storedFileId` указывает на `StoredFile` (xlsx upload). |
|
|
521
|
+
| **Google Sheets feedback** ([`31-google-sheets-feedback.md`](31-google-sheets-feedback.md)) | Workbook с тестировщиками может быть размечен как feedback-sheet для linker'а |
|