dp-zp-agent/tools/answers/question_intent.py

119 lines
4.5 KiB
Python

"""Explicit answer aspects; these select evidence, never supply facts."""
import re
from tools.answers.context_policy import normalized
FACET_PATTERNS = {
"assignment": r"\b(?:zadani\w*|ciel\w*)\b",
"plans": r"\b(?:planovan\w*|dalsi\w*\s+uloh\w*|uloh\w*)\b",
"problems": r"\b(?:problem\w*|tazkost\w*|prekazk\w*|chyb\w*)\b",
"methods": r"\b(?:metod\w*|technolog\w*|nastroj\w*|pouziv\w*|pouzil\w*|pristup\w*|frontend\w*|backend\w*)\b",
"experiments": r"\b(?:experiment\w*|merani\w*|testovani\w*|vyhodnocov\w*)\b",
"progress": r"\b(?:priebezn\w*\s+)?stav\w*\b",
"results": r"\b(?:vysledk\w*|dosiahn\w*|funkcnost\w*)\b",
}
FACET_LABELS = {
"assignment": "Zadanie a ciele",
"plans": "Plánované úlohy",
"problems": "Riešené problémy",
"methods": "Metódy a technológie",
"experiments": "Experimenty a vyhodnotenie",
"progress": "Priebežný stav",
"results": "Zaznamenané výsledky",
}
def normalize_question(question: str) -> str:
"""Only a narrow phrase typo; identities, dates and conditions stay literal."""
text=re.sub(r'^ma\s+(?:čom|com)\b','na čom',question.strip(),flags=re.I)
text=re.sub(r'(?<!\d)(\d{1,2})\.\s*(\d{1,2})\.\s*([12]\d{3})(?!\d)',
lambda m:f'{m[1]}.{m[2]}.{m[3]}',text)
return re.sub(r'((?:stretnut[iíe]|dňa|dna)\s+)([12]\d{3})-(\d{2})-(\d{2})\b',
lambda m:f'{m[1]}{int(m[4])}.{int(m[3])}.{m[2]}',text,flags=re.I)
def requested_facets(question: str) -> list[str]:
text = normalized(re.sub(r'[„“"«][^„“”"»]+[“”"»]', " ", question))
text = re.sub(r'\bnezamienaj\s+(?:ich|to|ciele|zadanie|plany|ulohy)\s+za\s+(?:hotove\s+)?vysled\w*|\bbez domyslania vysled\w*',' ',text)
matches = [
(m.start(), key)
for key, pattern in FACET_PATTERNS.items()
if (m := re.search(pattern, text))
]
return [key for _, key in sorted(matches)]
def content_question(question: str) -> bool:
text = normalized(question)
return bool(
requested_facets(question)
or re.search(
r"\b(?:comu|o com|obsah\w*|opis\w*|popis\w*|zamer\w*|zhrn\w*|implement\w*)\b", text
)
)
def facet_mode(question: str) -> bool:
facets = requested_facets(question)
return any(
facet in facets
for facet in ["plans", "progress", "problems", "methods", "experiments", "results"]
)
def actual_methods_requested(question: str) -> bool:
return bool(
re.search(
r"\b(?:skutocne|pouziva\w*|pouzil\w*|vyuzival\w*|implementoval\w*|vyskus(?:al|ala|ali)\w*)\b|"
r"\bake technolog\w* ma\b|"
r"\b(?:pouzit\w*|aplikovan\w*|vyuzit\w*)\s+(?:metod\w*|technolog\w*|nastroj\w*)\b",
normalized(question),
)
)
def unresolved_followup(question: str) -> bool:
return bool(
re.fullmatch(r'(?:(?:a|ale)(?: co)?\s+)?v roku [12]\d{3}[.?]?',normalized(question)) or
re.search(r'\b(?:vtedy|(?:v|na|o|z|pri) (?:danom|tomto|tom|tomto istom) stretnuti|(?:tej|pri) druhej praci)\b',normalized(question)) or
re.fullmatch(
r"(?:(?:daj|uved|ukaz|vypis|porovnaj|chcem|prosim)\s+)?"
r"(?:obe|oba|obidv\w*|vsetky|prvy|druhy)(?:\s+(?:prace|projekty))?[.!?]?",
normalized(question),
)
)
def output_year_requested(question: str) -> bool:
# A supplied year scopes the evidence; it does not itself ask for a year claim.
text = normalized(question)
without_filters = re.sub(r"\b(?:v|z|pre|na)\s+rok(?:u)?\s+\d+\b", " ", text)
return bool(re.search(r"\brok\w*\b", without_filters))
def graph_needs_text(question: str) -> bool:
text = normalized(re.sub(r'[„“"«][^„“”"»]+[“”"»]', " ", question))
# Explicit graph properties stay on their existing path.
if re.search(r"\b(?:tag\w*|klucov\w*|kategor\w*|tem\w*|nazov\w*|autor\w*)\b", text):
return False
return bool(
set(requested_facets(question))
& {"assignment", "plans", "problems", "methods", "experiments", "progress", "results"}
)
def planned_experiments(question: str) -> bool:
text = normalized(question)
if re.search(
r"\b(?:vykonal\w*|vykonan\w*|uskutocnil\w*|uskutocnen\w*|realizoval\w*|realizovan\w*|otestoval\w*|vyhodnotil\w*)\b",
text,
):
return False
return bool(
re.search(
r"\b(?:plan\w*|pripravit|navrhnut)|"
r"\b(?:mal|mali|mala|ma|maju)\s+(?:priprav\w*|navrhn\w*|otest\w*|vyhodnot\w*)",
text,
)
)