Files
portal/knowledge/aleksandra/brain/research.py
T

94 lines
4.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Главная точка для движка: по произнесённой нише вернуть готовую справку.
Лесенка: niche → берём карточку + родителя; domain → общее домена (+опц. Sonar на специфику);
miss → Sonar + заводим карточку. Sonar и эмбеддер инъектируются (тестируемо).
Индекс ПРЕДПОСЧИТАН и передаётся снаружи — на живом звонке эмбеддится только произнесённая
ниша (1 вызов), а не весь индекс каждый раз. Новую карточку добавляем в индекс точечно."""
import re
from pathlib import Path
from brain.cards import parse_card, assemble_brief, Card
from brain.index_store import save_index
from brain.lookup import find_niche
ORPHAN_PARENT = "(не разобрано)" # метка сиротки (miss без домена), чтобы её НЕ считали доменом
def _load_cards(nishi_dir):
cards, by_name = [], {}
for md in sorted(Path(nishi_dir).rglob("*.md")):
c = parse_card(md)
cards.append(c)
by_name[c.nisha] = c
return cards, by_name
def _slug(s):
s = re.sub(r"[^\wа-яё\- ]+", "", s.lower()).strip()
return re.sub(r"\s+", "-", s)[:60] or "nisha"
def _write_card(nishi_dir, niche, brief, roditel=None, obnovleno=""):
folder = Path(nishi_dir)
fm = [f"nisha: {niche}"]
if roditel:
folder = folder / _slug(roditel)
fm.append(f"roditel: {roditel}")
else:
fm.append(f"roditel: {ORPHAN_PARENT}") # miss-сиротка помечена, не считается доменом
folder.mkdir(parents=True, exist_ok=True)
base = _slug(niche)
path = folder / f"{base}.md"
n = 2
while path.exists(): # не затираем существующую карточку
path = folder / f"{base}-{n}.md"
n += 1
fm += ["istochnik: perplexity-sonar", f"obnovleno: {obnovleno}",
f"trendy_obnovleno: {obnovleno}"]
path.write_text("---\n" + "\n".join(fm) + "\n---\n" + brief.strip() + "\n",
encoding="utf-8")
return path
def _index_append(index, path, embed_fn, index_path=None):
"""Добавить одну новую карточку в индекс (эмбеддим ТОЛЬКО её). Опц. сохранить индекс на диск."""
card = parse_card(path)
key_text = card.nisha + " " + " ".join(card.sinonimy)
index.append({
"nisha": card.nisha,
"roditel": card.roditel,
"sinonimy": card.sinonimy,
"path": str(path),
"vector": embed_fn(key_text),
})
if index_path:
save_index(index, index_path)
return index
def get_brief(spoken, nishi_dir, index, embed_fn, sonar_fn,
today="", sonar_on_domain=False, index_path=None):
"""index — ПРЕДПОСЧИТАННЫЙ индекс (список dict), грузится снаружи один раз (load_index).
Здесь эмбеддится только `spoken` (внутри find_niche). Возвращает (brief, source)."""
_, by_name = _load_cards(nishi_dir)
res = find_niche(spoken, index, embed_fn=embed_fn)
if res.kind == "niche":
card = by_name.get(res.entry["nisha"])
return assemble_brief(card, by_name), "niche"
if res.kind == "domain":
domain_card = by_name.get(res.entry["nisha"])
common = assemble_brief(domain_card, by_name)
if sonar_on_domain:
specifics = sonar_fn(spoken)
newpath = _write_card(nishi_dir, spoken, specifics, roditel=domain_card.nisha,
obnovleno=today)
_index_append(index, newpath, embed_fn, index_path)
return (common + "\n\n" + specifics).strip(), "domain"
return common, "domain"
# miss: совсем новое → Sonar + новая карточка-сиротка (помечена ORPHAN_PARENT)
brief = sonar_fn(spoken)
newpath = _write_card(nishi_dir, spoken, brief, roditel=None, obnovleno=today)
_index_append(index, newpath, embed_fn, index_path)
return brief, "miss"