Teacher model (o'qituvchi model): Gemma ma'lumot yozadi, lekin Tezga kirmaydi
Ma'lumot qayerdan olinadi
To'rtinchi qism — training ma'lumoti haqida. 7-bobda ko'rdik: kichik modelni promptni yaxshilab emas, o'qitib o'zgartirish kerak. Training uchun esa minglab "so'rov → plan" juftliklari kerak, uch tilda, aralash yozuvda, xato bilan, qisqa va uzun.
Ularni qayerdan olish mumkin?
- Haqiqiy bank so'rovlari — mumkin emas. Ular bankdan tashqariga chiqmaydi, va bizda yo'q.
- Qo'lda yozish — yigirma ming juftlikni bir kishi yoza olmaydi, va yozsa ham bitta odamning uslubi bo'ladi.
- Tayyor ochiq to'plamlar — 2-bobdagi tadqiqotda ko'rdik: asosan ingliz tilida, SQL uchun, litsenziyalari aralash. Bizning toollar tilimiz uchun hech narsa yo'q.
Qolgan yo'l — boshqa, kattaroq model yordamida yasash. Uni teacher (o'qituvchi) model deymiz. Lekin bu yerda tartib juda muhim, va u butun ma'lumot fabrikasining asosiy g'oyasi:
Avval plan, keyin matn. Planni kod yasaydi (13-bob) va DuckDB da bajarib tekshiradi. O'qituvchi faqat "ishchi bu planni qanday so'ragan bo'lardi" degan matnni yozadi.
Teskari yo'l — o'qituvchidan so'rov ham, plan ham yozishni so'rash — xavfli: o'qituvchi ham xato qiladi, va uning xato plani to'g'ridan-to'g'ri trainingga kirib ketadi. Bizning tartibimizda plan har doim to'g'ri. Xato bo'lishi mumkin bo'lgan narsa — faqat matn: u planni noto'g'ri tasvirlashi mumkin. Buni ushlash uchun alohida tekshiruv bor (14-bob).
Nega Gemma
7-bobda boshqa ochiq modellar ham sinalgan edi. Ulardan biri — Gemma 4 E4B — Tez vazifasida Qwen3.5-4B ga yaqin, ba'zi to'plamlarda undan ham yaxshi chiqdi. Saqlangan natijalarni yonma-yon qo'yamiz:
import json import pandas as pd def ball(fayl, model): xom = pd.read_json(fayl, lines=True) tanlangan = xom[xom["model"] == model] return f"{int(tanlangan['correct'].sum())}/{len(tanlangan)}", round(tanlangan["seconds"].mean(), 2) qatorlar = [] for model in ("qwen3.5-4b", "gemma-4-e4b"): r2 = ball(TEZ / ("accuracy_raw_v2.jsonl" if model == "qwen3.5-4b" else "accuracy_raw_v2_others.jsonl"), model) r3t = ball(TEZ / "results/round3_transactions.jsonl", model) r3l = ball(TEZ / "results/round3_loans.jsonl", model) qatorlar.append({"model": model, "2-raund": r2[0], "3-raund tranzaksiyalar": r3t[0], "3-raund kreditlar": r3l[0], "o'rtacha_s (2-raund)": r2[1]}) show(pd.DataFrame(qatorlar)) modellar = Path("/mnt/d/Tez_models") for nom in ("Qwen3.5-4B-Q4_K_M.gguf", "gemma-4-e4b-it-Q4_K_M.gguf"): print(f"{nom:28s} {(modellar / nom).stat().st_size / 2**20:,.0f} MB")
Qwen3.5-4B-Q4_K_M.gguf 2,614 MB gemma-4-e4b-it-Q4_K_M.gguf 4,747 MB
| model | 2-raund | 3-raund tranzaksiyalar | 3-raund kreditlar | o'rtacha_s (2-raund) |
|---|---|---|---|---|
| qwen3.5-4b | 39/40 | 37/40 | 30/40 | 4.71 |
| gemma-4-e4b | 32/40 | 38/40 | 33/40 | 8.2 |
Gemma aniq, lekin sekinroq (bir javobga o'rtacha 8.2 soniya, Qwen da 4.7) va fayli deyarli ikki barobar katta. Tahlilchining oddiy kompyuterida bu farq hal qiluvchi. O'qituvchi uchun esa tezlik muhim emas: u bir marta, bizning kompyuterimizda, videokartada ishlaydi. Shuning uchun qaror ikki qismdan iborat bo'ldi:
- Gemma — o'qituvchi. U so'rov matnlarini yozadi va (14-bobda) ularni tekshiradi.
- Gemma Tezga hech qachon kirmaydi. Mahsulotdagi model — o'qitilgan Qwen3.5-4B.
O'qituvchi haqiqiy ma'lumotni ko'rmaydi
Maxfiylik qoidasi o'qituvchiga ham tegishli. U faqat o'ylab topilgan jadvallarni ko'radi —
ularning tavsifini, ustun nomlari va namunalarini. Haqiqiy jadvallar (test to'plamlaridagi
real_ jadvallar) faqat sinov uchun, va o'qituvchiga hech qachon berilmagan.
Buni da'vo qilish oson — tekshirish kerak. O'qituvchi yozgan hamma matnlar saqlangan. Ularning qaysi jadval uchun yozilganini sanaymiz:
import glob from collections import Counter jadvallar = Counter() for fayl in glob.glob("/mnt/d/Tez_spikes/batch*/phrases*.jsonl"): for qator in open(fayl, encoding="utf-8"): jadvallar[json.loads(qator)["domain"]] += 1 print(f"o'qituvchi yozgan matnlar: {sum(jadvallar.values()):,}") print(f"jadvallar: {len(jadvallar)}") print(f"haqiqiy (real_) jadvallar uchun: {sum(n for nom, n in jadvallar.items() if nom.startswith('real_'))}") print(sorted(jadvallar))
o'qituvchi yozgan matnlar: 31,568 jadvallar: 18 haqiqiy (real_) jadvallar uchun: 0 ['atm', 'attendance', 'bank_shares', 'bank_transactions', 'branch_kpi', 'budget', 'cards', 'complaints', 'fx_exchange', 'hr_staff', 'leads', 'payments', 'payroll', 'procurement', 'region_balance', 'retail_sales', 'sales_returns', 'warehouse']
O'n sakkizta jadval, hammasi o'ylab topilgan. Haqiqiy jadval uchun — nol.
O'qituvchiga nima beriladi
O'qituvchining prompti — training/teacher_phrase.txt shabloni, unga jadval, plan va uslub
qo'yiladi. Uni Tezning o'z kodi bilan to'ldiramiz (model chaqirilmaydi, faqat matn yasaladi):
import random from prompt import build from training.phrase import teacher_messages sozlama = json.load(open(TEZ / "training/settings.json", encoding="utf-8")) eski_qoida = {**sozlama, "phrasing": {**sozlama["phrasing"], "translated_share": 0.0}} kontekst = build.load_context(TEZ / "prompt/domains/retail_sales") reja = {"steps": [ {"tool": "filter", "args": {"column": "tolov_turi", "op": "=", "value": "Click"}}, {"tool": "group_and_total", "args": {"group_by": ["dokon"], "value_column": "summa_uzs", "agg": "sum"}}, ]} shablon = (TEZ / "training/teacher_phrase.txt").read_text(encoding="utf-8") atamalar = json.load(open(TEZ / "training/terms.json", encoding="utf-8")) reja_matni = json.load(open(TEZ / "training/plan_text.json", encoding="utf-8")) prompt = teacher_messages(shablon, kontekst, reja, eski_qoida, random.Random(1), atamalar, reja_matni)[0]["content"] boshi = prompt.split("Tools the assistant has:")[0] oxiri = prompt.split("Plan in words")[1] print(boshi) print("...") print("Plan in words" + oxiri)
You write realistic requests that office workers in Uzbekistan type into a data assistant.
The worker sees the table below and wants exactly the result of the plan below. Write how they would ask for it.
Rules:
- Each request must mean exactly this plan: every filter, value, grouping, aggregate, count and order in the plan, and nothing more.
- Workers rarely use column names. Use the business words from the glossary and the values. Never write a column name that contains an underscore, and never write option names such as same_last_year or is_empty: say them in normal words.
- Style for this plan: short and informal, the way people type in a hurry.
- uz_latin: Uzbek in Latin script with correct apostrophes (o', g').
- ru: Russian.
- en: English.
- mixed: Uzbek and Russian or English words mixed in one request, the way people chat.
- Values from the data (names, statuses, branches) keep their exact spelling from the samples.
- For chart types, directions and aggregates use these words:
- bar chart: uz_latin ustunli diagramma; ru столбчатая диаграмма
- pie chart: uz_latin doiraviy diagramma; ru круговая диаграмма
- line chart: uz_latin chiziqli grafik; ru линейный график
- table: uz_latin jadval; ru таблица
- highest / top: uz_latin eng katta, eng yuqori, eng ko'p; ru самые большие, топ
- lowest / bottom: uz_latin eng kichik, eng kam, eng past; ru самые маленькие, наименьшие
- sort highest first: uz_latin kamayish tartibida, kattadan kichikka; ru по убыванию, от большего к меньшему
- sort lowest first: uz_latin o'sish tartibida, kichikdan kattaga; ru по возрастанию, от меньшего к большему
- sum / total: uz_latin jami, umumiy summa, yig'indi; ru сумма, итого
- average: uz_latin o'rtacha; ru среднее
- minimum: uz_latin eng kam qiymat; ru минимум
- maximum: uz_latin eng katta qiymat; ru максимум
- count: uz_latin soni, nechta; ru количество, сколько
- more than (>): uz_latin dan katta, dan ko'p, dan uzun, dan oshiq; ru больше, свыше
- at least (>=): uz_latin kamida, dan kam emas, dan katta yoki teng; ru не меньше, больше или равно, как минимум
- less than (<): uz_latin dan kichik, dan kam, dan qisqa; ru меньше, ниже
- at most (<=): uz_latin ko'pi bilan, dan oshmagan, dan kichik yoki teng; ru не больше, меньше или равно, максимум
- by / per: uz_latin bo'yicha, kesimida; ru по
- remove: uz_latin olib tashla, o'chir; ru удали, убери
- keep only: uz_latin faqat ... qoldir; ru оставь только
- empty: uz_latin bo'sh; ru пустой
- previous month / quarter / year: uz_latin o'tgan oy / chorak / yil; ru прошлый месяц / квартал / год
- same month last year: uz_latin o'tgan yilning shu oyi; ru тот же месяц прошлого года
- duplicates: uz_latin takroriy yozuvlar, dublikatlar; ru дубликаты
- fix dates: uz_latin sanalarni bir xil formatga keltir; ru приведи даты к одному формату
- export to Excel: uz_latin Excel faylga saqla; ru сохрани в Excel
...
Plan in words (this is exactly what the request must ask for):
1. Keep only the rows where tolov_turi is 'Click'.
2. For each dokon, compute the total of summa_uzs.
Plan as data:
{"steps": [{"tool": "filter", "args": {"column": "tolov_turi", "op": "=", "value": "Click"}}, {"tool": "group_and_total", "args": {"group_by": ["dokon"], "value_column": "summa_uzs", "agg": "sum"}}]}
Answer with JSON only: {"uz_latin": "...", "ru": "...", "en": "...", "mixed": "..."}
To'rtta narsaga e'tibor bering.
Plan ikki marta beriladi — so'z bilan ("Keep only the rows where tolov_turi is 'Click'...") va
JSON bilan. So'z bilan tasvirni kod yasaydi (training/describe.py), va u o'qituvchiga planni
noto'g'ri tushunmaslikka yordam beradi.
Ustun nomlarini yozmaslik qoidasi. Ishchi "summa_uzs bo'yicha" demaydi — "savdo summasi bo'yicha" deydi. Pastki chiziqli nom so'rovda chiqsa — bu o'qituvchi "dangasalik" qilgani.
Uslub har safar tasodifiy tanlanadi. Ro'yxat sozlamalarda:
for uslub in sozlama["phrasing"]["styles"]: print("-", uslub)
- short and informal, the way people type in a hurry - polite and complete, like a message to a colleague - one or two small typos, as in real typing - uses only business words, no column names - a question, not a command
Qiymatlar — namunalardagidek aniq yoziladi. Bu kitob yozilayotgan kunlarda bu qoidaga ikkinchi variant qo'shildi: so'rovning bir qismida qiymat ishchi aytgandek, tarjima qilib yoziladi ("Toshkent" o'rniga rus tilida "Ташкент"), plan esa jadvaldagi aniq qiymatni saqlaydi. Sababi 6-qismda ochiladi: real testlarda model tarjima qilingan qiymatlarda xato qilgan. Yuqoridagi prompt eski, yagona qoida bilan ko'rsatildi — avvalgi hamma ma'lumot shu qoida bilan yozilgan.
Kirill yozuvini kod yozadi
Prompt oxirida o'qituvchidan to'rtta matn so'raladi: o'zbek (lotin), rus, ingliz va aralash.
O'zbek kirill yo'q. Uni kod yasaydi — lotin matnidan, qoidalar jadvali bo'yicha
(training/uzbek_script.py):
import sys from training.uzbek_script import latin_to_cyrillic yozuv = json.load(open(TEZ / "training/uzbek_script.json", encoding="utf-8")) for matn, saqlanadi in (("Do'konlar bo'yicha jami savdo, faqat Click", ["Click"]), ("Toshkent filialidagi o'tgan oy kreditlari", []), ("Eng ko'p sotilgan 5 ta tovar", [])): print(f"{matn}\n -> {latin_to_cyrillic(matn, yozuv, saqlanadi)}")
Do'konlar bo'yicha jami savdo, faqat Click -> Дўконлар бўйича жами савдо, фақат Click Toshkent filialidagi o'tgan oy kreditlari -> Тошкент филиалидаги ўтган ой кредитлари Eng ko'p sotilgan 5 ta tovar -> Энг кўп сотилган 5 та товар
Nega o'qituvchiga kirillni yozdirmaymiz? Chunki lotin-kirill o'girish qoidaga asoslangan va tekshiriladigan ish — uni kod har safar bir xil qiladi, va har bir qoida test bilan qotirilgan. O'qituvchining kirill yozuvi qanchalik to'g'ri bo'lishini biz o'lchamaganmiz; kod yo'li o'lchovsiz ham ishonchli edi. Jadvaldagi qiymatlar ("Click") o'girilmaydi: ular planda qanday bo'lsa, so'rovda ham shunday qoladi.
Dangasa javobni ushlash
O'qituvchi ba'zan qoidani buzadi va so'rovga ustun nomini yozib yuboradi. Bunday matnni oddiy kod ushlaydi — pastki chiziq bilan qo'shilgan so'zlarni qidiradi, lekin plandagi haqiqiy qiymatlarga tegmaydi:
from training.leaks import leaked belgi = sozlama["raw_name_marker"] for matn in ("summa_uzs bo'yicha do'konlarni jamla", "Do'konlar bo'yicha savdo summasi", "Faqat ATM-101 bankomati"): print(f"{matn!r:45s} -> {leaked(matn, belgi) or 'toza'}")
"summa_uzs bo'yicha do'konlarni jamla" -> ['summa_uzs'] "Do'konlar bo'yicha savdo summasi" -> toza 'Faqat ATM-101 bankomati' -> toza
Ushlangan bo'lsa, o'qituvchiga aynan qaysi so'zlar xato ekani aytiladi va qayta yozish so'raladi
(sozlamalarda retry_note). Bir necha urinishdan keyin ham toza bo'lmasa — matn tashlanadi.
Haqiqiy natija
O'qituvchi yozgan matnlardan bitta planni olamiz — u qanday so'rovlar bergan:
guruh = {} for qator in open("/mnt/d/Tez_spikes/batch12/phrases_conditions.jsonl", encoding="utf-8"): yozuv_ = json.loads(qator) kalit = json.dumps(yozuv_["plan"], sort_keys=True) guruh.setdefault(kalit, {})[yozuv_["language"]] = yozuv_["text"] reja_, matnlar = next((k, v) for k, v in guruh.items() if len(v) == 5) print("reja:", reja_) for til, matn in matnlar.items(): print(f" {til:12s} {matn}")
reja: {"steps": [{"args": {"column": "summa_uzs", "op": ">", "value": 0}, "tool": "filter"}]}
en keep rows where amount is greater than 0
uz_latin amount dan katta 0 bo'lgan qatorlarni qoldir
uz_cyrillic амоунт дан катта 0 бўлган қаторларни қолдир
ru оставить только строки, где сумма больше 0
mixed amount > 0 bo'lgan qatorlarni qoldir
Bitta plan — beshta so'rov. Ruscha va inglizcha matnlar yaxshi. Lekin o'zbekcha matnni diqqat bilan o'qing: "amount dan katta 0" — o'qituvchi inglizcha so'zni o'zbek gapiga qo'yib yuborgan. Kod esa uni kirillga ko'r-ko'rona o'girgan: "амоунт" — o'zbek tilida bunday so'z yo'q.
Bu misolni ataylab tanlamadik — u fayldagi birinchi beshta tilli plan. Va eng yomoni: bu ikkala matn ham keyingi tekshiruvdan (14-bob) o'tgan va training ma'lumotiga kirgan, chunki tekshiruv faqat ma'noni tekshiradi — "bu matn bilan plan bir xil natija beradimi" — tilning sifatini emas. Bunday matnlar qanchalik ko'p — biz hali sanamaganmiz. Keyingi partiyada qo'lda ko'riladigan namunalarda aynan shu narsa qidiriladi.
O'qituvchining eng katta kamchiligi
Yaxshi o'qituvchi ham o'z odatlariga ega. Gemma ning odati — uzun va xushmuomala yozish. Uning yozgan hamma matnlarining uzunligini o'lchaymiz:
sozlar = [] for fayl in glob.glob("/mnt/d/Tez_spikes/batch*/phrases*.jsonl"): for qator in open(fayl, encoding="utf-8"): sozlar.append(len(json.loads(qator)["text"].split())) print(f"matnlar: {len(sozlar):,}") print(f"5 so'zdan qisqa yoki teng: {sum(s <= 5 for s in sozlar) / len(sozlar):.0%}") print(f"10 so'zdan uzun: {sum(s > 10 for s in sozlar) / len(sozlar):.0%}")
matnlar: 31,568 5 so'zdan qisqa yoki teng: 37% 10 so'zdan uzun: 37%
Tahlilchi esa ikki-besh so'z yozadi: "Toshkent kreditlar jami". Bu farq kitobda bir necha marta qaytadi: 16-bobda qamrov darvozasi uni ushlay boshladi va qisqa so'rovlar uchun alohida partiyalar yasaldi (har biri "2 to 5 words" uslubi va so'z chegarasi bilan), 6-qismda esa real testdagi xatolar bilan bog'lanadi.
Xulosa
- Training ma'lumoti "avval plan, keyin matn" tartibida yasaladi: planni kod yasaydi va tekshiradi, o'qituvchi faqat so'rov matnini yozadi.
- O'qituvchi — Gemma 4 E4B: Tez vazifasida aniq, lekin sekin va katta. U mahsulotga kirmaydi.
- O'qituvchi faqat o'ylab topilgan jadvallarni ko'rgan: 18 ta jadval, haqiqiy jadval uchun nol matn.
- O'zbek kirillini kod yozadi; ustun nomi qolib ketgan matnlar ushlanadi va qayta yoziladi.
- O'qituvchining odati — uzun yozish. Bu tahlilchining qisqa so'rovlariga mos emas va alohida tuzatildi.
Keyingi bobda plan qayerdan kelishini ko'ramiz: kod qanday qilib minglab har xil, lekin har doim bajariladigan planlar yasaydi.
Bu bobni bepul o'qidingiz.
Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.