Spaces:
Sleeping
Sleeping
Download src/agent.py from Wikkkkkoooo/gaia-agent-final: direct link, hf CLI and curl.
- Browser
- Download file 4.72 kB
-
https://huggingface.co/spaces/Wikkkkkoooo/gaia-agent-final/resolve/main/src/agent.py
- Command line
-
hf download hf://spaces/Wikkkkkoooo/gaia-agent-final/src/agent.py
-
curl -L -o agent.py https://huggingface.co/spaces/Wikkkkkoooo/gaia-agent-final/resolve/main/src/agent.py
4.72 kB
| """Core agent logic. | |
| Given one GAIA question object, produce a single submittable answer string. | |
| The agent: | |
| 1. inspects the question and any attached file | |
| 2. downloads the attached file (via ``task_id``) when present | |
| 3. lets the model gather evidence with tool use (web search) and the file | |
| 4. produces a final answer | |
| 5. normalizes that answer for exact-match scoring | |
| The returned value is **only** the answer text — no explanation, no markdown, | |
| no "FINAL ANSWER" label. Output discipline is enforced twice: once by the | |
| system prompt, and once by ``normalizer.finalize`` as a safety net. | |
| Two entry points: | |
| * :func:`run_agent` — returns just the normalized answer (used by the app). | |
| * :func:`run_agent_detailed` — returns an :class:`AgentResult` carrying the raw | |
| model output alongside the normalized answer, for local debugging. | |
| Model selection is delegated to ``llm.get_runner`` so the model can be swapped | |
| (Anthropic / OpenAI) via configuration without touching this file. File handling | |
| lives in ``files.py``; this module only maps a ``FileContext`` to model blocks. | |
| """ | |
| from __future__ import annotations | |
| from dataclasses import dataclass | |
| from typing import Any, Dict, List, Optional | |
| from . import config, files, normalizer, prompts | |
| from .llm import get_runner | |
| class AgentResult: | |
| """One question's run, with both raw and normalized output for debugging.""" | |
| task_id: str | |
| question: str | |
| raw_output: str | |
| submitted_answer: str | |
| file_kind: Optional[str] = None | |
| file_summary: Optional[str] = None | |
| def run_agent(question: Dict[str, Any]) -> str: | |
| """Answer one GAIA question and return the normalized answer string. | |
| ``question`` is a question dict from ``scoring_api.get_questions`` / | |
| ``get_random_question``. It must contain ``task_id`` and ``question``; a | |
| non-empty ``file_name`` indicates an attachment to fetch. | |
| """ | |
| return run_agent_detailed(question).submitted_answer | |
| def run_agent_detailed(question: Dict[str, Any]) -> AgentResult: | |
| """Answer one question, returning raw + normalized output and file info. | |
| The raw model output is preserved verbatim (never mutated) so a debug | |
| workflow can compare it against the normalized submission. | |
| """ | |
| task_id = str(question.get("task_id", "")) | |
| question_text = question.get("question", "") | |
| file_name = question.get("file_name") or "" | |
| # --- Steps 1 & 2: download/inspect the file and assemble model input --- | |
| ctx = files.load_file(task_id, file_name) if file_name else None | |
| has_file = ctx is not None and ctx.is_readable | |
| task_prompt = prompts.build_task_prompt(question_text, has_file=has_file) | |
| user_content: List[Dict[str, Any]] = [{"type": "text", "text": task_prompt}] | |
| if ctx is not None: | |
| user_content.extend(_blocks_from_file_context(ctx)) | |
| # --- Steps 3-5: run the agent, then enforce exact-match formatting --- | |
| # >>> The model call (and its tool loop) lives behind the runner. <<< | |
| system_prompt = prompts.get_system_prompt(short=config.SHORT_PROMPT) | |
| runner = get_runner() | |
| raw_output = runner.run(system_prompt, user_content) | |
| # finalize() never mutates raw_output; it returns a cleaned copy. | |
| submitted_answer = normalizer.finalize(raw_output) | |
| return AgentResult( | |
| task_id=task_id, | |
| question=question_text, | |
| raw_output=raw_output, | |
| submitted_answer=submitted_answer, | |
| file_kind=ctx.kind if ctx else None, | |
| file_summary=ctx.summary if ctx else None, | |
| ) | |
| def _blocks_from_file_context(ctx: files.FileContext) -> List[Dict[str, Any]]: | |
| """Map a provider-neutral FileContext to Anthropic content blocks. | |
| Images become vision blocks, PDFs become document blocks, and text-like | |
| files (txt/csv/json) become a text block prefixed with the file summary. | |
| Unreadable files contribute a short note so the model knows what's missing. | |
| """ | |
| if ctx.kind == "image" and ctx.data_b64: | |
| return [{ | |
| "type": "image", | |
| "source": { | |
| "type": "base64", | |
| "media_type": ctx.media_type or "image/png", | |
| "data": ctx.data_b64, | |
| }, | |
| }] | |
| if ctx.kind == "pdf" and ctx.data_b64: | |
| return [{ | |
| "type": "document", | |
| "source": { | |
| "type": "base64", | |
| "media_type": "application/pdf", | |
| "data": ctx.data_b64, | |
| }, | |
| }] | |
| if ctx.text is not None: # text / csv / json | |
| return [{"type": "text", "text": f"{ctx.summary}\n\n{ctx.text}"}] | |
| # Missing / unsupported: a note keeps the model informed without crashing. | |
| return [{"type": "text", "text": f"[{ctx.summary}]"}] | |