czrrr commited on
Commit
83c484b
·
verified ·
1 Parent(s): 1b12e7f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +160 -31
app.py CHANGED
@@ -1,6 +1,7 @@
1
  import os
2
  import re
3
  import base64
 
4
  from io import BytesIO
5
  from pathlib import Path
6
  from zipfile import ZipFile
@@ -369,6 +370,12 @@ class BasicAgent:
369
  gemini_api_key = os.getenv("GEMINI_API_KEY")
370
  configured_model = os.getenv("GAIA_MODEL_ID")
371
 
 
 
 
 
 
 
372
  model_id = configured_model or DEFAULT_HF_MODEL
373
 
374
  if model_id.startswith("gemini/"):
@@ -391,21 +398,44 @@ class BasicAgent:
391
  max_tokens=2_000,
392
  )
393
  self.hf_token = hf_token
 
394
  self.model_id = model_id
395
  print(f"Modelo principal selecionado: {model_id}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
396
  agent_tools = [
397
- DuckDuckGoSearchTool(max_results=8, rate_limit=1.0),
398
- VisitWebpageTool(max_output_length=30_000),
399
- WikipediaSearchTool(
400
- user_agent="GAIA-Course-Agent/1.0 (educational project)",
401
- language="en",
402
- ),
403
  InspectGaiaAttachmentTool(),
404
  YouTubeTranscriptTool(),
405
  AnalyzeGaiaImageTool(),
 
406
  ]
407
- if gemini_api_key:
408
- agent_tools.append(ConsultGeminiTool())
409
 
410
  self.agent = CodeAgent(
411
  tools=agent_tools,
@@ -430,28 +460,38 @@ class BasicAgent:
430
 
431
  exact_match_prompt = """
432
  You are an expert AI assistant solving tasks from the GAIA benchmark.
433
- Research carefully before answering and cross-check uncertain facts.
434
- Use web_search to find sources and visit_webpage to read a result in detail.
435
- If the task mentions an attached file, call inspect_gaia_attachment with the
436
- task_id given in the task.
437
- If the task asks what someone says in a YouTube video, call youtube_transcript
438
- with the exact video URL before searching the web.
439
- If the task depends on an attached image, call analyze_gaia_image with the
440
- task_id and complete question. Do not try to infer image contents from metadata.
441
- When consult_gemini is available, use it selectively for a second opinion after
442
- you have collected evidence, especially when sources conflict or your candidate
443
- answer is uncertain. Give it the complete question and relevant evidence. Do
444
- not blindly copy its response; compare it with the sources before deciding.
445
- Prefer primary or official sources. When search snippets conflict, open the
446
- source and verify the relevant passage instead of guessing.
447
- Only call tools that are explicitly available. Never invent a function such as
448
- visit_webpage if it is not listed, and never use subprocess or shell commands.
449
- Do not repeat nearly identical searches. If one approach fails, change source
450
- or method.
451
- Your final answer must contain only the exact requested answer. Do not add
452
- explanations, conversational text, Markdown, citations, or the words
453
- "FINAL ANSWER". If a comma-separated list is requested, return only that list.
454
- If a number is requested, return only that number.
 
 
 
 
 
 
 
 
 
 
455
  """
456
  self.agent.prompt_templates["system_prompt"] = (
457
  exact_match_prompt.strip()
@@ -484,7 +524,12 @@ If a number is requested, return only that number.
484
  "(GEMINI_API_KEY para Gemini ou HF_TOKEN para Hugging Face)."
485
  ) from exc
486
  raise
487
- return self.format_exact_answer(question, str(result))
 
 
 
 
 
488
 
489
  @staticmethod
490
  def deterministic_answer_cleanup(answer: str) -> str:
@@ -548,6 +593,90 @@ If a number is requested, return only that number.
548
 
549
  return self.deterministic_answer_cleanup(cleaned)
550
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
551
 
552
  def empty_results() -> pd.DataFrame:
553
  return pd.DataFrame(columns=RESULT_COLUMNS)
 
1
  import os
2
  import re
3
  import base64
4
+ import json
5
  from io import BytesIO
6
  from pathlib import Path
7
  from zipfile import ZipFile
 
370
  gemini_api_key = os.getenv("GEMINI_API_KEY")
371
  configured_model = os.getenv("GAIA_MODEL_ID")
372
 
373
+ if not gemini_api_key:
374
+ raise RuntimeError(
375
+ "O secret GEMINI_API_KEY é obrigatório porque todas as "
376
+ "respostas passam pela revisão final do Gemini."
377
+ )
378
+
379
  model_id = configured_model or DEFAULT_HF_MODEL
380
 
381
  if model_id.startswith("gemini/"):
 
398
  max_tokens=2_000,
399
  )
400
  self.hf_token = hf_token
401
+ self.gemini_api_key = gemini_api_key
402
  self.model_id = model_id
403
  print(f"Modelo principal selecionado: {model_id}")
404
+
405
+ web_search_tool = DuckDuckGoSearchTool(
406
+ max_results=8, rate_limit=1.0
407
+ )
408
+ web_search_tool.description = (
409
+ "Searches the public web and returns result titles, URLs, and short "
410
+ "snippets. Use it to discover candidate sources. It does NOT open "
411
+ "or read the full pages; call visit_webpage on a returned URL."
412
+ )
413
+ visit_page_tool = VisitWebpageTool(max_output_length=30_000)
414
+ visit_page_tool.description = (
415
+ "Opens one exact HTTP/HTTPS URL and returns the readable page "
416
+ "content. Use it after web_search when facts depend on the page "
417
+ "itself, a table, article text, or linked source."
418
+ )
419
+ wikipedia_tool = WikipediaSearchTool(
420
+ user_agent="GAIA-Course-Agent/1.0 (educational project)",
421
+ language="en",
422
+ )
423
+ wikipedia_tool.description = (
424
+ "Searches English Wikipedia content directly. Use it for questions "
425
+ "that explicitly mention Wikipedia or for encyclopedic facts. "
426
+ "For version-specific or nomination details, verify the exact page "
427
+ "or archive with visit_webpage."
428
+ )
429
+
430
  agent_tools = [
431
+ web_search_tool,
432
+ visit_page_tool,
433
+ wikipedia_tool,
 
 
 
434
  InspectGaiaAttachmentTool(),
435
  YouTubeTranscriptTool(),
436
  AnalyzeGaiaImageTool(),
437
+ ConsultGeminiTool(),
438
  ]
 
 
439
 
440
  self.agent = CodeAgent(
441
  tools=agent_tools,
 
460
 
461
  exact_match_prompt = """
462
  You are an expert AI assistant solving tasks from the GAIA benchmark.
463
+
464
+ TOOL ROUTING POLICY:
465
+ 1. web_search discovers URLs and snippets. It does not read full pages.
466
+ 2. visit_webpage opens and reads one exact URL. Use it after web_search to
467
+ verify articles, tables, archives, papers, and linked primary sources.
468
+ 3. wikipedia_search searches English Wikipedia. Use it when Wikipedia is
469
+ explicitly mentioned or for encyclopedic facts. For revision-specific,
470
+ nomination, archive, or table details, verify the exact page.
471
+ 4. inspect_gaia_attachment reads the official file for the supplied task_id.
472
+ Call it first for attached PDFs, spreadsheets, documents, audio, or code.
473
+ 5. analyze_gaia_image reads image pixels. Use it for diagrams, screenshots,
474
+ chess positions, or questions that visually depend on an attached image.
475
+ 6. youtube_transcript retrieves spoken subtitles. Use it when asked what a
476
+ person said. It cannot answer purely visual video questions.
477
+ 7. consult_gemini gives a second opinion. Use it after gathering evidence when
478
+ sources conflict or the candidate answer is uncertain. Include the complete
479
+ question, evidence, and candidate answer.
480
+
481
+ Research carefully, prefer primary or official sources, and cross-check
482
+ uncertain facts. A search snippet alone is insufficient when the source page
483
+ can be opened. Never invent a tool, use subprocess, or use shell commands.
484
+ Do not repeat nearly identical searches; change the source or method.
485
+
486
+ FINAL RESPONSE POLICY:
487
+ Call final_answer with only the requested value. Never include reasoning,
488
+ explanations, labels, Markdown, citations, or the words "FINAL ANSWER".
489
+ - Quantity/count: return only the number, unless units or currency are requested.
490
+ - Person: return only the requested name component.
491
+ - City/country/code: return only that value.
492
+ - List: return only items with the requested separator and ordering.
493
+ - Chess move: return only algebraic notation.
494
+ - Quote: return only the requested spoken words.
495
  """
496
  self.agent.prompt_templates["system_prompt"] = (
497
  exact_match_prompt.strip()
 
524
  "(GEMINI_API_KEY para Gemini ou HF_TOKEN para Hugging Face)."
525
  ) from exc
526
  raise
527
+ candidate = self.format_exact_answer(question, str(result))
528
+ return self.review_answer_with_gemini(
529
+ question=question,
530
+ candidate=candidate,
531
+ task_id=task_id,
532
+ )
533
 
534
  @staticmethod
535
  def deterministic_answer_cleanup(answer: str) -> str:
 
593
 
594
  return self.deterministic_answer_cleanup(cleaned)
595
 
596
+ def review_answer_with_gemini(
597
+ self, question: str, candidate: str, task_id: str | None = None
598
+ ) -> str:
599
+ """Revisa obrigatoriamente conteúdo e formato antes de salvar a resposta."""
600
+ reviewer_model = os.getenv(
601
+ "GAIA_GEMINI_REVIEW_MODEL", DEFAULT_GEMINI_MODEL
602
+ )
603
+ review_prompt = f"""
604
+ You are the mandatory final reviewer for a GAIA exact-match answer.
605
+
606
+ Task ID: {task_id or "test"}
607
+ Question:
608
+ {question}
609
+
610
+ Candidate answer:
611
+ {candidate}
612
+
613
+ Review the candidate for both likely correctness and exact requested format.
614
+ Preserve it unless there is a clear factual, logical, ordering, spelling, or
615
+ formatting error. Never add explanations to final_answer.
616
+
617
+ Formatting rules:
618
+ - If asked "how many", for a count, or for a numeric output: final_answer must
619
+ contain only the number, unless the question explicitly requests currency,
620
+ units, decimals, or another representation.
621
+ - If asked for a first name, surname, city, country, or IOC code: return only
622
+ that requested value.
623
+ - If asked for a list: return only the list, with the exact requested separator,
624
+ ordering, capitalization, and plurality.
625
+ - If asked for a quote: return only the requested spoken words.
626
+ - If asked for a chess move: return only algebraic notation.
627
+ - Never include labels, Markdown, citations, rationale, or "FINAL ANSWER".
628
+
629
+ Return one JSON object and nothing else:
630
+ {{
631
+ "final_answer": "exact value to submit",
632
+ "changed": true,
633
+ "review_note": "brief reason, maximum 20 words"
634
+ }}
635
+ """.strip()
636
+
637
+ last_error = None
638
+ for use_json_mode in (True, False):
639
+ try:
640
+ kwargs = {
641
+ "model": reviewer_model,
642
+ "api_key": self.gemini_api_key,
643
+ "messages": [{"role": "user", "content": review_prompt}],
644
+ "temperature": 0,
645
+ "max_tokens": 350,
646
+ }
647
+ if use_json_mode:
648
+ kwargs["response_format"] = {"type": "json_object"}
649
+ response = completion(**kwargs)
650
+ content = str(response.choices[0].message.content).strip()
651
+ content = re.sub(
652
+ r"^```(?:json)?\s*|\s*```$", "", content, flags=re.I
653
+ )
654
+ start = content.find("{")
655
+ end = content.rfind("}")
656
+ if start >= 0 and end > start:
657
+ content = content[start : end + 1]
658
+ payload = json.loads(content)
659
+ final_answer = self.deterministic_answer_cleanup(
660
+ str(payload.get("final_answer", ""))
661
+ )
662
+ if not final_answer:
663
+ raise ValueError("Gemini returned an empty final_answer.")
664
+
665
+ changed = final_answer != candidate
666
+ note = str(payload.get("review_note", "")).strip()
667
+ print(f"Candidate answer: {candidate}")
668
+ print(f"Gemini reviewed answer: {final_answer}")
669
+ print(f"Gemini changed answer: {changed}")
670
+ print(f"Gemini review note: {note}")
671
+ return final_answer
672
+ except Exception as exc:
673
+ last_error = exc
674
+
675
+ raise RuntimeError(
676
+ "A revisão obrigatória do Gemini falhou; a resposta não foi salva. "
677
+ f"Detalhe: {last_error}"
678
+ )
679
+
680
 
681
  def empty_results() -> pd.DataFrame:
682
  return pd.DataFrame(columns=RESULT_COLUMNS)