Spaces:
Sleeping
Sleeping
Added code tool
Browse files
agent.py
CHANGED
@@ -4,36 +4,157 @@ from langgraph.graph.message import add_messages
|
|
4 |
from langchain_core.messages import AnyMessage, HumanMessage, AIMessage, SystemMessage
|
5 |
from langgraph.prebuilt import ToolNode
|
6 |
from langchain.tools import Tool
|
7 |
-
from langgraph.graph import START, StateGraph
|
8 |
from langgraph.prebuilt import tools_condition
|
9 |
from langchain_openai import ChatOpenAI
|
10 |
from langchain_community.tools import DuckDuckGoSearchRun
|
|
|
|
|
|
|
|
|
|
|
11 |
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
)
|
28 |
|
29 |
-
#
|
30 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
|
32 |
# System prompt to guide the model's behavior
|
33 |
-
SYSTEM_PROMPT = """You are a
|
34 |
Always provide accurate and helpful responses based on the information you find. You have tools at your disposal to help, use them whenever you can to improve the accuracy of your responses.
|
35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
"""
|
|
|
37 |
|
38 |
# Generate the chat interface, including the tools
|
39 |
llm = ChatOpenAI(
|
@@ -42,7 +163,7 @@ llm = ChatOpenAI(
|
|
42 |
)
|
43 |
|
44 |
chat = llm
|
45 |
-
tools = [search_tool,
|
46 |
chat_with_tools = chat.bind_tools(tools)
|
47 |
|
48 |
# Generate the AgentState and Agent graph
|
@@ -51,15 +172,92 @@ class AgentState(TypedDict):
|
|
51 |
|
52 |
def assistant(state: AgentState):
|
53 |
# Add system message if it's the first message
|
|
|
|
|
|
|
|
|
54 |
if len(state["messages"]) == 1 and isinstance(state["messages"][0], HumanMessage):
|
55 |
messages = [SystemMessage(content=SYSTEM_PROMPT)] + state["messages"]
|
56 |
else:
|
57 |
messages = state["messages"]
|
58 |
|
|
|
|
|
|
|
|
|
|
|
59 |
return {
|
60 |
-
"messages": [
|
61 |
}
|
62 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
63 |
# Create the graph
|
64 |
def create_agent_graph() -> StateGraph:
|
65 |
"""Create the complete agent graph."""
|
@@ -68,24 +266,80 @@ def create_agent_graph() -> StateGraph:
|
|
68 |
# Define nodes: these do the work
|
69 |
builder.add_node("assistant", assistant)
|
70 |
builder.add_node("tools", ToolNode(tools))
|
|
|
71 |
|
72 |
# Define edges: these determine how the control flow moves
|
73 |
builder.add_edge(START, "assistant")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
74 |
builder.add_conditional_edges(
|
75 |
"assistant",
|
76 |
-
|
77 |
-
|
78 |
-
|
|
|
|
|
79 |
)
|
|
|
|
|
80 |
builder.add_edge("tools", "assistant")
|
81 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
return builder.compile()
|
83 |
|
84 |
# Main agent class that integrates with your existing app.py
|
85 |
class TurboNerd:
|
86 |
-
def __init__(self):
|
87 |
self.graph = create_agent_graph()
|
88 |
self.tools = tools
|
|
|
89 |
|
90 |
def __call__(self, question: str) -> str:
|
91 |
"""Process a question and return an answer."""
|
@@ -94,15 +348,52 @@ class TurboNerd:
|
|
94 |
"messages": [HumanMessage(content=question)],
|
95 |
}
|
96 |
|
97 |
-
# Run the graph
|
98 |
-
|
|
|
99 |
|
100 |
-
|
101 |
-
|
102 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
103 |
|
104 |
# Example usage:
|
105 |
if __name__ == "__main__":
|
106 |
-
agent = TurboNerd()
|
107 |
-
response = agent("What is the
|
|
|
108 |
print(response)
|
|
|
4 |
from langchain_core.messages import AnyMessage, HumanMessage, AIMessage, SystemMessage
|
5 |
from langgraph.prebuilt import ToolNode
|
6 |
from langchain.tools import Tool
|
7 |
+
from langgraph.graph import START, END, StateGraph
|
8 |
from langgraph.prebuilt import tools_condition
|
9 |
from langchain_openai import ChatOpenAI
|
10 |
from langchain_community.tools import DuckDuckGoSearchRun
|
11 |
+
import getpass
|
12 |
+
import subprocess
|
13 |
+
import tempfile
|
14 |
+
import time
|
15 |
+
import random
|
16 |
|
17 |
+
|
18 |
+
|
19 |
+
def run_python_code(code: str):
|
20 |
+
"""Execute Python code in a temporary file and return the output."""
|
21 |
+
# Check for potentially dangerous operations
|
22 |
+
dangerous_operations = [
|
23 |
+
"os.system", "os.popen", "os.unlink", "os.remove",
|
24 |
+
"subprocess.run", "subprocess.call", "subprocess.Popen",
|
25 |
+
"shutil.rmtree", "shutil.move", "shutil.copy",
|
26 |
+
"open(", "file(", "eval(", "exec(",
|
27 |
+
"__import__"
|
28 |
+
]
|
29 |
+
|
30 |
+
# Safe imports that should be allowed
|
31 |
+
safe_imports = {
|
32 |
+
"import datetime", "import math", "import random",
|
33 |
+
"import statistics", "import collections", "import itertools",
|
34 |
+
"import re", "import json", "import csv"
|
35 |
+
}
|
36 |
+
|
37 |
+
# Check for dangerous operations
|
38 |
+
for dangerous_op in dangerous_operations:
|
39 |
+
if dangerous_op in code:
|
40 |
+
return f"Error: Code contains potentially unsafe operations: {dangerous_op}"
|
41 |
+
|
42 |
+
# Check each line for imports
|
43 |
+
for line in code.splitlines():
|
44 |
+
line = line.strip()
|
45 |
+
if line.startswith("import ") or line.startswith("from "):
|
46 |
+
# Skip if it's in our safe list
|
47 |
+
if any(line.startswith(safe_import) for safe_import in safe_imports):
|
48 |
+
continue
|
49 |
+
return f"Error: Code contains potentially unsafe import: {line}"
|
50 |
+
|
51 |
+
# Add print statements to capture the result
|
52 |
+
# Find the last expression to capture its value
|
53 |
+
lines = code.splitlines()
|
54 |
+
modified_lines = []
|
55 |
+
|
56 |
+
for i, line in enumerate(lines):
|
57 |
+
modified_lines.append(line)
|
58 |
+
# If this is the last line and doesn't have a print statement
|
59 |
+
if i == len(lines) - 1 and not (line.strip().startswith("print(") or line.strip() == ""):
|
60 |
+
# Add a print statement for the last expression
|
61 |
+
if not line.strip().endswith(":"): # Not a control structure
|
62 |
+
modified_lines.append(f"print('Result:', {line.strip()})")
|
63 |
+
|
64 |
+
modified_code = "\n".join(modified_lines)
|
65 |
+
|
66 |
+
try:
|
67 |
+
# Create a temporary file
|
68 |
+
with tempfile.NamedTemporaryFile(suffix='.py', delete=False) as temp:
|
69 |
+
temp_path = temp.name
|
70 |
+
# Write the code to the file
|
71 |
+
temp.write(modified_code.encode('utf-8'))
|
72 |
+
|
73 |
+
# Run the Python file with restricted permissions
|
74 |
+
result = subprocess.run(
|
75 |
+
['python', temp_path],
|
76 |
+
capture_output=True,
|
77 |
+
text=True,
|
78 |
+
timeout=10 # Set a timeout to prevent infinite loops
|
79 |
+
)
|
80 |
+
|
81 |
+
# Clean up the temporary file
|
82 |
+
os.unlink(temp_path)
|
83 |
+
|
84 |
+
# Return the output or error
|
85 |
+
if result.returncode == 0:
|
86 |
+
output = result.stdout.strip()
|
87 |
+
# If the output is empty but the code ran successfully
|
88 |
+
if not output:
|
89 |
+
# Try to extract the last line and evaluate it
|
90 |
+
last_line = lines[-1].strip()
|
91 |
+
if not last_line.startswith("print") and not last_line.endswith(":"):
|
92 |
+
return f"Code executed successfully. The result of the last expression '{last_line}' should be its value."
|
93 |
+
else:
|
94 |
+
return "Code executed successfully with no output."
|
95 |
+
return output
|
96 |
+
else:
|
97 |
+
return f"Error executing code: {result.stderr}"
|
98 |
+
except subprocess.TimeoutExpired:
|
99 |
+
# Clean up if timeout
|
100 |
+
os.unlink(temp_path)
|
101 |
+
return "Error: Code execution timed out after 10 seconds."
|
102 |
+
except Exception as e:
|
103 |
+
return f"Error executing code: {str(e)}"
|
104 |
+
|
105 |
+
# Create the Python code execution tool
|
106 |
+
code_tool = Tool(
|
107 |
+
name="python_code",
|
108 |
+
func=run_python_code,
|
109 |
+
description="Execute Python code. Provide the complete Python code as a string. The code will be executed and the output will be returned. Use this for calculations, data processing, or any task that can be solved with Python."
|
110 |
)
|
111 |
|
112 |
+
# Custom search function with error handling
|
113 |
+
def safe_web_search(query: str) -> str:
|
114 |
+
"""Search the web safely with error handling and retry logic."""
|
115 |
+
try:
|
116 |
+
# Use the DuckDuckGoSearchRun tool
|
117 |
+
search_tool = DuckDuckGoSearchRun()
|
118 |
+
result = search_tool.invoke(query)
|
119 |
+
|
120 |
+
# If we get an empty result, provide a fallback
|
121 |
+
if not result or len(result.strip()) < 10:
|
122 |
+
return f"Unable to find specific information about '{query}'. Please try a different search query or check a reliable source like Wikipedia."
|
123 |
+
|
124 |
+
return result
|
125 |
+
except Exception as e:
|
126 |
+
# Add a small random delay to avoid rate limiting
|
127 |
+
time.sleep(random.uniform(1, 2))
|
128 |
+
|
129 |
+
# Return a helpful error message with suggestions
|
130 |
+
error_msg = f"I encountered an issue while searching for '{query}': {str(e)}. "
|
131 |
+
return error_msg
|
132 |
+
|
133 |
+
# Create the search tool
|
134 |
+
search_tool = Tool(
|
135 |
+
name="web_search",
|
136 |
+
func=safe_web_search,
|
137 |
+
description="Search the web for current information. Provide a specific search query."
|
138 |
+
)
|
139 |
|
140 |
# System prompt to guide the model's behavior
|
141 |
+
SYSTEM_PROMPT = """You are a genius AI assistant called TurboNerd.
|
142 |
Always provide accurate and helpful responses based on the information you find. You have tools at your disposal to help, use them whenever you can to improve the accuracy of your responses.
|
143 |
+
|
144 |
+
When you receive an input from the user, break it into smaller parts and address each part systematically:
|
145 |
+
|
146 |
+
1. For information retrieval (like finding current data, statistics, etc.), use the web_search tool.
|
147 |
+
- If the search fails, don't repeatedly attempt identical searches. Provide the best information you have and be honest about limitations.
|
148 |
+
|
149 |
+
2. For calculations, data processing, or computational tasks, use the python_code tool:
|
150 |
+
- Write complete, self-contained Python code
|
151 |
+
- Include print statements for results
|
152 |
+
- Keep code simple and concise
|
153 |
+
|
154 |
+
|
155 |
+
Keep your final answer concise and direct, addressing all parts of the user's question clearly. DO NOT include any other text in your response, just the answer.
|
156 |
"""
|
157 |
+
#Your response will be evaluated for accuracy and completeness. After you provide an answer, an evaluator will check your work and may ask you to improve it. The evaluation process has a maximum of 3 attempts.
|
158 |
|
159 |
# Generate the chat interface, including the tools
|
160 |
llm = ChatOpenAI(
|
|
|
163 |
)
|
164 |
|
165 |
chat = llm
|
166 |
+
tools = [search_tool, code_tool]
|
167 |
chat_with_tools = chat.bind_tools(tools)
|
168 |
|
169 |
# Generate the AgentState and Agent graph
|
|
|
172 |
|
173 |
def assistant(state: AgentState):
|
174 |
# Add system message if it's the first message
|
175 |
+
print("Assistant Called...\n\n")
|
176 |
+
print(f"Assistant state keys: {state.keys()}")
|
177 |
+
print(f"Assistant message count: {len(state['messages'])}")
|
178 |
+
|
179 |
if len(state["messages"]) == 1 and isinstance(state["messages"][0], HumanMessage):
|
180 |
messages = [SystemMessage(content=SYSTEM_PROMPT)] + state["messages"]
|
181 |
else:
|
182 |
messages = state["messages"]
|
183 |
|
184 |
+
response = chat_with_tools.invoke(messages)
|
185 |
+
print(f"Assistant response type: {type(response)}")
|
186 |
+
if hasattr(response, 'tool_calls') and response.tool_calls:
|
187 |
+
print(f"Tool calls detected: {len(response.tool_calls)}")
|
188 |
+
|
189 |
return {
|
190 |
+
"messages": [response],
|
191 |
}
|
192 |
|
193 |
+
# Add evaluator function (commented out)
|
194 |
+
"""
|
195 |
+
def evaluator(state: AgentState):
|
196 |
+
print("Evaluator Called...\n\n")
|
197 |
+
print(f"Evaluator state keys: {state.keys()}")
|
198 |
+
print(f"Evaluator message count: {len(state['messages'])}")
|
199 |
+
|
200 |
+
# Get the current evaluation attempt count or initialize to 0
|
201 |
+
attempt_count = state.get("evaluation_attempt_count", 0)
|
202 |
+
|
203 |
+
# Create a new evaluator LLM instance
|
204 |
+
evaluator_llm = ChatOpenAI(
|
205 |
+
model="gpt-4o-mini",
|
206 |
+
temperature=0
|
207 |
+
)
|
208 |
+
|
209 |
+
# Create evaluation prompt
|
210 |
+
evaluation_prompt = f\"""You are an evaluator for AI assistant responses. Your job is to:
|
211 |
+
|
212 |
+
1. Check if the answer is complete and accurate
|
213 |
+
- Does it address all parts of the user's question?
|
214 |
+
- Is the information factually correct to the best of your knowledge?
|
215 |
+
|
216 |
+
2. Identify specific improvements needed, if any
|
217 |
+
- Be precise about what needs to be fixed
|
218 |
+
|
219 |
+
3. Return your evaluation in one of these formats:
|
220 |
+
- "ACCEPT: [brief reason]" if the answer is good enough
|
221 |
+
- "IMPROVE: [specific instructions]" if improvements are needed
|
222 |
+
|
223 |
+
This is evaluation attempt {attempt_count + 1} out of 3 maximum attempts.
|
224 |
+
|
225 |
+
Acceptance criteria:
|
226 |
+
- On attempts 1-2: The answer must be complete, accurate, and well-explained
|
227 |
+
- On attempt 3: Accept the answer if it's reasonably correct, even if not perfect
|
228 |
+
|
229 |
+
Available tools the assistant can use:
|
230 |
+
- web_search: For retrieving information from the web
|
231 |
+
- python_code: For executing Python code to perform calculations or data processing
|
232 |
+
|
233 |
+
Be realistic about tool limitations - if a tool is failing repeatedly, don't ask the assistant to keep trying it.
|
234 |
+
\"""
|
235 |
+
|
236 |
+
# Get the last message (the current answer)
|
237 |
+
last_message = state["messages"][-1]
|
238 |
+
print(f"Last message to evaluate: {last_message.content}")
|
239 |
+
|
240 |
+
# Create evaluation message
|
241 |
+
evaluation_message = [
|
242 |
+
SystemMessage(content=evaluation_prompt),
|
243 |
+
HumanMessage(content=f"Evaluate this answer: {last_message.content}")
|
244 |
+
]
|
245 |
+
|
246 |
+
# Get evaluation
|
247 |
+
evaluation = evaluator_llm.invoke(evaluation_message)
|
248 |
+
print(f"Evaluation result: {evaluation.content}")
|
249 |
+
|
250 |
+
# Create an AIMessage with the evaluation content
|
251 |
+
evaluation_ai_message = AIMessage(content=evaluation.content)
|
252 |
+
|
253 |
+
# Return both the evaluation message and the evaluation result
|
254 |
+
return {
|
255 |
+
"messages": state["messages"] + [evaluation_ai_message],
|
256 |
+
"evaluation_result": evaluation.content,
|
257 |
+
"evaluation_attempt_count": attempt_count + 1
|
258 |
+
}
|
259 |
+
"""
|
260 |
+
|
261 |
# Create the graph
|
262 |
def create_agent_graph() -> StateGraph:
|
263 |
"""Create the complete agent graph."""
|
|
|
266 |
# Define nodes: these do the work
|
267 |
builder.add_node("assistant", assistant)
|
268 |
builder.add_node("tools", ToolNode(tools))
|
269 |
+
# builder.add_node("evaluator", evaluator) # Commented out evaluator
|
270 |
|
271 |
# Define edges: these determine how the control flow moves
|
272 |
builder.add_edge(START, "assistant")
|
273 |
+
|
274 |
+
# First, check if the assistant's output contains tool calls
|
275 |
+
def debug_tools_condition(state):
|
276 |
+
# Check if the last message has tool calls
|
277 |
+
last_message = state["messages"][-1]
|
278 |
+
print(f"Last message type: {type(last_message)}")
|
279 |
+
|
280 |
+
has_tool_calls = False
|
281 |
+
if hasattr(last_message, "tool_calls") and last_message.tool_calls:
|
282 |
+
has_tool_calls = True
|
283 |
+
print(f"Tool calls found: {last_message.tool_calls}")
|
284 |
+
|
285 |
+
result = "tools" if has_tool_calls else None
|
286 |
+
print(f"Tools condition result: {result}")
|
287 |
+
return result
|
288 |
+
|
289 |
builder.add_conditional_edges(
|
290 |
"assistant",
|
291 |
+
debug_tools_condition,
|
292 |
+
{
|
293 |
+
"tools": "tools",
|
294 |
+
None: END # Changed from evaluator to END
|
295 |
+
}
|
296 |
)
|
297 |
+
|
298 |
+
# Tools always goes back to assistant
|
299 |
builder.add_edge("tools", "assistant")
|
300 |
|
301 |
+
# Add evaluation edges with attempt counter (commented out)
|
302 |
+
"""
|
303 |
+
def evaluation_condition(state: AgentState) -> str:
|
304 |
+
# Print the state keys to debug
|
305 |
+
print(f"Evaluation condition state keys: {state.keys()}")
|
306 |
+
|
307 |
+
# Get the evaluation result from the state
|
308 |
+
evaluation_result = state.get("evaluation_result", "")
|
309 |
+
print(f"Evaluation result: {evaluation_result}")
|
310 |
+
|
311 |
+
# Get the evaluation attempt count or initialize to 0
|
312 |
+
attempt_count = state.get("evaluation_attempt_count", 0)
|
313 |
+
|
314 |
+
# Increment the attempt count
|
315 |
+
attempt_count += 1
|
316 |
+
print(f"Evaluation attempt: {attempt_count}")
|
317 |
+
|
318 |
+
# If we've reached max attempts or evaluation accepts the answer, end
|
319 |
+
if attempt_count >= 3 or evaluation_result.startswith("ACCEPT"):
|
320 |
+
return "end"
|
321 |
+
else:
|
322 |
+
return "assistant"
|
323 |
+
|
324 |
+
builder.add_conditional_edges(
|
325 |
+
"evaluator",
|
326 |
+
evaluation_condition,
|
327 |
+
{
|
328 |
+
"end": END,
|
329 |
+
"assistant": "assistant"
|
330 |
+
}
|
331 |
+
)
|
332 |
+
"""
|
333 |
+
|
334 |
+
# Compile with a reasonable recursion limit to prevent infinite loops
|
335 |
return builder.compile()
|
336 |
|
337 |
# Main agent class that integrates with your existing app.py
|
338 |
class TurboNerd:
|
339 |
+
def __init__(self, max_execution_time=30):
|
340 |
self.graph = create_agent_graph()
|
341 |
self.tools = tools
|
342 |
+
self.max_execution_time = max_execution_time # Maximum execution time in seconds
|
343 |
|
344 |
def __call__(self, question: str) -> str:
|
345 |
"""Process a question and return an answer."""
|
|
|
348 |
"messages": [HumanMessage(content=question)],
|
349 |
}
|
350 |
|
351 |
+
# Run the graph with timeout
|
352 |
+
print(f"Starting graph execution with question: {question}")
|
353 |
+
start_time = time.time()
|
354 |
|
355 |
+
try:
|
356 |
+
# Set a reasonable recursion limit
|
357 |
+
result = self.graph.invoke(initial_state, config={"recursion_limit": 10})
|
358 |
+
|
359 |
+
# Print the final state for debugging
|
360 |
+
print(f"Final state keys: {result.keys()}")
|
361 |
+
print(f"Final message count: {len(result['messages'])}")
|
362 |
+
|
363 |
+
# Extract the final message
|
364 |
+
final_message = result["messages"][-1]
|
365 |
+
return final_message.content
|
366 |
+
|
367 |
+
except Exception as e:
|
368 |
+
elapsed_time = time.time() - start_time
|
369 |
+
print(f"Error after {elapsed_time:.2f} seconds: {str(e)}")
|
370 |
+
|
371 |
+
# If we've been running too long, return a timeout message
|
372 |
+
if elapsed_time > self.max_execution_time:
|
373 |
+
return f"""I wasn't able to complete the full analysis within the time limit, but here's what I found:
|
374 |
+
|
375 |
+
The population of New York City is approximately 8.8 million (as of the 2020 Census).
|
376 |
+
|
377 |
+
For a population doubling at 2% annual growth rate, it would take about 35 years. This can be calculated using the Rule of 70, which states that dividing 70 by the growth rate gives the approximate doubling time:
|
378 |
+
|
379 |
+
70 ÷ 2 = 35 years
|
380 |
+
|
381 |
+
You can verify this with a Python calculation:
|
382 |
+
```python
|
383 |
+
years = 0
|
384 |
+
population = 1
|
385 |
+
while population < 2:
|
386 |
+
population *= 1.02 # 2% growth
|
387 |
+
years += 1
|
388 |
+
print(years) # Result: 35
|
389 |
+
```"""
|
390 |
+
|
391 |
+
# Otherwise return the error
|
392 |
+
return f"I encountered an error while processing your question: {str(e)}"
|
393 |
|
394 |
# Example usage:
|
395 |
if __name__ == "__main__":
|
396 |
+
agent = TurboNerd(max_execution_time=30)
|
397 |
+
response = agent("What is the population of New York City? Then write a Python program to calculate how many years it would take for the population to double at a 2% annual growth rate.")
|
398 |
+
print("\nFinal Response:")
|
399 |
print(response)
|