knowledge-loom-backend / backend /orchestrator.py
Souvikbasur's picture
Add Hugging Face README config properly encoded
ccb1bbd
Raw
History Blame Contribute Delete
3.84 kB
"""
orchestrator.py
----------------
This is the "multi-agent system" coordination layer.
Flow:
1. Keyword pre-filter on the raw topic (fast, cheap safety gate)
2. Run Overview, Video, and Paper agents CONCURRENTLY (they don't
depend on each other's output)
3. Pass all 3 results into the Aggregator Agent to merge into one report
4. Run an LLM-based safety check on the FINAL report before returning it
5. Return structured status per agent + the final report
"""
from concurrent.futures import ThreadPoolExecutor, as_completed
from backend import config
from backend.security import keyword_prefilter, llm_safety_check
from backend.agents.overview_agent import build_overview_agent, run_overview_agent
from backend.agents.video_agent import run_video_agent
from backend.agents.paper_agent import run_paper_agent
from backend.agents.aggregator_agent import run_aggregator_agent
def run_research_pipeline(topic: str) -> dict:
# --- Step 1: fast pre-filter -------------------------------------------------
if not keyword_prefilter(topic):
return {
"status": "blocked",
"reason": "Topic blocked by pre-filter safety rule.",
"report": None,
"agent_log": [],
}
agent_log = []
# Shared Gemini model instance for Overview + Aggregator + Safety check.
# (Video/Paper agents use their own separate, scoped API keys.)
gemini_model = build_overview_agent(config.GEMINI_API_KEY, config.GEMINI_MODEL)
# --- Step 2: run the 3 specialist agents concurrently ------------------------
results = {}
with ThreadPoolExecutor(max_workers=3) as executor:
futures = {
executor.submit(run_overview_agent, gemini_model, topic): "overview",
executor.submit(run_video_agent, config.YOUTUBE_API_KEY, topic): "videos",
executor.submit(run_paper_agent, "", topic, 10): "papers",
}
for future in as_completed(futures):
key = futures[future]
try:
results[key] = future.result()
agent_log.append(f"βœ… {key}_agent completed successfully")
except Exception as e:
results[key] = {"error": str(e)}
agent_log.append(f"❌ {key}_agent failed: {e}")
# Guard against a completely failed overview (nothing to aggregate)
if "content" not in results.get("overview", {}):
return {
"status": "error",
"reason": "Overview agent failed β€” cannot build report.",
"report": None,
"agent_log": agent_log,
}
if "error" in results.get("overview", {}):
agent_log.append("⚠️ overview_agent rate-limited; loaded degraded fallback overview")
# --- Step 3: aggregate everything into one report ---------------------------
final_report = run_aggregator_agent(
gemini_model,
topic,
overview=results.get("overview", {}),
videos=results.get("videos", {}),
papers=results.get("papers", {}),
)
agent_log.append("βœ… aggregator_agent merged all results")
# --- Step 4: safety gate on the FINAL report ---------------------------------
is_safe, reason = llm_safety_check(gemini_model, final_report)
if not is_safe:
agent_log.append(f"β›” safety_gate blocked final report: {reason}")
return {
"status": "blocked",
"reason": reason,
"report": None,
"agent_log": agent_log,
}
if reason and "Bypassed" in reason:
agent_log.append(f"⚠️ safety_gate bypassed: {reason}")
else:
agent_log.append("βœ… safety_gate passed")
return {
"status": "success",
"reason": None,
"report": final_report,
"agent_log": agent_log,
}