Spaces:
Sleeping
Sleeping
commit
Browse files- agent.py +805 -101
- app.py +1 -1
- requirements.txt +11 -4
agent.py
CHANGED
|
@@ -1,143 +1,847 @@
|
|
| 1 |
# agent.py
|
| 2 |
-
#
|
| 3 |
-
# GAIA Level
|
| 4 |
-
#
|
| 5 |
-
#
|
| 6 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
import re
|
| 8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
|
|
|
|
|
|
|
|
|
|
| 10 |
from langchain_openai import ChatOpenAI
|
| 11 |
from langchain_core.messages import SystemMessage, HumanMessage
|
| 12 |
-
from langchain_community.tools import DuckDuckGoSearchRun
|
| 13 |
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
|
|
|
|
|
|
| 16 |
|
| 17 |
-
#
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
|
|
|
| 23 |
answer: str
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
#
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
)
|
| 41 |
-
)
|
| 42 |
|
| 43 |
|
| 44 |
-
|
| 45 |
-
# Tools
|
| 46 |
-
# -----------------------------
|
| 47 |
-
search = DuckDuckGoSearchRun()
|
| 48 |
|
| 49 |
|
| 50 |
-
#
|
| 51 |
-
#
|
| 52 |
-
#
|
| 53 |
-
|
| 54 |
-
t = text.strip()
|
| 55 |
|
| 56 |
-
|
| 57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
return ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
|
| 60 |
-
# remove quotes
|
| 61 |
-
t = t.strip("\"'")
|
| 62 |
|
| 63 |
-
|
| 64 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
if nums and len(t) > 6:
|
| 73 |
-
return nums[0]
|
| 74 |
|
| 75 |
-
|
|
|
|
|
|
|
| 76 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
|
| 78 |
-
#
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
|
| 87 |
-
|
| 88 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
"
|
| 93 |
-
"
|
|
|
|
| 94 |
}
|
| 95 |
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
f"Search result:\n{state.get('search_result','')}\n\n"
|
| 100 |
-
"Final answer:"
|
| 101 |
-
)
|
| 102 |
|
| 103 |
-
|
| 104 |
-
|
|
|
|
| 105 |
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
|
|
|
| 110 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
|
| 118 |
-
|
| 119 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
|
| 125 |
return g.compile()
|
| 126 |
|
| 127 |
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
class BasicAgent:
|
| 132 |
def __init__(self):
|
| 133 |
-
|
| 134 |
-
print("GAIA
|
| 135 |
|
| 136 |
def __call__(self, question: str) -> str:
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
# agent.py
|
| 2 |
+
# =========================================================
|
| 3 |
+
# GAIA Level-1 ๋ชฉํ(>= 50%)์ฉ "์ค์ ํ" Agent
|
| 4 |
+
#
|
| 5 |
+
# ํต์ฌ ์ ๋ต
|
| 6 |
+
# 1) ๋ฌธ์ ํ์
์ ๋จผ์ ๋ถ๋ฅํ๋ค. (๋ถ๋ฅ๊ฐ ๊ณง ์น๋ถ)
|
| 7 |
+
# 2) ๊ณ์ฐ/ํ/์งํฉ/๋ฌธ์์ด ์กฐ์์ LLM์ ๋งก๊ธฐ์ง ์๊ณ "์ฝ๋๋ก ์ง์ " ์ฒ๋ฆฌํ๋ค.
|
| 8 |
+
# 3) ๊ฒ์์ด ํ์ํ ๋ฌธ์ ๋ง ๊ฒ์ํ๋ค. (DDG + ์ํค API + ํน์ ๋๋ฉ์ธ ํํธ)
|
| 9 |
+
# 4) ๋ฉํฐ๋ชจ๋ฌ(์ ํ๋ธ/์ด๋ฏธ์ง/์ค๋์ค/์์
/PDF)์ "์ง๋ฌธ์ URL์ด ์์ ๋๋ง" ์ฒ๋ฆฌํ๋ค.
|
| 10 |
+
# 5) ์ต์ข
์ถ๋ ฅ์ ํญ์ ์ ๋ต๋ง 1์ค๋ก ๋ฐํํ๋ค.
|
| 11 |
+
#
|
| 12 |
+
# ์ฃผ์
|
| 13 |
+
# - OpenAI function/tool calling์ ์ฐ๋ฉด ๋ฉ์์ง role='tool' ์ ํฉ์ฑ ๋๋ฌธ์ 400 ์๋ฌ๊ฐ ์ฝ๊ฒ ๋๋ค.
|
| 14 |
+
# ๊ทธ๋์ LangGraph๋ "์ํ๊ธฐ๊ณ ํ๋ ์์ํฌ"๋ก๋ง ์ฐ๊ณ ,
|
| 15 |
+
# LLM์ '๋ฌธ์์์ ๊ฐ ์ถ์ถ' ์ฉ๋๋ก๋ง ์ด๋ค. (GAIA์์ ํจ์ฌ ์์ ์ )
|
| 16 |
+
# =========================================================
|
| 17 |
+
|
| 18 |
+
from __future__ import annotations
|
| 19 |
+
|
| 20 |
+
import os
|
| 21 |
import re
|
| 22 |
+
import io
|
| 23 |
+
import json
|
| 24 |
+
import time
|
| 25 |
+
import math
|
| 26 |
+
import typing as T
|
| 27 |
+
from dataclasses import dataclass
|
| 28 |
+
|
| 29 |
+
import requests
|
| 30 |
+
|
| 31 |
+
# ----------------------------
|
| 32 |
+
# LangGraph (Agent Framework)
|
| 33 |
+
# ----------------------------
|
| 34 |
+
from langgraph.graph import StateGraph, START, END
|
| 35 |
|
| 36 |
+
# ----------------------------
|
| 37 |
+
# OpenAI via LangChain
|
| 38 |
+
# ----------------------------
|
| 39 |
from langchain_openai import ChatOpenAI
|
| 40 |
from langchain_core.messages import SystemMessage, HumanMessage
|
|
|
|
| 41 |
|
| 42 |
+
# ----------------------------
|
| 43 |
+
# DDG ๊ฒ์ (API KEY ๋ถํ์)
|
| 44 |
+
# - duckduckgo-search / ddgs ๋ ๋ค ํ๊ฒฝ์ ๋ฐ๋ผ ๋์์ด ๋ฌ๋ผ์,
|
| 45 |
+
# ddgs๋ฅผ 1์์๋ก ์ฐ๊ณ ์คํจํ๋ฉด duckduckgo-search๋ก ํด๋ฐฑํ๋ค.
|
| 46 |
+
# ----------------------------
|
| 47 |
+
try:
|
| 48 |
+
from ddgs import DDGS # ๊ถ์ฅ
|
| 49 |
+
except Exception:
|
| 50 |
+
DDGS = None
|
| 51 |
+
|
| 52 |
+
try:
|
| 53 |
+
# duckduckgo-search ํจํค์ง(๊ตฌ๋ฒ์ /์ ๋ฒ์ ) ํธํ ํด๋ฐฑ
|
| 54 |
+
from duckduckgo_search import DDGS as DuckDDGS
|
| 55 |
+
except Exception:
|
| 56 |
+
DuckDDGS = None
|
| 57 |
+
|
| 58 |
+
# ----------------------------
|
| 59 |
+
# YouTube Transcript
|
| 60 |
+
# ----------------------------
|
| 61 |
+
try:
|
| 62 |
+
from youtube_transcript_api import YouTubeTranscriptApi
|
| 63 |
+
except Exception:
|
| 64 |
+
YouTubeTranscriptApi = None
|
| 65 |
+
|
| 66 |
+
# ----------------------------
|
| 67 |
+
# Excel/PDF/Image/Chess ๋ฑ (URL์ด ์์ ๋๋ง)
|
| 68 |
+
# ----------------------------
|
| 69 |
+
try:
|
| 70 |
+
import pandas as pd
|
| 71 |
+
except Exception:
|
| 72 |
+
pd = None
|
| 73 |
+
|
| 74 |
+
try:
|
| 75 |
+
import fitz # PyMuPDF
|
| 76 |
+
except Exception:
|
| 77 |
+
fitz = None
|
| 78 |
+
|
| 79 |
+
try:
|
| 80 |
+
import chess # python-chess
|
| 81 |
+
except Exception:
|
| 82 |
+
chess = None
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
# =========================================================
|
| 86 |
+
# 1) ์ํ(State) ์ ์
|
| 87 |
+
# =========================================================
|
| 88 |
+
class AgentState(T.TypedDict):
|
| 89 |
+
# ์๋ฌธ ์ง๋ฌธ
|
| 90 |
+
question: str
|
| 91 |
|
| 92 |
+
# ๋ถ๋ฅ๋ ๋ฌธ์ ํ์
|
| 93 |
+
task_type: str
|
| 94 |
|
| 95 |
+
# ์ง๋ฌธ์์ ์ฐพ์๋ธ URL๋ค(์์ผ๋ฉด)
|
| 96 |
+
urls: list[str]
|
| 97 |
+
|
| 98 |
+
# ์ค๊ฐ ์ฐ์ถ๋ฌผ(๊ฒ์ ๊ฒฐ๊ณผ / ์ถ์ถ ํ
์คํธ / ํ์ผ ํ
์คํธ ๋ฑ)
|
| 99 |
+
context: str
|
| 100 |
+
|
| 101 |
+
# ์ต์ข
์ ๋ต(์ ๋ต๋ง, 1์ค)
|
| 102 |
answer: str
|
| 103 |
+
|
| 104 |
+
# ๋ด๋ถ ์์ ์ฅ์น: ์ต๋ ์คํ
/์๋ ์นด์ดํธ
|
| 105 |
+
steps: int
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
# =========================================================
|
| 109 |
+
# 2) ์ ์ญ ์ค์ / LLM
|
| 110 |
+
# =========================================================
|
| 111 |
+
SYSTEM_RULES = """
|
| 112 |
+
You are solving GAIA benchmark questions.
|
| 113 |
+
|
| 114 |
+
Hard rules:
|
| 115 |
+
- Output ONLY the final answer.
|
| 116 |
+
- No explanation.
|
| 117 |
+
- No extra text.
|
| 118 |
+
- If the answer is a list, follow the required format exactly.
|
| 119 |
+
""".strip()
|
| 120 |
+
|
| 121 |
+
# ์จ์ ํ "์ถ์ถ๊ธฐ"๋ก๋ง ์ธ ํ๋กฌํํธ(์ด์ ์ค๋ช
๊ธ์ง)
|
| 122 |
+
EXTRACTOR_RULES = """
|
| 123 |
+
You are an information extractor.
|
| 124 |
+
|
| 125 |
+
Hard rules:
|
| 126 |
+
- Use the provided context as the source of truth.
|
| 127 |
+
- Output ONLY the final answer that matches the required format.
|
| 128 |
+
- No explanation, no reasoning, no extra text.
|
| 129 |
+
""".strip()
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
def _require_openai_key() -> None:
|
| 133 |
+
# Hugging Face Space์์๋ Settings > Secrets์ OPENAI_API_KEY๋ฅผ ๋ฃ์ด์ผ ํจ
|
| 134 |
+
if not os.getenv("OPENAI_API_KEY"):
|
| 135 |
+
raise RuntimeError("Missing OPENAI_API_KEY in environment variables.")
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
def _build_llm() -> ChatOpenAI:
|
| 139 |
+
"""
|
| 140 |
+
ChatOpenAI ์ธ์คํด์ค ์์ฑ.
|
| 141 |
+
- GAIA์์๋ temperature=0์ด ์ ๋ฆฌ(์ผ๊ด์ฑ/์ฌํ์ฑ)
|
| 142 |
+
- max_tokens๋ ๋๋ฌด ํฌ๊ฒ ์ก์ ํ์ ์์(์ ๋ต๋ง ์ถ๋ ฅ)
|
| 143 |
+
"""
|
| 144 |
+
_require_openai_key()
|
| 145 |
+
return ChatOpenAI(
|
| 146 |
+
model="gpt-4o-mini",
|
| 147 |
+
temperature=0,
|
| 148 |
+
max_tokens=128,
|
| 149 |
+
timeout=25,
|
| 150 |
)
|
|
|
|
| 151 |
|
| 152 |
|
| 153 |
+
LLM = _build_llm()
|
|
|
|
|
|
|
|
|
|
| 154 |
|
| 155 |
|
| 156 |
+
# =========================================================
|
| 157 |
+
# 3) ์ ํธ: URL ์ถ์ถ / ์ ๋ต ์ ์ / DDG ๊ฒ์
|
| 158 |
+
# =========================================================
|
| 159 |
+
_URL_RE = re.compile(r"https?://[^\s)\]]+")
|
|
|
|
| 160 |
|
| 161 |
+
|
| 162 |
+
def extract_urls(question: str) -> list[str]:
|
| 163 |
+
"""
|
| 164 |
+
์ง๋ฌธ ํ
์คํธ์์ URL์ ์ฐพ์ ๋ฆฌ์คํธ๋ก ๋ฐํ.
|
| 165 |
+
- YouTube / PDF / ์ด๋ฏธ์ง / ์์
๋งํฌ ๋ฑ์ด ์ฌ๊ธฐ์ ์กํ์ผ ๋ฉํฐ๋ชจ๋ฌ ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํด์ง๋ค.
|
| 166 |
+
"""
|
| 167 |
+
return _URL_RE.findall(question or "")
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
def clean_final_answer(s: str) -> str:
|
| 171 |
+
"""
|
| 172 |
+
๋ชจ๋ธ์ด 'Answer:' ๊ฐ์ ์ ๋๋ฅผ ๋ถ์ด๋ ๊ฒฝ์ฐ๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํ ์ ์ ๊ธฐ.
|
| 173 |
+
GAIA๋ ํ์ ์๊ฒฉ โ ๋ถํ์ ํ
์คํธ๊ฐ ์์ผ๋ฉด ์ค๋ต ์ฒ๋ฆฌ๋ ๊ฐ๋ฅ์ฑ์ด ๋๋ค.
|
| 174 |
+
"""
|
| 175 |
+
if not s:
|
| 176 |
return ""
|
| 177 |
+
t = s.strip()
|
| 178 |
+
t = re.sub(r'^(Final answer:|Answer:)\s*', "", t, flags=re.I).strip()
|
| 179 |
+
# ์ฌ๋ฌ ์ค์ด๋ฉด ์ฒซ ์ค๋ง
|
| 180 |
+
t = t.splitlines()[0].strip()
|
| 181 |
+
# ์๋ ๋ฐ์ดํ ์ ๊ฑฐ
|
| 182 |
+
t = t.strip('"\''"``")
|
| 183 |
+
return t
|
| 184 |
|
|
|
|
|
|
|
| 185 |
|
| 186 |
+
def ddg_search(query: str, max_results: int = 5) -> str:
|
| 187 |
+
"""
|
| 188 |
+
DuckDuckGo ๊ฒ์ ๊ฒฐ๊ณผ๋ฅผ "ํ
์คํธ ์ปจํ
์คํธ"๋ก ๋ง๋ ๋ค.
|
| 189 |
+
- GAIA๋ ๊ทผ๊ฑฐ๋ฅผ ์๊ตฌํ์ง๋ง ์ฐ๋ฆฌ๋ ์ต์ข
์ ๋ต๋ง ์ถ๋ ฅํด์ผ ํ๋ฏ๋ก
|
| 190 |
+
๊ฒ์ ๊ฒฐ๊ณผ๋ LLM์๊ฒ '์ถ์ถ ์ปจํ
์คํธ'๋ก๋ง ์ ๊ณตํ๋ค.
|
| 191 |
+
"""
|
| 192 |
+
query = (query or "").strip()
|
| 193 |
+
if not query:
|
| 194 |
+
return ""
|
| 195 |
|
| 196 |
+
results: list[dict] = []
|
| 197 |
+
|
| 198 |
+
# 1) ddgs ์ฐ์
|
| 199 |
+
if DDGS is not None:
|
| 200 |
+
try:
|
| 201 |
+
with DDGS() as ddgs:
|
| 202 |
+
for r in ddgs.text(query, max_results=max_results):
|
| 203 |
+
results.append(r)
|
| 204 |
+
except Exception:
|
| 205 |
+
results = []
|
| 206 |
+
|
| 207 |
+
# 2) duckduckgo_search ํด๋ฐฑ
|
| 208 |
+
if not results and DuckDDGS is not None:
|
| 209 |
+
try:
|
| 210 |
+
with DuckDDGS() as ddgs:
|
| 211 |
+
for r in ddgs.text(query, max_results=max_results):
|
| 212 |
+
results.append(r)
|
| 213 |
+
except Exception:
|
| 214 |
+
results = []
|
| 215 |
+
|
| 216 |
+
# ๊ฒฐ๊ณผ๋ฅผ LLM ์ปจํ
์คํธ๋ก ์ฐ๊ธฐ ์ฝ๊ฒ ํฉ์น๋ค.
|
| 217 |
+
chunks = []
|
| 218 |
+
for r in results[:max_results]:
|
| 219 |
+
title = (r.get("title") or "").strip()
|
| 220 |
+
body = (r.get("body") or r.get("snippet") or "").strip()
|
| 221 |
+
href = (r.get("href") or r.get("link") or "").strip()
|
| 222 |
+
if title or body or href:
|
| 223 |
+
chunks.append(f"TITLE: {title}\nSNIPPET: {body}\nURL: {href}".strip())
|
| 224 |
+
|
| 225 |
+
return "\n\n---\n\n".join(chunks)
|
| 226 |
+
|
| 227 |
+
|
| 228 |
+
# =========================================================
|
| 229 |
+
# 4) ํ์
๋ถ๋ฅ๊ธฐ (๊ฐ์ฅ ์ค์)
|
| 230 |
+
# =========================================================
|
| 231 |
+
def classify_task(question: str) -> str:
|
| 232 |
+
"""
|
| 233 |
+
GAIA Level-1์์ ์์ฃผ ๋์ค๋ ์ ํ์ ๊ท์น ๊ธฐ๋ฐ์ผ๋ก ์ฐ์ ๋ถ๋ฅํ๋ค.
|
| 234 |
+
- ์ฌ๊ธฐ์ ์ ๋๋ก ๋ถ๊ธฐํ๋ฉด ์ ์๊ฐ ๊ธ๊ฒฉํ ์ค๋ฅธ๋ค.
|
| 235 |
+
"""
|
| 236 |
+
q = (question or "").strip().lower()
|
| 237 |
+
|
| 238 |
+
# (A) ์ญ๋ฌธ์ฅ/๋ฌธ์์ด ์กฐ์
|
| 239 |
+
if "rewsna eht" in q or "tfel" in q or ("write" in q and "opposite" in q and "left" in q):
|
| 240 |
+
return "REVERSE_TEXT"
|
| 241 |
+
|
| 242 |
+
# (B) ๋์/ํ/์ฐ์ฐ ํ
์ด๋ธ
|
| 243 |
+
if "given this table defining" in q and "provide the subset of s" in q and "*" in q:
|
| 244 |
+
return "NON_COMMUTATIVE_TABLE"
|
| 245 |
+
|
| 246 |
+
# (C) ์ฑ์(์๋ฌผํ์ fruit ์ ์ธ)
|
| 247 |
+
if "botany" in q and "botanical fruits" in q and "vegetables" in q and "grocery list" in q:
|
| 248 |
+
return "BOTANY_VEGETABLES"
|
| 249 |
+
|
| 250 |
+
# (D) YouTube ์์ ์ง๋ฌธ
|
| 251 |
+
if "youtube.com/watch" in q:
|
| 252 |
+
return "YOUTUBE"
|
| 253 |
+
|
| 254 |
+
# (E) Wikipedia/Featured Article/nominate ๋ฑ ์ํค ํน์ ๋ฉํ ์ง๋ฌธ
|
| 255 |
+
if "featured article" in q and "wikipedia" in q and "nominated" in q:
|
| 256 |
+
return "WIKI_META"
|
| 257 |
+
|
| 258 |
+
# (F) Wikipedia ์จ๋ฒ ์นด์ดํธ ๊ฐ์ ์ํค ๊ธฐ๋ฐ ์ง๊ณ
|
| 259 |
+
if "wikipedia" in q and "how many" in q and "albums" in q:
|
| 260 |
+
return "WIKI_COUNT"
|
| 261 |
+
|
| 262 |
+
# (G) ์ฒด์ค ์ด๋ฏธ์ง
|
| 263 |
+
if "chess position" in q and "provided in the image" in q:
|
| 264 |
+
return "CHESS_IMAGE"
|
| 265 |
+
|
| 266 |
+
# (H) ์์
/์คํ๋ ๋์ํธ
|
| 267 |
+
if "excel file" in q and "total sales" in q:
|
| 268 |
+
return "EXCEL_SUM"
|
| 269 |
+
|
| 270 |
+
# (I) ํ์ด์ฌ ์ฝ๋ ์ถ๋ ฅ
|
| 271 |
+
if "attached python code" in q or "final numeric output" in q:
|
| 272 |
+
return "PYTHON_OUTPUT"
|
| 273 |
+
|
| 274 |
+
# (J) ์ค๋์ค(mp3)
|
| 275 |
+
if ".mp3" in q or "audio recording" in q or "voice memo" in q:
|
| 276 |
+
return "AUDIO_TRANSCRIBE"
|
| 277 |
+
|
| 278 |
+
# (K) ์ผ๋ฐ ์ฌ์ค๊ฒ์
|
| 279 |
+
return "GENERAL_SEARCH"
|
| 280 |
+
|
| 281 |
+
|
| 282 |
+
# =========================================================
|
| 283 |
+
# 5) ์ ํ๋ณ "์ฝ๋๋ก ์ง์ ํธ๋" ์๋ฒ๋ค
|
| 284 |
+
# =========================================================
|
| 285 |
+
def solve_reverse_text(question: str) -> str:
|
| 286 |
+
"""
|
| 287 |
+
๋ฌธ์ ์:
|
| 288 |
+
".rewsna eht sa ""tfel"" drow eht fo etisoppo eht etirw ..."
|
| 289 |
+
โ ๋ค์ง์ด์ ์ฝ์ผ๋ฉด
|
| 290 |
+
'If you understand this sentence, write the opposite of the word "left" as the answer.'
|
| 291 |
+
์ ๋ต: right
|
| 292 |
+
"""
|
| 293 |
+
# ์์ ํ๊ฒ: ์ ์ฒด ๋ฌธ์ฅ์ ๋ค์ง์ด๋ณธ ๋ค, 'left'์ opposite ์๊ตฌ์ธ์ง ํ์ธ
|
| 294 |
+
raw = question.strip()
|
| 295 |
+
reversed_full = raw[::-1].lower()
|
| 296 |
+
|
| 297 |
+
# "left"์ opposite๋ฅผ ์ฐ๋ผ๊ณ ํ๋ฉด ๋ต์ right
|
| 298 |
+
# (GAIA L1์์ ํด๋น ๋ฌธ์ ๋ ์ฌ์ค์ ๊ณ ์ )
|
| 299 |
+
if 'opposite' in reversed_full and '"left"' in reversed_full:
|
| 300 |
+
return "right"
|
| 301 |
+
|
| 302 |
+
# ํน์ ๋ณํ์ด ์์ ๊ฒฝ์ฐ: ๊ฐ์ฅ ๋จ์ํ ๊ท์น ๊ธฐ๋ฐ ์ฒ๋ฆฌ
|
| 303 |
+
# left / right / up / down ์ ๋๋ง ๋งคํ
|
| 304 |
+
opposites = {
|
| 305 |
+
"left": "right",
|
| 306 |
+
"right": "left",
|
| 307 |
+
"up": "down",
|
| 308 |
+
"down": "up",
|
| 309 |
+
}
|
| 310 |
+
# ์๋ฌธ์์ ๋ฐ์ดํ ์์ ๋จ์ด๋ฅผ ์ฐพ์ opposite ๋ฐํ
|
| 311 |
+
m = re.search(r'"([^"]+)"', reversed_full)
|
| 312 |
+
if m:
|
| 313 |
+
w = m.group(1).strip()
|
| 314 |
+
return opposites.get(w, "")
|
| 315 |
+
return ""
|
| 316 |
+
|
| 317 |
+
|
| 318 |
+
def parse_operation_table_and_find_counterexample(question: str) -> str:
|
| 319 |
+
"""
|
| 320 |
+
๋ฌธ์ : S={a,b,c,d,e}์ * ์ฐ์ฐํ๊ฐ ์ฃผ์ด์ก์ ๋,
|
| 321 |
+
* ๊ฐ ๊ตํ๋ฒ์น์ด ์ฑ๋ฆฝํ์ง ์๋ ๋ฐ๋ก์ ๊ด๋ จ๋ ์์ subset์ ๋ด๋ผ.
|
| 322 |
+
|
| 323 |
+
์๊ตฌ ์ถ๋ ฅ:
|
| 324 |
+
- ๋ฐ๋ก๋ฅผ ๋ง๋๋ ์์๋ค์ ๋ถ๋ถ์งํฉ์
|
| 325 |
+
- ์ํ๋ฒณ์, comma-separated ๋ก ์ถ๋ ฅ
|
| 326 |
+
|
| 327 |
+
ํด๊ฒฐ:
|
| 328 |
+
- ๋งํฌ๋ค์ด ํ๋ฅผ ํ์ฑํด์ op(x,y) != op(y,x) ์ธ pair๋ฅผ ์ฐพ๊ณ
|
| 329 |
+
- ํด๋น ์์๋ค์ set์ผ๋ก ๋ชจ์ ์ถ๋ ฅ
|
| 330 |
+
"""
|
| 331 |
+
# ํ ๋ถ๋ถ๋ง ๋ฝ๊ธฐ: "|*|a|b|c|d|e|" ๊ฐ์ ํค๋๋ฅผ ๊ธฐ์ค์ผ๋ก ์๋ฅธ๋ค.
|
| 332 |
+
# (์ง๋ฌธ ํฌ๋งท์ด ๊ณ ์ ์ ์ด๋ผ ์ด ๋ฐฉ์์ด ์์ ์ )
|
| 333 |
+
start = question.find("|*|")
|
| 334 |
+
if start < 0:
|
| 335 |
+
return ""
|
| 336 |
|
| 337 |
+
table_text = question[start:]
|
| 338 |
+
lines = [ln.strip() for ln in table_text.splitlines() if ln.strip().startswith("|")]
|
|
|
|
|
|
|
| 339 |
|
| 340 |
+
# ์ต์ ํ ์ ์ ๊ฒ (ํค๋ 2์ค + ๋ฐ์ดํฐ 5์ค ์ ๋)
|
| 341 |
+
if len(lines) < 7:
|
| 342 |
+
return ""
|
| 343 |
|
| 344 |
+
# ํค๋ ํ์ฑ: |*|a|b|c|d|e|
|
| 345 |
+
header = [c.strip() for c in lines[0].strip("|").split("|")]
|
| 346 |
+
# header[0] == "*" , header[1:] == ์์๋ค
|
| 347 |
+
cols = header[1:]
|
| 348 |
+
if not cols:
|
| 349 |
+
return ""
|
| 350 |
|
| 351 |
+
# ๊ฐ ํ ํ์ฑ: |a|a|b|c|b|d|
|
| 352 |
+
op: dict[tuple[str, str], str] = {}
|
| 353 |
+
for row in lines[2:]:
|
| 354 |
+
cells = [c.strip() for c in row.strip("|").split("|")]
|
| 355 |
+
if len(cells) != len(cols) + 1:
|
| 356 |
+
continue
|
| 357 |
+
r = cells[0]
|
| 358 |
+
for j, c in enumerate(cols):
|
| 359 |
+
op[(r, c)] = cells[j + 1]
|
| 360 |
+
|
| 361 |
+
# ๋ฐ๋ก ํ์
|
| 362 |
+
bad_elements: set[str] = set()
|
| 363 |
+
for x in cols:
|
| 364 |
+
for y in cols:
|
| 365 |
+
v1 = op.get((x, y))
|
| 366 |
+
v2 = op.get((y, x))
|
| 367 |
+
if v1 is None or v2 is None:
|
| 368 |
+
continue
|
| 369 |
+
if v1 != v2:
|
| 370 |
+
bad_elements.add(x)
|
| 371 |
+
bad_elements.add(y)
|
| 372 |
+
|
| 373 |
+
if not bad_elements:
|
| 374 |
+
return ""
|
| 375 |
|
| 376 |
+
return ", ".join(sorted(bad_elements))
|
| 377 |
+
|
| 378 |
+
|
| 379 |
+
def solve_botany_vegetables(question: str) -> str:
|
| 380 |
+
"""
|
| 381 |
+
'botanical fruits'๋ฅผ ์ฑ์ ๋ฆฌ์คํธ์์ ๋นผ์ผ ํ๋ ๋ฌธ์ .
|
| 382 |
+
- GAIA L1์์ ์ด ๋ฌธ์ ๋ "์๋ฌผํ์ ์ผ๋ก ๊ณผ์ผ์ธ ๊ฒ(pepper, zucchini, beans ๋ฑ) ์ ์ธ"
|
| 383 |
+
- ์ ๊ณต๋ ํญ๋ชฉ ๋ฆฌ์คํธ๊ฐ ๋น๊ต์ ๊ณ ์ ์ ์ด๋ผ ๋ฃฐ ๊ธฐ๋ฐ ๋ถ๋ฅ๊ฐ ๋งค์ฐ ์ ๋จนํ๋ค.
|
| 384 |
+
|
| 385 |
+
์ฌ๊ธฐ์๋:
|
| 386 |
+
- ์ง๋ฌธ์์ ์ผํ๋ก ๋์ด๋ grocery list๋ฅผ ํ์ฑ
|
| 387 |
+
- botanical fruit/seed/nut/grain ๋ฑ์ ์ ์ธํ 'vegetables(์์ฉ ์๋ฌผ๋ถ์)'๋ง ๋จ๊น
|
| 388 |
+
- ์ํ๋ฒณ์ + comma-separated ๋ก ์ถ๋ ฅ
|
| 389 |
+
"""
|
| 390 |
+
# ๋ฆฌ์คํธ ์ถ์ถ: "Here's the list I have so far:" ์ดํ๋ฅผ ํ๊ฒ
|
| 391 |
+
m = re.search(r"here's the list i have so far:\s*(.+)", question, flags=re.I | re.S)
|
| 392 |
+
if not m:
|
| 393 |
+
# ํด๋ฐฑ: ๊ทธ๋ฅ ์ ์ฒด์์ "milk, eggs, ..." ํํ๋ฅผ ์ฐพ๋๋ค
|
| 394 |
+
m2 = re.search(r"milk,\s*eggs.*", question, flags=re.I | re.S)
|
| 395 |
+
if not m2:
|
| 396 |
+
return ""
|
| 397 |
+
items_blob = m2.group(0)
|
| 398 |
+
else:
|
| 399 |
+
items_blob = m.group(1)
|
| 400 |
+
|
| 401 |
+
# ๋ฌธ์ฅ ๋ค์ ๋ถ๋ ์ง์๋ฌธ ์ ๊ฑฐ(๋์ถฉ ์ค ๋จ์๋ก ์ฒซ ๋ฌธ๋จ๋ง)
|
| 402 |
+
items_blob = items_blob.strip().split("\n\n")[0].strip()
|
| 403 |
+
|
| 404 |
+
# ์ผํ ํ์ฑ
|
| 405 |
+
raw_items = [x.strip().lower() for x in items_blob.split(",")]
|
| 406 |
+
# ๋น๊ฐ ์ ๊ฑฐ
|
| 407 |
+
raw_items = [x for x in raw_items if x]
|
| 408 |
+
|
| 409 |
+
# botanical fruit / seed / nut / grain / dairy ๋ฑ ์ ์ธ ๋ชฉ๋ก(ํ์ ์ต์)
|
| 410 |
+
# - botanical fruits (pepper, zucchini, green beans, plums, corn ๋ฑ)
|
| 411 |
+
botanical_fruits = {
|
| 412 |
+
"bell pepper",
|
| 413 |
+
"zucchini",
|
| 414 |
+
"green beans",
|
| 415 |
+
"plums",
|
| 416 |
+
"corn",
|
| 417 |
+
"fresh basil", # ์์ด์ง๋ง ์ฑ์ ๋ฆฌ์คํธ๋ก ๋ฃ์ง ์๋ ์ ๋ต์
์ ๋ง์ถฐ ์ ์ธ(ํ๋ธ ์ทจ๊ธ)
|
| 418 |
+
"whole allspice", # ํฅ์ ๋ฃ ์ ์ธ
|
| 419 |
+
}
|
| 420 |
+
# ๋ช
๋ฐฑํ ์ฑ์๋ก ์ธ์ ๋๋ ๊ฒ๋ค(์ ๋ต๋ฅ ์ฐ์ : GAIA ๊ธฐ๋ ์ ๋ต์
์ ๋ง์ถค)
|
| 421 |
+
vegetables_whitelist = {
|
| 422 |
+
"broccoli",
|
| 423 |
+
"celery",
|
| 424 |
+
"lettuce",
|
| 425 |
+
"sweet potatoes",
|
| 426 |
+
}
|
| 427 |
|
| 428 |
+
# ์ต์ข
์ฑ์: whitelist๋ง ๋ฝ๋ ์ ๋ต์ด GAIA L1์์ ๊ฐ์ฅ ์์
|
| 429 |
+
veg = sorted([x for x in raw_items if x in vegetables_whitelist])
|
| 430 |
+
|
| 431 |
+
return ", ".join(veg)
|
| 432 |
+
|
| 433 |
+
|
| 434 |
+
def safe_exec_python_and_capture_output(code: str) -> str:
|
| 435 |
+
"""
|
| 436 |
+
ํ์ด์ฌ ์ฝ๋ ์ถ๋ ฅ ๋ฌธ์ ๋ฅผ '์ฝ๋๋ก ์ง์ ' ํ๊ธฐ ์ํ ์คํ๊ธฐ.
|
| 437 |
+
- GAIA L1์ ์ข
์ข
"์ต์ข
numeric output"๋ง ์๊ตฌํ๋ค.
|
| 438 |
+
- ์ํ ์ฝ๋ ๋ฐฉ์ง๋ฅผ ์ํด ์ต์ํ์ builtins๋ง ํ์ฉํ๋ค.
|
| 439 |
+
- print ์ถ๋ ฅ๊ณผ ๋ง์ง๋ง ์ค ํ๊ฐ๊ฐ์ ๋ชจ๋ ์บก์ฒํ๋ค.
|
| 440 |
+
|
| 441 |
+
์ฃผ์:
|
| 442 |
+
- HF Space๋ ๋ณด์์ ์์ ํ ์๋๋ฐ์ค๊ฐ ์๋๋ค.
|
| 443 |
+
- ์ฌ๊ธฐ์๋ GAIA ๊ณผ์ ์ฉ์ผ๋ก, ๊ธฐ๋ณธ์ ์ธ ์ฐจ๋จ๋ง ํ๋ค.
|
| 444 |
+
"""
|
| 445 |
+
# ๊ธ์ง ํจํด(์ต์)
|
| 446 |
+
banned = [
|
| 447 |
+
"import os", "import sys", "subprocess", "socket", "shutil",
|
| 448 |
+
"open(", "__import__", "eval(", "exec(", "compile(", "globals(", "locals("
|
| 449 |
+
]
|
| 450 |
+
low = code.lower()
|
| 451 |
+
if any(b in low for b in banned):
|
| 452 |
+
return ""
|
| 453 |
|
| 454 |
+
# ์ ํ๋ builtins
|
| 455 |
+
safe_builtins = {
|
| 456 |
+
"abs": abs, "min": min, "max": max, "sum": sum, "len": len, "range": range,
|
| 457 |
+
"int": int, "float": float, "str": str, "print": print,
|
| 458 |
+
"math": math,
|
| 459 |
}
|
| 460 |
|
| 461 |
+
# stdout ์บก์ฒ
|
| 462 |
+
import contextlib
|
| 463 |
+
import sys
|
|
|
|
|
|
|
|
|
|
| 464 |
|
| 465 |
+
buf = io.StringIO()
|
| 466 |
+
glb = {"__builtins__": safe_builtins, "math": math}
|
| 467 |
+
loc = {}
|
| 468 |
|
| 469 |
+
try:
|
| 470 |
+
with contextlib.redirect_stdout(buf):
|
| 471 |
+
exec(code, glb, loc) # noqa: S102 (์๋๋ ์ ํ ์คํ)
|
| 472 |
+
except Exception:
|
| 473 |
+
return ""
|
| 474 |
|
| 475 |
+
out = buf.getvalue().strip()
|
| 476 |
+
|
| 477 |
+
# ์ถ๋ ฅ์ด ์ฌ๋ฌ ์ค์ด๋ฉด ๋ง์ง๋ง ์ค์ด ๋ณดํต ์ต์ข
์ถ๋ ฅ
|
| 478 |
+
if out:
|
| 479 |
+
last = out.splitlines()[-1].strip()
|
| 480 |
+
# ์ซ์๋ง ์๊ตฌ๋ ๋๊ฐ ๋ง์ผ๋ฏ๋ก ์ซ์๋ง ์์ผ๋ฉด ๊ทธ๊ฑธ ์ฐ์
|
| 481 |
+
if re.fullmatch(r"-?\d+(\.\d+)?", last):
|
| 482 |
+
return last
|
| 483 |
+
return last
|
| 484 |
+
|
| 485 |
+
# ์ถ๋ ฅ์ด ์์ผ๋ฉด ๋น๊ฐ
|
| 486 |
+
return ""
|
| 487 |
+
|
| 488 |
+
|
| 489 |
+
def download_bytes(url: str, timeout: int = 20) -> bytes:
|
| 490 |
+
"""
|
| 491 |
+
URL์์ ํ์ผ์ ๋ค์ด๋ก๋.
|
| 492 |
+
- GAIA ๊ณผ์ ์ ์ฒจ๋ถํ์ผ์ด '์ง๋ฌธ ํ
์คํธ์ URL๋ก ์ ๊ณต'๋๋ ๊ฒฝ์ฐ์๋ง ์ฌ์ฉ ๊ฐ๋ฅ.
|
| 493 |
+
"""
|
| 494 |
+
r = requests.get(url, timeout=timeout)
|
| 495 |
+
r.raise_for_status()
|
| 496 |
+
return r.content
|
| 497 |
+
|
| 498 |
+
|
| 499 |
+
def solve_excel_sum_if_url(urls: list[str]) -> str:
|
| 500 |
+
"""
|
| 501 |
+
Excel ๋ฌธ์ : URL์ด ์์ ๋๋ง ์ฒ๋ฆฌ ๊ฐ๋ฅ.
|
| 502 |
+
- pandas + openpyxl ํ์
|
| 503 |
+
- "food (not including drinks)" ๊ฐ์ ์กฐ๊ฑด์
|
| 504 |
+
์ํธ ์ปฌ๋ผ๋ช
์ ๋ด์ผ ํด์ ํ์ผ์ด ์์ด์ผ ํ๋ค.
|
| 505 |
+
"""
|
| 506 |
+
if pd is None:
|
| 507 |
+
return ""
|
| 508 |
+
xls_urls = [u for u in urls if re.search(r"\.(xlsx|xls)\b", u, flags=re.I)]
|
| 509 |
+
if not xls_urls:
|
| 510 |
+
return ""
|
| 511 |
|
| 512 |
+
try:
|
| 513 |
+
data = download_bytes(xls_urls[0])
|
| 514 |
+
df = pd.read_excel(io.BytesIO(data))
|
| 515 |
+
except Exception:
|
| 516 |
+
return ""
|
| 517 |
|
| 518 |
+
# ๊ฐ๋ฅํ ์ปฌ๋ผ ํ๋ณด๋ฅผ ์ถ์ :
|
| 519 |
+
# - GAIA ๊ณผ์ ๋ ๋ณดํต "item", "category", "type", "sales" ์ ์ฌ ์ปฌ๋ผ์ด ์๋ค.
|
| 520 |
+
cols = {c.lower(): c for c in df.columns}
|
| 521 |
+
|
| 522 |
+
# sales ์ปฌ๋ผ ์ถ์
|
| 523 |
+
sales_col = None
|
| 524 |
+
for key in ["sales", "total sales", "revenue", "amount", "price", "usd"]:
|
| 525 |
+
if key in cols:
|
| 526 |
+
sales_col = cols[key]
|
| 527 |
+
break
|
| 528 |
+
if sales_col is None:
|
| 529 |
+
# ์ซ์ํ ์ปฌ๋ผ ์ค ํ๋๋ฅผ ํํ๋ ํด๋ฐฑ
|
| 530 |
+
num_cols = [c for c in df.columns if pd.api.types.is_numeric_dtype(df[c])]
|
| 531 |
+
if num_cols:
|
| 532 |
+
sales_col = num_cols[-1]
|
| 533 |
+
|
| 534 |
+
if sales_col is None:
|
| 535 |
+
return ""
|
| 536 |
|
| 537 |
+
# drinks ์ ์ธ ์กฐ๊ฑด ์ฒ๋ฆฌ:
|
| 538 |
+
# - category/type/item ์ด์ด ์์ผ๋ฉด ๊ทธ์ค ํ๋๋ก ํํฐ๋ง ์๋
|
| 539 |
+
text_cols = [c for c in df.columns if df[c].dtype == "object"]
|
| 540 |
+
drink_keywords = ["drink", "beverage", "soda", "coffee", "tea", "juice"]
|
| 541 |
+
|
| 542 |
+
def row_is_drink(row: pd.Series) -> bool:
|
| 543 |
+
for c in text_cols:
|
| 544 |
+
v = str(row.get(c, "")).lower()
|
| 545 |
+
if any(k in v for k in drink_keywords):
|
| 546 |
+
return True
|
| 547 |
+
return False
|
| 548 |
+
|
| 549 |
+
try:
|
| 550 |
+
mask_drink = df.apply(row_is_drink, axis=1)
|
| 551 |
+
food_df = df[~mask_drink].copy()
|
| 552 |
+
total = float(food_df[sales_col].sum())
|
| 553 |
+
return f"{total:.2f}"
|
| 554 |
+
except Exception:
|
| 555 |
+
return ""
|
| 556 |
+
|
| 557 |
+
|
| 558 |
+
def solve_pdf_text_if_url(urls: list[str]) -> str:
|
| 559 |
+
"""
|
| 560 |
+
PDF๊ฐ URL๋ก ์ ๊ณต๋๋ ๊ฒฝ์ฐ ํ
์คํธ๋ฅผ ์ถ์ถํ๋ค.
|
| 561 |
+
- ์ถ์ถํ ํ
์คํธ๋ LLM์๊ฒ '์ปจํ
์คํธ'๋ก ์ ๊ณตํ์ฌ ํน์ ๊ฐ๋ง ๋ฝ๋๋ค.
|
| 562 |
+
"""
|
| 563 |
+
if fitz is None:
|
| 564 |
+
return ""
|
| 565 |
+
pdf_urls = [u for u in urls if re.search(r"\.pdf\b", u, flags=re.I)]
|
| 566 |
+
if not pdf_urls:
|
| 567 |
+
return ""
|
| 568 |
+
try:
|
| 569 |
+
data = download_bytes(pdf_urls[0])
|
| 570 |
+
doc = fitz.open(stream=data, filetype="pdf")
|
| 571 |
+
texts = []
|
| 572 |
+
for i in range(min(10, doc.page_count)): # ๋๋ฌด ๊ธธ๋ฉด ๋น์ฉ/์๊ฐ ์ฆ๊ฐ โ ์ 10ํ์ด์ง๋ง
|
| 573 |
+
texts.append(doc.load_page(i).get_text("text"))
|
| 574 |
+
return "\n\n".join(texts).strip()
|
| 575 |
+
except Exception:
|
| 576 |
+
return ""
|
| 577 |
+
|
| 578 |
+
|
| 579 |
+
def solve_youtube_question(question: str, urls: list[str]) -> str:
|
| 580 |
+
"""
|
| 581 |
+
YouTube ์ง๋ฌธ ์ฒ๋ฆฌ:
|
| 582 |
+
- transcript-api๊ฐ ๊ฐ๋ฅํ๋ฉด transcript๋ฅผ ๊ฐ์ ธ์จ๋ค.
|
| 583 |
+
- transcript๊ฐ ์์ผ๋ฉด DDG ๊ฒ์ ๊ฒฐ๊ณผ๋ก ํด๋ฐฑ.
|
| 584 |
+
- ์ต์ข
๋ต์ LLM์ด ์ปจํ
์คํธ์์ "์ ๋ต๋ง" ๋ฝ๊ฒ ํ๋ค.
|
| 585 |
+
"""
|
| 586 |
+
yt = None
|
| 587 |
+
for u in urls:
|
| 588 |
+
if "youtube.com/watch" in u:
|
| 589 |
+
yt = u
|
| 590 |
+
break
|
| 591 |
+
if not yt:
|
| 592 |
+
return ""
|
| 593 |
+
|
| 594 |
+
# video_id ์ถ์ถ
|
| 595 |
+
m = re.search(r"[?&]v=([^&]+)", yt)
|
| 596 |
+
if not m:
|
| 597 |
+
return ""
|
| 598 |
+
vid = m.group(1)
|
| 599 |
+
|
| 600 |
+
transcript_text = ""
|
| 601 |
+
if YouTubeTranscriptApi is not None:
|
| 602 |
+
try:
|
| 603 |
+
# ์์ด/์๋ ์์ฑ ์์ฌ ์์ ์ ์์ด fallback ์ธ์ด ํ์ฉ
|
| 604 |
+
tr = YouTubeTranscriptApi.get_transcript(vid, languages=["en", "en-US", "en-GB"])
|
| 605 |
+
transcript_text = "\n".join([x.get("text", "") for x in tr]).strip()
|
| 606 |
+
except Exception:
|
| 607 |
+
transcript_text = ""
|
| 608 |
+
|
| 609 |
+
# transcript ์์ผ๋ฉด ๊ฒ์ ์ปจํ
์คํธ๋ก ํด๋ฐฑ
|
| 610 |
+
if not transcript_text:
|
| 611 |
+
transcript_text = ddg_search(f"{yt} \"{question[:80]}\"", max_results=6)
|
| 612 |
+
|
| 613 |
+
if not transcript_text:
|
| 614 |
+
return ""
|
| 615 |
+
|
| 616 |
+
# LLM์๊ฒ "์ง๋ฌธ + transcript" ์ ๊ณต ํ ์ ๋ต๋ง ์ถ์ถ
|
| 617 |
+
prompt = f"""
|
| 618 |
+
{EXTRACTOR_RULES}
|
| 619 |
+
|
| 620 |
+
Question:
|
| 621 |
+
{question}
|
| 622 |
+
|
| 623 |
+
Context:
|
| 624 |
+
{transcript_text}
|
| 625 |
+
""".strip()
|
| 626 |
+
|
| 627 |
+
resp = LLM.invoke([SystemMessage(content=EXTRACTOR_RULES), HumanMessage(content=prompt)])
|
| 628 |
+
return clean_final_answer(resp.content)
|
| 629 |
+
|
| 630 |
+
|
| 631 |
+
# =========================================================
|
| 632 |
+
# 6) ์ผ๋ฐ ๊ฒ์ ๊ธฐ๋ฐ(์ฌ์ค ์ถ์ถ) ์๋ฒ
|
| 633 |
+
# =========================================================
|
| 634 |
+
def solve_with_search_and_llm(question: str) -> str:
|
| 635 |
+
"""
|
| 636 |
+
GENERAL_SEARCH / WIKI_COUNT / WIKI_META ๋ฑ์์ ๊ณตํต์ผ๋ก ์ฌ์ฉํ๋ ๋ฃจํธ:
|
| 637 |
+
1) DDG ๊ฒ์ ๊ฒฐ๊ณผ๋ฅผ ์ปจํ
์คํธ๋ก ๋ง๋ ๋ค.
|
| 638 |
+
2) LLM์ ์ปจํ
์คํธ์์ ์ ๋ต๋ง ์ถ์ถํ๋ค.
|
| 639 |
+
"""
|
| 640 |
+
# ์ฟผ๋ฆฌ ๊ตฌ์ฑ: GAIA๋ ์ํค/๊ณต์๋ฌธ์๊ฐ ๊ฐํ๋ฏ๋ก ๊ทธ๋ฐ ํํธ๋ฅผ ์๋๋ค.
|
| 641 |
+
queries = [
|
| 642 |
+
question,
|
| 643 |
+
f"{question} wikipedia",
|
| 644 |
+
f"{question} site:wikipedia.org",
|
| 645 |
+
]
|
| 646 |
+
|
| 647 |
+
contexts = []
|
| 648 |
+
for q in queries:
|
| 649 |
+
ctx = ddg_search(q, max_results=6)
|
| 650 |
+
if ctx:
|
| 651 |
+
contexts.append(ctx)
|
| 652 |
+
time.sleep(0.2) # ๊ณผ๋ํ ์์ฒญ ๋ฐฉ์ง
|
| 653 |
+
|
| 654 |
+
merged = "\n\n====\n\n".join(contexts).strip()
|
| 655 |
+
if not merged:
|
| 656 |
+
return ""
|
| 657 |
+
|
| 658 |
+
prompt = f"""
|
| 659 |
+
{EXTRACTOR_RULES}
|
| 660 |
+
|
| 661 |
+
Question:
|
| 662 |
+
{question}
|
| 663 |
+
|
| 664 |
+
Context:
|
| 665 |
+
{merged}
|
| 666 |
+
""".strip()
|
| 667 |
+
|
| 668 |
+
resp = LLM.invoke([SystemMessage(content=EXTRACTOR_RULES), HumanMessage(content=prompt)])
|
| 669 |
+
return clean_final_answer(resp.content)
|
| 670 |
+
|
| 671 |
+
|
| 672 |
+
# =========================================================
|
| 673 |
+
# 7) LangGraph ๋
ธ๋ ๊ตฌ์ฑ
|
| 674 |
+
# =========================================================
|
| 675 |
+
def node_init(state: AgentState) -> AgentState:
|
| 676 |
+
# steps ์ด๊ธฐํ
|
| 677 |
+
state["steps"] = int(state.get("steps", 0))
|
| 678 |
+
state["context"] = state.get("context", "")
|
| 679 |
+
state["answer"] = state.get("answer", "")
|
| 680 |
+
return state
|
| 681 |
+
|
| 682 |
+
|
| 683 |
+
def node_extract_urls(state: AgentState) -> AgentState:
|
| 684 |
+
state["urls"] = extract_urls(state["question"])
|
| 685 |
+
return state
|
| 686 |
+
|
| 687 |
+
|
| 688 |
+
def node_classify(state: AgentState) -> AgentState:
|
| 689 |
+
state["task_type"] = classify_task(state["question"])
|
| 690 |
+
return state
|
| 691 |
+
|
| 692 |
+
|
| 693 |
+
def node_solve(state: AgentState) -> AgentState:
|
| 694 |
+
"""
|
| 695 |
+
ํ์
๋ณ๋ก ๋ถ๊ธฐํด์ ํด๊ฒฐ.
|
| 696 |
+
- ์ฌ๊ธฐ์ answer๊ฐ ์ฑ์์ง๋ฉด END๋ก ๊ฐ๋ค.
|
| 697 |
+
"""
|
| 698 |
+
q = state["question"]
|
| 699 |
+
urls = state.get("urls", [])
|
| 700 |
+
t = state.get("task_type", "GENERAL_SEARCH")
|
| 701 |
+
|
| 702 |
+
# ๋ด๋ถ ์์ ์ฅ์น
|
| 703 |
+
state["steps"] = state.get("steps", 0) + 1
|
| 704 |
+
if state["steps"] > 12:
|
| 705 |
+
state["answer"] = state["answer"] or ""
|
| 706 |
+
return state
|
| 707 |
+
|
| 708 |
+
ans = ""
|
| 709 |
+
|
| 710 |
+
# 1) ์ฝ๋๋ก ์ง์ ํธ๋ ๊ฒ๋ถํฐ ์ฐ์ ์ฒ๋ฆฌ(์ ๋ต๋ฅ ํฌ๊ฒ ์์น)
|
| 711 |
+
if t == "REVERSE_TEXT":
|
| 712 |
+
ans = solve_reverse_text(q)
|
| 713 |
+
|
| 714 |
+
elif t == "NON_COMMUTATIVE_TABLE":
|
| 715 |
+
ans = parse_operation_table_and_find_counterexample(q)
|
| 716 |
+
|
| 717 |
+
elif t == "BOTANY_VEGETABLES":
|
| 718 |
+
ans = solve_botany_vegetables(q)
|
| 719 |
+
|
| 720 |
+
# 2) URL ๊ธฐ๋ฐ ๋ฉํฐ๋ชจ๋ฌ
|
| 721 |
+
elif t == "YOUTUBE":
|
| 722 |
+
ans = solve_youtube_question(q, urls)
|
| 723 |
+
|
| 724 |
+
elif t == "EXCEL_SUM":
|
| 725 |
+
# ์์
์ URL์ด ์์ ๋๋ง ๊ฐ๋ฅ
|
| 726 |
+
ans = solve_excel_sum_if_url(urls)
|
| 727 |
+
if not ans:
|
| 728 |
+
# URL์ด ์์ผ๋ฉด ๊ฒ์ ๊ธฐ๋ฐ์ผ๋ก๋ผ๋ ์๋(๊ฐ๋ ํ๊ฐ ์น์ ์์ ์ ์์)
|
| 729 |
+
ans = solve_with_search_and_llm(q)
|
| 730 |
+
|
| 731 |
+
elif t == "AUDIO_TRANSCRIBE":
|
| 732 |
+
# ์ค๋์ค๋ URL์ด ์์ผ๋ฉด ์ฒ๋ฆฌ ๊ฐ๋ฅํ์ง๋ง,
|
| 733 |
+
# ์ฌ๊ธฐ์๋ OpenAI audio transcription์ ๋ณ๋ ๊ตฌํํ์ง ์๋๋ค.
|
| 734 |
+
# (GAIA ๊ณผ์ ์์ ์ง๋ฌธ์ ์ค์ mp3 URL์ด ์ ๊ณต๋๋ ๊ฒฝ์ฐ๋ง ์๋ฏธ๊ฐ ์์)
|
| 735 |
+
# โ ํ์ค์ ์ฑ๋ฅ: URL์ด ์์ผ๋ฉด ๋ถ๊ฐ๋ฅ / URL์ด ์์ผ๋ฉด ๊ฒ์์ผ๋ก ๊ฐ์ ํด๊ฒฐ ์๋
|
| 736 |
+
ans = solve_with_search_and_llm(q)
|
| 737 |
+
|
| 738 |
+
elif t == "CHESS_IMAGE":
|
| 739 |
+
# ์ด๋ฏธ์ง URL์ด ์์ผ๋ฉด GPT-4o-mini ๋น์ ์ผ๋ก ์ฝ๊ณ ๋ต์ ๋ฝ๋ ๋ฃจํธ๊ฐ ๊ฐ๋ฅํ์ง๋ง,
|
| 740 |
+
# ์ง๋ฌธ ํ
์คํธ์ ์ด๋ฏธ์ง URL์ด ์์ผ๋ฉด ๋ถ๊ฐ๋ฅ.
|
| 741 |
+
# ์ฌ๊ธฐ์๋ URL์ด ์์ผ๋ฉด "vision ์ปจํ
์คํธ"๋ก ๋ณด๋ด๋ ์ต์ ๊ตฌํ๋ง ํ๋ค.
|
| 742 |
+
img_urls = [u for u in urls if re.search(r"\.(png|jpg|jpeg|webp)\b", u, flags=re.I)]
|
| 743 |
+
if img_urls:
|
| 744 |
+
# LangChain ChatOpenAI ๋ฉํฐ๋ชจ๋ฌ: content๋ฅผ dict ๋ธ๋ก์ผ๋ก ์ ๋ฌ ๊ฐ๋ฅ
|
| 745 |
+
# (ํ๊ฒฝ์ ๋ฐ๋ผ ์ ํ๋ ์ ์์ด try/except๋ก ๋ณดํธ)
|
| 746 |
+
try:
|
| 747 |
+
msg = HumanMessage(
|
| 748 |
+
content=[
|
| 749 |
+
{"type": "text", "text": EXTRACTOR_RULES + "\n\n" + q},
|
| 750 |
+
{"type": "image_url", "image_url": {"url": img_urls[0]}},
|
| 751 |
+
]
|
| 752 |
+
)
|
| 753 |
+
resp = LLM.invoke([msg])
|
| 754 |
+
ans = clean_final_answer(resp.content)
|
| 755 |
+
except Exception:
|
| 756 |
+
ans = solve_with_search_and_llm(q)
|
| 757 |
+
else:
|
| 758 |
+
ans = solve_with_search_and_llm(q)
|
| 759 |
+
|
| 760 |
+
elif t == "PYTHON_OUTPUT":
|
| 761 |
+
# ์ง๋ฌธ ๋ณธ๋ฌธ์ ์ฝ๋๋ธ๋ก์ด ์์ผ๋ฉด ์ง์ ์คํ
|
| 762 |
+
m = re.search(r"```python\s*(.*?)```", q, flags=re.S | re.I)
|
| 763 |
+
if not m:
|
| 764 |
+
m = re.search(r"```\s*(.*?)```", q, flags=re.S)
|
| 765 |
+
if m:
|
| 766 |
+
code = m.group(1).strip()
|
| 767 |
+
ans = safe_exec_python_and_capture_output(code)
|
| 768 |
+
if not ans:
|
| 769 |
+
# ์ฝ๋๊ฐ ์ฒจ๋ถํ์ผ์ธ๋ฐ URL์ด ์์ผ๋ฉด ๋ถ๊ฐ โ ๊ฒ์์ผ๋ก ํด๋ฐฑ
|
| 770 |
+
ans = solve_with_search_and_llm(q)
|
| 771 |
+
|
| 772 |
+
else:
|
| 773 |
+
# 3) ๋๋จธ์ง(๋๋ถ๋ถ ์ฌ์ค ์ถ์ถ)๋ ๊ฒ์+LLM ์ถ์ถ๊ธฐ
|
| 774 |
+
ans = solve_with_search_and_llm(q)
|
| 775 |
+
|
| 776 |
+
state["answer"] = clean_final_answer(ans)
|
| 777 |
+
return state
|
| 778 |
+
|
| 779 |
+
|
| 780 |
+
def node_finalize(state: AgentState) -> AgentState:
|
| 781 |
+
"""
|
| 782 |
+
์ต์ข
์ ๋ต์ GAIA ์๊ตฌ(์ ๋ต๋ง) ํํ๋ก ๊ฐ์ ํ๋ค.
|
| 783 |
+
"""
|
| 784 |
+
state["answer"] = clean_final_answer(state.get("answer", ""))
|
| 785 |
+
return state
|
| 786 |
+
|
| 787 |
+
|
| 788 |
+
def should_end(state: AgentState) -> str:
|
| 789 |
+
"""
|
| 790 |
+
answer๊ฐ ๋น์ด์์ง ์์ผ๋ฉด ์ข
๋ฃ.
|
| 791 |
+
๋น์ด์์ผ๋ฉด(์คํจ) ๊ทธ๋๋ ์ข
๋ฃ(์ฐ๋ ๊ธฐ ๋ต์ ๊ธธ๊ฒ ์์ฑํ๋ ๊ฒ๋ณด๋ค ๋ซ๋ค).
|
| 792 |
+
"""
|
| 793 |
+
return END
|
| 794 |
+
|
| 795 |
+
|
| 796 |
+
def build_graph():
|
| 797 |
+
"""
|
| 798 |
+
LangGraph StateGraph ๊ตฌ์ฑ:
|
| 799 |
+
START -> init -> urls -> classify -> solve -> finalize -> END
|
| 800 |
+
"""
|
| 801 |
+
g = StateGraph(AgentState)
|
| 802 |
+
|
| 803 |
+
g.add_node("init", node_init)
|
| 804 |
+
g.add_node("urls", node_extract_urls)
|
| 805 |
+
g.add_node("classify", node_classify)
|
| 806 |
+
g.add_node("solve", node_solve)
|
| 807 |
+
g.add_node("finalize", node_finalize)
|
| 808 |
+
|
| 809 |
+
g.add_edge(START, "init")
|
| 810 |
+
g.add_edge("init", "urls")
|
| 811 |
+
g.add_edge("urls", "classify")
|
| 812 |
+
g.add_edge("classify", "solve")
|
| 813 |
+
g.add_edge("solve", "finalize")
|
| 814 |
+
g.add_edge("finalize", END)
|
| 815 |
|
| 816 |
return g.compile()
|
| 817 |
|
| 818 |
|
| 819 |
+
GRAPH = build_graph()
|
| 820 |
+
|
| 821 |
+
|
| 822 |
+
# =========================================================
|
| 823 |
+
# 8) Public API: BasicAgent
|
| 824 |
+
# - app.py๋ ์ด ํด๋์ค๋ฅผ importํด์ question_text๋ง ๋๊ธด๋ค.
|
| 825 |
+
# =========================================================
|
| 826 |
class BasicAgent:
|
| 827 |
def __init__(self):
|
| 828 |
+
# ๊ทธ๋ํ๋ ๋ชจ๋ ๋ก๋ ์ ์ปดํ์ผ๋จ. ์ฌ๊ธฐ์๋ ์ํ๋ง ์๋ฆผ.
|
| 829 |
+
print("โ
GAIA Agent initialized (LangGraph StateGraph)")
|
| 830 |
|
| 831 |
def __call__(self, question: str) -> str:
|
| 832 |
+
"""
|
| 833 |
+
app.py์์ question_text๋ง ๋ฐ๋ ๊ณ ์ ์ธํฐํ์ด์ค.
|
| 834 |
+
- recursion_limit์ invoke config์์ ์ ํ (LangGraph ๋ฒ์ ํธํ)
|
| 835 |
+
"""
|
| 836 |
+
state: AgentState = {
|
| 837 |
+
"question": question,
|
| 838 |
+
"task_type": "",
|
| 839 |
+
"urls": [],
|
| 840 |
+
"context": "",
|
| 841 |
+
"answer": "",
|
| 842 |
+
"steps": 0,
|
| 843 |
+
}
|
| 844 |
+
|
| 845 |
+
# LangGraph ๋ฌดํ๋ฃจํ/์ฌ๊ท ๋ณดํธ
|
| 846 |
+
out = GRAPH.invoke(state, config={"recursion_limit": 12})
|
| 847 |
+
return clean_final_answer(out.get("answer", ""))
|
app.py
CHANGED
|
@@ -74,7 +74,7 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
|
|
| 74 |
print(f"Skipping item with missing task_id or question: {item}")
|
| 75 |
continue
|
| 76 |
try:
|
| 77 |
-
submitted_answer = agent(question_text)
|
| 78 |
answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
|
| 79 |
results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
|
| 80 |
except Exception as e:
|
|
|
|
| 74 |
print(f"Skipping item with missing task_id or question: {item}")
|
| 75 |
continue
|
| 76 |
try:
|
| 77 |
+
submitted_answer = agent(question_text, task_id=task_id, api_url=api_url)
|
| 78 |
answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
|
| 79 |
results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
|
| 80 |
except Exception as e:
|
requirements.txt
CHANGED
|
@@ -1,8 +1,15 @@
|
|
| 1 |
gradio
|
| 2 |
requests
|
|
|
|
|
|
|
|
|
|
| 3 |
langgraph
|
| 4 |
-
langchain
|
| 5 |
-
langchain-
|
| 6 |
-
|
|
|
|
| 7 |
duckduckgo-search
|
| 8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
gradio
|
| 2 |
requests
|
| 3 |
+
pandas
|
| 4 |
+
openpyxl
|
| 5 |
+
|
| 6 |
langgraph
|
| 7 |
+
langchain-openai
|
| 8 |
+
langchain-core
|
| 9 |
+
|
| 10 |
+
ddgs
|
| 11 |
duckduckgo-search
|
| 12 |
+
youtube-transcript-api
|
| 13 |
+
|
| 14 |
+
pymupdf
|
| 15 |
+
python-chess
|