wrigleyDan commited on
Commit
0fbe2c5
·
verified ·
1 Parent(s): 8524297

Update agent.py

Browse files
Files changed (1) hide show
  1. agent.py +146 -0
agent.py CHANGED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import base64
3
+ from langchain_core.messages import HumanMessage, SystemMessage
4
+ from langchain_openai import ChatOpenAI
5
+ from langchain_community.tools import DuckDuckGoSearchResults
6
+ from langchain_community.utilities import DuckDuckGoSearchAPIWrapper
7
+ import wikipediaapi
8
+ import json
9
+ import asyncio
10
+ import aiohttp
11
+ from langchain_core.tools import tool
12
+ from langgraph.graph import START, StateGraph, MessagesState
13
+ from langgraph.prebuilt import tools_condition
14
+ from langgraph.prebuilt import ToolNode
15
+
16
+ import requests
17
+
18
+ system_prompt = """You are a helpful assistant tasked with answering questions using a set of tools.
19
+ Now, I will ask you a question. Report your thoughts, and finish your answer with the following template:
20
+ FINAL ANSWER: [YOUR FINAL ANSWER].
21
+ YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings. If you are asked for a number, don't use comma to write your number neither use units such as $ or percent sign unless specified otherwise. If you are asked for a string, don't use articles, neither abbreviations (e.g. for cities), and write the digits in plain text unless specified otherwise. If you are asked for a comma separated list, apply the above rules depending of whether the element to be put in the list is a number or a string.
22
+ Your answer should only start with "FINAL ANSWER: ", then follows with the answer.
23
+ """
24
+
25
+ api_key = os.getenv("OPENAI_API_KEY")
26
+
27
+ model = ChatOpenAI(model="gpt-4o-mini", api_key=api_key, temperature=0)
28
+
29
+ @tool
30
+ def search_wiki(query: str, max_results: int = 3) -> str:
31
+ """
32
+ Searches Wikipedia for the given query and returns a maximum of 'max_results'
33
+ relevant article summaries, titles, and URLs.
34
+
35
+ Args:
36
+ query (str): The search query for Wikipedia.
37
+ max_results (int): The maximum number of search results to retrieve (default is 3).
38
+
39
+ Returns:
40
+ str: A JSON string containing a list of dictionaries, where each dictionary
41
+ represents a Wikipedia article with its title, summary, and URL.
42
+ Returns an empty list if no results are found or an error occurs.
43
+ """
44
+
45
+ language_code = 'en'
46
+
47
+ headers={'User-Agent': 'LangGraphAgent/1.0 ([email protected])'}
48
+
49
+ base_url = 'https://api.wikimedia.org/core/v1/wikipedia/'
50
+ endpoint = '/search/page'
51
+ url = base_url + language_code + endpoint
52
+ parameters = {'q': query, 'limit': max_results}
53
+ response = requests.get(url, headers=headers, params=parameters)
54
+ response = json.loads(response.text)
55
+ return json.dumps(response, indent=2)
56
+
57
+
58
+ @tool
59
+ def search_web(query: str, max_results: int = 3) -> str:
60
+ """
61
+ Searches the web for the given query and returns a maximum of 'max_results'
62
+ relevant hits.
63
+
64
+ Args:
65
+ query (str): The search query for the web search.
66
+ max_results (int): The maximum number of search results to retrieve (default is 3).
67
+
68
+ Returns:
69
+ str: A JSON string containing a list, where each entry
70
+ represents a search result article with its snippet, title, link and other metadata.
71
+ Returns an empty list if no results are found or an error occurs.
72
+ """
73
+ try:
74
+ wrapper = DuckDuckGoSearchAPIWrapper(max_results=max_results)
75
+ search = DuckDuckGoSearchResults(api_wrapper=wrapper)
76
+ #search = DuckDuckGoSearchResults()
77
+ results = search.invoke(query)
78
+ return results
79
+ except Exception as e:
80
+ print(f"An error occurred during web search: {e}")
81
+ return json.dumps([]) # Return an empty JSON list on error
82
+
83
+ tools = [
84
+ search_web,
85
+ search_wiki
86
+ ]
87
+
88
+ def build_graph():
89
+ """Build the graph"""
90
+ # Bind tools to LLM
91
+ llm_with_tools = model.bind_tools(tools)
92
+
93
+ # Node
94
+ def assistant(state: MessagesState):
95
+ """Assistant node"""
96
+ return {"messages": [llm_with_tools.invoke(state["messages"])]}
97
+
98
+ builder = StateGraph(MessagesState)
99
+ builder.add_node("assistant", assistant)
100
+ builder.add_node("tools", ToolNode(tools))
101
+ builder.add_edge(START, "assistant")
102
+ builder.add_conditional_edges(
103
+ "assistant",
104
+ tools_condition,
105
+ )
106
+ builder.add_edge("tools", "assistant")
107
+
108
+ # Compile graph
109
+ return builder.compile()
110
+
111
+ # --- Testing the tools ---
112
+
113
+ # Test case: Basic Wikipedia search
114
+ print("--- Test Case 1: Basic Search ---")
115
+ query1 = "Principle of double effect"
116
+ result1 = search_wiki.invoke(query1)
117
+ print(f"Query: '{query1}'")
118
+ print(f"Result Type: {type(result1)}")
119
+ print(f"Result (first 500 chars): {result1[:500]}...")
120
+ print("\n")
121
+
122
+ # Test case: Basic web search
123
+ print("--- Test Case 1: Basic Search ---")
124
+ query1 = "Principle of double effect"
125
+ result1 = search_web.invoke(query1)
126
+ print(f"Query: '{query1}'")
127
+ print(f"Result Type: {type(result1)}")
128
+ print(f"Result (first 500 chars): {result1[:500]}...")
129
+ print("\n")
130
+
131
+ # test agent
132
+ if __name__ == "__main__":
133
+ question = "When was St. Thomas Aquinas born?"
134
+ # Build the graph
135
+ graph = build_graph()
136
+ # Run the graph
137
+ messages = [
138
+ SystemMessage(
139
+ content=system_prompt
140
+ ),
141
+ HumanMessage(
142
+ content=question
143
+ )]
144
+ messages = graph.invoke({"messages": messages})
145
+ for m in messages["messages"]:
146
+ m.pretty_print()