{ "cells": [ { "cell_type": "code", "execution_count": 11, "id": "a1dff932", "metadata": {}, "outputs": [], "source": [ "import requests\n", "\n", "DEFAULT_API_URL = \"https://agents-course-unit4-scoring.hf.space\"\n", "api_url = DEFAULT_API_URL\n", "questions_url = f\"{api_url}/questions\"\n", "\n", "response = requests.get(questions_url, timeout=15)\n", "response.raise_for_status()\n", "questions_data = response.json()\n", "questions_df = pd.DataFrame(questions_data)" ] }, { "cell_type": "code", "execution_count": 90, "id": "445b722e", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
task_idquestionLevelfile_nameFinal answer
08e867cd7-cff9-4e6c-867a-ff5ddc2550beHow many studio albums were published by Merce...13
1a1e91b78-d3d8-4675-bb8d-62741b4b68a6In the video https://www.youtube.com/watch?v=L...13
22d83110e-a098-4ebb-9987-066c06fa42d0.rewsna eht sa \"tfel\" drow eht fo etisoppo eht...1Right
3cca530fc-4052-43b2-b130-b30968d8aa44Review the chess position provided in the imag...1cca530fc-4052-43b2-b130-b30968d8aa44.pngRd5
44fc2f1ae-8625-45b5-ab34-ad4433bc21f8Who nominated the only Featured Article on Eng...1FunkMonk
56f37996b-2ac7-44b0-8e68-6d28256631b4Given this table defining * on the set S = {a,...1b, e
69d191bce-651d-4746-be2d-7ef8ecadb9c2Examine the video at https://www.youtube.com/w...1Extremely
7cabe07ed-9eca-40ea-8ead-410ef5e83f91What is the surname of the equine veterinarian...1Louvrier
83cef3a44-215e-4aed-8e3b-b1e3f08063b7I'm making a grocery list for my mom, but she'...1broccoli, celery, fresh basil, lettuce, sweet ...
999c9cc74-fdc8-46c6-8f8d-3ce2d3bfeea3Hi, I'm making a pie but I could use some help...199c9cc74-fdc8-46c6-8f8d-3ce2d3bfeea3.mp3cornstarch, freshly squeezed lemon juice, gran...
10305ac316-eef6-4446-960a-92d80d542f82Who did the actor who played Ray in the Polish...1Wojciech
11f918266a-b3e0-4914-865d-4faa564f1aefWhat is the final numeric output from the atta...1f918266a-b3e0-4914-865d-4faa564f1aef.py0
123f57289b-8c60-48be-bd80-01f8099ca449How many at bats did the Yankee with the most ...1519
131f975693-876d-457b-a649-393859e79bf3Hi, I was out sick from my classes on Friday, ...11f975693-876d-457b-a649-393859e79bf3.mp3132, 133, 134, 197, 245
14840bfca7-4f7b-481a-8794-c560c340185dOn June 6, 2023, an article by Carolyn Collins...180GSFC21M0002
15bda648d7-d618-4883-88f4-3466eabd860eWhere were the Vietnamese specimens described ...1Saint Petersburg
16cf106601-ab4f-4af9-b045-5295fe67b37dWhat country had the least number of athletes ...1CUB
17a0c07678-e491-4bbc-8f0b-07405144218fWho are the pitchers with the number before an...1Yoshida, Uehara
187bd855d8-463d-4ed5-93ca-5fe35145f733The attached Excel file contains the sales of ...17bd855d8-463d-4ed5-93ca-5fe35145f733.xlsx89706.00
195a0c1adf-205e-4841-a666-7c3ef95def9dWhat is the first name of the only Malko Compe...1Claus
\n", "
" ], "text/plain": [ " task_id \\\n", "0 8e867cd7-cff9-4e6c-867a-ff5ddc2550be \n", "1 a1e91b78-d3d8-4675-bb8d-62741b4b68a6 \n", "2 2d83110e-a098-4ebb-9987-066c06fa42d0 \n", "3 cca530fc-4052-43b2-b130-b30968d8aa44 \n", "4 4fc2f1ae-8625-45b5-ab34-ad4433bc21f8 \n", "5 6f37996b-2ac7-44b0-8e68-6d28256631b4 \n", "6 9d191bce-651d-4746-be2d-7ef8ecadb9c2 \n", "7 cabe07ed-9eca-40ea-8ead-410ef5e83f91 \n", "8 3cef3a44-215e-4aed-8e3b-b1e3f08063b7 \n", "9 99c9cc74-fdc8-46c6-8f8d-3ce2d3bfeea3 \n", "10 305ac316-eef6-4446-960a-92d80d542f82 \n", "11 f918266a-b3e0-4914-865d-4faa564f1aef \n", "12 3f57289b-8c60-48be-bd80-01f8099ca449 \n", "13 1f975693-876d-457b-a649-393859e79bf3 \n", "14 840bfca7-4f7b-481a-8794-c560c340185d \n", "15 bda648d7-d618-4883-88f4-3466eabd860e \n", "16 cf106601-ab4f-4af9-b045-5295fe67b37d \n", "17 a0c07678-e491-4bbc-8f0b-07405144218f \n", "18 7bd855d8-463d-4ed5-93ca-5fe35145f733 \n", "19 5a0c1adf-205e-4841-a666-7c3ef95def9d \n", "\n", " question Level \\\n", "0 How many studio albums were published by Merce... 1 \n", "1 In the video https://www.youtube.com/watch?v=L... 1 \n", "2 .rewsna eht sa \"tfel\" drow eht fo etisoppo eht... 1 \n", "3 Review the chess position provided in the imag... 1 \n", "4 Who nominated the only Featured Article on Eng... 1 \n", "5 Given this table defining * on the set S = {a,... 1 \n", "6 Examine the video at https://www.youtube.com/w... 1 \n", "7 What is the surname of the equine veterinarian... 1 \n", "8 I'm making a grocery list for my mom, but she'... 1 \n", "9 Hi, I'm making a pie but I could use some help... 1 \n", "10 Who did the actor who played Ray in the Polish... 1 \n", "11 What is the final numeric output from the atta... 1 \n", "12 How many at bats did the Yankee with the most ... 1 \n", "13 Hi, I was out sick from my classes on Friday, ... 1 \n", "14 On June 6, 2023, an article by Carolyn Collins... 1 \n", "15 Where were the Vietnamese specimens described ... 1 \n", "16 What country had the least number of athletes ... 1 \n", "17 Who are the pitchers with the number before an... 1 \n", "18 The attached Excel file contains the sales of ... 1 \n", "19 What is the first name of the only Malko Compe... 1 \n", "\n", " file_name \\\n", "0 \n", "1 \n", "2 \n", "3 cca530fc-4052-43b2-b130-b30968d8aa44.png \n", "4 \n", "5 \n", "6 \n", "7 \n", "8 \n", "9 99c9cc74-fdc8-46c6-8f8d-3ce2d3bfeea3.mp3 \n", "10 \n", "11 f918266a-b3e0-4914-865d-4faa564f1aef.py \n", "12 \n", "13 1f975693-876d-457b-a649-393859e79bf3.mp3 \n", "14 \n", "15 \n", "16 \n", "17 \n", "18 7bd855d8-463d-4ed5-93ca-5fe35145f733.xlsx \n", "19 \n", "\n", " Final answer \n", "0 3 \n", "1 3 \n", "2 Right \n", "3 Rd5 \n", "4 FunkMonk \n", "5 b, e \n", "6 Extremely \n", "7 Louvrier \n", "8 broccoli, celery, fresh basil, lettuce, sweet ... \n", "9 cornstarch, freshly squeezed lemon juice, gran... \n", "10 Wojciech \n", "11 0 \n", "12 519 \n", "13 132, 133, 134, 197, 245 \n", "14 80GSFC21M0002 \n", "15 Saint Petersburg \n", "16 CUB \n", "17 Yoshida, Uehara \n", "18 89706.00 \n", "19 Claus " ] }, "execution_count": 90, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import pandas as pd\n", "\n", "gaia_bench_1 = pd.read_json('gaia_bench_1_test.jsonl', lines=True)\n", "gaia_bench_1" ] }, { "cell_type": "code", "execution_count": 88, "id": "19ca16f8", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'Steps': '1. I did a search for Mercedes Sosa\\n2. I went to the Wikipedia page for her\\n3. I scrolled down to \"Studio albums\"\\n4. I counted the ones between 2000 and 2009',\n", " 'Number of steps': '4',\n", " 'How long did this take?': '5 minutes',\n", " 'Tools': '1. web browser\\n2. google search',\n", " 'Number of tools': '2'}" ] }, "execution_count": 88, "metadata": {}, "output_type": "execute_result" } ], "source": [ "gaia_bench_1.iloc[1]['Annotator Metadata']" ] }, { "cell_type": "code", "execution_count": null, "id": "6c1ffcd6", "metadata": {}, "outputs": [], "source": [ "# gaia_bench_1.to_json(\"gaia_bench_1.jsonl\", orient='records', lines=True)" ] }, { "cell_type": "code", "execution_count": 89, "id": "d1a0a049", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 89, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from dotenv import load_dotenv\n", "\n", "load_dotenv()" ] }, { "cell_type": "code", "execution_count": 90, "id": "d8f0c273", "metadata": {}, "outputs": [], "source": [ "import os\n", "from dotenv import load_dotenv\n", "from typing import List, Dict, Any, Optional\n", "import tempfile\n", "import re\n", "import json\n", "import requests\n", "from urllib.parse import urlparse\n", "from PIL import Image, ImageDraw, ImageFont, ImageEnhance, ImageFilter\n", "import cmath\n", "import pandas as pd\n", "import uuid\n", "import numpy as np\n", "import logging\n", "\n", "\"\"\"Langraph\"\"\"\n", "from langgraph.graph import START, END, StateGraph, MessagesState\n", "from langchain_core.messages import SystemMessage, HumanMessage\n", "\n", "from langchain_community.tools.tavily_search import TavilySearchResults\n", "from langchain_community.tools import DuckDuckGoSearchResults\n", "from langchain_community.document_loaders import WikipediaLoader\n", "from langchain_community.document_loaders import ArxivLoader\n", "from langgraph.prebuilt import ToolNode, tools_condition\n", "from langchain_core.tools import tool\n", "\n", "from langchain_google_genai import ChatGoogleGenerativeAI\n", "from langchain_ollama import ChatOllama\n", "from langchain_groq import ChatGroq\n", "from langchain_together import ChatTogether\n", "from langchain_huggingface import (\n", " ChatHuggingFace,\n", " HuggingFaceEndpoint,\n", " HuggingFaceEmbeddings,\n", ")\n", "\n", "import logging" ] }, { "cell_type": "code", "execution_count": null, "id": "f676ec2c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{'name': 'multiply',\n", " 'args': {'a': 2, 'b': 3},\n", " 'id': 'call_gt4117rcu8e447ygica99xzi',\n", " 'type': 'tool_call'}]" ] }, "execution_count": 88, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# import os\n", "# from langchain_together import ChatTogether\n", "\n", "# llm = ChatTogether(model=\"meta-llama/Llama-3.3-70B-Instruct-Turbo\")\n", "\n", "# # Define a tool\n", "# def multiply(a: int, b: int) -> int:\n", "# return a * b\n", "\n", "# # Augment the LLM with tools\n", "# llm_with_tools = llm.bind_tools([multiply])\n", "\n", "# # Invoke the LLM with input that triggers the tool call\n", "# msg = llm_with_tools.invoke(\"What is 2 times 3?\")\n", "\n", "# # Get the tool call\n", "# msg.tool_calls" ] }, { "cell_type": "code", "execution_count": null, "id": "aa7bf279", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "You are a helpful assistant tasked with answering questions using a set of tools.\n", "Your final answer for my question must strictly follow this format:\n", "FINAL ANSWER: [ANSWER]\n", "Your answer should only start with \"FINAL ANSWER: \", followed by the answer. \n", "Write the answer in that exact format.\n", "If Eliud Kipchoge could maintain his record-making marathon pace indefinitely, how many thousand hours would it take him to run the distance between the Earth and the Moon its closest approach? Please use the minimum perigee value on the Wikipedia page for the Moon when carrying out your calculation. Round your result to the nearest 1000 hours and do not use any comma separators if necessary.\n", "\n" ] }, { "ename": "", "evalue": "", "output_type": "error", "traceback": [ "\u001b[1;31mnotebook controller is DISPOSED. \n", "\u001b[1;31mView Jupyter log for further details." ] } ], "source": [ "# # Querying chat models with Together AI\n", "\n", "# from langchain_together import ChatTogether\n", "\n", "# # choose from our 50+ models here: https://docs.together.ai/docs/inference-models\n", "# chat = ChatTogether(\n", "# # together_api_key=\"YOUR_API_KEY\",\n", "# model=\"Qwen/QwQ-32B\",\n", "# max_tokens=None,\n", "# temperature=0,\n", "# timeout=None,\n", "# max_retries=2,\n", "# )\n", "\n", "# # stream the response back from the model\n", "# # for m in chat.stream(gaia_bench_1.iloc[0].Question):\n", "# # print(m.content, end=\"\", flush=True)\n", "\n", "# tools = [\n", "# calculator,\n", "# wiki_search,\n", "# web_search,\n", "# reverse_string,\n", "# ]\n", "\n", "# with open(\"system_prompt_short.txt\", \"r\", encoding=\"utf-8\") as f:\n", "# system_message = f.read()\n", "# # print(system_prompt)\n", "\n", "# question = gaia_bench_1.iloc[0].Question\n", "# initial_state = {\n", "# 'system_message': system_message,\n", "# 'question': question,\n", "# }\n", "\n", "# messages = [\n", "# (\n", "# \"system\",\n", "# {initial_state['system_message']},\n", "# ),\n", "# (\"human\", {initial_state['question']}),\n", "# ]\n", "\n", "# print(initial_state['system_message'])\n", "# print(initial_state['question'])\n", "\n", "# chat_with_tools = chat.bind_tools(tools)\n", "# response = chat_with_tools.invoke(messages)\n", "# # if you don't want to do streaming, you can use the invoke method\n", "# # chat.invoke(gaia_bench_1.iloc[0].Question)\n", "# print(response.content)" ] }, { "cell_type": "code", "execution_count": null, "id": "9ff78b54", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[{'name': 'wiki_search',\n", " 'args': {'query': 'Moon minimum perigee'},\n", " 'id': 'call_k7cm64w9l3r6xdh6xj9730el',\n", " 'type': 'tool_call'},\n", " {'name': 'calculator',\n", " 'args': {'expression': '362600 / 20.88'},\n", " 'id': 'call_tylbx4uc9j56lojmc7elukka',\n", " 'type': 'tool_call'},\n", " {'name': 'calculator',\n", " 'args': {'expression': '17366.0 rounded to nearest 1000'},\n", " 'id': 'call_i18xsfsyx07qw8eh9j7j750n',\n", " 'type': 'tool_call'}]" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# response.tool_calls" ] }, { "cell_type": "markdown", "id": "2c71f878", "metadata": {}, "source": [ "## Tools" ] }, { "cell_type": "code", "execution_count": 8, "id": "f0071884", "metadata": {}, "outputs": [], "source": [ "# @tool\n", "# def web_search(query: str) -> str:\n", "# \"\"\"Search Tavily for a query and return maximum 3 results.\n", "# Args:\n", "# query: The search query.\"\"\"\n", "# search_docs = TavilySearchResults(max_results=3).invoke(query)\n", "# formatted_search_docs = \"\\n\\n---\\n\\n\".join(\n", "# [\n", "# f'\\n{doc.get(\"content\", \"\")}\\n'\n", "# for doc in search_docs\n", "# ]\n", "# )\n", "# return {\"web_results\": formatted_search_docs}" ] }, { "cell_type": "code", "execution_count": null, "id": "305c0d01", "metadata": {}, "outputs": [], "source": [ "@tool\n", "def web_search(query: str) -> dict:\n", " \"\"\"Search DDGS for a query and return maximum 3 results.\n", " Args:\n", " query: The search query.\"\"\"\n", " search_docs = DuckDuckGoSearchResults(max_results=3, output_format='list').invoke(query)\n", " # return search_docs\n", " formatted_search_docs = \"\\n\\n---\\n\\n\".join(\n", " [\n", " f'\\n{doc.get(\"snippet\", \"\")}\\n'\n", " for doc in search_docs\n", " ]\n", " )\n", " return {\"web_results\": formatted_search_docs}\n", "\n", "@tool\n", "def wiki_search(query: str) -> dict:\n", " \"\"\"Search Wikipedia for a query and return maximum 2 results.\n", " \n", " Args:\n", " query: The search query.\"\"\"\n", " search_docs = WikipediaLoader(query=query, load_max_docs=3).load_and_split()\n", " formatted_search_docs = \"\\n\\n---\\n\\n\".join(\n", " [\n", " f'\\n{doc.page_content}\\n'\n", " for doc in search_docs\n", " ])\n", " return {\"wiki_results\": formatted_search_docs}\n", "\n", "@tool\n", "def reverse_string(query: str) -> dict:\n", " \"\"\"Reverse the input string.\n", " \n", " Args:\n", " query: The input string to reverse.\"\"\"\n", " return {\"reversed_string\": query[::-1]}" ] }, { "cell_type": "code", "execution_count": null, "id": "5352efa7", "metadata": {}, "outputs": [], "source": [ "# from langchain_community.document_loaders import WikipediaLoader\n", "\n", "\n", "# query = 'Mercedes Sosa discography'\n", "\n", "# search_docs = WikipediaLoader(query=query, load_max_docs=2, doc_content_chars_max=10000).load_and_split()\n", "# # formatted_search_docs = \"\\n\\n---\\n\\n\".join(\n", "# # [\n", "# # f'\\n{doc.page_content}\\n'\n", "# # for doc in search_docs\n", "# # ])" ] }, { "cell_type": "code", "execution_count": null, "id": "9665de0a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "== Awards ==\n", "Sosa won the Latin Grammy Award for Best Folk Album in 2000 (Misa Criolla), 2003 (Acústico), 2006 (Corazón Libre), 2009 (Cantora 1, which also won Best Recording Package and was nominated for Album of the Year), and 2011 (Deja La Vida Volar), as well as several international awards.\n", "In 1995, Konex Foundation from Argentina granted her the Diamond Konex Award, one of the most prestigious awards in Argentina, as the most important personality in the popular music of her country in the last decade.\n", "\n", "\n", "== Death ==\n", "\n", "Suffering from recurrent endocrine and respiratory problems in later years, the 74-year-old Sosa was hospitalized in Buenos Aires on 18 September 2009. She died from multiple organ failure on 4 October 2009, at 5:15 am. She was survived by one son, Fabián Matus (d. 15 March 2019), born of her first marriage. He said: \"She lived her 74 years to the fullest. She had done practically everything she wanted, she didn't have any type of barrier or any type of fear that limited her\". The hospital expressed its sympathies to her relatives. Her website featured the following: \"Her undisputed talent, her honesty and her profound convictions leave a great legacy to future generations\".\n", "Her body was placed on display at the National Congress building in Buenos Aires for the public to pay their respects, and President Fernández de Kirchner ordered three days of national mourning. Thousands had queued by the end of the day.\n", "Sosa's obituary in The Daily Telegraph said she was \"an unrivalled interpreter of works by her compatriot, the Argentine Atahualpa Yupanqui, and Chile's Violeta Parra\". Helen Popper of Reuters reported her death by saying she \"fought South America's dictators with her voice and became a giant of contemporary Latin American music\". Sosa received three Latin Grammy nominations for her album, in 2009 . She went on to win Best Folk Album about a month after her death.\n", "\n", "\n", "== Tributes ==\n", "In 2019, Sosa was celebrated by a Google Doodle. The doodle was showcased in Argentina, Chile, Uruguay, Paraguay, Bolivia, Peru, Ecuador, Cuba, Iceland, Sweden, Serbia, Greece, Israel and Vietnam.\n", "In 2023, Rolling Stone ranked Sosa at number 160 on its list of the 200 Greatest Singers of All Time.\n", "\n", "\n", "== Discography ==\n", "\n", "Sosa recorded forty albums.\n", "\n", "\n", "=== Studio albums ===\n", "\n", "\n", "=== EPs ===\n", "\n", "\n", "=== Live albums\n" ] } ], "source": [ "# print(search_docs[2].page_content)" ] }, { "cell_type": "code", "execution_count": null, "id": "151416c7", "metadata": {}, "outputs": [ { "ename": "NameError", "evalue": "name 'tool' is not defined", "output_type": "error", "traceback": [ "\u001b[31m---------------------------------------------------------------------------\u001b[39m", "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[38]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[38;5;129m@tool\u001b[39m\n\u001b[32m 2\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mcalculator\u001b[39m(expression: \u001b[38;5;28mstr\u001b[39m) -> \u001b[38;5;28mstr\u001b[39m:\n\u001b[32m 3\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"Perform mathematical calculations and return the result.\u001b[39;00m\n\u001b[32m 4\u001b[39m \n\u001b[32m 5\u001b[39m \u001b[33;03m This calculator can handle:\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 24\u001b[39m \u001b[33;03m calculator(\"(20 + 5) % 8\") -> \"5\"\u001b[39;00m\n\u001b[32m 25\u001b[39m \u001b[33;03m \"\"\"\u001b[39;00m\n\u001b[32m 26\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m 27\u001b[39m \u001b[38;5;66;03m# Clean the expression\u001b[39;00m\n", "\u001b[31mNameError\u001b[39m: name 'tool' is not defined" ] } ], "source": [ "@tool\n", "def calculator(expression: str) -> str:\n", " \"\"\"Perform mathematical calculations and return the result.\n", " \n", " This calculator can handle:\n", " - Basic arithmetic: +, -, *, /, % (modulus)\n", " - Parentheses for order of operations\n", " - Decimal numbers\n", " - Multiple operations in one expression\n", " \n", " Args:\n", " expression: A mathematical expression as a string\n", " \n", " Returns:\n", " A string containing the calculation result\n", " \n", " Examples:\n", " calculator(\"25 * 4\") -> \"100\"\n", " calculator(\"100 / 5\") -> \"20.0\"\n", " calculator(\"(15 + 30) * 2\") -> \"90\"\n", " calculator(\"50 - 20 + 10\") -> \"40\"\n", " calculator(\"17 % 5\") -> \"2\"\n", " calculator(\"100 % 7\") -> \"2\"\n", " calculator(\"(20 + 5) % 8\") -> \"5\"\n", " \"\"\"\n", " try:\n", " # Clean the expression\n", " expression = expression.strip()\n", " \n", " # Validate that the expression only contains safe characters (now including %)\n", " allowed_chars = set('0123456789+-*/.()% ')\n", " if not all(c in allowed_chars for c in expression):\n", " raise ValueError(\"Expression contains invalid characters. Only numbers and +, -, *, /, %, (, ) are allowed.\")\n", " \n", " result = eval(expression)\n", " \n", " # Format the result\n", " if isinstance(result, float) and result.is_integer():\n", " return str(int(result))\n", " else:\n", " return str(result)\n", " \n", " except ZeroDivisionError:\n", " return \"Error: Cannot divide by zero or modulus by zero\"\n", " except SyntaxError:\n", " return f\"Error: Invalid mathematical expression: {expression}\"\n", " except Exception as e:\n", " return f\"Error: {str(e)}\"" ] }, { "cell_type": "markdown", "id": "0ec29984", "metadata": {}, "source": [ "## Agent States" ] }, { "cell_type": "code", "execution_count": 93, "id": "42bcbff3", "metadata": {}, "outputs": [], "source": [ "from typing import Optional\n", "from langgraph.graph import MessagesState\n", "\n", "# Define the state type with annotations\n", "class AgentState(MessagesState):\n", " system_message: str\n", " last_ai_message: str\n", " question: str\n", " final_answer: str\n", " ready_to_answer: bool\n", " error: Optional[str]" ] }, { "cell_type": "markdown", "id": "1aac0e39", "metadata": {}, "source": [ "## Model" ] }, { "cell_type": "code", "execution_count": 94, "id": "e1b8e2f6", "metadata": {}, "outputs": [], "source": [ "def init_model():\n", " # llm = ChatOllama(\n", " # model=\"llama3.2\",\n", " # # model=\"qwen3\",\n", " # # model=\"qwen3:4b\",\n", " # temperature=0,\n", " # )\n", "\n", " # llm = ChatTogether(\n", " # model=\"openai/gpt-oss-20b\",\n", " # temperature=0,\n", " # )\n", "\n", " # llm = ChatTogether(\n", " # # together_api_key=\"YOUR_API_KEY\",\n", " # model=\"openai/gpt-oss-120b\",\n", " # max_tokens=None,\n", " # temperature=0,\n", " # timeout=None,\n", " # max_retries=2,\n", " # )\n", "\n", " # llm = ChatTogether(\n", " # # together_api_key=\"YOUR_API_KEY\",\n", " # model=\"meta-llama/Meta-Llama-3-8B-Instruct-Lite\",\n", " # max_tokens=None,\n", " # temperature=0,\n", " # timeout=None,\n", " # max_retries=2,\n", " # )\n", " \n", " # llm = ChatTogether(\n", " # # together_api_key=\"YOUR_API_KEY\",\n", " # model=\"Qwen/Qwen2.5-7B-Instruct-Turbo\",\n", " # max_tokens=None,\n", " # temperature=0,\n", " # timeout=None,\n", " # max_retries=2,\n", " # )\n", " llm = ChatTogether(\n", " # together_api_key=\"YOUR_API_KEY\",\n", " model=\"Qwen/QwQ-32B\",\n", " max_tokens=None,\n", " temperature=0,\n", " timeout=None,\n", " max_retries=2,\n", " ) \n", "\n", " return llm" ] }, { "cell_type": "markdown", "id": "cbc99c84", "metadata": {}, "source": [ "## Nodes" ] }, { "cell_type": "code", "execution_count": 95, "id": "609191d4", "metadata": {}, "outputs": [], "source": [ "tools = [\n", " calculator,\n", " wiki_search,\n", " web_search,\n", " reverse_string,\n", "]\n", "\n", "# Assistant node - generates responses\n", "def assistant(state: AgentState):\n", " \"\"\"Generate a response using the LLM.\"\"\"\n", "\n", " llm = ChatOllama(\n", " # model=\"llama3.2\",\n", " model=\"qwen3\",\n", " # model=\"qwen3:4b\",\n", " temperature=0,\n", " num_ctx=16384,\n", " )\n", "\n", " # llm = init_model()\n", "\n", " messages = []\n", " init = False\n", " if len(state[\"messages\"]) == 0:\n", " messages = [\n", " SystemMessage(content=state[\"system_message\"]),\n", " HumanMessage(content=state[\"question\"]),\n", " ]\n", " init = True\n", " for m in messages:\n", " m.pretty_print()\n", "\n", " # Bind tools to the LLM\n", " chat_with_tools = llm.bind_tools(tools)\n", " response = chat_with_tools.invoke(messages if init else state[\"messages\"])\n", " messages.append(response)\n", " # print(response)\n", " messages[-1].pretty_print()\n", " # print(f\"Assistant response: {response.content[:50]}...\")\n", " return {\n", " \"messages\": messages,\n", " \"last_ai_message\": response.content, # if state[\"messages\"] and isinstance(state[\"messages\"][-1], AIMessage) else None\n", " }" ] }, { "cell_type": "code", "execution_count": 97, "id": "d059281e", "metadata": {}, "outputs": [], "source": [ "from langchain_core.output_parsers import JsonOutputParser\n", "from langchain_core.prompts import PromptTemplate\n", "from langchain_core.messages import AIMessage\n", "from pydantic import BaseModel, Field\n", "\n", "# Define your desired data structure.\n", "class AnswerTemplate(BaseModel):\n", " final_answer: str = Field(description=\"Final answer to the question\")\n", "\n", "\n", "def validate_answer(state: AgentState):\n", " \"\"\"Validate the final answer.\"\"\"\n", " llm = ChatOllama(\n", " model=\"llama3.2\",\n", " # model=\"qwen3\",\n", " # model=\"qwen3:4b\",\n", " temperature=0,\n", " num_ctx=8192,\n", " )\n", "\n", " # llm = init_model()\n", "\n", " def escape_braces(text):\n", " return text.replace(\"{\", \"{{\").replace(\"}\", \"}}\")\n", "\n", " query = \"---\\n\\nYou are given a conversation between a human and an AI agent. Identify the final answer provided by the agent. Then, format that final answer according to the formatting rules described in the system message, but do not alter the content of the answer itself. Only apply formatting as instructed. Answer in JSON format.\"\n", "\n", " # Set up a parser + inject instructions into the prompt template.\n", " '''\n", " Создаётся парсер, который преобразует ответ модели в JSON-структуру, соответствующую AnswerTemplate (предположительно, это Pydantic-модель с полем final_answer).\n", " https://python.langchain.com/docs/how_to/output_parser_json/\n", " '''\n", " parser = JsonOutputParser(pydantic_object=AnswerTemplate)\n", " prompt = PromptTemplate(\n", " template=(\n", " f\"SYSTEM MESSAGE: {state['system_message']}\\n\\n\"\n", " f\"HUMAN QUERY: {escape_braces(state['question'])}\\n\\n\"\n", " f\"AGENT ANSWER: {escape_braces(state['last_ai_message'])}\\n\\n\"\n", " f\"{query}\\n\\n\"\n", " \"{format_instructions}\"\n", " ),\n", " input_variables=[\"query\"],\n", " partial_variables={\"format_instructions\": parser.get_format_instructions()},\n", " )\n", " # print(prompt)\n", " chain = prompt | llm | parser\n", " # final_answer = chain.invoke(\n", " # {\"format_instructions\": parser.get_format_instructions()}\n", " # )\n", " final_answer = chain.invoke({\"query\": query})\n", " print(final_answer)\n", " final_answer = final_answer[\"final_answer\"]\n", " # logger.info(f\"Final answer: {final_answer}\")\n", " return {\"final_answer\": final_answer}" ] }, { "cell_type": "code", "execution_count": 106, "id": "f336663b", "metadata": {}, "outputs": [], "source": [ "from langchain_core.output_parsers import JsonOutputParser\n", "from langchain_core.prompts import PromptTemplate\n", "from langchain_core.messages import AIMessage\n", "from pydantic import BaseModel, Field\n", "\n", "# Define your desired data structure.\n", "class AnswerTemplate(BaseModel):\n", " final_answer: str = Field(description=\"Final answer to the question\")\n", "\n", "\n", "def validate_answer(state: AgentState):\n", " \"\"\"Validate the final answer.\"\"\"\n", " llm = ChatOllama(\n", " model=\"llama3.2\",\n", " # model=\"qwen3\",\n", " # model=\"qwen3:4b\",\n", " temperature=0,\n", " num_ctx=8192,\n", " format=AnswerTemplate.model_json_schema(),\n", " )\n", "\n", " # llm = init_model()\n", "\n", " def escape_braces(text):\n", " return text.replace(\"{\", \"{{\").replace(\"}\", \"}}\")\n", "\n", " query = \"---\\n\\nYou are given a conversation between a human and an AI agent. Identify the final answer provided by the agent. Then, format that final answer according to the formatting rules described in the system message, but do not alter the content of the answer itself. Only apply formatting as instructed. Answer in JSON format.\"\n", "\n", " # Set up a parser + inject instructions into the prompt template.\n", " '''\n", " Создаётся парсер, который преобразует ответ модели в JSON-структуру, соответствующую AnswerTemplate (предположительно, это Pydantic-модель с полем final_answer).\n", " https://python.langchain.com/docs/how_to/output_parser_json/\n", " '''\n", " \n", " prompt = PromptTemplate(\n", " template=(\n", " f\"SYSTEM MESSAGE: {state['system_message']}\\n\\n\"\n", " f\"HUMAN QUERY: {escape_braces(state['question'])}\\n\\n\"\n", " f\"AGENT ANSWER: {escape_braces(state['last_ai_message'])}\\n\\n\"\n", " f\"{query}\\n\\n\"\n", " ),\n", " input_variables=[\"query\"],\n", " )\n", " chain = prompt | llm\n", " final_answer = chain.invoke({\"query\": query})\n", " final_answer = AnswerTemplate.model_validate_json(final_answer.content)\n", "\n", " print(final_answer)\n", " # final_answer = final_answer[\"final_answer\"]\n", " final_answer = final_answer.final_answer\n", "\n", " # logger.info(f\"Final answer: {final_answer}\")\n", " return {\"final_answer\": final_answer}" ] }, { "cell_type": "code", "execution_count": 99, "id": "79891ab9", "metadata": {}, "outputs": [], "source": [ "def ready_to_answer(state: AgentState):\n", " if state[\"ready_to_answer\"]:\n", " return \"validate_answer\"\n", " else:\n", " return \"assistant\"" ] }, { "cell_type": "markdown", "id": "5a1dbaf1", "metadata": {}, "source": [ "## JSON output example" ] }, { "cell_type": "code", "execution_count": null, "id": "d5886dba", "metadata": {}, "outputs": [ { "ename": "OutputParserException", "evalue": "Invalid json output: \nOkay, the user wants a joke. Let me think of a classic setup and punchline. How about a pun-based joke? Maybe something with animals. Oh, I remember one about a dog in a bakery. Wait, let me structure it properly. The setup needs to pose a question or scenario, and the punchline delivers the surprise. Let me check the schema again. They need \"setup\" and \"punchline\" as strings. Alright, here's an idea: \"Why didn't the skeleton ever go to the party? Because he had no body to dance with!\" That's a common one but works. Let me make sure it fits the schema. Yep, both parts are strings. No markdown, just JSON. Okay, ready.\n\n\n{\n \"setup\": \"Why didn't the skeleton ever go to the party?\",\n \"punchline\": \"Because he had no body to dance with!\"\n}\nFor troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/OUTPUT_PARSING_FAILURE ", "output_type": "error", "traceback": [ "\u001b[31m---------------------------------------------------------------------------\u001b[39m", "\u001b[31mJSONDecodeError\u001b[39m Traceback (most recent call last)", "\u001b[36mFile \u001b[39m\u001b[32m~/Git/hf_agents/Final_Assignment_Template/.venv/lib/python3.13/site-packages/langchain_core/output_parsers/json.py:82\u001b[39m, in \u001b[36mJsonOutputParser.parse_result\u001b[39m\u001b[34m(self, result, partial)\u001b[39m\n\u001b[32m 81\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m---> \u001b[39m\u001b[32m82\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mparse_json_markdown\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtext\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 83\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m JSONDecodeError \u001b[38;5;28;01mas\u001b[39;00m e:\n", "\u001b[36mFile \u001b[39m\u001b[32m~/Git/hf_agents/Final_Assignment_Template/.venv/lib/python3.13/site-packages/langchain_core/utils/json.py:150\u001b[39m, in \u001b[36mparse_json_markdown\u001b[39m\u001b[34m(json_string, parser)\u001b[39m\n\u001b[32m 149\u001b[39m json_str = json_string \u001b[38;5;28;01mif\u001b[39;00m match \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;28;01melse\u001b[39;00m match.group(\u001b[32m2\u001b[39m)\n\u001b[32m--> \u001b[39m\u001b[32m150\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43m_parse_json\u001b[49m\u001b[43m(\u001b[49m\u001b[43mjson_str\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mparser\u001b[49m\u001b[43m=\u001b[49m\u001b[43mparser\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[36mFile \u001b[39m\u001b[32m~/Git/hf_agents/Final_Assignment_Template/.venv/lib/python3.13/site-packages/langchain_core/utils/json.py:166\u001b[39m, in \u001b[36m_parse_json\u001b[39m\u001b[34m(json_str, parser)\u001b[39m\n\u001b[32m 165\u001b[39m \u001b[38;5;66;03m# Parse the JSON string into a Python dictionary\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m166\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mparser\u001b[49m\u001b[43m(\u001b[49m\u001b[43mjson_str\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[36mFile \u001b[39m\u001b[32m~/Git/hf_agents/Final_Assignment_Template/.venv/lib/python3.13/site-packages/langchain_core/utils/json.py:123\u001b[39m, in \u001b[36mparse_partial_json\u001b[39m\u001b[34m(s, strict)\u001b[39m\n\u001b[32m 120\u001b[39m \u001b[38;5;66;03m# If we got here, we ran out of characters to remove\u001b[39;00m\n\u001b[32m 121\u001b[39m \u001b[38;5;66;03m# and still couldn't parse the string as JSON, so return the parse error\u001b[39;00m\n\u001b[32m 122\u001b[39m \u001b[38;5;66;03m# for the original string.\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m123\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mjson\u001b[49m\u001b[43m.\u001b[49m\u001b[43mloads\u001b[49m\u001b[43m(\u001b[49m\u001b[43ms\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstrict\u001b[49m\u001b[43m=\u001b[49m\u001b[43mstrict\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[36mFile \u001b[39m\u001b[32m/opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/json/__init__.py:359\u001b[39m, in \u001b[36mloads\u001b[39m\u001b[34m(s, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)\u001b[39m\n\u001b[32m 358\u001b[39m kw[\u001b[33m'\u001b[39m\u001b[33mparse_constant\u001b[39m\u001b[33m'\u001b[39m] = parse_constant\n\u001b[32m--> \u001b[39m\u001b[32m359\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mcls\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkw\u001b[49m\u001b[43m)\u001b[49m\u001b[43m.\u001b[49m\u001b[43mdecode\u001b[49m\u001b[43m(\u001b[49m\u001b[43ms\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[36mFile \u001b[39m\u001b[32m/opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/json/decoder.py:345\u001b[39m, in \u001b[36mJSONDecoder.decode\u001b[39m\u001b[34m(self, s, _w)\u001b[39m\n\u001b[32m 341\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Return the Python representation of ``s`` (a ``str`` instance\u001b[39;00m\n\u001b[32m 342\u001b[39m \u001b[33;03mcontaining a JSON document).\u001b[39;00m\n\u001b[32m 343\u001b[39m \n\u001b[32m 344\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m345\u001b[39m obj, end = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mraw_decode\u001b[49m\u001b[43m(\u001b[49m\u001b[43ms\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43midx\u001b[49m\u001b[43m=\u001b[49m\u001b[43m_w\u001b[49m\u001b[43m(\u001b[49m\u001b[43ms\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[32;43m0\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m.\u001b[49m\u001b[43mend\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 346\u001b[39m end = _w(s, end).end()\n", "\u001b[36mFile \u001b[39m\u001b[32m/opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/json/decoder.py:363\u001b[39m, in \u001b[36mJSONDecoder.raw_decode\u001b[39m\u001b[34m(self, s, idx)\u001b[39m\n\u001b[32m 362\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mStopIteration\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m err:\n\u001b[32m--> \u001b[39m\u001b[32m363\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m JSONDecodeError(\u001b[33m\"\u001b[39m\u001b[33mExpecting value\u001b[39m\u001b[33m\"\u001b[39m, s, err.value) \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 364\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m obj, end\n", "\u001b[31mJSONDecodeError\u001b[39m: Expecting value: line 1 column 1 (char 0)", "\nThe above exception was the direct cause of the following exception:\n", "\u001b[31mOutputParserException\u001b[39m Traceback (most recent call last)", "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[100]\u001b[39m\u001b[32m, line 33\u001b[39m\n\u001b[32m 25\u001b[39m prompt = PromptTemplate(\n\u001b[32m 26\u001b[39m template=\u001b[33m\"\u001b[39m\u001b[33mAnswer the user query.\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;132;01m{format_instructions}\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;132;01m{query}\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m\"\u001b[39m,\n\u001b[32m 27\u001b[39m input_variables=[\u001b[33m\"\u001b[39m\u001b[33mquery\u001b[39m\u001b[33m\"\u001b[39m],\n\u001b[32m 28\u001b[39m partial_variables={\u001b[33m\"\u001b[39m\u001b[33mformat_instructions\u001b[39m\u001b[33m\"\u001b[39m: parser.get_format_instructions()},\n\u001b[32m 29\u001b[39m )\n\u001b[32m 31\u001b[39m chain = prompt | model | parser\n\u001b[32m---> \u001b[39m\u001b[32m33\u001b[39m \u001b[43mchain\u001b[49m\u001b[43m.\u001b[49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\u001b[43m{\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mquery\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mjoke_query\u001b[49m\u001b[43m}\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[36mFile \u001b[39m\u001b[32m~/Git/hf_agents/Final_Assignment_Template/.venv/lib/python3.13/site-packages/langchain_core/runnables/base.py:3049\u001b[39m, in \u001b[36mRunnableSequence.invoke\u001b[39m\u001b[34m(self, input, config, **kwargs)\u001b[39m\n\u001b[32m 3047\u001b[39m input_ = context.run(step.invoke, input_, config, **kwargs)\n\u001b[32m 3048\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m-> \u001b[39m\u001b[32m3049\u001b[39m input_ = \u001b[43mcontext\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[43mstep\u001b[49m\u001b[43m.\u001b[49m\u001b[43minvoke\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minput_\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 3050\u001b[39m \u001b[38;5;66;03m# finish the root run\u001b[39;00m\n\u001b[32m 3051\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n", "\u001b[36mFile \u001b[39m\u001b[32m~/Git/hf_agents/Final_Assignment_Template/.venv/lib/python3.13/site-packages/langchain_core/output_parsers/base.py:197\u001b[39m, in \u001b[36mBaseOutputParser.invoke\u001b[39m\u001b[34m(self, input, config, **kwargs)\u001b[39m\n\u001b[32m 189\u001b[39m \u001b[38;5;129m@override\u001b[39m\n\u001b[32m 190\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34minvoke\u001b[39m(\n\u001b[32m 191\u001b[39m \u001b[38;5;28mself\u001b[39m,\n\u001b[32m (...)\u001b[39m\u001b[32m 194\u001b[39m **kwargs: Any,\n\u001b[32m 195\u001b[39m ) -> T:\n\u001b[32m 196\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(\u001b[38;5;28minput\u001b[39m, BaseMessage):\n\u001b[32m--> \u001b[39m\u001b[32m197\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_call_with_config\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 198\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mlambda\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43minner_input\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mparse_result\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 199\u001b[39m \u001b[43m \u001b[49m\u001b[43m[\u001b[49m\u001b[43mChatGeneration\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmessage\u001b[49m\u001b[43m=\u001b[49m\u001b[43minner_input\u001b[49m\u001b[43m)\u001b[49m\u001b[43m]\u001b[49m\n\u001b[32m 200\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 201\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 202\u001b[39m \u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 203\u001b[39m \u001b[43m \u001b[49m\u001b[43mrun_type\u001b[49m\u001b[43m=\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mparser\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 204\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 205\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m._call_with_config(\n\u001b[32m 206\u001b[39m \u001b[38;5;28;01mlambda\u001b[39;00m inner_input: \u001b[38;5;28mself\u001b[39m.parse_result([Generation(text=inner_input)]),\n\u001b[32m 207\u001b[39m \u001b[38;5;28minput\u001b[39m,\n\u001b[32m 208\u001b[39m config,\n\u001b[32m 209\u001b[39m run_type=\u001b[33m\"\u001b[39m\u001b[33mparser\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 210\u001b[39m )\n", "\u001b[36mFile \u001b[39m\u001b[32m~/Git/hf_agents/Final_Assignment_Template/.venv/lib/python3.13/site-packages/langchain_core/runnables/base.py:1938\u001b[39m, in \u001b[36mRunnable._call_with_config\u001b[39m\u001b[34m(self, func, input_, config, run_type, serialized, **kwargs)\u001b[39m\n\u001b[32m 1934\u001b[39m child_config = patch_config(config, callbacks=run_manager.get_child())\n\u001b[32m 1935\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m set_config_context(child_config) \u001b[38;5;28;01mas\u001b[39;00m context:\n\u001b[32m 1936\u001b[39m output = cast(\n\u001b[32m 1937\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mOutput\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m-> \u001b[39m\u001b[32m1938\u001b[39m \u001b[43mcontext\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 1939\u001b[39m \u001b[43m \u001b[49m\u001b[43mcall_func_with_variable_args\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# type: ignore[arg-type]\u001b[39;49;00m\n\u001b[32m 1940\u001b[39m \u001b[43m \u001b[49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1941\u001b[39m \u001b[43m \u001b[49m\u001b[43minput_\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1942\u001b[39m \u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1943\u001b[39m \u001b[43m \u001b[49m\u001b[43mrun_manager\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1944\u001b[39m \u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1945\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m,\n\u001b[32m 1946\u001b[39m )\n\u001b[32m 1947\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[32m 1948\u001b[39m run_manager.on_chain_error(e)\n", "\u001b[36mFile \u001b[39m\u001b[32m~/Git/hf_agents/Final_Assignment_Template/.venv/lib/python3.13/site-packages/langchain_core/runnables/config.py:429\u001b[39m, in \u001b[36mcall_func_with_variable_args\u001b[39m\u001b[34m(func, input, config, run_manager, **kwargs)\u001b[39m\n\u001b[32m 427\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m run_manager \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m accepts_run_manager(func):\n\u001b[32m 428\u001b[39m kwargs[\u001b[33m\"\u001b[39m\u001b[33mrun_manager\u001b[39m\u001b[33m\"\u001b[39m] = run_manager\n\u001b[32m--> \u001b[39m\u001b[32m429\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[36mFile \u001b[39m\u001b[32m~/Git/hf_agents/Final_Assignment_Template/.venv/lib/python3.13/site-packages/langchain_core/output_parsers/base.py:198\u001b[39m, in \u001b[36mBaseOutputParser.invoke..\u001b[39m\u001b[34m(inner_input)\u001b[39m\n\u001b[32m 189\u001b[39m \u001b[38;5;129m@override\u001b[39m\n\u001b[32m 190\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34minvoke\u001b[39m(\n\u001b[32m 191\u001b[39m \u001b[38;5;28mself\u001b[39m,\n\u001b[32m (...)\u001b[39m\u001b[32m 194\u001b[39m **kwargs: Any,\n\u001b[32m 195\u001b[39m ) -> T:\n\u001b[32m 196\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(\u001b[38;5;28minput\u001b[39m, BaseMessage):\n\u001b[32m 197\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m._call_with_config(\n\u001b[32m--> \u001b[39m\u001b[32m198\u001b[39m \u001b[38;5;28;01mlambda\u001b[39;00m inner_input: \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mparse_result\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 199\u001b[39m \u001b[43m \u001b[49m\u001b[43m[\u001b[49m\u001b[43mChatGeneration\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmessage\u001b[49m\u001b[43m=\u001b[49m\u001b[43minner_input\u001b[49m\u001b[43m)\u001b[49m\u001b[43m]\u001b[49m\n\u001b[32m 200\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m,\n\u001b[32m 201\u001b[39m \u001b[38;5;28minput\u001b[39m,\n\u001b[32m 202\u001b[39m config,\n\u001b[32m 203\u001b[39m run_type=\u001b[33m\"\u001b[39m\u001b[33mparser\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 204\u001b[39m )\n\u001b[32m 205\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m._call_with_config(\n\u001b[32m 206\u001b[39m \u001b[38;5;28;01mlambda\u001b[39;00m inner_input: \u001b[38;5;28mself\u001b[39m.parse_result([Generation(text=inner_input)]),\n\u001b[32m 207\u001b[39m \u001b[38;5;28minput\u001b[39m,\n\u001b[32m 208\u001b[39m config,\n\u001b[32m 209\u001b[39m run_type=\u001b[33m\"\u001b[39m\u001b[33mparser\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 210\u001b[39m )\n", "\u001b[36mFile \u001b[39m\u001b[32m~/Git/hf_agents/Final_Assignment_Template/.venv/lib/python3.13/site-packages/langchain_core/output_parsers/json.py:85\u001b[39m, in \u001b[36mJsonOutputParser.parse_result\u001b[39m\u001b[34m(self, result, partial)\u001b[39m\n\u001b[32m 83\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m JSONDecodeError \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[32m 84\u001b[39m msg = \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mInvalid json output: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mtext\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m\n\u001b[32m---> \u001b[39m\u001b[32m85\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m OutputParserException(msg, llm_output=text) \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01me\u001b[39;00m\n", "\u001b[31mOutputParserException\u001b[39m: Invalid json output: \nOkay, the user wants a joke. Let me think of a classic setup and punchline. How about a pun-based joke? Maybe something with animals. Oh, I remember one about a dog in a bakery. Wait, let me structure it properly. The setup needs to pose a question or scenario, and the punchline delivers the surprise. Let me check the schema again. They need \"setup\" and \"punchline\" as strings. Alright, here's an idea: \"Why didn't the skeleton ever go to the party? Because he had no body to dance with!\" That's a common one but works. Let me make sure it fits the schema. Yep, both parts are strings. No markdown, just JSON. Okay, ready.\n\n\n{\n \"setup\": \"Why didn't the skeleton ever go to the party?\",\n \"punchline\": \"Because he had no body to dance with!\"\n}\nFor troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/OUTPUT_PARSING_FAILURE " ] } ], "source": [ "# from langchain_core.output_parsers import JsonOutputParser\n", "# from langchain_core.prompts import PromptTemplate\n", "# from pydantic import BaseModel, Field\n", "\n", "# # model = ChatOllama(\n", "# # model=\"llama3.2\",\n", "# # # model=\"qwen3\",\n", "# # # model=\"qwen3:4b\",\n", "# # temperature=0,\n", "# # )\n", "# model = init_model()\n", "\n", "# # Define your desired data structure.\n", "# class Joke(BaseModel):\n", "# setup: str = Field(description=\"question to set up a joke\")\n", "# punchline: str = Field(description=\"answer to resolve the joke\")\n", "\n", "\n", "# # And a query intented to prompt a language model to populate the data structure.\n", "# joke_query = \"Tell me a joke.\"\n", "\n", "# # Set up a parser + inject instructions into the prompt template.\n", "# parser = JsonOutputParser(pydantic_object=Joke)\n", "\n", "# prompt = PromptTemplate(\n", "# template=\"Answer the user query.\\n{format_instructions}\\n{query}\\n\",\n", "# input_variables=[\"query\"],\n", "# partial_variables={\"format_instructions\": parser.get_format_instructions()},\n", "# )\n", "\n", "# chain = prompt | model | parser\n", "\n", "# chain.invoke({\"query\": joke_query})" ] }, { "cell_type": "code", "execution_count": null, "id": "57d8ef1c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The output should be formatted as a JSON instance that conforms to the JSON schema below.\n", "\n", "As an example, for the schema {\"properties\": {\"foo\": {\"title\": \"Foo\", \"description\": \"a list of strings\", \"type\": \"array\", \"items\": {\"type\": \"string\"}}}, \"required\": [\"foo\"]}\n", "the object {\"foo\": [\"bar\", \"baz\"]} is a well-formatted instance of the schema. The object {\"properties\": {\"foo\": [\"bar\", \"baz\"]}} is not well-formatted.\n", "\n", "Here is the output schema:\n", "```\n", "{\"properties\": {\"setup\": {\"description\": \"question to set up a joke\", \"title\": \"Setup\", \"type\": \"string\"}, \"punchline\": {\"description\": \"answer to resolve the joke\", \"title\": \"Punchline\", \"type\": \"string\"}}, \"required\": [\"setup\", \"punchline\"]}\n", "```\n" ] }, { "ename": "", "evalue": "", "output_type": "error", "traceback": [ "\u001b[1;31mnotebook controller is DISPOSED. \n", "\u001b[1;31mView Jupyter log for further details." ] } ], "source": [ "# print(parser.get_format_instructions())" ] }, { "cell_type": "markdown", "id": "531affd6", "metadata": {}, "source": [ "## Graph" ] }, { "cell_type": "code", "execution_count": 101, "id": "6580bbfa", "metadata": {}, "outputs": [], "source": [ "# Build graph function\n", "def build_graph():\n", " \"\"\"Build the graph\"\"\"\n", " builder = StateGraph(AgentState)\n", " builder.add_node(\"assistant\", assistant)\n", " builder.add_node(\"tools\", ToolNode(tools))\n", " builder.add_node(\"validate_answer\", validate_answer)\n", "\n", " builder.add_edge(START, \"assistant\")\n", " builder.add_conditional_edges(\n", " \"assistant\",\n", " tools_condition,\n", " {\n", " \"tools\": \"tools\", # Route to tools if needed\n", " # END: \"END\" # Route to end if no tools needed\n", " END: \"validate_answer\", # Route to validate_answer if no tools needed\n", " },\n", " )\n", " builder.add_edge(\"tools\", \"assistant\")\n", " # builder.add_edge(\"assistant\", \"validate_answer\")\n", " # builder.add_conditional_edges(\n", " # \"assistant\",\n", " # ready_to_answer,\n", " # {\"validate_answer\": \"validate_answer\"},\n", " # )\n", " builder.add_edge(\"validate_answer\", END)\n", "\n", " return builder.compile()" ] }, { "cell_type": "markdown", "id": "0114c4d4", "metadata": {}, "source": [ "## Show graph" ] }, { "cell_type": "code", "execution_count": 107, "id": "bc770799", "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Visualize your graph\n", "from IPython.display import Image, display\n", "graph = build_graph()\n", "png = graph.get_graph().draw_mermaid_png()\n", "\n", "display(Image(png))" ] }, { "cell_type": "markdown", "id": "8f704438", "metadata": {}, "source": [ "## Run sample" ] }, { "cell_type": "code", "execution_count": 108, "id": "a917724c", "metadata": {}, "outputs": [], "source": [ "logger = logging.getLogger(__name__)" ] }, { "cell_type": "code", "execution_count": 109, "id": "29b414db", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'.rewsna eht sa \"tfel\" drow eht fo etisoppo eht etirw ,ecnetnes siht dnatsrednu uoy fI'" ] }, "execution_count": 109, "metadata": {}, "output_type": "execute_result" } ], "source": [ "gaia_bench_1.iloc[8].Question" ] }, { "cell_type": "code", "execution_count": 110, "id": "5ca2843f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "================================\u001b[1m System Message \u001b[0m================================\n", "\n", "You are a helpful assistant tasked with answering questions using a set of tools.\n", "Your final answer for my question must strictly follow this format:\n", "FINAL ANSWER: [ANSWER]\n", "Your answer should only start with \"FINAL ANSWER: \", followed by the answer. \n", "Write the answer in that exact format.\n", "================================\u001b[1m Human Message \u001b[0m=================================\n", "\n", "How many studio albums were published by Mercedes Sosa between 2000 and 2009 (included)? You can use the latest 2022 version of english wikipedia.\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "\n", "\n", "Okay, let's tackle this question: \"How many studio albums were published by Mercedes Sosa between 2000 and 2009 (included)? You can use the latest 2022 version of English Wikipedia.\"\n", "\n", "First, I need to find information about Mercedes Sosa's studio albums released in that specific time frame. Since the user mentioned using the 2022 English Wikipedia, I should start by searching Wikipedia for her discography.\n", "\n", "I'll use the wiki_search function with the query \"Mercedes Sosa albums\" to get relevant information. The results might include a list of her albums, their release dates, and details about each. \n", "\n", "Once I have the search results, I'll look through the entries to identify which albums were released between 2000 and 2009. I need to check the publication years of each album in that range. It's important to note that some albums might have been released in the specified years, so I should count each one that falls within 2000-2009 inclusive.\n", "\n", "If the initial search doesn't provide enough details, I might need to look for more specific information, perhaps by searching for her discography or specific albums from that period. However, since the user specified using Wikipedia, I'll focus on that source first. \n", "\n", "After gathering all the necessary data from the search results, I'll compile the list of albums within the given timeframe and count them to provide the final answer.\n", "\n", "Tool Calls:\n", " wiki_search (1414d392-34b4-49f4-802b-166286582637)\n", " Call ID: 1414d392-34b4-49f4-802b-166286582637\n", " Args:\n", " query: Mercedes Sosa studio albums 2000-2009\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "\n", "\n", "Okay, let's see. The user is asking how many studio albums Mercedes Sosa published between 2000 and 2009, inclusive. They mentioned using the 2022 version of English Wikipedia.\n", "\n", "First, I need to check the provided tool responses. The first document about Mercedes Sosa mentions her career spanning four decades and her Latin Grammy awards from 2000 to 2011. However, it doesn't list her albums by year. The second document is about best-selling albums in Argentina, which doesn't seem relevant. The third document is about Linda Ronstadt, which is unrelated.\n", "\n", "Since none of the provided documents directly list Mercedes Sosa's albums with their release dates, I can't find the exact number from the given sources. The user might need a more detailed search or a different approach. But based on the available info, there's no specific data on her albums between 2000-2009. Therefore, the answer can't be determined from the provided tools.\n", "\n", "\n", "FINAL ANSWER: The provided search results do not specify the exact number of studio albums Mercedes Sosa released between 2000 and 2009. Further detailed discography information would be required to determine this figure accurately.\n", "final_answer='The provided search results do not specify the exact number of studio albums Mercedes Sosa released between 2000 and 2009. Further detailed discography information would be required to determine this figure accurately.'\n", "The provided search results do not specify the exact number of studio albums Mercedes Sosa released between 2000 and 2009. Further detailed discography information would be required to determine this figure accurately.\n" ] } ], "source": [ "with open(\"system_prompt_short.txt\", \"r\", encoding=\"utf-8\") as f:\n", " system_message = f.read()\n", "# print(system_prompt)\n", "\n", "question = gaia_bench_1.iloc[1].Question\n", "initial_state = {\n", " 'system_message': system_message,\n", " 'question': question,\n", "}\n", "\n", "final_state = graph.invoke(initial_state)\n", "final_answer = final_state.get(\"final_answer\", None)\n", "print(final_answer)" ] }, { "cell_type": "code", "execution_count": 220, "id": "69bb6eca", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'messages': [SystemMessage(content='You are a helpful assistant tasked with answering questions using a set of tools.\\nYour final answer for my question must strictly follow this format:\\nFINAL ANSWER: [ANSWER]\\nYour answer should only start with \"FINAL ANSWER: \", followed by the answer. \\nWrite the answer in that exact format. Do not include any other text. Do not explain anything. \\nUse tools only if the current question is different from the similar one.\\nExamples:\\n- FINAL ANSWER: Monk\\n- FINAL ANSWER: Rome\\n- FINAL ANSWER: 228\\nIf you do not follow this format exactly, your response will be considered incorrect.', additional_kwargs={}, response_metadata={}, id='cca8ea85-212c-4959-bb87-de46361f6ab6'),\n", " HumanMessage(content='If Eliud Kipchoge could maintain his record-making marathon pace indefinitely, how many thousand hours would it take him to run the distance between the Earth and the Moon its closest approach? Please use the minimum perigee value on the Wikipedia page for the Moon when carrying out your calculation. Round your result to the nearest 1000 hours and do not use any comma separators if necessary.', additional_kwargs={}, response_metadata={}, id='8764c65e-a532-43bb-bfb9-e851ffb97cbf'),\n", " AIMessage(content='analysisWe need to compute: distance between Earth and Moon at closest approach (perigee). Use minimum perigee value from Wikipedia. Then compute how many hours it would take to run that distance at Eliud Kipchoge\\'s record marathon pace. Then convert to thousand hours, round to nearest 1000 hours, no comma separators.\\n\\nWe need to find perigee distance. Wikipedia page for Moon: perigee distance ~ 363,300 km? Actually perigee distance is about 363,300 km (minimum). Let\\'s confirm: The average distance is 384,400 km. Perigee ~ 363,300 km. The minimum perigee is about 363,104 km? Let\\'s check. Wikipedia: \"Perigee: 363,104 km (225,000 mi)\". So use 363,104 km. We\\'ll use that.\\n\\nNow Kipchoge\\'s record marathon pace: He set 2:01:39 for 42.195 km. Pace: 2:01:39 = 2 hours 1 minute 39 seconds = 2*3600 + 1*60 + 39 = 7200 + 60 + 39 = 7299 seconds. Pace per km: 7299 / 42.195 = about 173.0 seconds per km? Let\\'s compute: 42.195 km. 7299 / 42.195 = 173.0? Let\\'s calculate precisely: 42.195 * 173 = 7295.835. So 173.0 sec per km gives 7295.835 sec. Slight difference. Let\\'s compute more precisely: 7299 / 42.195 = 173.0? Let\\'s do division: 42.195 * 173.1 = 7299.0? 42.195*173.1 = 42.195*173 + 42.195*0.1 = 7295.835 + 4.2195 = 7300.0545. So 173.1 sec gives 7300.05 sec. So 173.0 sec gives 7295.835. So actual pace ~173.0 sec per km. So 173.0 sec per km.\\n\\nNow distance to Moon: 363,104 km. Time = distance * pace per km = 363,104 km * 173 sec/km = 363,104 * 173 = compute: 363,104 * 100 = 36,310,400; *70 = 25,417,280; *3 = 1,089,312. Sum: 36,310,400 + 25,417,280 = 61,727,680; +1,089,312 = 62,816,992 seconds. So about 62,816,992 seconds.\\n\\nConvert to hours: divide by 3600. 62,816,992 / 3600 = 17,460.277... Let\\'s compute: 3600*17,460 = 62,856,000. That\\'s slightly higher. Let\\'s compute precisely: 62,816,992 / 3600 = 17,460.277... Actually 3600*17,460 = 62,856,000. Our number is 39,008 less. 39,008/3600 = 10.822. So 17,460 - 10.822 = 17,449.178? Wait let\\'s do proper division: 62,816,992 / 3600. Use long division: 62,816,992 / 36 = 1,744,0?? Actually 3600 = 36*100. So divide by 36 then by 100. 62,816,992 / 36 =? 36*1,744,000 = 62,784,000. Remainder 32,992. 36*915 = 32,940. Remainder 52. So 1,744,915 remainder 52. So /36 = 1,744,915 + 52/36 = 1,744,915. 1,444? Wait we need to compute precisely: 62,816,992 / 36 = 1,744,915.111... Actually 36*1,744,915 = 62,816,940. Remainder 52. So 52/36 = 1.444... So 1,744,915.444... Then divide by 100 gives 17,449.15444 hours. So about 17,449.15 hours.\\n\\nNow thousand hours: divide by 1000: 17.449... thousand hours. Round to nearest 1000 hours? Wait the question: \"Round your result to the nearest 1000 hours and do not use any comma separators if necessary.\" They want the number of thousand hours? Actually \"how many thousand hours would it take him to run the distance between the Earth and the Moon its closest approach?\" So we need to express the answer in thousand hours. So we have 17,449 hours. In thousand hours, that\\'s 17.449 thousand hours. Round to nearest 1000 hours? Wait \"Round your result to the nearest 1000 hours\" could mean round the number of hours to nearest 1000 hours. So 17,449 hours rounds to 17,000 hours? Actually nearest 1000 hours: 17,449 is closer to 17,000 than 18,000? The difference: 449 vs 551. So round to 17,000 hours. But they want the answer in thousand hours? The phrase \"how many thousand hours would it take him\" suggests we should give the answer in thousand hours. So we should give 17 thousand hours? But rounding to nearest 1000 hours yields 17,000 hours. Then express as thousand hours: 17 thousand hours. But they want no comma separators. So \"17000\" or \"17\"? The instruction: \"Round your result to the nearest 1000 hours and do not use any comma separators if necessary.\" So we should output the number of hours rounded to nearest 1000, no commas. So 17000. But the question: \"how many thousand hours would it take him to run the distance between the Earth and the Moon its closest approach?\" That phrasing is ambiguous. It could mean \"how many thousand hours\" i.e., the answer in thousands of hours. But then rounding to nearest 1000 hours would be the same as rounding the hours to nearest thousand. So answer: 17000. But maybe they want \"17\" thousand hours. But they said no comma separators if necessary. So \"17000\" is fine. But maybe they want \"17000\" as the answer. Let\\'s double-check the calculation.\\n\\nCheck perigee distance: Wikipedia says \"Perigee: 363,104 km (225,000 mi)\". Use 363,104 km. Good.\\n\\nKipchoge pace: 2:01:39 for 42.195 km. Let\\'s compute pace precisely: 2h 1m 39s = 2*3600 + 1*60 + 39 = 7200 + 60 + 39 = 7299 seconds. Pace per km = 7299 / 42.195 = 173.0? Let\\'s compute more precisely: 42.195 * 173 = 7295.835. So 173.0 sec per km gives 7295.835 sec. We need 7299 sec, so pace is 173.0 + (3.165/42.195) = 173.0 + 0.075 = 173.075 sec per km. Actually 3.165/42.195 = 0.075. So pace ~173.075 sec per km. Let\\'s compute: 42.195 * 173.075 = 42.195*173 + 42.195*0.075 = 7295.835 + 3.164625 = 7298.999625 ~ 7299. So pace ~173.075 sec per km.\\n\\nNow time = 363,104 km * 173.075 sec/km = 363,104 * 173.075. Compute: 363,104 * 173 = 62,816,992 (as before). Then add 363,104 * 0.075 = 27,232.8. Sum = 62,844,224.8 seconds. Wait 62,816,992 + 27,232.8 = 62,844,224.8. So time ~62,844,225 sec.\\n\\nDivide by 3600: 62,844,225 / 3600 = 17,460.0625? Let\\'s compute: 3600*17,460 = 62,856,000. That\\'s 11,775 more. So 62,844,225 is 11,775 less. 11,775/3600 = 3.2708. So 17,460 - 3.2708 = 17,456.7292 hours. Wait that seems off. Let\\'s do precise division: 62,844,225 / 3600. Use 62,844,225 / 36 = 1,744, 0?? Let\\'s do: 36*1,744,000 = 62,784,000. Remainder 60,225. 36*1,674 = 60,264. Too high. 36*1,673 = 60,228. Remainder -3? Actually 36*1,673 = 60,228. Remainder -3? Wait 60,225 - 60,228 = -3. So 1,673 is', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 2048, 'prompt_tokens': 592, 'total_tokens': 2640, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'openai/gpt-oss-20b', 'system_fingerprint': None, 'id': 'o7Nx3mr-62bZhn-9700d7f45880562f', 'service_tier': None, 'finish_reason': 'length', 'logprobs': None}, id='run--199e0b97-8817-4c59-a9a2-3260d9269a96-0', usage_metadata={'input_tokens': 592, 'output_tokens': 2048, 'total_tokens': 2640, 'input_token_details': {}, 'output_token_details': {}})],\n", " 'system_message': 'You are a helpful assistant tasked with answering questions using a set of tools.\\nYour final answer for my question must strictly follow this format:\\nFINAL ANSWER: [ANSWER]\\nYour answer should only start with \"FINAL ANSWER: \", followed by the answer. \\nWrite the answer in that exact format. Do not include any other text. Do not explain anything. \\nUse tools only if the current question is different from the similar one.\\nExamples:\\n- FINAL ANSWER: Monk\\n- FINAL ANSWER: Rome\\n- FINAL ANSWER: 228\\nIf you do not follow this format exactly, your response will be considered incorrect.',\n", " 'last_ai_message': 'analysisWe need to compute: distance between Earth and Moon at closest approach (perigee). Use minimum perigee value from Wikipedia. Then compute how many hours it would take to run that distance at Eliud Kipchoge\\'s record marathon pace. Then convert to thousand hours, round to nearest 1000 hours, no comma separators.\\n\\nWe need to find perigee distance. Wikipedia page for Moon: perigee distance ~ 363,300 km? Actually perigee distance is about 363,300 km (minimum). Let\\'s confirm: The average distance is 384,400 km. Perigee ~ 363,300 km. The minimum perigee is about 363,104 km? Let\\'s check. Wikipedia: \"Perigee: 363,104 km (225,000 mi)\". So use 363,104 km. We\\'ll use that.\\n\\nNow Kipchoge\\'s record marathon pace: He set 2:01:39 for 42.195 km. Pace: 2:01:39 = 2 hours 1 minute 39 seconds = 2*3600 + 1*60 + 39 = 7200 + 60 + 39 = 7299 seconds. Pace per km: 7299 / 42.195 = about 173.0 seconds per km? Let\\'s compute: 42.195 km. 7299 / 42.195 = 173.0? Let\\'s calculate precisely: 42.195 * 173 = 7295.835. So 173.0 sec per km gives 7295.835 sec. Slight difference. Let\\'s compute more precisely: 7299 / 42.195 = 173.0? Let\\'s do division: 42.195 * 173.1 = 7299.0? 42.195*173.1 = 42.195*173 + 42.195*0.1 = 7295.835 + 4.2195 = 7300.0545. So 173.1 sec gives 7300.05 sec. So 173.0 sec gives 7295.835. So actual pace ~173.0 sec per km. So 173.0 sec per km.\\n\\nNow distance to Moon: 363,104 km. Time = distance * pace per km = 363,104 km * 173 sec/km = 363,104 * 173 = compute: 363,104 * 100 = 36,310,400; *70 = 25,417,280; *3 = 1,089,312. Sum: 36,310,400 + 25,417,280 = 61,727,680; +1,089,312 = 62,816,992 seconds. So about 62,816,992 seconds.\\n\\nConvert to hours: divide by 3600. 62,816,992 / 3600 = 17,460.277... Let\\'s compute: 3600*17,460 = 62,856,000. That\\'s slightly higher. Let\\'s compute precisely: 62,816,992 / 3600 = 17,460.277... Actually 3600*17,460 = 62,856,000. Our number is 39,008 less. 39,008/3600 = 10.822. So 17,460 - 10.822 = 17,449.178? Wait let\\'s do proper division: 62,816,992 / 3600. Use long division: 62,816,992 / 36 = 1,744,0?? Actually 3600 = 36*100. So divide by 36 then by 100. 62,816,992 / 36 =? 36*1,744,000 = 62,784,000. Remainder 32,992. 36*915 = 32,940. Remainder 52. So 1,744,915 remainder 52. So /36 = 1,744,915 + 52/36 = 1,744,915. 1,444? Wait we need to compute precisely: 62,816,992 / 36 = 1,744,915.111... Actually 36*1,744,915 = 62,816,940. Remainder 52. So 52/36 = 1.444... So 1,744,915.444... Then divide by 100 gives 17,449.15444 hours. So about 17,449.15 hours.\\n\\nNow thousand hours: divide by 1000: 17.449... thousand hours. Round to nearest 1000 hours? Wait the question: \"Round your result to the nearest 1000 hours and do not use any comma separators if necessary.\" They want the number of thousand hours? Actually \"how many thousand hours would it take him to run the distance between the Earth and the Moon its closest approach?\" So we need to express the answer in thousand hours. So we have 17,449 hours. In thousand hours, that\\'s 17.449 thousand hours. Round to nearest 1000 hours? Wait \"Round your result to the nearest 1000 hours\" could mean round the number of hours to nearest 1000 hours. So 17,449 hours rounds to 17,000 hours? Actually nearest 1000 hours: 17,449 is closer to 17,000 than 18,000? The difference: 449 vs 551. So round to 17,000 hours. But they want the answer in thousand hours? The phrase \"how many thousand hours would it take him\" suggests we should give the answer in thousand hours. So we should give 17 thousand hours? But rounding to nearest 1000 hours yields 17,000 hours. Then express as thousand hours: 17 thousand hours. But they want no comma separators. So \"17000\" or \"17\"? The instruction: \"Round your result to the nearest 1000 hours and do not use any comma separators if necessary.\" So we should output the number of hours rounded to nearest 1000, no commas. So 17000. But the question: \"how many thousand hours would it take him to run the distance between the Earth and the Moon its closest approach?\" That phrasing is ambiguous. It could mean \"how many thousand hours\" i.e., the answer in thousands of hours. But then rounding to nearest 1000 hours would be the same as rounding the hours to nearest thousand. So answer: 17000. But maybe they want \"17\" thousand hours. But they said no comma separators if necessary. So \"17000\" is fine. But maybe they want \"17000\" as the answer. Let\\'s double-check the calculation.\\n\\nCheck perigee distance: Wikipedia says \"Perigee: 363,104 km (225,000 mi)\". Use 363,104 km. Good.\\n\\nKipchoge pace: 2:01:39 for 42.195 km. Let\\'s compute pace precisely: 2h 1m 39s = 2*3600 + 1*60 + 39 = 7200 + 60 + 39 = 7299 seconds. Pace per km = 7299 / 42.195 = 173.0? Let\\'s compute more precisely: 42.195 * 173 = 7295.835. So 173.0 sec per km gives 7295.835 sec. We need 7299 sec, so pace is 173.0 + (3.165/42.195) = 173.0 + 0.075 = 173.075 sec per km. Actually 3.165/42.195 = 0.075. So pace ~173.075 sec per km. Let\\'s compute: 42.195 * 173.075 = 42.195*173 + 42.195*0.075 = 7295.835 + 3.164625 = 7298.999625 ~ 7299. So pace ~173.075 sec per km.\\n\\nNow time = 363,104 km * 173.075 sec/km = 363,104 * 173.075. Compute: 363,104 * 173 = 62,816,992 (as before). Then add 363,104 * 0.075 = 27,232.8. Sum = 62,844,224.8 seconds. Wait 62,816,992 + 27,232.8 = 62,844,224.8. So time ~62,844,225 sec.\\n\\nDivide by 3600: 62,844,225 / 3600 = 17,460.0625? Let\\'s compute: 3600*17,460 = 62,856,000. That\\'s 11,775 more. So 62,844,225 is 11,775 less. 11,775/3600 = 3.2708. So 17,460 - 3.2708 = 17,456.7292 hours. Wait that seems off. Let\\'s do precise division: 62,844,225 / 3600. Use 62,844,225 / 36 = 1,744, 0?? Let\\'s do: 36*1,744,000 = 62,784,000. Remainder 60,225. 36*1,674 = 60,264. Too high. 36*1,673 = 60,228. Remainder -3? Actually 36*1,673 = 60,228. Remainder -3? Wait 60,225 - 60,228 = -3. So 1,673 is',\n", " 'question': 'If Eliud Kipchoge could maintain his record-making marathon pace indefinitely, how many thousand hours would it take him to run the distance between the Earth and the Moon its closest approach? Please use the minimum perigee value on the Wikipedia page for the Moon when carrying out your calculation. Round your result to the nearest 1000 hours and do not use any comma separators if necessary.',\n", " 'final_answer': ''}" ] }, "execution_count": 220, "metadata": {}, "output_type": "execute_result" } ], "source": [ "final_state" ] }, { "cell_type": "code", "execution_count": 57, "id": "4b53d246", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "\n", "FINAL ANSWER: 1500000\n" ] } ], "source": [ "final_state[\"messages\"][-1].pretty_print()" ] }, { "cell_type": "code", "execution_count": 54, "id": "7f7c9f16", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "================================\u001b[1m System Message \u001b[0m================================\n", "\n", "You are a helpful assistant tasked with answering questions using a set of tools.\n", "Your final answer for my question must strictly follow this format:\n", "FINAL ANSWER: [ANSWER]\n", "Your answer should only start with \"FINAL ANSWER: \", followed by the answer. \n", "Write the answer in that exact format. Do not include any other text. Do not explain anything. \n", "Examples:\n", "- FINAL ANSWER: Monk\n", "- FINAL ANSWER: Rome\n", "- FINAL ANSWER: 228\n", "If you do not follow this format exactly, your response will be considered incorrect.\n", "================================\u001b[1m Human Message \u001b[0m=================================\n", "\n", "If Eliud Kipchoge could maintain his record-making marathon pace indefinitely, how many thousand hours would it take him to run the distance between the Earth and the Moon its closest approach? Please use the minimum perigee value on the Wikipedia page for the Moon when carrying out your calculation. Round your result to the nearest 1000 hours and do not use any comma separators if necessary.\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "Tool Calls:\n", " wiki_search (02868f7e-a3df-4327-aa16-3ef5fc3b6294)\n", " Call ID: 02868f7e-a3df-4327-aa16-3ef5fc3b6294\n", " Args:\n", " query: Moon distance between Earth and its closest approach\n", "=================================\u001b[1m Tool Message \u001b[0m=================================\n", "Name: wiki_search\n", "\n", "{\"wiki_results\": \"\\nThe instantaneous Earth–Moon distance, or distance to the Moon, is the distance from the center of Earth to the center of the Moon. In contrast, the Lunar distance (LD or \\n \\n \\n \\n \\n Δ\\n \\n ⊕\\n L\\n \\n \\n \\n \\n {\\\\textstyle \\\\Delta _{\\\\oplus L}}\\n \\n), or Earth–Moon characteristic distance, is a unit of measure in astronomy. More technically, it is the semi-major axis of the geocentric lunar orbit. The average lunar distance is approximately 385,000 km (239,000 mi), or 1.3 light-seconds. It is roughly 30 times Earth's diameter and a non-stop plane flight traveling that distance would take more than two weeks. Around 389 lunar distances make up an astronomical unit (roughly the distance from Earth to the Sun).\\nLunar distance is commonly used to express the distance to near-Earth object encounters. Lunar semi-major axis is an important astronomical datum. It has implications for testing gravitational theories such as general relativity and for refining other astronomical values, such as the mass, radius, and rotation of Earth. The measurement is also useful in measuring the lunar radius, as well as the distance to the Sun.\\nMillimeter-precision measurements of the lunar distance are made by measuring the time taken for laser light to travel between stations on Earth and retroreflectors placed on the Moon. The precision of the range measurements determines the semi-major axis to a few decimeters. The Moon is spiraling away from Earth at an average rate of 3.8 cm (1.5 in) per year, as detected by the Lunar Laser Ranging experiment.\\n\\n\\n== Value ==\\n\\nBecause of the influence of the Sun and other perturbations, the Moon's orbit around the Earth is not a precise ellipse. Nevertheless, different methods have been used to define a semi-major axis. Ernest William Brown provided a formula for the parallax of the Moon as viewed from opposite sides of the Earth, involving trigonometric terms. This is equivalent to a formula for the inverse of the distance, and the average value of this is the inverse of 384,399 km (238,854 mi). On the other hand, the time-averaged distance (rather than the inverse of the average inverse distance) between the centers of Earth and the Moon is 385,000.6 km (239,228.3 mi). One can also model the orbit as an ellipse that is constantly changing, and in this case one can find a formula for the semi-major axis, again involving trigonometric terms. The average value by this method is 383,397 km.\\nThe actual distance varies over the course of the orbit of the Moon. Values at closest approach (perigee) or at farthest (apogee) are rarer the more extreme they are. The graph at right shows the distribution of perigee and apogee over six thousand years.\\nJean Meeus gives the following extreme values for 1500 BC to AD 8000:\\n\\ngreatest distance: 406 719.97 km on January 7, AD 2266\\nsmallest distance: 356 352.93 km on November 13, 1054 BC\\n\\nAn AU is 389 Lunar distances.\\nA lightyear is 24,611,700 Lunar distances.\\nGeostationary Earth Orbit is 42,164 km (26,199 mi) from Earth center, or ⁠1/9.117⁠ LD = 0.10968 LD (or 0.10968 LDEO)\\n\\n\\n== Variation ==\\n\\nThe instantaneous lunar distance is constantly changing. The actual distance between the Moon and Earth can change as quickly as 75 meters per second, or more than 1,000 km (620 mi) in just 6 hours, due to its non-circular orbit. There are other effects that also influence the lunar distance. Some factors are listed in the sections below.\\n\\n\\n=== Perturbations and eccentricity ===\\nThe distance to the Moon can be measured to an accuracy of 2 mm over a 1-hour sampling period, which results in an overall uncertainty of a decimeter for the semi-major axis. However, due to its elliptical orbit with varying eccentricity, the instantaneous distance varies with monthly periodicity. Furthermore, the distance is perturbed by the gravitational effects of various astronomical bodies – most significantly the Sun and less so Venus and Jupi\\n\\n\\n---\\n\\n\\nThis is a list of examples where an asteroid or meteoroid travels close to the Earth. Some of these objects are regarded as potentially hazardous objects if they are estimated to be large enough to cause regional devastation. This list also gives an overview of more detailed lists dedicated to specific years, such as List of asteroid close approaches to Earth in 2025.\\nNear-Earth object detection technology began to improve around 1998, so objects being detected as of 2004 could have been missed only a decade earlier due to a lack of dedicated near-Earth astronomical surveys. As sky surveys improve, smaller and smaller asteroids are regularly being discovered. As smaller asteroids are more numerous, ever more close approaches are detected within a given distance. In 2014, scientists estimated that several dozen asteroids in the 6–12 m (20–39 ft) size range fly by Earth at a distance closer than the Moon every year, but only a fraction of these are actually detected.\\n\\n\\n== Definitions ==\\nThe lists below are based on the close approach database of the Center for Near-Earth Object Studies (CNEOS), in its state as of 27 February 2025. The database lists any approaches with a minimum distance less than 0.2 astronomical units (AU) from 1900 and until a century into the future which have been derived by orbit calculations. This includes some close approaches a full orbit or more before or after the object has been observed.\\nThe distance calculated for an approach has an uncertainty, the magnitude of which depends on the amount, length in time and quality of observations used, the extrapolation time from the observations, and perturbations by other objects along the predicted orbit. The uncertainty is usually characterised by the 3-sigma uncertainty region, which is the nominal close approach distance plus or minus three times the standard deviation, and includes 99.7% of the probability distribution. For predicted close approaches in the future, if Earth is near the uncertainty region or intersects it, an impact risk is calculated. Confirmed impacts, however, aren't considered close approaches and are excluded from the CNEOS close approach database. Asteroids whose detection in space led to predicted impacts on Earth are listed separately, as are the hundreds of other objects that collided with Earth's atmosphere which were not discovered in advance but were observed visually or recorded by sensors designed to detect detonation of nuclear devices. The CNEOS list also does not include Earth-grazers, objects that enter Earth's atmosphere at a very shallow angle and leave it again without burning up completely, but they are listed separately below. Although Earth's atmosphere thins out continuously with distance from Earth's surface, the nominal limit of space is the Kármán line, which is 100 km (62 mi) above sea level.\\n\\n\\n== Timeline of closest approaches ever observed ==\\nThe list below shows all approaches by potentially hazardous objects (objects which can approach Earth within 0.05 AU) without atmospheric contact which have been the closest ever observed at some point in time, from the discovery of the first such object to the record holder, as of February 2025.\\n\\n\\n== Close approaches within one lunar distance ==\\nThe average distance to the Moon (or lunar distance (LD)) is 384,399 km (238,854 mi), which is around 30 times the diameter of the Earth. The lists in this section are of close approaches in less than one LD.\\n\\n\\n=== Time of discovery ===\\nThe bar graphs below show the time of discovery relative to the time of the closest approach for each year. The asteroids are listed in separate list articles for each year. The statistics below only include close approaches that are evidenced by observations, thus the pre-discovery close approaches are only included if the object was found by precovery.\\n\\n\\n=== Closest per year ===\\n\\nFrom the annual lists summarized in the preceding section, these are the closest known asteroids per year that appr\\n\\n\\n---\\n\\n\\nA near-Earth object (NEO) is any small Solar System body orbiting the Sun whose closest approach to the Sun (perihelion) is less than 1.3 times the Earth–Sun distance (astronomical unit, AU). This definition applies to the object's orbit around the Sun, rather than its current position, thus an object with such an orbit is considered an NEO even at times when it is far from making a close approach of Earth. If an NEO's orbit crosses the Earth's orbit, and the object is larger than 140 meters (460 ft) across, it is considered a potentially hazardous object (PHO). Most known PHOs and NEOs are asteroids, but about a third of a percent are comets.\\nThere are over 37,000 known near-Earth asteroids (NEAs) and over 120 known short-period near-Earth comets (NECs). A number of solar-orbiting meteoroids were large enough to be tracked in space before striking Earth. It is now widely accepted that collisions in the past have had a significant role in shaping the geological and biological history of Earth. Asteroids as small as 20 metres (66 ft) in diameter can cause significant damage to the local environment and human populations. Larger asteroids penetrate the atmosphere to the surface of the Earth, producing craters if they impact a continent or tsunamis if they impact the sea. Interest in NEOs has increased since the 1980s because of greater awareness of this risk. Asteroid impact avoidance by deflection is possible in principle, and methods of mitigation are being researched.\\nTwo scales, the simple Torino scale and the more complex Palermo scale, rate the risk presented by an identified NEO based on the probability of it impacting the Earth and on how severe the consequences of such an impact would be. Some NEOs have had temporarily positive Torino or Palermo scale ratings after their discovery. Since 1998, the United States, the European Union, and other nations have been scanning the sky for NEOs in an effort called Spaceguard. The initial US Congress mandate to NASA to catalog at least 90% of NEOs that are at least 1 kilometre (0.62 mi) in diameter, sufficient to cause a global catastrophe, was met by 2011. In later years, the survey effort was expanded to include smaller objects which have the potential for large-scale, though not global, damage.\\nNEOs have low surface gravity, and many have Earth-like orbits that make them easy targets for spacecraft. As of December 2024, five near-Earth comets and six near-Earth asteroids, one of them with a moon, have been visited by spacecraft. Samples of three have been returned to Earth, and one successful deflection test was conducted. Similar missions are in progress. Preliminary plans for commercial asteroid mining have been drafted by private startup companies, but few of these plans were pursued.\\n\\n\\n== Definitions ==\\n\\nNear-Earth objects (NEOs) are formally defined by the International Astronomical Union (IAU) as all small Solar System bodies with orbits around the Sun that are at least partially closer than 1.3 astronomical units (AU; Sun–Earth distance) from the Sun. This definition excludes larger bodies such as planets, like Venus; natural satellites which orbit bodies other than the Sun, like Earth's Moon; and artificial bodies orbiting the Sun. A small Solar System body can be an asteroid or a comet, thus an NEO is either a near-Earth asteroid (NEA) or a near-Earth comet (NEC). The organisations cataloging NEOs further limit their definition of NEO to objects with an orbital period under 200 years, a restriction that applies to comets in particular, but this approach is not universal. Some authors further restrict the definition to orbits that are at least partly further than 0.983 AU away from the Sun. NEOs are thus not necessarily currently near the Earth, but they can potentially approach the Earth relatively closely. Many NEOs have complex orbits due to constant perturbation by the Earth's gravity, and some of them can temporarily change from an orbit around the Sun to one aroun\\n\"}\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "\n", "FINAL ANSWER: 1500000\n" ] } ], "source": [ "for m in final_state[\"messages\"]:\n", " m.pretty_print()" ] } ], "metadata": { "kernelspec": { "display_name": "gaia_agent", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.5" } }, "nbformat": 4, "nbformat_minor": 5 }