Перейти к содержанию

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 → рецепты и описания

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

Источник кода: 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) — выше = точнее, медленнее build
  • ef_construction: 500 — выше = точнее, медленнее build
  • top_k: default 10
  • rerank: 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

Диагностика «выдан не тот документ»

  1. Проверить Tags в DocsChunks — tags = главный сигнал (weight 4.0)
  2. Проверить PageRank — возможно документ с высоким PR побеждает
  3. Проверить Phase 0/1 weights — возможно section демотирует правильный документ
  4. Проверить frontmatter — section/tags должны быть корректными
  5. Проверить 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

  1. 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.

  2. 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# стендах.

  3. PG vector_search.docs_chunks REMOVED. [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 на удаление VECTORDB API + VectorDbConnectionString config; DROP SCHEMA vector_search CASCADE на PG-стендах.

  4. QMD MCP (docs-srv:3000) shutdown. Legacy MCP server использует OpenAI embeddings + Cohere rerank. Планируется выключение после миграции всех потребителей на zvec-search. Точный срок не определён.

  5. 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. Требует проверки.

  6. FTS LANGUAGE 1033. Fulltext index на DocsChunks создан с LANGUAGE 1033 (English). Русский текст индексируется English stemmer. FREETEXTTABLE работает морфологически, но качество может страдать на русских word forms. MSSQL 2019+ поддерживает LANGUAGE 1049 (Russian) — но переключение требует rebuild FTS catalog.

  7. Embedding column не используется в runtime. DocsChunks.Embedding (varbinary, float32[1024]) существует, но cascade v4/v5 не использует cosine similarity. Embedding нужен только для «Full mode» (с external embedding API) — этот режим не deployed.

  8. 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

Review log (placeholder)