AI Search — End-to-End Pipeline¶
Зачем этот документ¶
Entry point для сессий, работающих с поиском по документации 1Формы. Связывает write-pipeline (git push → индекс) и read-pipeline (4 канала потребления) в одну картинку. Детальные спецификации — в отдельных файлах (см. Cross-references).
Архитектура — общая картина¶
WRITE PATH
═════════
┌──────────┐ hook 3 ┌────────────┐ cascade ┌──────────────┐
│ git push│───────────▸│ SS 508 │───────────▸│ SS 510 │──▸ FileStorage (Disk)
│ docs/ │ │ dispatcher │ │ disk-sync │ FileContentAbstracts
│ master │ └────────────┘ └──────────────┘
└──────────┘ │ │
│ fileIdMap │
▼ │
┌──────────────┐ │
│ SS 509 │◀──────────────────────┘
│ indexer │
└──────┬───────┘
│
┌──────────┼──────────┐
▼ ▼ ▼
┌──────────┐ ┌────────┐ ┌──────────┐
│DocsChunks│ │DocsLinks│ │(embed: │
│ 55K rows │ │ 12K │ │optional) │
│ FTS auto │ │ links │ │ │
└──────────┘ └────────┘ └──────────┘
┌──────────┐ hook 8 ┌──────────────────────────────┐
│ git push│───────────▸│ zvec-search (docs-srv:3005) │──▸ git pull → chunk → embed → upsert
│ docs/ │ │ Docker container │ Qwen3-Embedding-8B (Fireworks)
└──────────┘ └──────────────────────────────┘
┌────────────────────┐ cron 6:03 MSK ┌────────────────────┐
│ build_pagerank.py │────────────────▸│ DocsPageRank │
│ (daily) │ full rebuild │ 7.9K docs │
│ │ │ + DocsLinks refresh │
└────────────────────┘ └─────────────────────┘
READ PATH
════════
┌────────────────────────────────────────────────────────────────────────────┐
│ DocsChunks (MSSQL) + DocsPageRank │
└────────┬─────────────┬──────────────────────────────────┬─────────────────┘
│ │ │
┌─────▼─────┐ ┌─────▼──────┐ ┌────────▼─────┐
│ Канал 1 │ │ Канал 2 │ │ Канал 4 │
│ Анфиса SS │ │ docsearch │ │ Standalone │
│ (prod) │ │ CLI (Denis)│ │ scripts │
│ │ │ (local) │ │ │
│ MSSQL │ │ Zvec local │ │ docs-sync- │
│ cascade │ │ + Fireworks│ │ push.py │
│ + zvec │ │ rerank │ │ │
│ parallel │ │ │ │ │
└───────────┘ └────────────┘ └──────────────┘
Канал 3 (MCP server / PG vector_search.docs_chunks) — REMOVED 2026-05-08.
Write pipeline (git push → index)¶
Ветка 1: GitLab webhook → SS 508 → SS 510 + SS 509¶
Источник: docs/domains/ai/architecture/data-flows.md:20-28
GitLab настроен с двумя webhook hooks на push в docs master:
| Hook | SS | Что делает | Задержка |
|---|---|---|---|
| hook 3 | SS 508 v8 (dispatcher, HD) | Принимает webhook, оркестрирует SS 510 и SS 509 | мгновенно |
| наследуемый | SS 510 v11 (disk-sync) | Upload файлов на Диск 1Формы → FileContentAbstracts (TypeId=2) |
~15-30 сек |
| наследуемый | SS 509 v6 (indexer) | Chunk → INSERT DocsChunks + DocsLinks | ~5-15 сек |
| hook 8 | zvec-search (docs-srv:3005) | git pull → chunk → embed Qwen3-Embedding-8B → upsert zvec | ~5-10 сек |
Порядок: SS 508 вызывает SS 510 сначала, получает fileIdMap, затем передаёт его в SS 509.
SS 509 indexer pipeline:
SS 509 v6
├── 1. Enumerate changed files (ModifiedAt > last IndexedAt)
├── 2. Parse frontmatter (YAML → section, domain, tags, box, layer)
├── 3. Chunk by heading structure (merge short <500, split long >3200, target ~3000 chars)
├── 4. Replace chunks: DELETE WHERE FileId + batch INSERT
├── 5. Delete stale chunks (file removed or chunk count decreased)
├── 6. Optional: batch embedding (OpenAI text-embedding-3-large, 1024D → varbinary)
└── 7. FTS catalog auto-update (CHANGE_TRACKING AUTO)
Single-write (с 2026-04-08): SS 509 v5 пишет только в MSSQL dbo.DocsChunks. Legacy PG dual-write в vector_search.docs_chunks удалён 2026-04-08 (миграция в MSSQL native завершена). [REMOVED 2026-05-08: csharp port убрал все обращения к vector_search; cleanup сессия]
Ветка 2: zvec-search container¶
Источник: docs/domains/search/zvec-search-spec.md
Отдельный GitLab webhook (hook 8) триггерит контейнер zvec-search на docs-srv (192.168.109.131:3005):
GitLab push webhook (hook 8)
→ POST /webhook/gitlab (zvec-search:3005)
→ Validate: branch = main/master, token matches
→ git pull в /data/repos/docs/
→ Для каждого changed файла:
- Читать файл с диска
- Chunk (800 tokens, 15% overlap)
- Embed chunks → Fireworks API batch (Qwen3-Embedding-8B, 4096D)
- Upsert в zvec (delete old chunks by path, insert new)
→ Flush index
Стоимость: ~$0.001 за файл. Latency: один файл ~7 chunks → ~1 секунда.
zvec schema:
CollectionSchema(
name="docs_v2",
fields=[
FieldSchema("path", STRING),
FieldSchema("collection", STRING), # docs, licard, efko, ...
FieldSchema("title", STRING),
FieldSchema("chunk_text", STRING, # BM25-индексирован
index_param=InvertIndexParam()),
FieldSchema("chunk_seq", INT32),
FieldSchema("content_hash", STRING), # SHA256[:16]
],
vectors=[
VectorSchema("dense", FP32, 4096, # Qwen3-Embedding-8B
HnswIndexParam(COSINE, m=50, ef_construction=500)),
VectorSchema("sparse", SPARSE_FP32, 0), # log-TF BM25
],
)
Ветка 3: PageRank cron¶
Источник: docs/domains/ai/architecture/data-flows.md:35
- Расписание: cron 6:03 MSK ежедневно
- Скрипт:
build_pagerank.py— парсит markdown-ссылки из всех docs → строит граф → PageRank + HITS (damping=0.85, 20 итераций) - Результат: полная перезапись DocsPageRank (7,902 документа) + DocsLinks (12,318 ссылок)
- Время: ~3 минуты на 7K docs
Актуализация (v2.268, MR !1528, задача #2087134): PR/HITS-математика перенесена из
build_pagerank.pyв хранимую процедуруdbo.sp_BuildPageRank(парная миграция MSSQL + PG). Парсинг markdown-ссылок остаётся в SS 509 Indexer / SS 502 (клиентские стенды). Процедура читает фильтр секций изSettingsCustom['anfisa_search_section_exclude'](default:["scenarios","clients"]) — страницы исключённых секций (docs/<section>/...) не попадают в граф и не получают строки вDocsPageRank. Управление настройкой — admin.md, раздел «AI Search документации — исключение секций из PageRank».
Типы ссылок и веса:
| LinkType | Вес | Пример |
|---|---|---|
| navigator | ×1.5 | navigator.md → entities/process/analytics |
| crosslink | ×1.3 | crosslinks.md → связи между коробками |
| readme | ×1.2 | README.md → ключевые файлы папки |
| inline | ×1.0 | backend.md → smart-access/backend.md |
| glossary | ×0.8 | glossary.md → рецепты и описания |
Schema DocsChunks / DocsLinks / DocsPageRank¶
DocsChunks (DB_MSSQL/dbo.DocsChunks.Table.sql):
CREATE TABLE dbo.DocsChunks (
Id int IDENTITY(1,1) PRIMARY KEY,
FileId int NOT NULL, -- ID файла в FileStorage
ChunkIndex smallint NOT NULL, -- 0-based, порядок в файле
Path nvarchar(500) NOT NULL, -- относительный: domains/ai/backend.md
Title nvarchar(500) NULL, -- заголовок чанка (из heading)
HeadingPath nvarchar(1000) NULL, -- # AI > ## Backend > ### MCP
SectionType nvarchar(50) NULL, -- text, table, code, list
Body nvarchar(max) NOT NULL, -- содержимое чанка (markdown)
Embedding varbinary(max) NULL, -- float32[1024], optional
ModifiedAt datetime2 NOT NULL,
IndexedAt datetime2 NOT NULL DEFAULT GETUTCDATE(),
Frontmatter nvarchar(max) NULL, -- полный YAML
Section nvarchar(100) NULL, -- extracted: domains, boxes, platform, ...
Tags nvarchar(max) NULL, -- extracted: space-separated from YAML tags[]
Box nvarchar(100) NULL, -- extracted: crm, dev, servicedesk, ...
Layer nvarchar(100) NULL, -- extracted: backend, frontend, business, ...
Company nvarchar(200) NULL, -- extracted: имя клиента (для clients/)
DomainIds nvarchar(500) NULL -- extracted: domain IDs
);
-- Indexes: IX_DocsChunks_FileId_ChunkIndex (unique), IX_DocsChunks_Path,
-- IX_DocsChunks_FileId, IX_DocsChunks_Section, IX_DocsChunks_Box
-- FTS: DocsChunksFTCatalog on (Body, Frontmatter, Tags), CHANGE_TRACKING AUTO
DocsLinks (DB_MSSQL/dbo.DocsLinks.Table.sql):
CREATE TABLE dbo.DocsLinks (
Id int IDENTITY(1,1) PRIMARY KEY,
SourcePath nvarchar(500) NOT NULL, -- откуда ссылка
TargetPath nvarchar(500) NOT NULL, -- куда ссылка
LinkType varchar(20) NOT NULL, -- inline, navigator, crosslink, readme, glossary
AnchorText nvarchar(500) NULL -- текст ссылки
);
-- Indexes: IX_DocsLinks_Source, IX_DocsLinks_Target
DocsPageRank (DB_MSSQL/dbo.DocsPageRank.Table.sql):
CREATE TABLE dbo.DocsPageRank (
Path nvarchar(500) PRIMARY KEY, -- PK = document path
InLinks int NOT NULL, -- количество входящих ссылок
PageRank float NOT NULL DEFAULT 0, -- итоговый PageRank score
HubScore float NOT NULL DEFAULT 0, -- HITS hub score
AuthScore float NOT NULL DEFAULT 0, -- HITS authority score
UpdatedAt datetime2 NOT NULL DEFAULT GETUTCDATE()
);
Масштаб (типичный production):
| Таблица | Rows | Размер |
|---|---|---|
| DocsChunks | 55,012 чанков (~7,000 файлов) | ~250 MB |
| DocsLinks | 12,318 ссылок | ~2 MB |
| DocsPageRank | 7,902 документа | ~0.5 MB |
Read pipeline — 4 канала¶
Канал 1: Анфиса SS (production runtime)¶
Основной потребитель: ~2,530 вызовов search_docs / неделю.
Тулы: search_docs (alias grep path="docs/"), get_document, doc_file_content
Implementation (с 2026-05-08):
- Module-ai-search-docs.cs — единая точка входа, MSSQL cascade FTS + параллельный recall через zvec
- Module-agent-tools-docs.cs — inline DocsSearchMssql (используется SS 612)
- Module-ai-search-docs-pg.cs — [REMOVED 2026-05-08] (PG hybrid engine удалён вместе с SS 631)
Engine (исторически — feature flag): anfisa_docs_search_engine в SettingsCustom — теперь только mssql. Default fallback тоже mssql (раньше — pg). Значение pg игнорируется csharp port'ом.
search_docs → MSSQL cascade path¶
search_docs(query)
→ docsSearch(query, "fast", 5)
→ docsSearchMssql(query, limit, contextBox)
→ 7 SQL queries:
Signal 1: CONTAINSTABLE(Frontmatter, OR terms) default 3.0 override 1.5 (~5ms)
Signal 2: CONTAINSTABLE(Tags, OR terms) default 3.0 override 1.5 (~5ms)
Signal 3: CONTAINSTABLE(Body, AND terms) default 1.0 override 2.0 (~10ms)
Signal 4: FREETEXTTABLE(Tags, query) default 4.0 override 2.0 (~5ms)
Signal 5: FREETEXTTABLE(Body, query) default 1.0 override 2.0 (~15ms)
Signal 6: DocsPageRank lookup (IN paths) default 2.0 override 1.5 (~3ms)
→ RRF merge (k=60)
→ Phase 0: Global section demote (press:0.3, blog:0.3, academy:0.3, website:0.5, admin-manual:0.3)
→ Phase 1: Box-aware section weights (if contextBox known)
→ Dedup by Path → top-K → snippet extraction
→ Total: ~43ms
search_docs → zvec parallel recall¶
Параллельно с cascade:
→ SS 492 _callZvecSearch()
→ HTTP POST http://docs-srv:3005/search
{query, top_k: 10, collection: "docs", rerank: true}
→ zvec pipeline:
1. Embed query → Fireworks qwen3-embedding-8b (4096D, ~50ms)
2. zvec hybrid: dense HNSW + sparse BM25 → RRF merge (k=60), top-40
3. [optional] Rerank top-40 → Fireworks qwen3-reranker-8b (~100ms)
4. Dedup by path → top_k
→ timeout: ZVEC_TIMEOUT_MS = 5000
→ silent fallback при ошибке (cascade работает standalone)
Результат: cascade + zvec → dedup → 5-8 unique docs → LLM-as-reranker выбирает релевантное.
Eval (213 queries): cascade only nDCG@5 = 0.563 (см. toolsearchdocs-mssql-spec.md:476). Цифры для cascade+zvec комбо — пока не опубликованы в spec'ах, требуют отдельного eval (см. Open Questions).
get_document¶
get_document(path)
→ Primary: docsReadFromDisk() — resolve folder path → FILES.get_file_content_string
→ Fallback: DocsChunks (Body, ChunkIndex, Title WHERE Path = X AND ChunkIndex = 0)
→ Лимит: 15,000 символов
doc_file_content¶
doc_file_content(path, offset, limit)
→ Paginated read, лимит 30,000 символов
Канал 2: docsearch CLI (Denis local)¶
Путь: ~/repo/docsearch/.venv/bin/docsearch
Команды:
docsearch query "тема" # hybrid + rerank
docsearch search "ключевое слово" # sparse only (BM25, без embedding)
docsearch get "path" # читает документ
docsearch embed [-f] [-c name] # индексация
docsearch status # статистика
Архитектура:
docsearch CLI
├── Indexer
│ ├── File scanner (glob + exclude + max_file_kb)
│ ├── Chunker (token-based, 900 tok, 15% overlap)
│ └── Embedder → OpenRouter API (Qwen3-Embedding-8B, 4096D)
│ fallback → oMLX :8100 (Qwen3-Embedding-8B-4bit-DWQ, cos≈0.98)
├── Storage → Zvec (in-process HNSW + sparse BM25)
│ ├── Dense: float32[4096], HNSW cosine, m=50, ef=500
│ ├── Sparse: hash-based BM25 (log-TF, 50K dim)
│ └── Hybrid: RRF merge (rank_constant=60)
├── Search
│ ├── Query embed → Fireworks API → fallback oMLX :8100
│ ├── Hybrid retrieval (dense + sparse + RRF, top-40)
│ └── Reranker → Fireworks API (Qwen3-Reranker-8B)
│ fallback → local:8081 → RRF only
└── Output → JSON / --human
Индекс: ~/.cache/docsearch/index/ (Zvec, platform-specific: arm64 ≠ x86_64).
Коллекции: docs (~3,930 файлов), serviceflow (~150), env/sqlrefactor/etc (~67).
Производительность:
| Операция | Время |
|---|---|
| query (hybrid + rerank) | ~3-5 сек |
| query (hybrid, без rerank) | ~2-3 сек |
| search (BM25 only) | ~100 мс |
| Полная индексация (3444 файла, 4 воркера) | ~16 мин (OpenRouter) |
Качество: 10/12 идеальный top-1, 12/12 релевантный top-3 (с reranker).
Канал 3: MCP server tools (REMOVED)¶
Статус: удалён. [REMOVED 2026-05-08: csharp port убрал все обращения к vector_search; cleanup сессия]. QMD MCP (docs-srv:3000) — legacy, запланировано выключение после миграции на zvec-search. PG vector_search.docs_chunks не заполняется и не читается продакшеном с 2026-04-27, csharp port не делает PG-запросов в vector_search schema.
Было (история): search.py на docs-srv — PG напрямую (BM25 + pgvector + Cohere rerank). Заменено на zvec-search (docs-srv:3005, Qwen3-Embedding-8B).
Канал 4: Standalone скрипты¶
docs-sync-push (docs/projects/gitlab-integration/docs-sync-push.py):
- Standalone Python-драйвер для синхронизации docs/ на PG-стенды, где webhook не работает (dev-pg, local)
- Эмулирует SS 510 + SS 509 + build_pagerank.py
- Запуск: docs-sync-push.py --target dev-pg --reset --workers 8 (~5 мин)
build_pagerank.py:
- Парсинг markdown-ссылок → PageRank/HITS расчёт → DocsPageRank + DocsLinks
- Запускается по крону (6:03 MSK) и вручную: python3 build_pagerank.py
indexer_docs_mssql.py: - FileContentAbstracts → chunk → (optional embed) → DocsChunks - Используется при full reindex
Cascade FTS — 6 сигналов (docs search)¶
Источник кода: Module-agent-tools-docs.cs:332-340 (MSSQL_W_DEFAULT) inline DocsSearchMssql. Дополнительная (вторичная) точка: Module-ai-search-docs-mssql.cs:21-29. Spec: tuning-guide.md, docs-chunks-mssql-spec.md.
Все сигналы — детерминированные (MSSQL FTS + PageRank), без внешних API.
Дефолты в коде (MSSQL_W_DEFAULT)¶
| # | Signal | Default | Column | SQL | Что делает |
|---|---|---|---|---|---|
| 1 | CONTAINS_FM_OR | 3.0 | Frontmatter | CONTAINSTABLE(DocsChunks, Frontmatter, '"term1" OR "term2"') |
Exact keyword в frontmatter |
| 2 | CONTAINS_TAGS_OR | 3.0 | Tags | CONTAINSTABLE(DocsChunks, Tags, '"term1" OR "term2"') |
Exact keyword в tags |
| 3 | CONTAINS_BODY | 1.0 | Body | CONTAINSTABLE(DocsChunks, Body, '"term1" AND "term2"') |
Exact keyword в теле (AND, top-3 longest) |
| 4 | FREETEXT_TAGS | 4.0 | Tags | FREETEXTTABLE(DocsChunks, Tags, @query) |
Morphological match в tags |
| 5 | FREETEXT_BODY | 1.0 | Body | FREETEXTTABLE(DocsChunks, Body, @query) |
Morphological match в теле |
| 6 | PAGERANK | 2.0 | DocsPageRank | SELECT Path, PageRank FROM DocsPageRank WHERE Path IN (...) |
Авторитетность по ссылкам |
Production override (Variant A, с 2026-05-07)¶
С 2026-05-07 на проде и dev активен runtime override через SettingsCustom["anfisa_docs_search_weights"]. Tunable без redeploy, TTL 60s в _mssqlWCache.
| Signal | Override | vs default |
|---|---|---|
| CONTAINS_FM_OR | 1.5 | ↓ |
| CONTAINS_TAGS_OR | 1.5 | ↓ |
| CONTAINS_BODY | 2.0 | ↑ |
| FREETEXT_TAGS | 2.0 | ↓ |
| FREETEXT_BODY | 2.0 | ↑ |
| PAGERANK | 1.5 | ↓ |
Соотношение TAGS:BODY изменено 5:1 → 5:4. Закрывает Bug #9 (TAGS-bias на CACHE/JWT/Lua queries). Полная история и tuning workflow — tuning-guide.md. Research + acceptance criteria — projects/1f-agent/initiatives/e2e-2026-05-07-fixes/ai-search-ranking-bias.md.
RRF merge: RRF_SCORE(path) = Σ weight[signal] / (k + rank + 1), k = 60.
Post-RRF:
1. Phase 0: Global section demote (press:0.3, blog:0.3, academy:0.3, website:0.5, admin-manual:0.0)
2. Phase 1: Box-aware section weights (если contextBox передан) — tuning-guide.md § Phase 1
3. Dedup by Path → top-K
Eval: nDCG@5 = 0.563 на дефолтах (213 queries, grid search 4500 combos, best = 0.640). Variant A: nDCG@5 ≈ 0.553 (-2.1%), Hit@1 +1pp, 7/7 acceptance pass (vs default 5/7).
toolSearchDocs — runtime для Анфисы¶
Pipeline Prefetch и LLM tool search_docs идут через Module:agent-tools-docs (SS 612) inline DocsSearchMssql — тот же код что описан выше. Engine выбирается через SettingsCustom["anfisa_docs_search_engine"] (mssql по дефолту, pg для PG-стендов).
Отличие от PG-версии: нет embedding в runtime, нет OpenAI dependency, 6 детерминированных сигналов.
Vector path — embedding + ANN + rerank¶
zvec-search (docs-srv:3005, production)¶
Embedding model: Qwen3-Embedding-8B (4096D) через Fireworks API
HNSW параметры: - Dimensions: 4096 - Metric: COSINE - m: 50 (connections per node) - ef_construction: 500
Reranker: Qwen3-Reranker-8B (Fireworks, cross-encoder)
Fallback: без reranker → RRF only (dense+sparse merge, без cross-encoder)
Latency: embed query ~50ms, ANN search ~50ms, rerank ~100ms, total ~200ms
Стоимость: embed ~$0.001/query, rerank ~$0.001/query
docsearch CLI (local, Denis)¶
Embedding: Qwen3-Embedding-8B через OpenRouter API (primary), oMLX :8100 (fallback, cos≈0.98)
Reranker: Qwen3-Reranker-8B через Fireworks API (primary), local:8081 (fallback)
Fallback cascade: Fireworks embed → oMLX embed (без сети); Fireworks rerank → local server → RRF only
Tuning — где что менять¶
SettingsCustom ключи¶
| Ключ | Значения | Где используется |
|---|---|---|
anfisa_docs_search_engine |
mssql (default fallback тоже mssql с 2026-05-08) |
Module-ai-search-docs.cs — engine flag, csharp port игнорирует значение pg |
anfisa_context_search |
off / on |
(запланировано) Phase 1 section weights |
anfisa_docs_search_zvec_timeout |
ms (default 5000) | zvec HTTP timeout |
Signal weights (ai-search-docs-mssql.js → Module-ai-search-docs-mssql.cs)¶
Объект MSSQL_W в начале файла:
var MSSQL_W = {
CONTAINS_FM_OR: 3.0,
CONTAINS_TAGS_OR: 3.0,
CONTAINS_BODY: 1.0,
FREETEXT_TAGS: 4.0, // ← MAIN signal
FREETEXT_BODY: 1.0,
PAGERANK: 2.0,
BOX_AFFINITY: 2.0
};
RRF k parameter¶
RRF_K = 60 (balanced). Выше = smoother, ниже = sharper.
Phase 0 — Global section demote¶
GLOBAL_SECTION_DEMOTE = {
press: 0.3, blog: 0.3, academy: 0.3, website: 0.5, "admin-manual": 0.3
};
Phase 1 — Box-aware section weights¶
Матрица box → section → multiplier (применяется post-RRF если contextBox передан):
| Box | domains | platform | reference | regulations | website | projects |
|---|---|---|---|---|---|---|
| dev | 1.5 | 1.5 | 1.3 | 0.5 | 0.3 | 0.7 |
| crm | 1.0 | 0.7 | 0.8 | 1.0 | 0.5 | 0.7 |
| servicedesk | 1.2 | 1.0 | 1.0 | 0.7 | 0.3 | 0.7 |
| pm | 1.0 | 0.7 | 0.8 | 1.0 | 0.5 | 0.7 |
| platform-user | 1.5 | 1.0 | 1.3 | 0.5 | 0.3 | 0.7 |
PageRank tuning¶
- Damping factor: 0.85
- Iterations: 20
- Link type weights: navigator 1.5, crosslink 1.3, readme 1.2, inline 1.0, glossary 0.8
zvec-search tuning¶
m: 50 (HNSW connections) — выше = точнее, медленнее buildef_construction: 500 — выше = точнее, медленнее buildtop_k: default 10rerank: true/false — cross-encoder reranking
docsearch CLI env vars¶
| Env var | Default | Что |
|---|---|---|
DOCSEARCH_EMBED_BACKEND |
openrouter |
Backend для embed: openrouter, fireworks, omlx, ollama |
DOCSEARCH_EMBED_FALLBACK |
omlx |
Fallback backend |
Observability — диагностика¶
perfLog поля¶
В AutomationScriptsLog (form 91) и AnfisaConversationLog (Role = 'perf_log'):
docsSearch engine=mssql query="..." signals=7 results=5 ms=43
colon_parse: fp=... st=... uids=... sids=...
search_parse: mode=...
AnfisaConversationLog маркеры¶
-- Последние search вызовы
SELECT SUBSTRING(CAST(Content AS NVARCHAR(MAX)), 1, 500)
FROM AnfisaConversationLog
WHERE Role = 'perf_log'
AND CAST(Content AS NVARCHAR(MAX)) LIKE '%search_%'
AND CreatedAt > DATEADD(HOUR, -1, GETDATE())
ORDER BY Id DESC
Типичные диагностические запросы¶
-- Файл проиндексирован?
SELECT ChunkIndex, Title, LEN(Body) as body_len, Tags, IndexedAt
FROM DocsChunks WHERE Path LIKE '%keyword%' ORDER BY Path, ChunkIndex
-- FTS catalog активен?
SELECT FULLTEXTCATALOGPROPERTY('DocsChunksFTCatalog', 'PopulateStatus') -- 0=idle
-- PageRank документа
SELECT * FROM DocsPageRank WHERE Path LIKE '%keyword%'
-- Топ-20 по PageRank
SELECT TOP 20 Path, InLinks, PageRank FROM DocsPageRank ORDER BY PageRank DESC
-- Тест FTS
SELECT TOP 5 dc.Path, ft.[RANK]
FROM CONTAINSTABLE(DocsChunks, Body, N'"keyword"') ft
JOIN DocsChunks dc ON dc.Id = ft.[KEY]
ORDER BY ft.[RANK] DESC
Диагностика «выдан не тот документ»¶
- Проверить Tags в DocsChunks — tags = главный сигнал (weight 4.0)
- Проверить PageRank — возможно документ с высоким PR побеждает
- Проверить Phase 0/1 weights — возможно section демотирует правильный документ
- Проверить frontmatter — section/tags должны быть корректными
- Проверить zvec parallel recall — если zvec нашёл правильный, но cascade нет
Сравнение каналов¶
| Канал | Стек | Latency | Recall | Когда выбирать |
|---|---|---|---|---|
| Анфиса SS (prod) | MSSQL cascade + zvec parallel | ~43ms (cascade) + ~200ms (zvec, parallel) | nDCG@5 = 0.593, effective recall 96% (top-8) | Production runtime для Анфисы |
| docsearch CLI (Denis) | Zvec local (dense+sparse+rerank) | ~3-5 сек (с rerank), ~100ms (BM25 only) | 10/12 top-1, 12/12 top-3 | Локальная разработка, ad-hoc queries, batch-анализ |
| Standalone | Python (docs-sync-push, build_pagerank, indexer) | минуты (batch) | N/A | Синхронизация индексов, reindex, maintenance |
Канал 3 (MCP server / PG vector_search) удалён 2026-05-08 — [REMOVED 2026-05-08: csharp port убрал все обращения к vector_search; cleanup сессия]. Историческая информация: PG pgvector + Cohere rerank, ~250ms, nDCG@5 = 0.367. Заменён на zvec-search (Канал 1 параллельно).
Sequence diagrams (ASCII)¶
Сценарий 1: git push → indexed (write path)¶
Developer GitLab SS 508 SS 510 SS 509 MSSQL zvec-search
│ │ │ │ │ │ │
│ git push │ │ │ │ │ │
│───────────────▸│ │ │ │ │ │
│ │ │ │ │ │ │
│ │ webhook │ │ │ │ │
│ │ (hook 3) │ │ │ │ │
│ │───────────────▸│ │ │ │ │
│ │ │ │ │ │ │
│ │ │ run SS 510 │ │ │ │
│ │ │──────────────▸│ │ │ │
│ │ │ │ │ │ │
│ │ │ │ git pull + │ │ │
│ │ │ │ upload Disk │ │ │
│ │ │ │──────────────▸│ │ │
│ │ │ │ FileContent │ │ │
│ │ │ │ Abstracts │ │ │
│ │ │ │ ▸│ │ │
│ │ │ fileIdMap │ │ │ │
│ │ │◂──────────────│ │ │ │
│ │ │ │ │ │ │
│ │ │ run SS 509 │ │ │ │
│ │ │ (with fileIdMap) │ │ │
│ │ │──────────────────────────────▸│ │ │
│ │ │ │ │ │ │
│ │ │ │ │ parse FM │ │
│ │ │ │ │ chunk │ │
│ │ │ │ │──────────────▸ │
│ │ │ │ │ INSERT │ │
│ │ │ │ │ DocsChunks │ │
│ │ │ │ │ DocsLinks │ │
│ │ │ │ │ │ │
│ │ webhook (hook 8) │ │ │ │
│ │──────────────────────────────────────────────────────────────▸│ │
│ │ │ │ │ │ git pull │
│ │ │ │ │ │ chunk + embed │
│ │ │ │ │ │ upsert zvec │
│ │ │ │ │ │ │
│ │ │ │ │ │ │
│ │ (6:03 MSK daily) │ │ │ │
│ │ │ build_pagerank.py │ │ │
│ │ │──────────────────────────────▸│ │ │
│ │ │ │ │──────────────▸ │
│ │ │ │ │ UPSERT │ │
│ │ │ │ │ DocsPageRank │ │
Сценарий 2: Анфиса search_docs end-to-end (read path)¶
User Анфиса agent-loop ai-search-docs MSSQL zvec-search
│ │ (SS 492) │ (SS 498) │ │ │
│ вопрос │ │ │ │ │
│────────────────▸│ │ │ │ │
│ │ │ │ │ │
│ │ LLM: "grep │ │ │ │
│ │ path=docs/" │ │ │ │
│ │ │ │ │ │
│ │ search_docs() │ │ │ │
│ │───────────────▸│ │ │ │
│ │ │ │ │ │
│ │ │ docsSearch() │ │ │
│ │ │────────────────▸│ │ │
│ │ │ │ │ │
│ │ │ │ [PARALLEL] │ │
│ │ │ │ │ │
│ │ │ │ cascade FTS: │ │
│ │ │ │ 6 CONTAINS/ │ │
│ │ │ │ FREETEXT signals│ │
│ │ │ │────────────────▸│ │
│ │ │ │ (~43ms) │ │
│ │ │ │ │ │
│ │ _callZvecSearch() │ │ │
│ │──────────────────────────────────────────────────────────────────▸│
│ │ │ │ │ POST /search │
│ │ │ │ │ embed+ANN+ │
│ │ │ │ │ rerank │
│ │ │ │ │ (~200ms) │
│ │ │ │ │ │
│ │ │ cascade │ │ │ zvec results
│ │ │◂────────────────│ │ │◂───
│ │ zvec results │ │ │ │
│ │◂──────────────────────────────────────────────────────────────────│
│ │ │ │ │ │
│ │ dedup by path │ │ │ │
│ │ 5-8 unique docs│ │ │ │
│ │ │ │ │ │
│ │ LLM context: │ │ │ │
│ │ all docs → │ │ │ │
│ │ model selects │ │ │ │
│ │ │ │ │ │
│ ответ │ │ │ │ │
│◂────────────────│ │ │ │ │
Сценарий 3: docsearch CLI hybrid (read path)¶
Denis docsearch CLI Zvec (local) Fireworks API oMLX :8100
│ │ │ │ │
│ query "..." │ │ │ │
│────────────────▸│ │ │ │
│ │ │ │ │
│ │ embed query │ │ │
│ │─────────────────────────────────▸│ │
│ │ │ │ Qwen3-Emb-8B │
│ │ │ │ 4096D, ~50ms │
│ │ embedding │ │ │
│ │◂────────────────────────────────── │
│ │ │ │ │
│ │ [fallback: embed via oMLX] │ │
│ │───────────────────────────────────────────────────▸│
│ │ │ │ Qwen3-Emb-8B │
│ │ │ │ -4bit-DWQ │
│ │ │ │ │
│ │ hybrid search │ │ │
│ │────────────────▸│ │ │
│ │ │ dense HNSW │ │
│ │ │ + sparse BM25 │ │
│ │ │ → RRF merge │ │
│ │ │ → top-40 │ │
│ │ top-40 results │ │ │
│ │◂────────────────│ │ │
│ │ │ │ │
│ │ rerank top-40 │ │ │
│ │─────────────────────────────────▸│ │
│ │ │ │ Qwen3-Reranker │
│ │ │ │ -8B, ~100ms │
│ │ reranked │ │ │
│ │◂────────────────────────────────── │
│ │ │ │ │
│ │ [fallback: no rerank, RRF only] │ │
│ │ │ │ │
│ │ dedup → top_k │ │ │
│ │ format output │ │ │
│ results │ │ │ │
│◂────────────────│ │ │ │
Open questions / drift¶
-
C# port: PascalCase issue (C-5). С 2026-05-01 все стенды на C#. Typed records без
[JsonProperty]в cross-module dispatch дают PascalCase keys вместо lowercase → consumer-side lookup возвращает null. ЗатронутыUserContextResult,AuthorResult,CommProfileResultвModule-agent-enrichment.cs. AI Search конкретно не затронут напрямую, но если cascade/search функции возвращают typed records через cross-module dispatch — тот же риск drift'а. Требует проверки: просмотреть Module-ai-search-docs-mssql.cs на наличие typed record returns через cross-module boundary. -
zvec-search не вызывается из C# модулей. Grep по
docs/domains/ai/scripts/csharp/*.csне нашёл ссылок на zvec/docs-srv:3005. Vector recall (параллельный zvec-вызов) живёт в JS-слое (SS 492 agent-loop.js_callZvecSearch). После C# port'а — проверить, что zvec integration перенесена. Если нет — vector recall потерян на C# стендах. -
PG
vector_search.docs_chunksREMOVED.[REMOVED 2026-05-08: csharp port убрал все обращения к vector_search; cleanup сессия]. Не заполняется и не читается с 2026-04-27. Module-ai-search-docs-pg.cs удалён, SS 631 удалён на local/dev/dev-pg (HD не трогали — там SS 631 = другой Lua-скрипт). Module-ai-search-docs.cs упрощён (736→607 LOC), Module-agent-tools-docs.cs (1934→1534 LOC). Out-of-scope follow-up: backend MR на удалениеVECTORDBAPI +VectorDbConnectionStringconfig;DROP SCHEMA vector_search CASCADEна PG-стендах. -
QMD MCP (docs-srv:3000) shutdown. Legacy MCP server использует OpenAI embeddings + Cohere rerank. Планируется выключение после миграции всех потребителей на zvec-search. Точный срок не определён.
-
Section weights Phase 1: код отсутствует в C#.
ss498-context-aware-patch.jsреализовывал BOX_AFFINITY (отвергнут). SECTION_WEIGHTS matrix — validated (+3.4% на box-labeled eval), но код может не быть перенесён в C# Module-ai-search-docs-mssql.cs. Требует проверки. -
FTS LANGUAGE 1033. Fulltext index на DocsChunks создан с
LANGUAGE 1033(English). Русский текст индексируется English stemmer. FREETEXTTABLE работает морфологически, но качество может страдать на русских word forms. MSSQL 2019+ поддерживаетLANGUAGE 1049(Russian) — но переключение требует rebuild FTS catalog. -
Embedding column не используется в runtime. DocsChunks.Embedding (varbinary, float32[1024]) существует, но cascade v4/v5 не использует cosine similarity. Embedding нужен только для «Full mode» (с external embedding API) — этот режим не deployed.
-
Scale limits. 55K chunks в DocsChunks — растёт при добавлении docs. FTS catalog rebuild может занять минуты при >100K chunks. PageRank ~3 мин при текущем масштабе, линейно растёт.
Cross-references¶
| Тема | Файл |
|---|---|
| Cascade FTS архитектура | cascade-fts-spec.md |
| toolSearchDocs MSSQL дизайн | toolsearchdocs-mssql-spec.md |
| DocsChunks schema + indexer | docs-chunks-mssql-spec.md |
| PageRank алгоритм | docs-pagerank-spec.md |
| Context-aware ranking | context-aware-search-spec.md |
| zvec-search контейнер | zvec-search-spec.md |
| Operational runbook | runbook.md |
| Tuning guide | tuning-guide.md |
| Docs pipeline map (Анфиса) | ../ai/architecture/docs-pipeline-map.md |
| Потоки данных Анфисы | ../ai/architecture/data-flows.md |
| docsearch CLI спека | ../../projects/docsearch/docsearch-spec.md |
| 1f-agent проект | ../../projects/1f-agent/CLAUDE.md |
| C# port PascalCase issue | ../../projects/1f-agent/initiatives/csharp-cutover-pilot/probe-followup-2026-05-01.md |
| Frontmatter-требования | frontmatter-requirements.md |
| docs-sync-push | ../../projects/gitlab-integration/docs-sync-push.py |