Spaces:
Sleeping
Sleeping
Update agent.py
Browse files
agent.py
CHANGED
|
@@ -157,27 +157,37 @@ Produce the exact correct answer β nothing more, nothing less.
|
|
| 157 |
- Use web_search for recent events, specific articles, prices, or anything time-sensitive.
|
| 158 |
- Use fetch_page when a URL is provided or a search result points to a relevant page.
|
| 159 |
- Use run_python for any arithmetic, counting, sorting, or data transformation.
|
| 160 |
-
- Use run_python with pandas for Excel/CSV questions.
|
| 161 |
-
|
| 162 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
- Use reverse_text only when asked to reverse a string.
|
| 164 |
- You may use up to 5 tool calls. Stop as soon as you have a confident answer.
|
| 165 |
-
- After 3 tool calls with no clear answer, stop
|
| 166 |
- When writing Python code, keep scripts under 50 lines. Never paste raw page content into a script.
|
| 167 |
|
| 168 |
## Answer format rules
|
| 169 |
1. Output the raw value only β no explanation, no preamble.
|
| 170 |
-
2. If asked for a first name, output
|
| 171 |
-
3. If asked for a surname, output
|
| 172 |
4. Numbers: digits only unless units were explicitly requested.
|
| 173 |
-
5. Lists: comma-separated on one line.
|
| 174 |
-
6.
|
| 175 |
-
7.
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
-
|
| 179 |
-
-
|
| 180 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 181 |
|
| 182 |
## Required final line
|
| 183 |
Always end your response with exactly:
|
|
@@ -208,7 +218,40 @@ def maybe_answer_direct(question: str) -> str | None:
|
|
| 208 |
|
| 209 |
|
| 210 |
# ==========================================================
|
| 211 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 212 |
# ==========================================================
|
| 213 |
|
| 214 |
def invoke(messages: list) -> object:
|
|
@@ -219,6 +262,7 @@ def invoke(messages: list) -> object:
|
|
| 219 |
|
| 220 |
seen: set[str] = set()
|
| 221 |
first = True
|
|
|
|
| 222 |
|
| 223 |
for model in FALLBACK_CHAIN:
|
| 224 |
key = model.model_name
|
|
@@ -231,11 +275,33 @@ def invoke(messages: list) -> object:
|
|
| 231 |
if not first:
|
| 232 |
LAST_MODEL_FALLBACK = "Yes"
|
| 233 |
first = False
|
| 234 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 235 |
except Exception as e:
|
| 236 |
LAST_MODEL_ERROR = str(e)
|
| 237 |
continue
|
| 238 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 239 |
raise RuntimeError(f"All models failed. Last error: {LAST_MODEL_ERROR}")
|
| 240 |
|
| 241 |
# ==========================================================
|
|
|
|
| 157 |
- Use web_search for recent events, specific articles, prices, or anything time-sensitive.
|
| 158 |
- Use fetch_page when a URL is provided or a search result points to a relevant page.
|
| 159 |
- Use run_python for any arithmetic, counting, sorting, or data transformation.
|
| 160 |
+
- Use run_python with pandas for Excel/CSV questions. When the question contains
|
| 161 |
+
[ATTACHED FILE DATA], that IS the file content as CSV text. Parse it with:
|
| 162 |
+
import pandas as pd, io
|
| 163 |
+
data = '<copy the CSV lines from the question here>'
|
| 164 |
+
df = pd.read_csv(io.StringIO(data))
|
| 165 |
+
Then compute and print() the answer.
|
| 166 |
+
- If the question says [ATTACHED FILE CONTENT], that is Python code. Run it with run_python directly
|
| 167 |
+
β copy the code exactly as given into the run_python tool and print the final output.
|
| 168 |
- Use reverse_text only when asked to reverse a string.
|
| 169 |
- You may use up to 5 tool calls. Stop as soon as you have a confident answer.
|
| 170 |
+
- After 3 tool calls with no clear answer, stop and output your best guess as FINAL ANSWER.
|
| 171 |
- When writing Python code, keep scripts under 50 lines. Never paste raw page content into a script.
|
| 172 |
|
| 173 |
## Answer format rules
|
| 174 |
1. Output the raw value only β no explanation, no preamble.
|
| 175 |
+
2. If asked for a first name, output ONLY the first/given name β not the full name, not the surname.
|
| 176 |
+
3. If asked for a surname or last name, output ONLY the family name β not the full name.
|
| 177 |
4. Numbers: digits only unless units were explicitly requested.
|
| 178 |
+
5. Lists: comma-separated on one line, no extra spaces after commas unless the question uses them.
|
| 179 |
+
6. For subset/set questions (e.g. "subset of S involving..."), output only the elements, comma-separated.
|
| 180 |
+
7. If you cannot find the answer after searching, output: N/A
|
| 181 |
+
8. For discography questions: count studio albums only. Live albums, compilations, box sets do not count.
|
| 182 |
+
9. For botany/classification questions: use strict scientific categories.
|
| 183 |
+
- Fruits (botanical): tomato, pepper, cucumber, avocado, squash, beans, corn kernels.
|
| 184 |
+
- Vegetables (botanical): true vegetables are leaves (lettuce, spinach), stems (celery), roots
|
| 185 |
+
(carrot, sweet potato), bulbs (onion), or flowers (broccoli, cauliflower).
|
| 186 |
+
- Do NOT confuse culinary and botanical definitions. A tomato is a fruit botanically.
|
| 187 |
+
10. Never abbreviate. Always write full words:
|
| 188 |
+
- City/place names in full: "Ho Chi Minh City" not "HCMC", "Saint Petersburg" not "St. Petersburg"
|
| 189 |
+
- Country names in full: "United States" not "US" or "USA", "United Kingdom" not "UK"
|
| 190 |
+
- Exception: only use an abbreviation if the question itself uses it or explicitly asks for it.
|
| 191 |
|
| 192 |
## Required final line
|
| 193 |
Always end your response with exactly:
|
|
|
|
| 218 |
|
| 219 |
|
| 220 |
# ==========================================================
|
| 221 |
+
# ANSWER QUALITY CHECK β used by invoke to decide fallback
|
| 222 |
+
# ==========================================================
|
| 223 |
+
|
| 224 |
+
def _answer_looks_weak(result) -> bool:
|
| 225 |
+
"""
|
| 226 |
+
Return True if the model's response content does not contain a usable
|
| 227 |
+
FINAL ANSWER β meaning we should try the next model in the chain.
|
| 228 |
+
Only applies when the model made NO tool calls (pure text response).
|
| 229 |
+
If tool calls are present, we let the graph continue normally.
|
| 230 |
+
"""
|
| 231 |
+
# If the model wants to call tools, don't short-circuit β let the graph run
|
| 232 |
+
tool_calls = getattr(result, "tool_calls", None)
|
| 233 |
+
if tool_calls:
|
| 234 |
+
return False
|
| 235 |
+
|
| 236 |
+
content = getattr(result, "content", "") or ""
|
| 237 |
+
if not isinstance(content, str):
|
| 238 |
+
return False
|
| 239 |
+
|
| 240 |
+
# Check if a FINAL ANSWER line is present and non-empty
|
| 241 |
+
match = re.search(r"FINAL ANSWER:\s*(.+)", content, re.I | re.S)
|
| 242 |
+
if not match:
|
| 243 |
+
return True
|
| 244 |
+
|
| 245 |
+
answer = match.group(1).strip()
|
| 246 |
+
# Treat explicit N/A or empty as weak
|
| 247 |
+
if not answer or answer.lower() in ("n/a", "none", "unknown", ""):
|
| 248 |
+
return True
|
| 249 |
+
|
| 250 |
+
return False
|
| 251 |
+
|
| 252 |
+
|
| 253 |
+
# ==========================================================
|
| 254 |
+
# INVOKE β exception fallback + content-quality fallback
|
| 255 |
# ==========================================================
|
| 256 |
|
| 257 |
def invoke(messages: list) -> object:
|
|
|
|
| 262 |
|
| 263 |
seen: set[str] = set()
|
| 264 |
first = True
|
| 265 |
+
last_result = None
|
| 266 |
|
| 267 |
for model in FALLBACK_CHAIN:
|
| 268 |
key = model.model_name
|
|
|
|
| 275 |
if not first:
|
| 276 |
LAST_MODEL_FALLBACK = "Yes"
|
| 277 |
first = False
|
| 278 |
+
|
| 279 |
+
result = model.bind_tools(TOOLS).invoke(messages)
|
| 280 |
+
last_result = result
|
| 281 |
+
|
| 282 |
+
# If the model wants tool calls, return immediately β
|
| 283 |
+
# the graph will handle the tool execution and loop back
|
| 284 |
+
tool_calls = getattr(result, "tool_calls", None)
|
| 285 |
+
if tool_calls:
|
| 286 |
+
return result
|
| 287 |
+
|
| 288 |
+
# No tool calls β check if the answer is actually useful
|
| 289 |
+
if not _answer_looks_weak(result):
|
| 290 |
+
return result
|
| 291 |
+
|
| 292 |
+
# Answer is weak (N/A or missing FINAL ANSWER) β try next model
|
| 293 |
+
LAST_MODEL_FALLBACK = "Yes"
|
| 294 |
+
LAST_MODEL_ERROR = f"weak answer from {key}"
|
| 295 |
+
continue
|
| 296 |
+
|
| 297 |
except Exception as e:
|
| 298 |
LAST_MODEL_ERROR = str(e)
|
| 299 |
continue
|
| 300 |
|
| 301 |
+
# All models tried β return whatever the last one gave us
|
| 302 |
+
if last_result is not None:
|
| 303 |
+
return last_result
|
| 304 |
+
|
| 305 |
raise RuntimeError(f"All models failed. Last error: {LAST_MODEL_ERROR}")
|
| 306 |
|
| 307 |
# ==========================================================
|