mirror of
https://gitverse.ru/kpa39l/vesti.git
synced 2026-09-29 09:55:03 +00:00
fix+docs: classifier/publisher правки, sources.yaml расширен, глоссарий CONTEXT.md + docs/agents, backup.sh; openspec post-translate/rewrite-as-author/rich-repost-card/selected-layout-stay
This commit is contained in:
+22
-3
@@ -24,16 +24,33 @@ def classify_text(post: dict) -> dict:
|
||||
пробуем LLM-уточнение (relevance/interest/summary). При недоступности LLM —
|
||||
фолбэк: direction из словаря, relevance=low, interest=1, classified=False.
|
||||
|
||||
Если у поста задан source_slug с правилом SOURCE_RULES (например, lwn → tech) —
|
||||
направление принудительное, LLM не вызывается (детерминированно, быстро).
|
||||
|
||||
СВОЙ контент (is_own=1): «сильный кандидат» — при словарном попадании
|
||||
relevance=critical, classified=True даже без LLM (фолбэк 'dict-own').
|
||||
Если словарь не дал направление — классификации нет, пост не теряется.
|
||||
|
||||
post: {"text": str, "views": int, "reactions_total": int, "is_own": int, ...}
|
||||
post: {"text": str, "views": int, "reactions_total": int, "is_own": int, "source_slug": str|None, ...}
|
||||
"""
|
||||
text = (post.get("text") or "").strip()
|
||||
views = int(post.get("views") or 0)
|
||||
reactions = int(post.get("reactions_total") or 0)
|
||||
is_own = int(post.get("is_own") or 0) == 1
|
||||
source_slug = post.get("source_slug")
|
||||
|
||||
# правило источника (SOURCE_RULES): принудительное направление, без LLM
|
||||
from classifier.keywords import source_directions
|
||||
forced = source_directions(source_slug)
|
||||
if forced:
|
||||
return {
|
||||
"direction": forced[0],
|
||||
"relevance": "high",
|
||||
"interest": 3,
|
||||
"summary": "",
|
||||
"classified": True,
|
||||
"method": "source-rule",
|
||||
}
|
||||
|
||||
direction = find_direction(text)
|
||||
if not direction:
|
||||
@@ -170,7 +187,9 @@ def classify_posts_in_db(db_path: str, limit: int = 200, direction: str | None =
|
||||
where += f" AND direction IS NULL" # не классифицированы в этом направлении
|
||||
|
||||
rows = cur.execute(
|
||||
f"SELECT id, text, views, reactions_total, is_own FROM posts {where} ORDER BY is_own DESC, id LIMIT ?",
|
||||
f"SELECT p.id, p.text, p.views, p.reactions_total, p.is_own, s.slug AS source_slug "
|
||||
f"FROM posts p LEFT JOIN sources s ON s.id = p.source_id {where} "
|
||||
f"ORDER BY p.is_own DESC, p.id LIMIT ?",
|
||||
(limit,),
|
||||
).fetchall()
|
||||
|
||||
@@ -188,7 +207,7 @@ def classify_posts_in_db(db_path: str, limit: int = 200, direction: str | None =
|
||||
)
|
||||
# мультинаправления: все словарные попадания → classifications (для fan-out)
|
||||
from classifier.keywords import find_directions_all
|
||||
dirs_all = find_directions_all(row["text"] or "")
|
||||
dirs_all = find_directions_all(row["text"] or "", row["source_slug"])
|
||||
if res["direction"] and res["direction"] not in dirs_all:
|
||||
dirs_all.append(res["direction"])
|
||||
for d in dirs_all:
|
||||
|
||||
+29
-4
@@ -93,9 +93,28 @@ import re
|
||||
# Направление по умолчанию, если ничего не подошло
|
||||
DEFAULT_DIRECTION = "linux" # прототип: все посты про linux окружение
|
||||
|
||||
# Правила по источнику (slug в sources.yaml): принудительное направление
|
||||
# для всего контента источника, независимо от словаря/LLM.
|
||||
# LWN — технологическое СМИ: весь контент → tech.
|
||||
SOURCE_RULES: dict[str, list[str]] = {
|
||||
"lwn": ["tech"],
|
||||
}
|
||||
|
||||
def find_directions_all(text: str) -> list[str]:
|
||||
"""Возвращает ВСЕ направления, которым соответствует текст (для мультинаправлений/fan-out)."""
|
||||
|
||||
def source_directions(source_slug: str | None) -> list[str]:
|
||||
"""Направления, принудительно назначенные источнику (SOURCE_RULES)."""
|
||||
return list(SOURCE_RULES.get(source_slug or "", []))
|
||||
|
||||
|
||||
def find_directions_all(text: str, source_slug: str | None = None) -> list[str]:
|
||||
"""Возвращает ВСЕ направления, которым соответствует текст (для мультинаправлений/fan-out).
|
||||
|
||||
Если для источника задано правило SOURCE_RULES — возвращает его направления
|
||||
(принудительно), иначе — словарные попадания.
|
||||
"""
|
||||
forced = source_directions(source_slug)
|
||||
if forced:
|
||||
return forced
|
||||
text_low = (text or "").lower()
|
||||
hits = set()
|
||||
for direction, cfg in DIRECTIONS.items():
|
||||
@@ -112,8 +131,14 @@ def find_directions_all(text: str) -> list[str]:
|
||||
return list(hits)
|
||||
|
||||
|
||||
def find_direction(text: str) -> str | None:
|
||||
"""Возвращает направление, если текст совпал со словарём (иначе None)."""
|
||||
def find_direction(text: str, source_slug: str | None = None) -> str | None:
|
||||
"""Возвращает направление: правило источника (SOURCE_RULES) или словарное.
|
||||
|
||||
Принудительное правило источника имеет приоритет над словарём.
|
||||
"""
|
||||
forced = source_directions(source_slug)
|
||||
if forced:
|
||||
return forced[0]
|
||||
dirs = find_directions_all(text)
|
||||
if dirs:
|
||||
return dirs[0]
|
||||
|
||||
Reference in New Issue
Block a user