docs: EN source-of-truth workflow (en2ru + DOCSTRAT)

- en2ru.py: EN markdown -> Russian via Ollama translategemma-4b (tag: translategemma:latest)
  strips code-fence artifacts, prints jargon-review hints to stderr
- DOCSTRAT.md: rule -- EN canonical source, RU for comprehension only,
  never edit on drifted Russian (dedup/receipt mistranslation guard)
- ROADMAP.ru.md: sample RU generated from EN source
- AGENTS.md: section 8 documenting the EN-first workflow
This commit is contained in:
Pegas committed 2026-10-15 23:13:12 +05:00
1 parent bc68b68598
commit b4552df1a6
4 files changed
+292

No files matched your search

+11
View File
@@ -134,3 +134,14 @@ A **gap** is a question you know you cannot answer yet. Manage them, don't just
- If the question turned out irrelevant → `status='dismissed'` (prefer this over deleting). - If the question turned out irrelevant → `status='dismissed'` (prefer this over deleting).
- Raise `priority` when the user asks about the topic; lower it when it stops mattering. - Raise `priority` when the user asks about the topic; lower it when it stops mattering.
- Keep questions **specific and answerable** ("How does HNSW build its graph?" not "HNSW"). - Keep questions **specific and answerable** ("How does HNSW build its graph?" not "HNSW").
## 8. Documentation language (English source of truth)
All docs (`*.md`) are written in **English = canonical source of truth**. Russian
(`*.ru.md`) is generated for quick comprehension only; terminology may drift and must
NEVER be used as the editing basis (editing on drifted Russian breaks intent).
Workflow: write/edit EN → `python3 en2ru.py file.md` → `file.ru.md`. See
[`DOCSTRAT.md`](./DOCSTRAT.md). `translategemma:latest` renders clean orthography but
mishandles jargon (`dedup`→«дублирование», `receipt`→«результат»), so review the
stderr hints; keep unsure terms in Latin.
+35
View File
@@ -0,0 +1,35 @@
# Стратегия документов (English source of truth)
## Правило (обязательное)
- **Английский — канонический источник (source of truth).** Все правки вносятся
**только** в исходный `*.md` (EN).
- **Русский — только для бегного понимания.** Это вычислительный **инструмент**;
термиология может «поплыть».
- **Ник не использовать `.ru.md` за основу для правок.** Правим EN → перезапускаем
`en2ru.py`. Нарушение → накаплием ошибочный русский текст и **не можем сделать то,
что задумано** (опора на «поплывшую» термиологию ломает смысл).
### Причина
`translategemma:latest` **чисто пишет орфографию**, но **джаргон** переводит криво —
вплоть до переворота значения: `dedup` → «дублирование» (обратное значение),
`receipt` → «результат». Порусскому — вторичен и нуждается в ревью джаргона.
## Как
```bash
python3 en2ru.py ROADMAP.md # -> ROADMAP.ru.md
cat doc.md | python3 en2ru.py - # stdin -> doc.ru.md
```
- Вывод: `<file>.ru.md`.
- Джаргон-ревью (термы, где модель ошибается) — в **stderr**, не в RU-выводе.
- Оффлайн: зависимостей нет (`urllib`), модель уже в Ollama (`translategemma:latest`).
## Порядок работ
1. Пишем/правим **EN** источник.
2. `en2ru.py` → генерим **RU**.
3. Смотри **stderr** — правь flagged-термы (ревью, не авто).
4. Храни **оба** файла рядом: `X.md` (EN, канон) + `X.ru.md` (RU, понимание).
+148
View File
@@ -0,0 +1,148 @@
# Pegas Memory — Roadmap
Подсистема памяти и базы знаний агента Alicorn («Pegas»). Реализация на Python как отдельный
сервис, который Rust-ядро Alicorn вызывает через MCP/HTTP. Спецификация — в
`alicorn-agent/ARCHITECTURE.md` (ADR-4, ADR-11, ADR-12).
## Принятые решения (по итогам обсуждения)
1. **PostgreSQL = единственный источник истины.** Текст, метаданные, трейсы, верификация, outbox.
2. **Qdrant = векторный срез** (EmbeddingGemma 2, dim=768). Замена pgvector.
3. **Memgraph = графовый срез** (связи атомов, зависимости кода). Замена Apache AGE.
4. **pgvector и AGE — НЕ используем.** Векторы только в Qdrant, граф только в Memgraph.
5. **Единица знания — типизированный «атом»**, не чистый текст.
Типы: `text | definition | logical_rule | code | table | fact`.
6. **Память агента (episodic) ≠ база знаний (curated).** Разные таблицы/подсистемы.
7. **Код хранится отдельно**: content-addressed исходники + tree-sitter символы + граф зависимостей.
8. **Логические знания** — структурные правила (pattern→conclusion) с инференцией в графе.
9. **Исследование пробелов** — замкнутый цикл: detect→plan→acquire→parse→extract→value→verify→commit.
## Инфраструктура (проверено на сервере)
| Компонент | Адрес | Состояние |
|---|---|---|
| PostgreSQL 16 | atom-db :5434 (user atom, db content_atom) | работает |
| Qdrant | :6333 | работает |
| Memgraph (bolt) | :7687 | работает |
| Ollama | :11434 | работает |
| EmbeddingGemma 2 | `embeddinggemma-2-740m-Q8_0` | dim=768, R@1 0.922 (лучший для ru) |
| LLM-генерация | `lfm25-24b-a2b:128k`, `qwen3.5:4b`, `gemma4:E4B` | работает |
> EmbeddingGemma 2: полная размерность 768 (проверено curl-тестом). Q8_0 — оптимальный
> баланс производительность/память (MRR 0.944, не теряет производительность). Не путать с v1 `embeddinggemma:300m`.
## Модель данных (PostgreSQL — канон)
```
document — источники (web|pdf|epub|text|code_repo), uri, authority, content_hash
chunk — чистые сегменты (доказательства, locator) — НЕ для извлечения напрямую
atom — типизированные единицы знания (заявление + контент + доверие + статус)
atom_link — связи (inference|cause|reference|contradicts|depends_on|calls|defines)
citation — доказательная база (atom → document → locator → quoted_text)
code_symbol — tree-sitter символы (repo, path, language, name, kind, signature)
episodic_trace— память агента (шаги/действия) — отдельно от базы знаний
research_gap — выявленные пробелы для исследования
outbox — CDC-журнал для синхронизации в Qdrant + Memgraph
```
Статусы атома: `candidate → verified | stale | contested`.
Векторы НЕ хранятся в Postgres — только в Qdrant (payload: type/confidence/status/doc_id).
## Фазы
### Фаза 1 — Фундамент (MVP) ✅ ЗАВЕРШЕНА
- [x] Структура проекта + venv + зависимости
- [x] DDL Postgres (schema.sql)
- [x] Клиент эмбеддингов EmbeddingGemma 2 (dim=768)
- [x] Клиент Qdrant (коллекция `alicorn_atoms`)
- [x] Клиент Memgraph (узлы atom + рёбра atom_link)
- [x] Ингест: text/pdf/epub/web → чанки
- [x] Дистилляция чанка → атомы (LLM)
- [x] Синхронизация outbox PG → Qdrant + Memgraph
- [x] Smoke-тест end-to-end
### Фаза 2 — Исследование пробелов ✅ (реализовано, snippet-fallback)
- [x] Детект пробелов (research_gap + add_gap/list_gaps)
- [x] Планирование (приоритетная очередь пробелов)
- [x] Acquire: web-поиск (3 источника) + pdf/epub/text
- [x] Оценка ценности (verify_atom + min_confidence)
- [~] Верификация (verify_atom готов; полный автономный запуск требует прокси для зарубежных сайтов)
### Фаза 3 — Код ✅ / Логика ✅
- [x] tree-sitter: извлечение символов (function/class/method/type) — 43 символа сохранено
- [x] Граф зависимостей кода (calls/imports/inherits) — 310 зависимостей, 162 узла CodeSymbol
- [x] Логические правила + инференция в Memgraph (forward-chaining до fixpoint, 38 рёбер выведено)
### Фаза 4 — Интеграция с DSH (MCP) ✅ ВЫПОЛНЕНА
- [x] MCP-сервер (memory_search/store/ingest/add_gap/list_gaps/research/stats/get_context)
- [x] Подключение к DSH через @deepseek-ai/dsh-mcp-client (cordis.patch.yml, live-reload)
- [ ] Подключение к Rust-ядру Alicorn (Planner/Researcher) — когда ядро будет готово
## Дополнительно реализовано
- Опциональный прокси (PROXY_URL, SOCKS5/HTTP) + улучшенный User-Agent для полной загрузки страниц.
- Фильтрация артефактов графа кода (dst="." от относительных импортов).
- Дедупликация в memory_store (опциональный dedup).
(после MVP)
- Семантическая дедупликация атомов (эмбеддинги EmbeddingGemma 2, порог 0.90, конфигуруемо).
- Фильтрация шумных logical_rule (только kind-рёбры) в инференции.
## Критические риски
- EmbeddingGemma 2 dim=768 — не совместима с bge-m3 (1024) → отдельная коллекция, без миграции.
- Memgraph in-memory: объём графа ограничен RAM.
- Дистилляция зависит от качества локальной LLM — нужна верификация.
## Самообучение (self-improvement) — статус и планы
### Что работает (4 механизма, но НЕ замкнутый loop)
1. **Верификация** (`verify_atom`/`memory_verify` + `verify.py`). LLM self-critique
(`duplicate | corroborates | contradicts`) + **меж-системная подтверждение** (≥1 другой
`doc_id`, cosine ≥ 0.90) → ставка растёт, статус `verified`. Самый зрелый этап.
2. **Логическая инференция** (`memory_infer` + `inference.py`). Применение правил к рёбрам
графа до fixpoint (`imports → depends_on` и т.д.). ⚠️ Даст **только связи**, не новые факты.
Правила **статичные/ручное написание**, новых не выводит сама.
3. **Синтез контекста** (`memory_get_context`). Ищет Qdrant, кластеризует + суммирует атомы.
Может **создавать gap** из исходного текста → есть *реактивное* появление вопроса из источника.
4. **Эпизодические следы** (`pm_episodic_trace`). План/действие/наблюдение/рефлексия —
отдельно от БЗ. Самый «рефлексивный», но **условный**, не привязанный к gap.
### Что не хватает (дыры в петлях, а не в примитивах)
| Пробел | Почему критично |
|---|---|
| **① цепочка gap↛answer («дыра commit»)** | При закрытии вакуума **ответ не автоматически
пишется обратно в атомы** → знания улетучиваются, приходится пере-исследовать. |
| **② Самообучение схемы инференции** | Ничего не *предлагает новые правила* из повторяющихся
`verified`-атомов. Это как раз слабое место Hermes-аналога. |
| **③ Застарелость/забывание (staleness guard)** | `verify` ставит `verified`, но **ничто не
помечает `stale`** пере-проверкой по свежему источнику. Путь →`stale` не идёт автоматически. |
| **④ Поверхность противоречий** | `verify` ловит конфликт только в окрестности атома. Ничего
не сканирует весь граф → противоречия не переходят в gap автоматически. |
| **⑤ Процедурная/рецепт-память** | Повторяющиеся успешные воркфлоу не ловятся как переиспользуемые атомы. |
| **⑥ Пассивное создание gap** | Gap создаются через `add_gap` (ручно) или из `memory_get_context`.
Нет авто-обнаружения из не-верифицированного/переносного контента. |
### Приоритет
Hermes-анализ показал: Pegas сильна в **«detect»** (verify, infer), слаба в **«commit»** и
**«maintain»**. Слабые места — **feedback-петли**, а не примитивы. Поэтому максимальный выигрыш:
① дыра «commit» × ③ гвард сталкнутости × ② самообучение правил инференции — это **закрывает loop**.
### Планируемые улучшения (Tier)
- **Tier 1 — высокий приоритет, безопасно / малый**
- ① **Окончите дырой** — после `verify`, подняв атомы из gap, запустить синтез обратно в
`memory_ingest/synthesis` (надпись верифицированного ответа в атомы).
- ③ **Гвард сталкнутости** — `update_status`: пере-`web_search` по URL/date атома;
противоречит/устарел → `stale`. Реализуйте путь →`stale`, который сейчас не ходит.
- **Tier 2 — средний приоритет**
- ② **Самообучение правил инференции** — `memory_infer` **предлагает кандидаты**
`logical_rule` на ревью (никогда не применяет автоматически).
- ⑥ **Процедурная/рецепт-память** (паттерн из mem0, `procedural memory`) — агент действует
(это я через `mcp__pegas-memory__*`), поэтому повторы успешных `/запрос` или воркфлоу ≥N
раз с `verified`-результатом → **атомный** паттерн-рецепт (не файлы Hermes, а атомно с
dedup). Формат receipt: **goal → шаги → условия → результат** (переиспользуемый воркфлоу).
- **Tier 3 — структурные**
- ④ **Сканирование на противоречия** — периодический бэк-проход графа на противоречия → авто gap.
- ⑥ **Авто-обнаружение gap** — `verify` открывает gap на конфликт/дыру в источнике.
+98
View File
@@ -0,0 +1,98 @@
#!/usr/bin/env python3
"""en2ru — English-source-of-truth -> Russian comprehension doc.
RULE (see DOCSTRAT.md): English is the CANONICAL source of truth. Russian is
only a comprehension aid; terminology may drift and MUST NEVER be used as the
editing basis. Always edit the `*.md` (EN), then re-run this to refresh
`<file>.ru.md`.
Usage:
python3 en2ru.py path/to/doc.md # writes path/to/doc.ru.md
cat doc.md | python3 en2ru.py - # stdin -> stdout + doc.ru.md
Model: translategemma:latest (Ollama, local @ 127.0.0.1:11434).
Jargon drift hints are printed to STDERR (never inside the RU output).
No external deps (urllib only) so it works offline after the model is pulled.
"""
import sys
import json
import urllib.request
OLLAMA_URL = "http://127.0.0.1:11434/v1/chat/completions"
MODEL = "translategemma:latest"
# Terms TranslateGemma-4b mishandles -> flag for human review (never auto-fix).
# Maps the EN token to the safe RU handling. JAPN is intentional: keep short.
JARGON_HINTS = [
("dedup", "дедупляция / удаление дублей"), # NOT «дублирование» (reversed!)
("receipt", "рецепт (паттерн действий)"), # NOT «результат»
("atom-level", "на уровне атомов"),
("stale", "устаревший (status=stale)"),
("gap", "пробел (gap)"),
("verify", "верификация / verify"),
("rerank", "реранг / reranking"),
("cache", "кэш"),
("inferred", "выведено / inferred"),
("web_search", "web_search (инструмент)"),
("ingest", "ингестия / загрузка / ingest"),
]
def translate(text: str) -> str:
prompt = (
"Translate the following Markdown technical text to Russian.\n"
"Rules:\n"
"- Output ONLY the Russian text. Preserve Markdown structure, code blocks,\n"
" backticks, code identifiers, URLs, command and tool names verbatim.\n"
"- Do NOT translate: code, identifiers, tool names, file extensions, URLs,\n"
" and product names (Memgraph, PostgreSQL, Qdrant, Memgraph).\n"
"- If unsure about a term, keep it as-is in Latin (do not guess).\n"
"- Words you must handle carefully: "
+ ", ".join(en for en, _ in JARGON_HINTS) + ".\n\n"
"TEXT:\n```\n" + text + "\n```"
)
payload = {
"model": MODEL,
"stream": False,
"messages": [{"role": "user", "content": prompt}],
}
req = urllib.request.Request(
OLLAMA_URL,
data=json.dumps(payload).encode("utf-8"),
headers={"Content-Type": "application/json"},
)
with urllib.request.urlopen(req, timeout=300) as resp:
return json.loads(resp.read())["choices"][0]["message"]["content"]
def main() -> None:
if len(sys.argv) < 2 or sys.argv[1] == "-":
src = sys.stdin.read()
base = "doc"
else:
with open(sys.argv[1], encoding="utf-8") as fh:
src = fh.read()
base = sys.argv[1].rsplit(".", 1)[0]
ru = translate(src)
# model echoes the prompt's ``` fences -> drop leading/trailing fence lines
lines = ru.splitlines()
if lines and lines[0].strip() == "```":
lines = lines[1:]
if lines and lines[-1].strip() == "```":
lines = lines[:-1]
ru = "\n".join(lines).strip()
out_path = base + ".ru.md"
with open(out_path, "w", encoding="utf-8") as fh:
fh.write(ru)
low = src.lower()
print("# en2ru -> " + out_path, file=sys.stderr)
for en, hint in JARGON_HINTS:
if en.lower() in low:
print(" review: '%s' -> ensure '%s'" % (en, hint), file=sys.stderr)
print("OK: " + out_path)
if __name__ == "__main__":
main()