Stack AI: карта проєкту
Цей документ ділить stack-ai на основні частини і пояснює, навіщо кожна з них існує. Детальні документи по сервісах лежать у index, по процесах - у index, по базі й інфраструктурі - у index.
Що таке stack-ai
stack-ai - backend-сервіс для AI-задач BeSocial/Stack. У поточному вигляді в ньому є два великі домени:
- Legend - генерація Lady Legend для TU: canon, timeline, facts, narrative blocks, full text, translations і QA.
- Tips / Memory - допомога оператору в діалозі RU←>TU: синхронізація даних, векторна пам’ять, retrieval, LLM-аналіз контексту і генерація 3 варіантів відповіді.
Це не основний Stack backend і не Electron-клієнт. stack-ai стоїть поруч: приймає запити від внутрішніх клієнтів, ходить в upstream API партнерських сайтів, читає/пише Supabase і викликає LLM-провайдерів.
Головна бізнес-ідея
Оператор не має відповідати “з повітря”. Хороший AI-помічник повинен знати:
- хто такий RU;
- хто така TU;
- яка в них історія спілкування;
- які є notes;
- які дані профілю актуальні;
- що вже було сказано раніше;
- який режим відповіді потрібен зараз;
- які факти не можна порушувати.
Тому в проєкті є два різні механізми пам’яті:
| Механізм | Для чого |
|---|---|
| Legend pipeline | Створює структурований фон TU: біографія, характер, дитинство, плани, блоки легенди |
| Vector memory | Зберігає історію конкретного діалогу RU←>TU: profile, photo, notes, conversation chunks |
Legend відповідає на питання “яка легенда у TU?“. Tips/memory відповідає на питання “що зараз написати цьому RU в цьому діалозі?“.
Частини проєкту
| Частина | Код | Документація | Роль |
|---|---|---|---|
| Runtime / Nest shell | src/main.ts, src/app.module.ts, src/common/* | runtime | Global prefix, wrapper responses, errors, validation, Swagger |
| Auth / roles | src/auth/*, src/common/stack-roles.enum.ts | auth | JWT parsing, role gates, @ApiRoles |
| Config | src/settings.ts, src/modules/tips-memory/tips-memory-config.service.ts | config | Env, provider keys, Supabase config, defaults |
| Supabase / DB | src/database/*, supabase/migrations/* | supabase | Postgres, pgvector, snapshots, token usage |
| Legend | src/modules/legend/* | legend-service | Lady Legend pipeline |
| Tips API | src/modules/tips/* | tips-service | Актуальна orchestration-логіка /tips/status і /tips/generate |
| Experimental tips-memory API | src/modules/tips-memory/tips-memory.controller.ts | tips-memory-controller | Debug/experimental endpoints для memory і assistant workflow |
| Memory | src/modules/tips-memory/memory.service.ts | memory-service, memory-chunk | Ingest/search chunks у Supabase pgvector |
| Assistant | src/modules/tips-memory/services/assistant-service.ts | assistant-service | LLM workflow: photo/context/drafts/evaluate/repair |
| AI providers | gemini-provider.service.ts, xai-provider.service.ts | ai-providers | Єдиний transport Gemini/xAI structured JSON |
| Upstream | src/upstream/*, src/modules/tips/services/* | upstream-service | Забір profiles/messages/notes із зовнішніх систем |
| Token usage | src/modules/tips/services/token-usage.service.ts | token-usage-service | Облік вартості LLM calls |
Як читати проєкт
Якщо потрібно зрозуміти весь runtime, читати в такому порядку:
- runtime - як Nest-застосунок обгортає відповіді й помилки.
- auth - які ролі реально закривають endpoints.
- project-overview - ця карта.
- tips-service - актуальний користувацький flow генерації відповіді.
- generation-pipeline - детальна покрокова механіка generate.
- memory-service і supabase - як працює long-term memory.
- assistant-service і ai-providers - як викликаються LLM.
- legend-service - окремий домен Lady Legend.
Якщо задача тільки про базу, йти одразу в supabase. Якщо задача про Swagger/API, звіряти docs з DTO і controller code.
Межі доменів
Legend
Legend не залежить від vector memory і не пише результат у Supabase. Його стан живе в LegendJobStore in-memory. Він працює як staged async pipeline: один stage на один запит, результат забирається polling-ом.
Це важливо: Legend не можна вважати durable storage. Якщо frontend хоче зберегти підсумкову легенду, він має зберегти її сам або викликати окремий зовнішній сервіс.
Tips
Tips - актуальний шар для Electron-клієнта оператора. Він працює по парі ruId + tuId і family. Його задача:
- Перевірити, що вже є в Supabase.
- Підтягнути свіжі дані з upstream.
- Оновити vector memory.
- Вибрати top chunks.
- Запустити assistant workflow.
- Записати token usage.
- Повернути оператору варіанти відповіді.
Зараз є важливий gap: POST /stack-ai/tips/generate уже виконує майже весь workflow, але HTTP response поки повертає hardcoded 3 рядки замість workflow.assistantOutput.drafts. Це окремо описано в generation-pipeline.
tips-memory
tips-memory - експериментальний і debug-friendly API, який залишився поруч із новим tips flow. Він корисний для ручного ingest/search/analyze/workflow, але стратегічно новий користувацький flow має жити навколо TipsController.
Supabase
Supabase - durable шар для:
- vector memory (
client_memory_chunks); - daily snapshots (
tips_daily_entity_snapshots); - token usage (
token_usage_log); - SQL RPC для retrieval (
match_client_memory_chunks).
Legend туди зараз не пише.
TODO: аналіз фото як окремий шар пам’яті
Зараз у tips-flow у базу фактично потрапляє URL фото з профілю, а не стабільний текстовий опис самого зображення. Це слабке місце: LLM під час генерації може отримувати photoUrl, але long-term memory не має нормального опису зовнішності, стилю фото, емоції, одягу, локації чи видимих деталей.
Потрібно додати окремий шар:
- Взяти photo URL з профілю TU або RU.
- Завантажити фото.
- Проаналізувати його через vision model.
- Перетворити результат у короткий структурований текст.
- Записати цей текст у
client_memory_chunksякsource='photo'. - Використовувати саме цей текст у retrieval, а не передавати сирий screenshot/image у кожен prompt.
Детальний план: photo-analysis-ingest і photo-analysis.
Простими словами про складні терміни
| Термін | Просте пояснення |
|---|---|
| Pipeline | Послідовність етапів, де кожен етап бере результат попереднього і додає новий шар |
| Canon | ”Залізна” база фактів, якій не можна суперечити далі |
| Anchor | Важлива точка або період життя, на якому будується біографія |
| Fact bank | Великий список маленьких фактів, з яких потім збирається текст |
| Chunk | Малий шматок пам’яті діалогу, який можна шукати через embedding |
| Embedding | Числовий вектор сенсу тексту; потрібен, щоб шукати не по словах, а по змісту |
| Retrieval | Вибір релевантних chunks із пам’яті перед LLM-викликом |
| Source weight | Невеликий boost для важливіших джерел пам’яті, наприклад notes важливіші за звичайну переписку |
| Daily sync | Один раз на день оновити profile/photo/notes і записати snapshots |
| Snapshot | Сирий знімок даних для cache/audit, не обов’язково використовується у vector search |
| Token usage | Облік, скільки токенів витратив кожен LLM call |
| Fallback | Запасний шлях, якщо основний зовнішній сервіс недоступний |
Потік даних у Tips
Electron
|
v
POST /stack-ai/tips/generate
|
v
DailySyncService -----> upstream profiles/notes
| |
v v
Supabase snapshots MemoryService ingest
| |
v v
MessagesService -----> upstream messages
|
v
fresh messages -> snapshots + vector chunks
|
v
MemoryService.search -> selectedChunks
|
v
AssistantService -> Gemini/xAI
|
v
TokenUsageService -> token_usage_log
|
v
HTTP responseПотік даних у Legend
Frontend
|
v
POST /stack-ai/legend/generate-profile
|
v
LegendJobStore running job
|
v
stage_0_canon -> stage_1_anchors -> stage_2_fact_bank -> stage_3_blocks
|
v
POST /stack-ai/legend/status
|
v
result.pipelineЩо зараз вважається стабільним
- Supabase memory schema і
match_client_memory_chunksописані як core для tips. MemoryService.ingest/search/getDialogStatus- основний шар роботи з vector memory.DailySyncService.syncFirstClick- основний механізм daily refresh.AssistantService.runWorkflow- основний LLM workflow, хоча response/tips/generateще не підключений до його drafts.- Legend staged pipeline - окремий async service, але без durable persistence.
Головні ризики
- HTTP errors завжди 200: клієнти мають дивитися
success, а не HTTP status. - RolesGuard пропускає endpoint без
@ApiRoles: забутий decorator відкриває route. - Legend jobs in-memory: restart backend видаляє status/result.
/tips/generateresponse ще placeholder: workflow уже працює, але drafts не повертаються.- Upstream реалізований не для всіх families: зараз багато routes є тільки для
golden; інші кидаютьNotImplementedException. - Supabase retrieval залежить від Gemini embeddings: без API key ingest/search падають.
- Debug
tips-memoryі новийtipsflow співіснують: важливо не плутати endpoints. - Photo memory ще неповна: потрібно зберігати не тільки URL фото, а й текстовий vision-аналіз.