Arxitektura¶
Bu sahifa Qanot AI ning ichki tuzilishini tavsiflaydi: agent loop, ma'lumotlar oqimi va komponentlar qanday bog'lanadi.
Tizim umumiy ko'rinishi¶
Telegram
|
TelegramAdapter
(aiogram 3.x)
|
+-------+-------+
| |
Agent CronScheduler
(har bir user) (APScheduler)
| |
+-----+-----+ spawn_isolated_agent()
| | |
Provider | ToolRegistry
(LLM) | |
| Context +---> Built-in Tools
| Tracker +---> Cron Tools
| +---> RAG Tools
| +---> Plugin Tools
|
+-----+-----+
| | |
Anthropic OpenAI Gemini Groq
| | | |
+--FailoverProvider--+
Ishga tushirish ketma-ketligi¶
qanot/main.py initsializatsiyani shu tartibda boshqaradi:
- Config yuklash --
load_config()config.jsonni o'qiydi - Workspace initsializatsiyasi --
init_workspace()birinchi ishga tushirishda shablonlarni ko'chiradi - Provider yaratish -- Yagona provider yoki ko'p provayderli
FailoverProvider - Context tracker yaratish -- Sessiya uchun token kuzatuvi
- Tool registry yaratish -- Bo'sh registry
- RAG engine initsializatsiyasi (agar yoqilgan bo'lsa) -- Embedder, vector store, RAG engine yaratish; workspace xotira fayllarini indekslash
- Built-in tool'larni ro'yxatga olish --
read_file,write_file,list_files,run_command,web_search,memory_search,session_status - Session writer yaratish -- JSONL log yozuvchi
- Cron scheduler yaratish -- Tool registry havolasi bilan APScheduler
- Cron tool'larni ro'yxatga olish --
cron_create,cron_list,cron_update,cron_delete - Plugin'larni yuklash -- Topish, import qilish, sozlash, plugin tool'larni ro'yxatga olish
- Agent yaratish -- Provider, tool'lar, sessiya, kontekstni ulash
- RAG tool'larni ro'yxatga olish --
rag_index,rag_search,rag_list,rag_forget(agent havolasi kerak) - Memory hook'larni ro'yxatga olish -- RAG indexer'ni memory write hodisalariga ulash
- Scheduler'ni ishga tushirish -- Job'larni yuklash, APScheduler'ni ishga tushirish
- Telegram'ni ishga tushirish -- Polling yoki webhook serverni boshlash
Agent loop¶
Asosiy agent loop har bir user xabar uchun 25 ta iteratsiyagacha ishlaydi:
User xabar
|
v
WAL Protocol scan (tuzatishlar, afzalliklar, qarorlar)
|
v
Compaction recovery tekshiruvi (kerak bo'lsa working buffer inject qilish)
|
v
Xabarni suhbat tarixiga qo'shish
|
+---> [Loop boshlanishi: iteratsiya 1..25]
| |
| Proaktiv compaction tekshiruvi (agar > 60%, compact qilish)
| |
| Xabarlarni tuzatish (yetim tool_result'larni tuzatish)
| |
| Tizim prompt'ini yig'ish (workspace fayllardan)
| |
| LLM provider'ni chaqirish (vaqtinchalik xatolar uchun qayta urinish bilan)
| |
| Token ishlatilishini kuzatish
| |
| +--- stop_reason == "tool_use" ---+
| | |
| | Tool call loop'larini tekshirish |
| | (3x bir xil chaqiruv, A-B-A-B) |
| | | |
| | Tool'larni bajarish (30s timeout)|
| | | |
| | Natijalarni tarixga qo'shish |
| | | |
| | [Loop davom] |
| | |
| +--- stop_reason == "end_turn" ----+
| | |
| | Oxirgi matn javobi |
| | Sessiyaga loglash |
| | Working buffer'ga qo'shish |
| | Kunlik yozuv yozish |
| | [Javobni qaytarish] |
| | |
| +--- boshqa / max iteratsiyalar ---+
| |
v v
Javob matni Xato xabar
Streaming varianti¶
run_turn_stream() bir xil loop'ni kuzatadi, lekin StreamEvent obyektlarini yield qiladi:
text_delta-- LLM dan matn qismitool_use-- tool bajarilmoqda (ko'rsatish uchun matn yo'q)done-- to'liqProviderResponsebilan oxirgi javob
Streaming variantida fallback bor: agar streaming vaqtinchalik xato bilan muvaffaqiyatsiz bo'lsa, bir marta streaming'siz chat() bilan qayta urinadi.
Har bir foydalanuvchi izolatsiyasi¶
Har bir Telegram foydalanuvchi izolyatsiya qilingan suhbat holatiga ega:
Agent._conversations: dict[str | None, list[dict]]
# kalit: user_id string (yoki cron job'lar uchun None)
# qiymat: xabar tarixi ro'yxati
Agent._locks: dict[str | None, asyncio.Lock]
# har bir foydalanuvchi uchun yozish xavfsizligi lock'i
Agent._last_active: dict[str | None, float]
# bo'sh turishni aniqlash uchun monotonic vaqt belgisi
- Turli foydalanuvchilarning xabarlari hech qachon aralashmaydi
- Har bir foydalanuvchi uchun lock'lar bir xil foydalanuvchidan xabarlarning bir vaqtda qayta ishlanishini oldini oladi
- 1 soatdan (3600s) ko'proq bo'sh turgan suhbatlar avtomatik o'chiriladi
- Cron job'lar o'zlarining izolyatsiya qilingan suhbatlari uchun
Noneni user_id sifatida ishlatadi
Tizim prompt yig'ish¶
build_system_prompt() prompt'ni workspace fayllardan yig'adi:
1. SOUL.md -- Asosiy shaxsiyat va ko'rsatmalar
2. IDENTITY.md -- Agent nomi, uslubi, emoji afzalliklari
3. SKILL.md -- Proaktiv agent xatti-harakatlari
4. TOOLS.md -- Tool hujjatlari
5. *_TOOLS.md -- Plugin tool hujjatlari
6. AGENTS.md -- Ish qoidalari
7. SESSION-STATE.md -- WAL yozuvlari (faol sessiya konteksti)
8. USER.md -- Inson konteksti
9. BOOTSTRAP.md -- Birinchi ishga tushirish marosimi (agar fayl mavjud bo'lsa)
+ Tool call uslub qoidalari (hardcoded)
+ Sessiya ma'lumotlari (sana, vaqt, kontekst %, tokenlar)
Minimal rejim (cron isolated agent'lar uchun): Faqat SOUL.md + TOOLS.md + sessiya ma'lumotlari.
Belgi byudjeti: - Har bir fayl uchun: maksimal 20,000 belgi (70% bosh / 20% quyruq qirqish) - Umumiy prompt: maksimal 150,000 belgi
{date}, {bot_name}, {owner_name}, {timezone} o'zgaruvchilari oxirgi prompt'da almashtiriladi.
Streaming pipeline¶
LLM Provider
|
| yields StreamEvent(type="text_delta", text="...")
v
Agent.run_turn_stream()
|
| yields StreamEvent chaqiruvchiga
v
TelegramAdapter._respond_stream()
|
| matnni yig'adi
| flush_interval da draft yuboradi
v
Bot.sendMessageDraft(chat_id, draft_id, text)
|
| final
v
Bot.sendMessage(chat_id, formatted_html)
Muhim nuqtalar:
- Tool bajarilayotganda draft yangilanishlari to'xtatiladi — race condition'larni oldini oladi
- Telegram adapter ortiqcha yangilanishlarni oldini olish uchun oxirgi yuborilgan draft matnini kuzatadi
- Har bir streaming sessiya noyob draft_id oladi
- Oxirgi xabar HTML formatlash bilan yuboriladi (Markdown konvertatsiya qilinadi)
Xato boshqaruvi va failover oqimi¶
Agent provider.chat() ni chaqiradi
|
+--- Muvaffaqiyat --> javobni qaytarish
|
+--- Exception ushlandi
| |
| classify_error(e) --> error_type
| |
| +--- PERMANENT (auth, billing)
| | --> darhol raise qilish
| |
| +--- TRANSIENT (rate_limit, overloaded, timeout)
| | --> eksponensial backoff bilan qayta urinish (2s, 4s, max 30s)
| | --> 2 ta qayta urinishgacha
| |
| +--- UNKNOWN
| --> darhol raise qilish
|
[Barcha qayta urinishlar muvaffaqiyatsiz bo'lsa]
|
+--- rate_limit --> "Limitga yetdik..."
+--- auth --> "API kalitda xatolik..."
+--- billing --> "API hisob muammosi..."
+--- boshqa --> "Xatolik yuz berdi..."
FailoverProvider bilan oqim kengayadi:
FailoverProvider.chat()
|
Faol provider'ni sinash
| |
| Muvaffaqiyat --> mark_success(), qaytarish
| |
| Muvaffaqiyatsizlik --> classify_error(), mark_failed()
| |
| cooldown = 120s * failure_count (max 600s)
| |
Keyingi mavjud provider'ni sinash
| ...
|
Barcha provider'lar tugadi --> oxirgi xatoni raise qilish
Kontekst boshqaruvi oqimi¶
Navbat N: input_tokens = 45,000 / 200,000 max (22.5%)
--> Oddiy ish
Navbat N+5: input_tokens = 100,000 (50%)
--> Working buffer FAOLLASHADI
--> Almashuvlar working-buffer.md ga loglanadi
Navbat N+10: taxminiy keyingi = 128,000 (64% > 60% chegarasi)
--> Proaktiv compaction ishga tushadi
--> Xabarlar: [birinchi 2] + [xulosa belgisi] + [oxirgi 4]
--> Token taxmini ~35% ga tushiriladi
Navbat N+20: xabarlarda compaction aniqlandi
--> Tiklash konteksti inject qilinadi:
- working-buffer.md
- SESSION-STATE.md
- bugungi kunlik yozuvlar
Sessiya loglash¶
Har bir xabar almashuvi sessions papkasidagi JSONL fayllariga loglanadi:
sessions/
├── 2025-01-15.jsonl # Oddiy suhbatlar
├── cron-heartbeat-20250115-160000.jsonl # Cron job sessiyalari
Har bir satr JSON obyekti:
{
"type": "message",
"id": "msg_000001",
"parentId": "",
"timestamp": "2025-01-15T10:30:00+00:00",
"message": {"role": "user", "content": "Hello"},
}
Assistant xabarlari ishlatilish statistikasi va model ma'lumotlarini o'z ichiga oladi. Fayl yozuvlari krossplatforma qulflashni ishlatadi (Unix'da fcntl.LOCK_EX, Windows'da graceful degradation).
Ma'lumotlar oqimi xulosasi¶
| Ma'lumot | Kim yozadi | Kim o'qiydi |
|---|---|---|
config.json |
Foydalanuvchi | load_config() |
SOUL.md, TOOLS.md, va boshqalar |
Foydalanuvchi / Agent / Plugin'lar | build_system_prompt() |
SESSION-STATE.md |
WAL protocol | Tizim prompt, memory_search |
memory/*.md (kunlik yozuvlar) |
Agent loop | memory_search, RAG indexer |
MEMORY.md |
Agent (tool'lar orqali) | memory_search, RAG indexer |
memory/working-buffer.md |
Context tracker | Compaction tiklash |
sessions/*.jsonl |
Session writer | Tashqi monitoring tool'lar |
cron/jobs.json |
Cron tool'lar / Foydalanuvchi | Cron scheduler |
rag.db |
RAG engine | RAG search |
uploads/* |
Telegram adapter | Agent (read_file orqali) |
Modul ma'lumotnomasi¶
Yuqorida tavsiflangan asosiy modullardan tashqari, Qanot quyidagi qo'shimcha komponentlarni o'z ichiga oladi:
Asosiy modullar¶
| Modul | Vazifasi |
|---|---|
agent.py |
Asosiy agent loop (25 iteratsiya, circuit breaker, natijaga yo'naltirilgan loop'lar) |
agent_bot.py |
Alohida agent bot runtime |
backup.py |
Ishga tushirishdagi backup funksionallik |
config.py |
JSON config yuklovchi, Config dataclass, SecretRef |
context.py |
Token kuzatuvi, 50% buffer, 60% compaction chegarasi |
compaction.py |
Ko'p bosqichli LLM xulosalanishi (OpenClaw uslubida) |
routing.py |
3 bosqichli model routing (Haiku/Sonnet/Opus) |
voice.py |
Voice provider integratsiyasi (Muxlisa, KotibAI, Aisha, Whisper) |
ratelimit.py |
Har bir foydalanuvchi uchun sliding window rate limiter |
links.py |
Avtomatik URL preview inject qilish |
utils.py |
Yordamchi funksiyalar (qirqish, helper'lar) |
fs_safe.py |
Xavfsiz fayl yozish (tizim papkalarni bloklash, symlink tekshirish) |
secrets.py |
SecretRef resolver (env var'lar, fayllar) |
session.py |
JSONL append-only sessiya loglash (krossplatforma qulflash) |
prompt.py |
Tizim prompt yig'uvchi (9 bo'lim + MEMORY.md inject) |
telegram.py |
aiogram 3.x adapter (stream/partial/blocked + inline tugmalar) |
dashboard.py |
:8765 portdagi web dashboard server (aiohttp) |
dashboard_html.py |
Dashboard HTML (Bloomberg Terminal estetikasi) |
daemon.py |
Krossplatforma daemon (systemd/launchd/schtasks) |
scheduler.py |
APScheduler cron (isolated + systemEvent rejimlari) |
cli.py |
CLI: init/start/stop/restart/status/config/update/doctor |
mcp_client.py |
MCP (Model Context Protocol) klient -- tashqi tool serverlariga ulanish |
webchat.py |
WebSocket asosidagi webchat adapter |
hooks.py |
Hayot sikli hooklari (on_startup, on_shutdown, on_pre_turn, on_post_turn) |
Tool modullari (tools/)¶
| Modul | Vazifasi |
|---|---|
builtin.py |
read/write/list/run_command/send_file/memory/session/cost |
cron.py |
4 ta cron boshqaruv tool'lari |
web.py |
web_search (Brave) + web_fetch (SSRF himoyalangan) |
image.py |
generate_image + edit_image (Gemini) |
rag.py |
4 ta RAG tool (search/index/list/forget) |
delegate.py |
Ko'p agentli delegatsiya (delegate/converse/spawn) |
subagent.py |
Sub-agent boshqaruvi |
agent_manager.py |
agent'larni create/update/delete/restart qilish |
doctor.py |
Tizim diagnostikasi |
workspace.py |
Workspace init + shablonlar |
jobs_io.py |
Cron jobs JSON I/O yordamchi funksiyalar |
browser.py |
Playwright asosidagi brauzer toollar (browse/click/fill/screenshot/extract) |
skills.py |
Ko'nikma boshqaruvi (create/list/run/delete) |
memories.py |
Anthropic xotira tooli (/memories CRUD operatsiyalari) |