AI pipeline (Gemini)¶
SDK-гүй REST урсгал: чат, дуу хоолой, шууд орчуулга — серверийн талд ажиллах хэрэгслүүдтэй (function calling).
Ерөнхий зураг¶
Хөтөч (/me/ai, /me/translate)
│ ижил-origin fetch (CSRF толгойтой)
▼
Next.js BFF /api/ai/{chat,stt,tts,translate} ← хэлбэрийг шалгаж JWT хавсаргана
│ server→server
▼
Go API /api/v1/ai/* (JWT + хязгаар ~20/мин)
│
▼
usecases/ai ──────────► pkg/gemini ──────► Gemini REST API
│ ▲ (429/5xx/сүлжээнд 3× backoff retry)
│ └─ functionResponse
▼
ToolDef.Execute() ← BACKEND дээр, хүсэлтийн контексттэй ажиллана
├─ search_knowledge → ai_knowledge хүснэгт
└─ get_server_time → жишээ хэрэгсэл
Гол зарчим
Загвар нь аль хэрэгслийг дуудахыг шийднэ; backend нь түүнийг гүйцэтгэнэ. Загвар код ажиллуулдаггүй. Хэрэгслүүд хүсэлтийн контексттэй ажилладаг тул тэдгээрийн хүрэх бүх өгөгдөлд RLS болон timeout нэгэн адил үйлчилнэ.
Чатын урсгал (function-calling давталт)¶
- Түүх (≤ 20 ээлж) дээр шинэ prompt нэмж
contentsугсарна. Дуут мессеж нь base64 inline audio хэсгээр ирнэ — Gemini үүнийг шууд ойлгодог тул тусад нь STT алхам хэрэггүй. - Давхаргат system instruction + хэрэгслийн тодорхойлолттой хамт Gemini рүү дуудна.
- Хариунд function call байвал: хэрэгсэл бүрийг гүйцэтгэж, загварын ээлж +
functionResponseээлжийг нэмээд давтана (дээд тал ньMaxSteps, default 4). Гүйцэтгэсэн дуудалт бүрийгStep{Tool, Args, Result}болгон клиент рүү буцаадаг тул UI нь «AI юу хийсэн»-ийг харуулж чадна. - Хариу текст бол шууд буцаана.
Алдааны семантик¶
| Тохиолдол | Үр дүн |
|---|---|
| Gemini түр зуурын алдаа (клиентийн 3× retry-ийн дараа) | 5xx БИШ — хэрэглэгчийн хэл дээрх нөөц хариу, degraded: true |
GEMINI_API_KEY байхгүй |
Жинхэнэ алдаа — 500, шалтгааныг лог руу |
| Хэрэгсэл унасан / танихгүй | Загварт {"error": …} гэж мэдэгдэнэ — клиент рүү шууд гарахгүй |
Энэ зан төлөвийг бүү өөрчил
Чат нь түр зуурын Gemini алдаанд нөөц хариу руу «зөөлөн» уналттай байх ёстой — үүнийг 5xx болгож хувиргаж болохгүй.
Prompt-ийн давхаргууд¶
System prompt нь хүсэлт бүрд гурван давхаргаас угсрагдана:
- Кодод хатуу бичсэн хамгаалалтын дүрэм (guardrails) — өөрчлөгддөггүй.
- Хамрах хүрээ (scope) —
ai_promptsхүснэгтээс, админ засварладаг. - Заавар (instructions) — мөн DB-ээс тохируулагдана.
Хариултын хэл
Frontend нь UI-ийн хэлээ (mn/en/zh/ru) lang талбараар илгээх ба
туслах ЗӨВХӨН тэр хэлээр хариулна — хэрэглэгчийн бичсэн хэл, ярианы түүх,
мэдлэгийн сан, tool-ийн үр дүн үүнийг өөрчлөхгүй (өөр хэлтэй эх сурвалжийг
орчуулж өгнө). degraded нөөц хариу ч мөн тэр хэлээр ирнэ.
Guardrail давхаргыг хэзээ ч тохируулга болгож болохгүй
Хамгаалалтын давхарга нь зөвхөн кодод байх ёстой. Зөвхөн scope ба
instructions нь өгөгдлийн сангаас удирдагдана.
Хэрэгсэл нэмэх¶
ai.ToolDef{
Declaration: gemini.FunctionDeclaration{
Name: "my_tool",
Description: "Загвар үүнийг хэзээ дуудах ёстойг тайлбарла…",
Parameters: map[string]any{ /* JSON Schema */ },
},
Execute: func(ctx context.Context, args map[string]any) (map[string]any, error) {
// backend дээр ажиллана; ctx нь хүсэлтийн identity-г авч явна (RLS үйлчилнэ)
return map[string]any{"result": "…"}, nil
},
}
cmd/api/server/server.go-д бүртгэнэ:
Бэлэн ирдэг хэрэгслүүд¶
Семантик хайлт (RAG)
Платформын мэдлэг ai_knowledge-д ~58 бүлгээр хадгалагдана. Асуултыг
Gemini-ээр вектор болгож pgvector дээр cosine ойролцооллоор хайдаг тул
өөр үг хэллэгээр асуусан ч зөв бүлэг олдоно. Embedding нь ачаалалтын дараа
автоматаар гүйцээгдэнэ; Админ → Тохиргоо дотор гараар дахин индексжүүлэх
товч бий.
search_knowledge—ai_knowledgeдээрх семантик хайлт: асуултыг embed хийж pgvector-ийн cosine зайгаар top-8-ыг аваад, шилдэг таарцтай харьцуулж шүүнэ (шилдгээс 0.03-оос хол зөрсөнг хаяна, 2–4 бичлэг). Тогтмол босго ажиллахгүй — энэ корпус дээр хамааралгүй хоёр бүлэг хүртэл 0.64+ ижилсэлтэй. Вектор боломжгүй үедILIKEруу уналт хийж, асуултыг үг болгон задлаад урт үгсийн үндсээр хайна. Суурь guardrail нь платформын тухай асуултад хариулахын өмнө үүнийг дуудаж, олдоогүй бол таамаглахын оронд «мэдэхгүй» гэж хэлэхийг шаарддаг. Мэдлэгийн санг мөр нэмж өсгөнө — шинэ мөр автоматаар embed хийгдэнэ.get_server_time— хамгийн жижиг жишээ (Улаанбаатарын цаг).
Нүүрийн нээлттэй чат (нэвтрэлтгүй)
Landing-ийн баруун доод буланд хөвөгч чат виджет байна —
POST /public/ai/chat (токенгүй). Тусдаа usecase instance-аар зөвхөн
мэдлэгийн сангийн tool-той холбогдсон тул хэрэглэгчийн өгөгдөлд хүрэхгүй.
IP тус бүрт ~6/мин, мессеж ≤ 1000 тэмдэгт, түүх ≤ 6 ээлж; system prompt
дээр «зочин» гэсэн нэмэлт хориг нэмэгдэнэ. Виджет POST /public/ai/chat/stream
(SSE) хэрэглэдэг тул хариулт бичигдэж байхад нь харагдана. Push-to-talk
(том дугуй товчийг дарж барих) нь ~250 KB base64 (≈ 15 сек) бичлэгийг
явуулах ба model НЭГ дуудалтаар хуулбар (transcript event) ба хариултыг
өгнө. Дуугаар асуувал хариултыг өгүүлбэр тус бүрээр нь
POST /public/ai/tts-ээр шууд дуугаргана.
Дуу хоолой¶
| Чадвар | Endpoint | Хэрхэн ажилладаг |
|---|---|---|
| Дуут чат мессеж | POST /ai/chat (audio-тай) |
Аудио нь хэрэглэгчийн ээлжид inline data болж шууд орно — чат загвар мультимодаль |
| Яриа→текст (STT) | POST /ai/stt |
«Үг үсгээр нь буулга» гэсэн хатуу зааврын нэг удаагийн дуудалт; хоосон текст = яриа алга |
| Текст→яриа (TTS) | POST /ai/tts |
Тусдаа TTS загвар, responseModalities: ["AUDIO"]; түүхий PCM (L16/24kHz)-ийг WAV толгойд боож хөтөч тоглуулна |
| Шууд орчуулга | POST /ai/translate |
Текст → орчуулга; аудио → хоёр алхам STT→орчуулга; speak: true бол TTS нэмнэ (TTS уналт чимээгүй — текст буцсан хэвээр) |
Аудио оролт нь mime-ээр шүүгдэж (webm/ogg/wav/mpeg/mp3/mp4/m4a/aac/flac), BFF болон backend DTO хоёуланд ~700 KB base64 (~30 сек opus)-аар хязгаарлагдана.
Шууд орчуулгын онцлог
Микрофон ~7 секундын хэсгүүдээр бичдэг ба хэсэг бүрт шинэ MediaRecorder
үүсгэдэг — timeslice хэсгүүдийн зөвхөн эхнийх нь контейнерийн толгойг авч
явдаг тул ингэж хийж байж хэсэг бүр бие даан хүчинтэй болно. Чимээгүй хэсэг
хоосон талбар буцаадаг ба алдаа гэж үзэлгүй хаягдана.
Хязгаарлалт¶
/ai/* нь IP тус бүрд ~20 хүсэлт/минут. Шууд орчуулга минутад ~8 хэсэг
дамжуулдаг тул энэ хязгаарыг багасгахдаа болгоомжтой.
Дэлгэрэнгүй: репозиторын backend/docs/AI_PIPELINE_MN.md.