"""Дистилляция чанка в типизированные атомы (LLM).""" from __future__ import annotations import json import httpx from . import config _client = httpx.Client(timeout=600) SYSTEM = """Ты — экстрактор знаний. Преврати текст в атомарные, типизированные утверждения. Верни ТОЛЬКО JSON-массив без пояснений. Каждый элемент: { "type": "text|definition|logical_rule|code|table|fact", "claim": "короткое каноническое утверждение (одно предложение)", "content": "полное значение атома", "confidence": 0.0-1.0 } Правила: - definition: термин и его точное определение. - logical_rule: правило "если X то Y" (content = JSON со ключами if/then). - code: фрагмент кода или сигнатура функции (content = сам код). - table: табличные данные (content = JSON или markdown-таблица). - fact: проверяемый факт с числом или датой. - text: всё остальное ценное. - Отбрасывай воду, повторы, навигацию, рекламу. Только новое и ценное. - Если ничего ценного — верни []. No markdown fences.""" def distill_chunk(chunk_text: str, model: str | None = None) -> list[dict]: """Вернуть список атомов (dict) из чанка.""" m = model or config.LLM_MODEL r = _client.post(f"{config.OLLAMA_URL}/api/chat", json={ "model": m, "messages": [ {"role": "system", "content": SYSTEM}, {"role": "user", "content": chunk_text[:6000]}, ], "stream": False, "options": {"temperature": 0.1}, }) r.raise_for_status() content = r.json()["message"]["content"] start = content.find("[") end = content.rfind("]") if start == -1 or end == -1: return [] try: arr = json.loads(content[start:end + 1]) except json.JSONDecodeError: return [] return [a for a in arr if isinstance(a, dict) and a.get("type") and a.get("content")] def verify_atom(atom: dict, context_atoms: list[dict], model: str | None = None) -> float: """Оценить новизну/ценность атома против существующих (0..1).""" m = model or config.LLM_MODEL ctx = "\n".join(f"- {a.get('claim','')}" for a in context_atoms[:8]) prompt = ( f"Существующие знания:\n{ctx}\n\n" f"Новый атом ({atom.get('type')}): {atom.get('claim')}\n{atom.get('content','')[:500]}\n\n" f"Оцени от 0 до 1, насколько это НОВОЕ и ценное знание (0=дубль/вода, 1=уникальное важное). " f"Верни только число." ) r = _client.post(f"{config.OLLAMA_URL}/api/chat", json={ "model": m, "messages": [{"role": "user", "content": prompt}], "stream": False, "options": {"temperature": 0.0}, }) r.raise_for_status() out = r.json()["message"]["content"].strip() try: return float(out) except ValueError: return 0.5