-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcode.py
More file actions
79 lines (57 loc) · 2.8 KB
/
Copy pathcode.py
File metadata and controls
79 lines (57 loc) · 2.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
"""Tool Use — let the model call real functions instead of guessing.
An LLM cannot know today's date or do reliable arithmetic. Bind real Python
functions to it and it stops hallucinating those answers: it emits a structured
tool call, we execute it, and hand the result back.
The loop is the pattern: invoke -> if tool_calls, run them, append results,
invoke again -> repeat until the model answers in plain text.
"""
from datetime import date, datetime
from langchain_core.messages import HumanMessage, ToolMessage
from langchain_core.tools import tool
from resources.agent import llm
from resources.helper import show_response
# @tool turns a plain function into something the model can call. The docstring
# is not decoration - it is the description the model reads to decide whether
# this tool fits, so it is part of the prompt surface.
@tool
def days_until(target_date: str) -> str:
"""Return the number of days from today until the given YYYY-MM-DD date."""
delta = datetime.strptime(target_date, "%Y-%m-%d").date() - date.today()
return f"{delta.days} days"
@tool
def compound_interest(principal: float, rate_percent: float, years: int) -> str:
"""Compute compound interest. rate_percent is annual, compounded yearly."""
final = principal * (1 + rate_percent / 100) ** years
return f"{final:.2f}"
@tool
def today() -> str:
"""Return today's date in YYYY-MM-DD format."""
return date.today().isoformat()
TOOLS = {t.name: t for t in [days_until, compound_interest, today]}
llm_with_tools = llm.bind_tools(list(TOOLS.values()))
MAX_STEPS = 5 # a tool loop without a cap can ping-pong forever
def run(question):
"""Run the tool loop until the model produces a plain-text answer."""
messages = [HumanMessage(question)]
for step in range(1, MAX_STEPS + 1):
reply = llm_with_tools.invoke(messages)
messages.append(reply)
# No tool calls means the model is done and this is the real answer.
if not reply.tool_calls:
return reply
# Execute every tool the model asked for, then feed the results back.
# ToolMessage must carry the matching tool_call_id or the model can't
# tell which result answers which call.
for call in reply.tool_calls:
result = TOOLS[call["name"]].invoke(call["args"])
print(f"[step {step}] {call['name']}({call['args']}) -> {result}")
messages.append(ToolMessage(result, tool_call_id=call["id"]))
return reply
if __name__ == "__main__":
for question in [
"How many days until 2027-01-01?",
"If I invest $5,000 at 7% for 12 years, what do I end up with?",
"What is today's date, and how many days until New Year's Day 2027?",
]:
print(f"\n{'=' * 60}\nQUESTION: {question}\n{'=' * 60}")
show_response(run(question))