Проблемът с хубавия текст
„Разбира се! Родителската среща е на 24 септември от 18:30 в кабинет 204. Надявам се това да помогне!“ За човек — отличен отговор. За програма, която трябва да сложи събитието в таблица — безполезен: няма как надеждно да извади датата от него, а при следващото пускане изречението ще е друго.
Структуриран изход
Отговор в предварително зададен машинен формат — обикновено JSON по схема, — който може да се прочете от код без разчитане на текст.
| Свободен текст | Структуриран изход | |
|---|---|---|
| За кого е | за човек | за програма |
| Разчитане | търсене на думи и надежда | json.loads() |
| При промяна на формулировката | кодът се чупи | нищо не се променя |
| Подходящо за | обяснения, чат | извличане, класификация, запис в база |
JSON — кратък преговор
Срещнахте JSON при експорта от phpMyAdmin в урок 9. Това е текстов формат за данни: обекти във фигурни скоби с двойки „ключ“: стойност, списъци в квадратни скоби, низове в двойни кавички, числа, true/false и null.
{
"zaglavie": "Родителска среща за 12а клас",
"data": "2026-09-24",
"chas": "18:30",
"myasto": "кабинет 204",
"vid": "среща"
}В Python JSON обектът става речник (урок 18), списъкът — списък, null — None. Преводът е в двете посоки:
import json
zapis = json.loads('{"zaglavie": "Среща", "chas": null}') # текст → речник
print(zapis["zaglavie"], zapis["chas"]) # Среща None
tekst = json.dumps(zapis, ensure_ascii=False) # речник → текст
print(tekst) # {"zaglavie": "Среща", "chas": null}Как се иска JSON от модела
Първо ниво: с думи в промпта
Описвате полетата и давате пример (урок 17). Работи често, но моделът понякога добавя изречение преди JSON-а или го загражда в блок за код.
Второ ниво: режим JSON и схема
Услугата може да бъде задължена да върне само JSON — при Gemini с response_mime_type="application/json". Ако подадем и схема — описание на полетата и типовете им, — изходът е валиден JSON с точно тези полета.
from google.genai import types
SHEMA = {
"type": "object",
"properties": {
"zaglavie": {"type": "string"},
"data": {"type": "string"}, # ГГГГ-ММ-ДД
"chas": {"type": "string", "nullable": True},
"myasto": {"type": "string", "nullable": True},
"vid": {"type": "string", "enum": ["среща", "състезание", "срок", "друго"]},
},
"required": ["zaglavie", "data", "vid"],
}
otgovor = client.models.generate_content(
model="gemini-2.5-flash",
contents=f"Извлечи събитието от съобщението: {tekst}",
config=types.GenerateContentConfig(
temperature=0,
response_mime_type="application/json",
response_schema=SHEMA,
),
)
zapis = json.loads(otgovor.text)Валидация — схемата не пази от глупости
Схемата гарантира формата, не смисъла. Дата „2026-13-45“, празно заглавие, час „след обяд“ — всичко това е валиден JSON. Проверките за смисъл остават ваша работа, както при формуляр в уеб приложение:
- Задължителни полета — има ли ги и не са ли празни.
- Типове — числото число ли е, датата разчита ли се (
datetime.date.fromisoformat). - Допустими стойности —
vidе от списъка; часът е между 00:00 и 23:59. - Смисъл — датата не е в миналото, ако събитието е предстоящо.
import datetime
DOPUSTIMI_VIDOVE = {"среща", "състезание", "срок", "друго"}
def proveri(z):
problemi = []
for pole in ("zaglavie", "data", "vid"):
if pole not in z or not z[pole]:
problemi.append(f"липсва {pole}")
if z.get("data"):
try:
datetime.date.fromisoformat(z["data"])
except ValueError:
problemi.append(f"невалидна дата: {z['data']}")
if z.get("vid") not in DOPUSTIMI_VIDOVE:
problemi.append(f"недопустим vid: {z.get('vid')}")
return problemi # празен списък = всичко е наредФункцията връща списък от проблеми, а не само да/не — така при неуспех знаете какво точно да поправите или да покажете.
Невалиден отговор и запис в базата
- Опитайте да разчетете отговора с json.loads.
- Ако не е JSON или проверката върне проблеми — повторете заявката веднъж, с изрично напомняне за формата.
- Ако пак не стане — отхвърлете записа: запишете случая в журнал и върнете на потребителя разбираемо съобщение.
- Никога не записвайте в базата непроверени данни.
Проверените записи отиват в базата. В Colab има вградена SQLite — същата, която ползва учебното приложение. Свързваме се със стандартния модул sqlite3:
import sqlite3
con = sqlite3.connect("sabitiya.db")
con.execute("""CREATE TABLE IF NOT EXISTS sabitiya (
id INTEGER PRIMARY KEY AUTOINCREMENT,
zaglavie TEXT NOT NULL,
data TEXT NOT NULL,
chas TEXT,
myasto TEXT,
vid TEXT NOT NULL
)""")
con.execute(
"INSERT INTO sabitiya (zaglavie, data, chas, myasto, vid) VALUES (?, ?, ?, ?, ?)",
(z["zaglavie"], z["data"], z.get("chas"), z.get("myasto"), z["vid"]),
)
con.commit()Какво трябва да запомниш
- Свободният текст е за хора; програмата иска JSON по схема — иначе кодът, който го разчита, се чупи при всяко пускане.
- JSON → речник с json.loads, речник → JSON с json.dumps; при Gemini response_mime_type="application/json" и response_schema дават гарантирано валиден формат.
- Схемата гарантира формата, не смисъла: задължителни полета, типове, допустими стойности и смисъл се проверяват от вашия код.
- Проверката връща списък от проблеми; при неуспех — един повторен опит, после отхвърляне и журнал.
- Записът в базата е с параметри (?), никога със слепване на низове; в базата влизат само проверени данни.
- Температура 0 за извличане на данни.