ResearchRAG / app /fallback.py
riezqidr's picture
feat(rag): implement multilingual retrieval pipeline with HyDE, hybrid search, reranking, and reasoning
619c352
Raw
History Blame Contribute Delete
3.06 kB
"""
Relevance fallback for ResearchRAG.
When the reranker's top-1 score falls below cfg.relevance_threshold, the local
knowledge base is judged not relevant enough and the answer context comes from
live sources instead β€” same if-else pattern as the notebook, with one domain
adaptation: OpenAlex (250M papers, already integrated) is tried before
DuckDuckGo, which is only the last resort.
Every fallback context is labelled so the UI can tell the user the answer did
NOT come from their own knowledge base.
"""
from __future__ import annotations
from app.config import get_settings
def openalex_fallback(query: str, max_results: int) -> tuple[str, list[dict]]:
"""
Live OpenAlex search β†’ context blocks + reference dicts.
Returns ("", []) when nothing is found or the API fails.
"""
try:
from app.openalex_service import search_openalex
works = search_openalex(query, max_results=max_results)
except Exception as exc:
print(f"[fallback] OpenAlex failed: {exc}")
return "", []
blocks, refs = [], []
for i, work in enumerate(works, start=1):
blocks.append(f"[{i}] {work.title} ({work.published})\n{work.abstract}")
refs.append({
"title": work.title,
"authors": ", ".join(work.authors),
"published": work.published,
"url": work.url,
"source": "openalex-live",
})
return "\n\n".join(blocks), refs
def duckduckgo_fallback(query: str, max_results: int) -> tuple[str, list[dict]]:
"""
DuckDuckGo web search β†’ context blocks + reference dicts.
Returns ("", []) when nothing is found or the search fails.
"""
try:
from ddgs import DDGS
results = list(DDGS().text(query, max_results=max_results))
except Exception as exc:
print(f"[fallback] DuckDuckGo failed: {exc}")
return "", []
blocks, refs = [], []
for i, r in enumerate(results, start=1):
title = r.get("title", "")
body = r.get("body", "")
if not body:
continue
blocks.append(f"[{i}] {title}\n{body}")
refs.append({
"title": title,
"authors": "",
"published": "",
"url": r.get("href", ""),
"source": "web",
})
return "\n\n".join(blocks), refs
def fallback_context(query: str) -> tuple[str, list[dict], str]:
"""
Build answer context from live sources when the local KB is not relevant.
Order: OpenAlex β†’ DuckDuckGo β†’ empty.
Returns (context, refs, source_note). An empty context means both sources
failed and the caller must answer honestly that nothing relevant was found.
"""
cfg = get_settings()
n = getattr(cfg, "fallback_max_results", 3)
context, refs = openalex_fallback(query, n)
if context:
return context, refs, "openalex-live"
context, refs = duckduckgo_fallback(query, n)
if context:
return context, refs, "web"
return "", [], "none"