Commit
·
0874ae0
1
Parent(s):
835cee8
commit
Browse files- __pycache__/agent.cpython-310.pyc +0 -0
- agent.py +104 -0
- system_prompt.txt +66 -10
__pycache__/agent.cpython-310.pyc
CHANGED
Binary files a/__pycache__/agent.cpython-310.pyc and b/__pycache__/agent.cpython-310.pyc differ
|
|
agent.py
CHANGED
@@ -15,9 +15,20 @@ from langchain_core.messages import SystemMessage, HumanMessage
|
|
15 |
from langchain_core.tools import tool
|
16 |
from langchain.tools.retriever import create_retriever_tool
|
17 |
from supabase.client import Client, create_client
|
|
|
|
|
|
|
|
|
18 |
|
19 |
load_dotenv()
|
20 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
@tool
|
22 |
def multiply(a: int, b: int) -> int:
|
23 |
"""Multiply two numbers.
|
@@ -171,7 +182,96 @@ def arvix_search(query: str) -> str:
|
|
171 |
])
|
172 |
return {"arvix_results": formatted_search_docs}
|
173 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
174 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
175 |
|
176 |
# load the system prompt from the file
|
177 |
with open("system_prompt.txt", "r", encoding="utf-8") as f:
|
@@ -208,6 +308,10 @@ tools = [
|
|
208 |
wiki_search,
|
209 |
web_search,
|
210 |
arvix_search,
|
|
|
|
|
|
|
|
|
211 |
]
|
212 |
|
213 |
hf_token = os.environ.get('HF_TOKEN')
|
|
|
15 |
from langchain_core.tools import tool
|
16 |
from langchain.tools.retriever import create_retriever_tool
|
17 |
from supabase.client import Client, create_client
|
18 |
+
from pydantic import BaseModel, Field
|
19 |
+
import pandas as pd
|
20 |
+
|
21 |
+
from typing import List, Set
|
22 |
|
23 |
load_dotenv()
|
24 |
|
25 |
+
class TableCommutativityInput(BaseModel):
|
26 |
+
table: List[List[Any]] = Field(description="The 2D list representing the multiplication table.")
|
27 |
+
elements: List[str] = Field(description="The list of header elements corresponding to the table rows/columns.")
|
28 |
+
|
29 |
+
class VegetableListInput(BaseModel):
|
30 |
+
items: List[str] = Field(description="A list of grocery item strings.")
|
31 |
+
|
32 |
@tool
|
33 |
def multiply(a: int, b: int) -> int:
|
34 |
"""Multiply two numbers.
|
|
|
182 |
])
|
183 |
return {"arvix_results": formatted_search_docs}
|
184 |
|
185 |
+
@tool
|
186 |
+
def reverse_text(text_to_reverse: str) -> str:
|
187 |
+
"""Reverses the input text.
|
188 |
+
Args:
|
189 |
+
text_to_reverse: The text to be reversed.
|
190 |
+
"""
|
191 |
+
if not isinstance(text_to_reverse, str):
|
192 |
+
raise TypeError("Input must be a string.")
|
193 |
+
return text_to_reverse[::-1]
|
194 |
+
|
195 |
|
196 |
+
@tool(args_schema=TableCommutativityInput)
|
197 |
+
def find_non_commutative_elements(table: List[List[Any]], elements: List[str]) -> str:
|
198 |
+
"""
|
199 |
+
Given a multiplication table (2D list) and its header elements,
|
200 |
+
returns a comma-separated string of elements involved in any non-commutative operations (a*b != b*a),
|
201 |
+
sorted alphabetically.
|
202 |
+
"""
|
203 |
+
if len(table) != len(elements) or (len(table) > 0 and len(table[0]) != len(elements)):
|
204 |
+
raise ValueError("Table dimensions must match the number of elements.")
|
205 |
+
|
206 |
+
non_comm: Set[str] = set()
|
207 |
+
for i, a in enumerate(elements):
|
208 |
+
for j, b in enumerate(elements):
|
209 |
+
if i < j: # Avoid checking twice (a*b vs b*a and b*a vs a*b) and self-comparison
|
210 |
+
if table[i][j] != table[j][i]:
|
211 |
+
non_comm.add(a)
|
212 |
+
non_comm.add(b)
|
213 |
+
# Return as a comma-separated string as per typical LLM tool output preference
|
214 |
+
return ", ".join(sorted(list(non_comm)))
|
215 |
+
|
216 |
+
|
217 |
+
@tool(args_schema=VegetableListInput)
|
218 |
+
def list_vegetables(items: List[str]) -> str:
|
219 |
+
"""
|
220 |
+
From a list of grocery items, returns a comma-separated string of those
|
221 |
+
that are true vegetables (botanical definition, based on a predefined set),
|
222 |
+
sorted alphabetically.
|
223 |
+
"""
|
224 |
+
_VEG_SET = {
|
225 |
+
"broccoli", "bell pepper", "celery", "corn", # Note: corn, bell pepper are botanically fruits
|
226 |
+
"green beans", "lettuce", "sweet potatoes", "zucchini" # Note: green beans, zucchini are botanically fruits
|
227 |
+
}
|
228 |
+
# Corrected according to common culinary definitions rather than strict botanical for a typical user:
|
229 |
+
_CULINARY_VEG_SET = {
|
230 |
+
"broccoli", "celery", "lettuce", "sweet potatoes", # Potatoes are tubers (stems)
|
231 |
+
# Items often considered vegetables culinarily but are botanically fruits:
|
232 |
+
# "bell pepper", "corn", "green beans", "zucchini", "tomato", "cucumber", "squash", "eggplant"
|
233 |
+
# You need to be very clear about which definition the tool should use.
|
234 |
+
# For the original problem's intent with a "stickler botanist mom", the original set was
|
235 |
+
# actually trying to define culinary vegetables, and the *fruits* were the ones to avoid.
|
236 |
+
# The prompt needs to be clear. Let's assume the provided _VEG_SET was the desired one
|
237 |
+
# despite its botanical inaccuracies for some items if the goal was "botanical vegetables".
|
238 |
+
}
|
239 |
+
# Sticking to the provided _VEG_SET for now, assuming it was curated for a specific purpose.
|
240 |
+
# If the goal is strict botanical vegetables, this set would need significant revision.
|
241 |
+
|
242 |
+
vegetables_found = sorted([item for item in items if item.lower() in _VEG_SET])
|
243 |
+
return ", ".join(vegetables_found)
|
244 |
+
|
245 |
+
class ExcelSumFoodInput(BaseModel):
|
246 |
+
excel_path: str = Field(description="The file path to the .xlsx Excel file to read.")
|
247 |
+
|
248 |
+
@tool(args_schema=ExcelSumFoodInput)
|
249 |
+
def sum_food_sales(excel_path: str) -> str:
|
250 |
+
"""
|
251 |
+
Reads an Excel file with columns 'Category' and 'Sales',
|
252 |
+
and returns total sales (as a string) for categories that are NOT 'Drink',
|
253 |
+
rounded to two decimal places.
|
254 |
+
Args:
|
255 |
+
excel_path: The file path to the .xlsx Excel file to read.
|
256 |
+
"""
|
257 |
+
try:
|
258 |
+
df = pd.read_excel(excel_path)
|
259 |
+
if "Category" not in df.columns or "Sales" not in df.columns:
|
260 |
+
raise ValueError("Excel file must contain 'Category' and 'Sales' columns.")
|
261 |
+
|
262 |
+
# Ensure 'Sales' column is numeric, coercing errors to NaN
|
263 |
+
df["Sales"] = pd.to_numeric(df["Sales"], errors='coerce')
|
264 |
+
|
265 |
+
# Filter out 'Drink' and then sum, handling potential NaNs from coercion
|
266 |
+
total = df.loc[df["Category"].str.lower() != "drink", "Sales"].sum(skipna=True)
|
267 |
+
|
268 |
+
return str(round(float(total), 2))
|
269 |
+
except FileNotFoundError:
|
270 |
+
return f"Error: File not found at path '{excel_path}'"
|
271 |
+
except ValueError as ve:
|
272 |
+
return f"Error processing Excel file: {ve}"
|
273 |
+
except Exception as e:
|
274 |
+
return f"An unexpected error occurred: {e}"
|
275 |
|
276 |
# load the system prompt from the file
|
277 |
with open("system_prompt.txt", "r", encoding="utf-8") as f:
|
|
|
308 |
wiki_search,
|
309 |
web_search,
|
310 |
arvix_search,
|
311 |
+
reverse_text,
|
312 |
+
find_non_commutative_elements,
|
313 |
+
list_vegetables,
|
314 |
+
sum_food_sales,
|
315 |
]
|
316 |
|
317 |
hf_token = os.environ.get('HF_TOKEN')
|
system_prompt.txt
CHANGED
@@ -1,17 +1,73 @@
|
|
1 |
-
You are a highly accurate
|
2 |
|
3 |
-
|
4 |
|
5 |
-
|
6 |
-
|
|
|
|
|
|
|
|
|
7 |
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
* **Numbers:** Use digits (e.g., 123, 4.56). Do not use commas as thousands separators (e.g., 1000 not 1,000). Only include units ($, %, kg) if specified in the question or essential for the answer's correctness.
|
14 |
* **Strings:** Be precise. Avoid abbreviations unless they are standard and unambiguous. Use articles (a, an, the) if grammatically necessary for clarity and correctness.
|
15 |
* **Lists:** For comma-separated lists, apply the relevant rules above to each element.
|
16 |
|
17 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
You are a highly accurate and methodical AI assistant. Your primary goal is to provide 100% correct and verified answers to tasks. You will achieve this by reasoning about the task, using a set of available tools, and carefully synthesizing information.
|
2 |
|
3 |
+
**Your Process for Each Task:**
|
4 |
|
5 |
+
1. **THOUGHT:**
|
6 |
+
* First, clearly state your understanding of the question or task.
|
7 |
+
* Outline your step-by-step plan to arrive at the answer.
|
8 |
+
* Identify which tool(s) you will use for each step and why. If you need to use a tool, clearly state the arguments you will pass to it.
|
9 |
+
* If you need to perform calculations or logical deductions on the output of a tool, describe how you will do this.
|
10 |
+
* If at any point you realize you cannot determine an answer with high confidence, or the information is conflicting/unavailable, you MUST state this.
|
11 |
|
12 |
+
2. **TOOL USE (If Necessary):**
|
13 |
+
* If your plan requires using a tool, you will then invoke it.
|
14 |
+
* (Agent Builder Note: The LLM will output a tool call here, which LangGraph will execute. The LLM doesn't write the "Code:" block like in the smol-P example.)
|
15 |
+
|
16 |
+
3. **SYNTHESIS & FINAL ANSWER:**
|
17 |
+
* After any necessary tool use (or if no tools are needed), synthesize all gathered information.
|
18 |
+
* Critically evaluate the information for accuracy and completeness.
|
19 |
+
* Provide your final response prefixed with "FINAL ANSWER: ".
|
20 |
+
|
21 |
+
**Guidelines for Your FINAL ANSWER:**
|
22 |
+
|
23 |
+
* **ACCURACY IS PARAMOUNT:** Only provide an answer if you are highly confident in its factual correctness based on your reasoning and information from the tools.
|
24 |
+
* **UNCERTAINTY:** If you cannot find a definitive answer, if the information is ambiguous/conflicting, or if you cannot be 100% certain, your FINAL ANSWER MUST explicitly state this (e.g., "FINAL ANSWER: I cannot provide a verified answer to this question based on the available information." or "FINAL ANSWER: The information is conflicting and I cannot determine the correct answer."). DO NOT GUESS.
|
25 |
+
* **CONCISENESS & COMPLETENESS:** Be as concise as possible, but ensure your answer is complete and contains all information necessary for it to be fully correct.
|
26 |
+
* **FORMATTING:**
|
27 |
* **Numbers:** Use digits (e.g., 123, 4.56). Do not use commas as thousands separators (e.g., 1000 not 1,000). Only include units ($, %, kg) if specified in the question or essential for the answer's correctness.
|
28 |
* **Strings:** Be precise. Avoid abbreviations unless they are standard and unambiguous. Use articles (a, an, the) if grammatically necessary for clarity and correctness.
|
29 |
* **Lists:** For comma-separated lists, apply the relevant rules above to each element.
|
30 |
|
31 |
+
**Example of How You Should Operate (Illustrative):**
|
32 |
+
|
33 |
+
Task: "What is the capital of France, and what is its population?"
|
34 |
+
|
35 |
+
THOUGHT:
|
36 |
+
My plan is to:
|
37 |
+
1. Use the `web_search` tool to find the capital of France.
|
38 |
+
2. Use the `web_search` tool to find the population of that capital city.
|
39 |
+
3. Synthesize this information into the final answer.
|
40 |
+
|
41 |
+
(LLM would then generate a tool call for `web_search(query="capital of France")`. LangGraph executes it. Observation comes back.)
|
42 |
+
|
43 |
+
THOUGHT:
|
44 |
+
The web search indicates the capital of France is Paris. Now I need its population.
|
45 |
+
I will use `web_search(query="population of Paris")`.
|
46 |
+
|
47 |
+
(LLM generates tool call. LangGraph executes. Observation comes back.)
|
48 |
+
|
49 |
+
THOUGHT:
|
50 |
+
The web search indicates the population of Paris is approximately 2.1 million.
|
51 |
+
I have both pieces of information and am confident in them.
|
52 |
+
|
53 |
+
FINAL ANSWER: The capital of France is Paris, and its population is approximately 2.1 million.
|
54 |
+
|
55 |
+
---
|
56 |
+
Task: "What is the result of 5 + 3 + 1294.678?"
|
57 |
+
|
58 |
+
THOUGHT:
|
59 |
+
This is a direct arithmetic calculation. I do not need external tools. I will compute this directly.
|
60 |
+
5 + 3 = 8.
|
61 |
+
8 + 1294.678 = 1302.678.
|
62 |
+
|
63 |
+
FINAL ANSWER: 1302.678
|
64 |
+
---
|
65 |
+
|
66 |
+
(Agent Builder Note: The above "THOUGHT:" block for calculation is for the LLM's internal reasoning. For LangGraph, if it's pure math the LLM might just output the FINAL ANSWER directly. If you had a `calculator` tool, it would plan to use that.)
|
67 |
+
|
68 |
+
**Tool Invocation Rules (Important for Agent Builder):**
|
69 |
+
* When you decide to use a tool, you will format your request for that tool. The system will handle the actual execution.
|
70 |
+
* Do not try to write Python code yourself to call tools.
|
71 |
+
* Always use the right arguments for the tools.
|
72 |
+
* Take care not to chain too many sequential tool calls without reassessing.
|
73 |
+
* Call a tool only when needed and avoid redundant calls.
|