atlasbot: restore quick fact-sheet injection path
This commit is contained in:
parent
9911c3fdef
commit
edf1d66ae0
@ -203,7 +203,15 @@ class AnswerEngine:
|
|||||||
timeout_sec = min(self._settings.ollama_timeout_sec, time_left)
|
timeout_sec = min(self._settings.ollama_timeout_sec, time_left)
|
||||||
call_count += 1
|
call_count += 1
|
||||||
messages = build_messages(system, prompt, context=context)
|
messages = build_messages(system, prompt, context=context)
|
||||||
response = await self._llm.chat(messages, model=model or plan.model, timeout_sec=timeout_sec)
|
try:
|
||||||
|
llm_call = self._llm.chat(messages, model=model or plan.model, timeout_sec=timeout_sec)
|
||||||
|
if timeout_sec is not None:
|
||||||
|
response = await asyncio.wait_for(llm_call, timeout=max(0.001, timeout_sec))
|
||||||
|
else:
|
||||||
|
response = await llm_call
|
||||||
|
except asyncio.TimeoutError as exc:
|
||||||
|
time_budget_hit = True
|
||||||
|
raise LLMTimeBudgetExceeded("time_budget") from exc
|
||||||
log.info(
|
log.info(
|
||||||
"atlasbot_llm_call",
|
"atlasbot_llm_call",
|
||||||
extra={"extra": {"mode": mode, "tag": tag, "call": call_count, "limit": call_cap}},
|
extra={"extra": {"mode": mode, "tag": tag, "call": call_count, "limit": call_cap}},
|
||||||
@ -240,6 +248,61 @@ class AnswerEngine:
|
|||||||
classify: dict[str, Any] = {}
|
classify: dict[str, Any] = {}
|
||||||
tool_hint: dict[str, Any] | None = None
|
tool_hint: dict[str, Any] | None = None
|
||||||
try:
|
try:
|
||||||
|
if mode in {"quick", "fast"} and not limitless:
|
||||||
|
if observer:
|
||||||
|
observer("factsheet", "building fact sheet")
|
||||||
|
kb_lines = (
|
||||||
|
self._kb.chunk_lines(
|
||||||
|
max_files=plan.kb_max_files,
|
||||||
|
max_chars=max(1200, plan.kb_max_chars),
|
||||||
|
)
|
||||||
|
if self._kb
|
||||||
|
else []
|
||||||
|
)
|
||||||
|
fact_lines = _quick_fact_sheet_lines(
|
||||||
|
question,
|
||||||
|
summary_lines,
|
||||||
|
kb_lines,
|
||||||
|
limit=max(14, plan.chunk_top * 5),
|
||||||
|
)
|
||||||
|
if observer:
|
||||||
|
observer("quick", "answering from fact sheet")
|
||||||
|
classify = {
|
||||||
|
"needs_snapshot": True,
|
||||||
|
"needs_kb": bool(kb_lines),
|
||||||
|
"question_type": "quick_factsheet",
|
||||||
|
"answer_style": "direct",
|
||||||
|
"follow_up": False,
|
||||||
|
}
|
||||||
|
quick_context = _quick_fact_sheet_text(fact_lines)
|
||||||
|
quick_prompt = (
|
||||||
|
"Question: "
|
||||||
|
+ question
|
||||||
|
+ "\nAnswer using only the Fact Sheet. Keep it to 1-3 sentences. "
|
||||||
|
+ "If the Fact Sheet is missing key data, say exactly what is missing and suggest atlas-smart."
|
||||||
|
)
|
||||||
|
reply = await call_llm(
|
||||||
|
prompts.ANSWER_SYSTEM,
|
||||||
|
quick_prompt,
|
||||||
|
context=quick_context,
|
||||||
|
model=plan.fast_model,
|
||||||
|
tag="quick_factsheet",
|
||||||
|
)
|
||||||
|
reply = _strip_followup_meta(reply)
|
||||||
|
scores = _default_scores()
|
||||||
|
meta = _build_meta(
|
||||||
|
mode,
|
||||||
|
call_count,
|
||||||
|
call_cap,
|
||||||
|
limit_hit,
|
||||||
|
time_budget_hit,
|
||||||
|
time_budget_sec,
|
||||||
|
classify,
|
||||||
|
tool_hint,
|
||||||
|
started,
|
||||||
|
)
|
||||||
|
return AnswerResult(reply, scores, meta)
|
||||||
|
|
||||||
if observer:
|
if observer:
|
||||||
observer("normalize", "normalizing")
|
observer("normalize", "normalizing")
|
||||||
normalize_prompt = prompts.NORMALIZE_PROMPT + "\nQuestion: " + question
|
normalize_prompt = prompts.NORMALIZE_PROMPT + "\nQuestion: " + question
|
||||||
@ -3307,6 +3370,92 @@ def _state_from_payload(payload: dict[str, Any] | None) -> ConversationState | N
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _quick_fact_sheet_lines(
|
||||||
|
question: str,
|
||||||
|
summary_lines: list[str],
|
||||||
|
kb_lines: list[str],
|
||||||
|
*,
|
||||||
|
limit: int,
|
||||||
|
) -> list[str]:
|
||||||
|
tokens = {
|
||||||
|
token
|
||||||
|
for token in re.findall(r"[a-z0-9][a-z0-9_-]{2,}", question.lower())
|
||||||
|
if token not in GENERIC_METRIC_TOKENS
|
||||||
|
}
|
||||||
|
priority_markers = (
|
||||||
|
"snapshot:",
|
||||||
|
"nodes_total",
|
||||||
|
"nodes_ready",
|
||||||
|
"nodes_not_ready",
|
||||||
|
"workers_ready",
|
||||||
|
"workers_not_ready",
|
||||||
|
"control_plane",
|
||||||
|
"worker_nodes",
|
||||||
|
"hottest",
|
||||||
|
"postgres",
|
||||||
|
"pods",
|
||||||
|
"longhorn",
|
||||||
|
"titan-",
|
||||||
|
"rpi5",
|
||||||
|
"rpi4",
|
||||||
|
"jetson",
|
||||||
|
"amd64",
|
||||||
|
)
|
||||||
|
scored: list[tuple[int, str]] = []
|
||||||
|
for raw in summary_lines:
|
||||||
|
line = raw.strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
lowered = line.lower()
|
||||||
|
score = 0
|
||||||
|
if any(marker in lowered for marker in priority_markers):
|
||||||
|
score += 4
|
||||||
|
overlap = sum(1 for token in tokens if token in lowered)
|
||||||
|
score += overlap * 3
|
||||||
|
if len(line) <= 180:
|
||||||
|
score += 1
|
||||||
|
if score > 0:
|
||||||
|
scored.append((score, line))
|
||||||
|
|
||||||
|
scored.sort(key=lambda item: item[0], reverse=True)
|
||||||
|
selected = [line for _, line in scored[:limit]]
|
||||||
|
if not selected:
|
||||||
|
selected = [line.strip() for line in summary_lines if line.strip()][:limit]
|
||||||
|
|
||||||
|
kb_selected: list[str] = []
|
||||||
|
for raw in kb_lines:
|
||||||
|
line = raw.strip()
|
||||||
|
if not line or len(line) > 220:
|
||||||
|
continue
|
||||||
|
lowered = line.lower()
|
||||||
|
if "kb file:" in lowered or "kb: atlas.json" in lowered:
|
||||||
|
continue
|
||||||
|
overlap = sum(1 for token in tokens if token in lowered)
|
||||||
|
if overlap > 0:
|
||||||
|
kb_selected.append(line)
|
||||||
|
elif any(marker in lowered for marker in ("runbook", "titan-", "rpi5", "rpi4", "amd64", "jetson")):
|
||||||
|
kb_selected.append(line)
|
||||||
|
if len(kb_selected) >= max(4, limit // 3):
|
||||||
|
break
|
||||||
|
|
||||||
|
merged = []
|
||||||
|
seen: set[str] = set()
|
||||||
|
for line in selected + kb_selected:
|
||||||
|
if line not in seen:
|
||||||
|
seen.add(line)
|
||||||
|
merged.append(line)
|
||||||
|
if len(merged) >= limit:
|
||||||
|
break
|
||||||
|
return merged
|
||||||
|
|
||||||
|
|
||||||
|
def _quick_fact_sheet_text(lines: list[str]) -> str:
|
||||||
|
if not lines:
|
||||||
|
return "Fact Sheet:\n- No snapshot facts available."
|
||||||
|
body = "\n".join([f"- {line}" for line in lines])
|
||||||
|
return "Fact Sheet:\n" + body
|
||||||
|
|
||||||
|
|
||||||
def _json_excerpt(summary: dict[str, Any], max_chars: int = 12000) -> str:
|
def _json_excerpt(summary: dict[str, Any], max_chars: int = 12000) -> str:
|
||||||
raw = json.dumps(summary, ensure_ascii=False)
|
raw = json.dumps(summary, ensure_ascii=False)
|
||||||
return raw[:max_chars]
|
return raw[:max_chars]
|
||||||
|
|||||||
@ -22,6 +22,8 @@ class FakeLLM:
|
|||||||
return '[{"id":"q1","question":"What is Atlas?","priority":1}]'
|
return '[{"id":"q1","question":"What is Atlas?","priority":1}]'
|
||||||
if "sub-question" in prompt:
|
if "sub-question" in prompt:
|
||||||
return "Atlas has 22 nodes."
|
return "Atlas has 22 nodes."
|
||||||
|
if "Answer using only the Fact Sheet" in prompt:
|
||||||
|
return "Atlas has 22 nodes."
|
||||||
if "final response" in prompt:
|
if "final response" in prompt:
|
||||||
return "Atlas has 22 nodes."
|
return "Atlas has 22 nodes."
|
||||||
if "Score response quality" in prompt:
|
if "Score response quality" in prompt:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user