Spaces:
Running
Running
# agent.py | |
import os | |
from dotenv import load_dotenv | |
from typing import TypedDict, Annotated, Sequence, Dict, Any, List | |
from langchain_core.messages import BaseMessage, HumanMessage | |
from langchain_core.tools import tool | |
from langchain_openai import ChatOpenAI | |
from langgraph.graph import END, StateGraph | |
from langgraph.prebuilt import ToolNode | |
from langchain_community.tools import DuckDuckGoSearchResults | |
from langchain_community.utilities import WikipediaAPIWrapper | |
from langchain.agents import create_tool_calling_agent, AgentExecutor | |
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder | |
import operator | |
from langchain_experimental.utilities import PythonREPL | |
load_dotenv() | |
class AgentState(TypedDict): | |
messages: Annotated[Sequence[BaseMessage], operator.add] | |
sender: str | |
def wikipedia_search(query: str) -> str: | |
"""Search Wikipedia for information.""" | |
return WikipediaAPIWrapper().run(query) | |
def web_search(query: str, num_results: int = 3) -> list: | |
"""Search the web for current information.""" | |
return DuckDuckGoSearchResults(num_results=num_results).run(query) | |
def calculate(expression: str) -> str: | |
"""Evaluate mathematical expressions.""" | |
python_repl = PythonREPL() | |
return python_repl.run(expression) | |
class BasicAgent: | |
"""A complete langgraph agent implementation.""" | |
def __init__(self, model_name: str = "gpt-3.5-turbo"): | |
self.tools = [wikipedia_search, web_search, calculate] | |
self.llm = ChatOpenAI(model=model_name, temperature=0.7) | |
self.agent_executor = self._build_agent_executor() | |
self.workflow = self._build_workflow() # Initialize workflow here | |
def _build_agent_executor(self) -> AgentExecutor: | |
"""Build the agent executor with tools.""" | |
prompt = ChatPromptTemplate.from_messages([ | |
("system", "You are a helpful AI assistant. Use tools when needed."), | |
MessagesPlaceholder(variable_name="messages"), | |
MessagesPlaceholder(variable_name="agent_scratchpad"), | |
]) | |
agent = create_tool_calling_agent(self.llm, self.tools, prompt) | |
return AgentExecutor(agent=agent, tools=self.tools, verbose=True) | |
def _build_workflow(self) -> StateGraph: | |
"""Build and compile the agent workflow.""" | |
workflow = StateGraph(AgentState) | |
workflow.add_node("agent", self._run_agent) | |
workflow.add_node("tools", ToolNode(self.tools)) | |
workflow.set_entry_point("agent") | |
workflow.add_conditional_edges( | |
"agent", | |
self._should_continue, | |
{"continue": "tools", "end": END} | |
) | |
workflow.add_edge("tools", "agent") | |
return workflow.compile() | |
def _run_agent(self, state: AgentState) -> Dict[str, Any]: | |
"""Execute the agent.""" | |
response = self.agent_executor.invoke({"messages": state["messages"]}) | |
return {"messages": [response["output"]]} | |
def _should_continue(self, state: AgentState) -> str: | |
"""Determine if the workflow should continue.""" | |
last_message = state["messages"][-1] | |
return "continue" if last_message.additional_kwargs.get("tool_calls") else "end" | |
def __call__(self, question: str) -> str: | |
"""Process a user question and return a response.""" | |
# Initialize state with the user's question | |
state = AgentState(messages=[HumanMessage(content=question)], sender="user") | |
# Execute the workflow | |
for output in self.workflow.stream(state): | |
for key, value in output.items(): | |
if key == "messages": | |
for message in value: | |
if isinstance(message, BaseMessage): | |
return message.content | |
return "Sorry, I couldn't generate a response." | |
# Example usage | |
if __name__ == "__main__": | |
agent = BasicAgent() | |
response = agent("What's the capital of France?") | |
print(response) |