99 lines
4.3 KiB
Python
99 lines
4.3 KiB
Python
"""One bounded corrective request based solely on retrieval evidence."""
|
|
|
|
import json
|
|
from tools.answers.answer_format import parse_answer, Answer, Claim
|
|
from tools.answers.context_policy import answer_context_issues
|
|
from tools.answers.render_answer import render_graph_answer
|
|
|
|
|
|
def generate_checked_answer(question: str, context: dict, prompt: str, complete) -> dict:
|
|
# Candidate provenance is used by the guard, but is not part of a Claim.
|
|
# Keep the model's selection shape identical to the actual output schema.
|
|
sent = {
|
|
**context,
|
|
"answer_candidates": [
|
|
{k: v for k, v in row.items() if k in Claim.model_fields}
|
|
for row in context.get("answer_candidates", [])
|
|
],
|
|
}
|
|
aggregation = context.get("aggregation")
|
|
if aggregation is not None:
|
|
sent["aggregation"] = {
|
|
k: v for k, v in aggregation.items() if k in {"count", "count_domain", "people", "complete"}
|
|
}
|
|
policy = "\nDo claims nevkladaj source_urls ani evidence_documents; source_urls patrí iba na vrchnú úroveň odpovede."
|
|
if (
|
|
aggregation
|
|
and aggregation.get("complete") is True
|
|
and type(aggregation.get("count")) is int
|
|
):
|
|
policy += (
|
|
"\nÚplná overená agregácia tohto dotazu má count="
|
|
+ str(aggregation["count"])
|
|
+ ". Pri status=ok zachovaj tento count; počet dodaných facts, claims ani answer_candidates ho nemení."
|
|
)
|
|
messages = [
|
|
{"role": "system", "content": prompt + policy},
|
|
{
|
|
"role": "user",
|
|
"content": json.dumps(
|
|
{"question": question, "graph_context": sent}, ensure_ascii=False
|
|
),
|
|
},
|
|
]
|
|
result = {"attempts": [], "accepted_for_delivery": False}
|
|
for number in range(2):
|
|
record = {}
|
|
text = ""
|
|
try:
|
|
response = complete(messages)
|
|
result["response"] = response
|
|
record["response"] = response
|
|
text = response["choices"][0]["message"]["content"].strip()
|
|
except Exception as error:
|
|
result["error"] = type(error).__name__
|
|
result["reason"] = "model_unavailable"
|
|
result["attempts"].append(record)
|
|
return result
|
|
result["attempts"].append(record)
|
|
try:
|
|
answer, normalized = parse_answer(text)
|
|
record["answer"] = answer
|
|
record["format_normalized"] = normalized
|
|
except (ValueError, TypeError) as error:
|
|
record["format_error"] = type(error).__name__
|
|
if number == 1:
|
|
result["error"] = "InvalidAnswerFormat: two invalid JSON/schema responses"
|
|
return result
|
|
feedback = (
|
|
"Vráť jediný platný JSON objekt podľa schémy, bez zmeny faktov z pôvodného kontextu. Schéma: "
|
|
+ json.dumps(Answer.model_json_schema(), ensure_ascii=False)
|
|
)
|
|
else:
|
|
issues = answer_context_issues(answer, context, check_text=False)
|
|
record["context_issues"] = issues
|
|
result["answer"] = answer
|
|
if not issues:
|
|
rendered = render_graph_answer(answer, context)
|
|
delivery_issues = answer_context_issues(rendered, context)
|
|
if delivery_issues:
|
|
issues = delivery_issues
|
|
record["context_issues"] = issues
|
|
else:
|
|
record["rendered_answer"] = rendered
|
|
result["model_answer"] = answer
|
|
result["answer"] = rendered
|
|
result["accepted_for_delivery"] = True
|
|
return result
|
|
if number == 1:
|
|
result["context_rejected"] = True
|
|
return result
|
|
feedback = (
|
|
"Odpoveď porušila pravidlá dodaného kontextu: " + ", ".join(issues) + ". "
|
|
"Oprav jediný JSON objekt výhradne z pôvodných podkladov. Použi iba source_urls zo sources. "
|
|
"Typ práce nie je keyword. answer_candidates dokladajú spojenie požadovaného názvu alebo roku a tagu cez tú istú osobu. "
|
|
"Answer ponechaj prázdny; text aj upozornenie zostaví aplikácia."
|
|
)
|
|
messages += [{"role": "assistant", "content": text}, {"role": "user", "content": feedback}]
|
|
return result
|