Created
July 17, 2026 03:45
-
-
Save KunalKumarSwift/349c6cc1ba40a3c8111ba5ca94b588fd to your computer and use it in GitHub Desktop.
Minimal React loop agent in python
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| Minimal ReAct-style loop with LangChain tool calling. | |
| ReAct = "Reason + Act": the model thinks, decides whether to call a tool, | |
| observes the result, and repeats until it has a final answer. | |
| This uses LangChain's tool-calling primitives directly (no AgentExecutor | |
| magic) so you can see exactly what's happening at each step. | |
| """ | |
| from langchain_core.tools import tool | |
| from langchain_core.messages import HumanMessage, ToolMessage | |
| from langchain_anthropic import ChatAnthropic | |
| # Swap for: from langchain_openai import ChatOpenAI | |
| # ---- 1. Define tools ------------------------------------------------- | |
| @tool | |
| def get_weather(city: str) -> str: | |
| """Get the current weather for a given city.""" | |
| # Replace with a real API call | |
| fake_data = {"toronto": "18°C, cloudy", "waterloo": "17°C, rainy"} | |
| return fake_data.get(city.lower(), "No data for that city") | |
| @tool | |
| def add(a: float, b: float) -> float: | |
| """Add two numbers together.""" | |
| return a + b | |
| tools = [get_weather, add] | |
| tools_by_name = {t.name: t for t in tools} | |
| # ---- 2. Bind tools to the model --------------------------------------- | |
| llm = ChatAnthropic(model="claude-haiku-4-5-20251001", temperature=0) | |
| llm_with_tools = llm.bind_tools(tools) | |
| # ---- 3. The ReAct loop -------------------------------------------------- | |
| def run_agent(user_input: str, max_steps: int = 5) -> str: | |
| messages = [HumanMessage(content=user_input)] | |
| for step in range(max_steps): | |
| ai_msg = llm_with_tools.invoke(messages) | |
| messages.append(ai_msg) | |
| # No tool calls -> model gave a final answer, stop looping | |
| if not ai_msg.tool_calls: | |
| return ai_msg.content | |
| # Otherwise, execute each requested tool call and feed results back | |
| for call in ai_msg.tool_calls: | |
| tool_fn = tools_by_name[call["name"]] | |
| result = tool_fn.invoke(call["args"]) | |
| messages.append( | |
| ToolMessage(content=str(result), tool_call_id=call["id"]) | |
| ) | |
| return "Max steps reached without a final answer." | |
| if __name__ == "__main__": | |
| answer = run_agent( | |
| "What's the weather in Waterloo, and what is 18 plus 24?" | |
| ) | |
| print(answer) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment