Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ New routers import domain services directly (`from modules.monitoring.service im

**Platform agent fallback** (`prompts/platform-agent.md`): When a chat session has no `system_prompt` set, `modules/chat/facade.py` loads this file as the system prompt (lazy, cached per-process) before falling back to `llm.get_system_prompt()`. Persona helps end-users configure their own assistants; no admin/ops content. Override path via `PLATFORM_AGENT_PROMPT_FILE`.

**Role-specific prompt templates** (`prompts/lawyer-ru.md`, `lawyer-kz.md`, `accountant-kz.md`, `README-roles.md`): hand-written system prompts for widget/bot instances tied to the static legal/accountancy collections. Not loaded automatically — admin pastes content into the instance's `system_prompt` field. Each enforces: cite article + code, warn about редакция drift, refuse aiding crime, no legal/tax opinions. README maps slug → role → which collections to attach.
**Role-specific prompt templates** (`prompts/lawyer-ru.md`, `lawyer-kz.md`, `accountant-kz.md`, `seo-ru.md`, `README-roles.md`): hand-written system prompts for widget/bot instances tied to the static legal / accountancy / SEO collections. Not loaded automatically — admin pastes content into the instance's `system_prompt` field. Each enforces source-of-truth discipline (legal/tax: cite article + code, warn about редакция drift, refuse aiding crime; SEO: always knowledge_search first, mark archive age, distinguish white/grey/black-hat, never promise rankings). README maps slug → role → which collections to attach.

**Bridge HOME isolation** (`services/bridge/src/providers/claude/provider.py`): The Claude CLI subprocess spawned by the bridge inherits `$HOME` from the service, so it picks up `/root/CLAUDE.md` and user-memory files — which historically leaked admin context into end-user chats. Set `BRIDGE_ISOLATE_HOME=1` in `.env` to spawn the CLI with `HOME=/var/lib/ai-secretary-bridge` and `cwd=<home>/sandbox` (outside `/root`), with credentials symlinked from the real `~/.claude/`. Default off to avoid breaking dev setups without the isolated dir.

Expand All @@ -258,7 +258,7 @@ New routers import domain services directly (`from modules.monitoring.service im

**Per-user Claude token tracking** (shared $100 Anthropic plan): `usage_log` table has nullable `user_id` (migration `0024_usage_log_user_id`) keyed to chat session owner. `OpenAICompatibleProvider.last_usage` populated from response usage in non-stream path, from final-chunk usage or `tiktoken` estimate in stream path; `CloudLLMService.last_usage` proxies to provider. `modules/chat/facade.py:_log_llm_usage` writes one row per Claude/`claude_bridge` response (best-effort, never raises) with `service_type=llm`, `units_consumed=input+output`, `details={input_tokens, output_tokens, model, estimated}`. Period bounds in `modules/monitoring/period.py` — anchor day-of-month (default 30, capped to last day for short months). Endpoints: `GET /admin/usage/me` (any auth, own total), `GET /admin/usage/by-user` (admin, all users sorted desc by tokens). Mobile shows a thin orange→red bar under the context indicator with the user's own period total ([mobile/src/views/ChatView.vue](mobile/src/views/ChatView.vue)); admin Users view has a "Токены" column ([admin/src/views/UsersView.vue](admin/src/views/UsersView.vue)). Only Claude is tracked — Gemini/OpenAI/vLLM are skipped by `_is_claude_provider`. Streaming numbers are `tiktoken`-estimated (the bridge doesn't currently emit `usage` chunks); non-streaming responses use real Anthropic numbers.

**Static legal & accountancy collections** (`scripts/scrape_digitax/`): scraping pipeline for fixed corpora (federal codes, tax authority pages, professional bodies). Three steps: `scrape.py --site <slug>` (BFS crawler, BFS link-extractors per site), `parse.py --site <slug>` (HTML→Markdown via lxml + per-site `CONTENT_SELECTORS`), `upload.py --site <slug>` (writes DB rows + copies to `wiki-pages/<slug>/`). Site catalog in `config.py:SITES` — 7 Irish accountancy sites (digitax) + 3 RU bookkeeping (USN) + 10 RU federal codes (consultant.ru) + 7 KZ codes (adilet.zan.kz) + 2 KZ accountancy practical (kgd.gov.kz, mybuh.kz; configured but not scraped). consultant.ru codes: each `cons_doc_LAW_<id>` is a BFS root with article-level URLs branching out; sidebar pollution stripped via `div.seo-links` removal in `parse.py:strip_boilerplate`. adilet.zan.kz codes: each Kazakh code is a single monolithic page (~1.5–5 MB), needs `verify_ssl: False` (Kazakh root CA not in certifi); content selector `div.container_gamma.text.text_upd`. **Critical: global `POST /admin/wiki-rag/reload` only re-indexes the legacy WIKI_DIR (root-level files), NOT collections.** After bulk upload, loop `POST /admin/wiki-rag/collections/{id}/reload` per collection. Server-side runs of `upload.py` skip self-copy when source dir is `wiki-pages/` (parsed/ absent on production).
**Static legal & accountancy collections** (`scripts/scrape_digitax/`): scraping pipeline for fixed corpora (federal codes, tax authority pages, professional bodies, SEO knowledge bases). Three steps: `scrape.py --site <slug>` (BFS crawler, BFS link-extractors per site), `parse.py --site <slug>` (HTML→Markdown via lxml + per-site `CONTENT_SELECTORS`), `upload.py --site <slug>` (writes DB rows + copies to `wiki-pages/<slug>/`). Site catalog in `config.py:SITES` — 7 Irish accountancy sites (digitax) + 3 RU bookkeeping (USN) + 10 RU federal codes (consultant.ru) + 7 KZ codes (adilet.zan.kz) + 2 KZ accountancy practical (kgd.gov.kz, mybuh.kz; configured but not scraped) + 1 RU SEO portal (sbup.com, type `forum`, engine `smf`). SMF (Simple Machines Forum 2.x) sites use `filter_smf_forum` + `extract_links_smf` in `scrape.py`: drops action handlers (`?action=login|register|profile|admin|search|...`), profile/admin/themes/avatars/attachments paths, and strips volatile `PHPSESSID` / `sa=` / `msg=` query params so BFS doesn't explode on session-tagged duplicate URLs. Sites with `engine: smf` in their cfg get a dedicated thread parser (`parse_forum_smf`) that merges all `.post_wrapper` posts on a page into one md doc with per-post sub-headers, drops nested quotes/signatures and SMF action chrome ("Записан", "« Ответ #N »", "Цитировать"); non-thread URLs (board indexes, MediaWiki `/wiki/` pages, articles) silently fall through to `parse_generic_page` with the multi-engine selector chain (`#mw-content-text` → `#forumposts` → `#main_content_section` → `<main>` → `<body>`). consultant.ru codes: each `cons_doc_LAW_<id>` is a BFS root with article-level URLs branching out; sidebar pollution stripped via `div.seo-links` removal in `parse.py:strip_boilerplate`. adilet.zan.kz codes: each Kazakh code is a single monolithic page (~1.5–5 MB), needs `verify_ssl: False` (Kazakh root CA not in certifi); content selector `div.container_gamma.text.text_upd`. **Critical: global `POST /admin/wiki-rag/reload` only re-indexes the legacy WIKI_DIR (root-level files), NOT collections.** After bulk upload, loop `POST /admin/wiki-rag/collections/{id}/reload` per collection. Server-side runs of `upload.py` skip self-copy when source dir is `wiki-pages/` (parsed/ absent on production).

### Frontend Architecture

Expand Down
1 change: 1 addition & 0 deletions prompts/README-roles.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
| `lawyer-ru.md` | Юрист РФ | `ru-uk-rf`, `ru-koap-rf`, `ru-upk-rf`, `ru-gk-rf-1..4`, `ru-tk-rf`, `ru-sk-rf`, `ru-zhk-rf`, `ru-nk-rf-glava-26-2` |
| `lawyer-kz.md` | Юрист РК | `kz-uk-rk`, `kz-koap-rk`, `kz-upk-rk`, `kz-gk-rk-general`, `kz-gk-rk-special`, `kz-tk-rk`, `kz-nk-rk` |
| `accountant-kz.md` | Бухгалтер РК | `kz-nk-rk`, `kz-tk-rk` (и в будущем — `kz-kgd`, `kz-mybuh` после второго захода) |
| `seo-ru.md` | SEO-ассистент (RU) | `ru-sbup-seo` (учебник «SEO от А до Я» + SEO-вики + статьи + архив форума sbup.com) |

Бухгалтер РФ уже частично работает на коллекциях `ru-fns-usn`, `ru-nk-rf-glava-26-2`, `ru-moedelo-usn` — отдельный системный промпт для него можно будет дописать после того, как добавим практические разъяснения по ОСНО / НДС / зарплате (сейчас покрыто только УСН).

Expand Down
31 changes: 31 additions & 0 deletions prompts/seo-ru.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Ты — SEO-ассистент, специализирующийся на поисковой оптимизации сайтов под Яндекс и Google. Тебе доступна база знаний на основе материалов sbup.com: учебник «SEO от А до Я», SEO-вики, статьи и многолетний архив профильного форума вебмастеров.

Правила работы:

1. **Всегда ищи в базе знаний.** Перед ответом вызывай инструмент knowledge_search — найди релевантные разделы учебника, статьи или ветки форума. Не отвечай по памяти даже на простые вопросы — алгоритмы поисковиков и практики SEO меняются, источник важнее «общих знаний».

2. **Цитируй источник.** В ответе указывай, откуда взято: учебник («SEO от А до Я», глава X), статья на sbup.com, или ветка форума (название темы + автор поста, если виден). Если в базе ничего точного не нашлось — честно скажи и предложи задать более конкретный вопрос.

3. **Помни про возраст материалов.** sbup.com — большой архив, в нём есть посты разных лет. Алгоритмы Яндекса и Google переписываются регулярно: то, что работало 5 лет назад, сейчас может быть нейтральным фактором или даже наказываться. Всегда отмечай: «по материалам форума за такой-то период», «с тех пор алгоритм мог измениться». Для свежих апдейтов (последние 6–12 месяцев) дополнительно используй web_search.

4. **Различай белое, серое и чёрное SEO.**
- **Белое:** контент, удобство, скорость, корректная техничка, естественные ссылки, соответствие интенту запроса. Это рекомендуется по умолчанию.
- **Серое:** биржи ссылок, агрессивный анкорный профиль, накрутка ПФ, дорвеи-сателлиты с редиректом. Объясняй риски (фильтры Минусинск, АГС, Пингвин/SpamBrain), но не запрещай — пользователь имеет право знать, чем рискует.
- **Чёрное:** взлом чужих сайтов, паразитирование на чужих доменах, клоакинг с обманом, негативное SEO против конкурентов. Отказывайся от запросов «как накатить негативное SEO на конкурента», «как взломать сайт ради ссылки», «как обмануть антифрод» — кратко объясни почему (бан в поиске + возможная уголовная статья) и закрой тему.

5. **Структура ответа на типичный SEO-вопрос:**
- Краткий вывод (1–2 предложения).
- Что делать пошагово (нумерованный список).
- Какие риски / типичные ошибки.
- Откуда это (источник в базе знаний + год, если виден).
- Когда стоит проверить актуальность (если материал может устареть).

6. **Не обещай позиции.** SEO — это вероятности, а не гарантии. Никогда не пиши «через 2 недели вы будете в ТОП-3». Пиши «эти меры обычно дают рост такого-то порядка», «у конкурентов в этой нише это сработало за столько-то месяцев». Если пользователь требует «гарантию ТОП-1» — объясни, почему её не существует, и предложи реалистичные KPI (видимость, доля небрендового трафика, рост по группе ключей).

7. **Учитывай регион пользователя.** SEO под Яндекс (РФ, СНГ) и под Google (мир) — две разных дисциплины: разные факторы ранжирования, разный вес ссылок vs ПФ, разные инструменты вебмастера. Если непонятно — спроси, под какой поисковик оптимизируем. По умолчанию для рунета — приоритет Яндекс.

8. **Технические рекомендации давай конкретные.** Не «сделайте sitemap», а «sitemap.xml в корне, до 50 000 URL или 50 МБ, ссылка в robots.txt и в Search Console / Вебмастере». Если речь про robots.txt, мета-теги, заголовки HTTP, структурированные данные — приводи минимальный рабочий пример кода/разметки.

9. **Язык ответа** — русский, профессиональный, без воды. Термины SEO (краулинг, индекс, поведенческие факторы, анкор, перелинковка, каннибализация, EEAT, спам-фильтры) использовать свободно. Если пользователь явно новичок — кратко расшифровывай при первом упоминании.

10. **Когда нужен живой специалист.** В конце ответа на сложные вопросы (вывод сайта из-под фильтра, аудит большого e-commerce, миграция на новый домен/CMS, спор с Яндексом через Платон Щукин) — рекомендуй профильного SEO-специалиста с практикой именно в этой нише. Ассистент даёт справку и направление, но за конкретным проектом нужен человек.
29 changes: 29 additions & 0 deletions scripts/scrape_digitax/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,35 @@
"type": "legal",
"description": "Гражданский кодекс Республики Казахстан (особенная часть) от 01.07.1999 № 409-I.",
},
# ------------------------------------------------------------------
# Russian SEO assistant — sbup.com (SMF 2.1 forum + wiki + articles)
# ------------------------------------------------------------------
"ru-sbup-seo": {
"name": "sbup.com — SEO учебник, вики, форум",
"base_url": "https://www.sbup.com",
"seed_paths": [
"/",
"/seo-forum/",
"/wiki/",
"/seo-articales/",
# The flagship A-to-Z SEO tutorial — long thread, ~21 pages.
"/seo-forum/poiskovaya_optimizaciya_v_obshih_chertah/seo_poiskovaya_optimizaciya_ot_a_do_ya/",
# Top-level forum sections we definitely want to cover.
"/seo-forum/poiskovaya_optimizaciya_v_obshih_chertah/",
"/seo-forum/indeksaciya_saita/",
"/seo-forum/vneshnyaya_optimizaciya_saita_-_seo/",
"/seo-forum/vnutrennyaya_optimizaciya_saita_-_seo/",
],
"stay_under": "/", # whole sbup.com — content lives across forum/wiki/articles
"max_pages": 8000, # SMF forum is large, BFS will naturally exhaust useful URLs
"type": "forum",
"engine": "smf", # Simple Machines Forum 2.1
"description": (
"sbup.com — русскоязычный SEO-портал: учебник «Поисковая оптимизация "
"от А до Я», SEO-вики, статьи, форум вебмастеров (SMF 2.1). "
"Архив за много лет, охватывает белое/серое SEO под Яндекс и Google."
),
},
"icaew-ireland": {
"name": "ICAEW Ireland Standards",
"base_url": "https://www.icaew.com",
Expand Down
Loading
Loading