73 lines
3.3 KiB
Python
73 lines
3.3 KiB
Python
"""Дистилляция чанка в типизированные атомы (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
|