diff --git a/examples/results/research_ai_agent_reasoning.md b/examples/results/research_ai_agent_reasoning.md new file mode 100644 index 0000000..ea56395 --- /dev/null +++ b/examples/results/research_ai_agent_reasoning.md @@ -0,0 +1,89 @@ +--- +Subject: Techniques used to enable complex reasoning in LLM AI agents +--- + +The following research report provides an overview of the techniques being developed and employed to enable complex reasoning in Large Language Model (LLM) AI agents. The information is based on the insights gathered from several web resources, including surveys, reviews, and articles. + +## Background + +Large Language Models (LLMs), such as GPT-4, have made significant strides in natural language understanding and generation. However, they face challenges such as multimodality, human value alignment, hallucinations, and evaluation when it comes to complex reasoning tasks. To address these limitations, researchers and developers are exploring the design of autonomous agents that leverage external data, supplementary tools, and innovative prompting techniques to enhance the reasoning capabilities of LLMs. + +## Key Techniques + +### Chain-of-thought Prompting (CoT) + +This technique encourages LLMs to generate intermediate reasoning steps before arriving at a final answer, particularly useful for complex problems. CoT prompts guide the model to think aloud and provide an explicit explanation of their thought process. + +Example: For a math problem like "What is the sum of the consecutive numbers starting from 1 up to 100?", a CoT prompt might look like this: + +Q: What is the sum of the consecutive numbers starting from 1 up to 100? +A: Let's think step by step. +------ (LLMs Output) +First, we need to find the last number in the sequence, which is 100. +The sum of a consecutive sequence can be found by the formula (n/2)*(first number + last number) where n is the total number of terms. +So, we have (100/2)*(1+100), which simplifies to 50*101. +Therefore, the sum is 5050. + +### Self-consistency with CoT + +This approach generates multiple reasoning paths and answers for the same question and then selects the most common answer across these paths. + +Example: For a question about rainfall, the model might generate different chains of thought, and the most common answer is selected for consistency. + +### Tree-of-thought (ToT) Prompting + +This technique represents the reasoning process as a tree structure, with branches representing different lines of reasoning. + +Example: For a multi-step logic puzzle, ToT might involve branching out possibilities like this: + +Q: Start at the root: What is the color of the bear? +------ A: Branch 1: If the bear is in the North Pole, it must be white because polar bears live there. +Branch 2: If the bear is in the forest, it could be brown or black. +Conclusion: Given the additional information that the bear is in the North Pole, we follow Branch 1 and determine the bear is white. + +### Reasoning via Planning (RAP) + +This technique generates a plan of action based on given goals and constraints to solve a problem. + +Example: For a cooking recipe, RAP could generate a plan like this: + +Goal: Bake a chocolate cake. +Step 1: Gather ingredients - flour, sugar, cocoa powder, etc. +Step 2: Preheat the oven to 350°F. +Step 3: Mix dry ingredients. +... +Step n: Bake for 30 minutes and let cool. + +### ReAct + +This prompting technique helps LLMs not only reason about a problem but also take actions like interacting with an API or a database, based on the reasoning process. + +Example: If tasked with finding the weather forecast, a ReAct prompt might look like this: + +First, determine the user's location. +Next, access the weather API with the location data. +Then, retrieve the forecast information and present it to the user in a readable format. + +### Self-Refine + +This approach leverages a cyclical process of self-improvement, enabling the model to enhance its own outputs autonomously through iterative self-assessment (feedback). + +Example: An example application of the Self-refine method could be in generating a summary of a complex article. + +### Reflexion + +This innovative approach enhances language agents by using linguistic feedback as a means of reinforcement, diverging from conventional practice. + +### Language Agent Tree Search (LATS) + +This strategy combines language models with tree search algorithms to explore a large number of potential actions and outcomes, particularly powerful in game-playing scenarios or situations with a wide range of possible decisions. + +## Conclusion + +These techniques represent significant leaps forward in the capabilities of LLMs to perform more complex reasoning tasks and interact with their environment in meaningful ways. As research continues and these models evolve, we can expect even more sophisticated prompting methods to emerge, further closing the gap between artificial and human intelligence. The design of autonomous agents that leverage external data, supplementary tools, and innovative prompting techniques will enable LLMs to accomplish complex goals that require enhanced reasoning, planning, and tool execution capabilities. + +Sources: +https://arxiv.org/html/2404.11584v1 +https://arxiv.org/html/2404.04442v1 +https://medium.com/the-modern-scientist/a-complete-guide-to-llms-based-autonomous-agents-part-i-69515c016792 +https://llms-blog.medium.com/unlocking-advanced-reasoning-in-large-language-models-a-deep-dive-into-innovative-prompting-f3d8c2530831 \ No newline at end of file diff --git a/src/llama_cpp_agent/llm_agent.py b/src/llama_cpp_agent/llm_agent.py index 7e6ff5d..b592303 100644 --- a/src/llama_cpp_agent/llm_agent.py +++ b/src/llama_cpp_agent/llm_agent.py @@ -414,36 +414,36 @@ def get_response_role_and_completion( if structured_output_settings.add_thoughts_and_reasoning_field and self.provider.is_using_json_schema_constraints(): messages[0][ "content"] += function_calling_thoughts_and_reasoning_json_schema + structured_output_settings.get_llm_documentation( - provider=self.provider) + provider=self.provider) + "\n" elif not structured_output_settings.add_thoughts_and_reasoning_field and self.provider.is_using_json_schema_constraints(): messages[0][ "content"] += function_calling_without_thoughts_and_reasoning_json_schema + structured_output_settings.get_llm_documentation( - provider=self.provider) + provider=self.provider)+ "\n" elif structured_output_settings.add_thoughts_and_reasoning_field and not self.provider.is_using_json_schema_constraints(): messages[0][ "content"] += function_calling_thoughts_and_reasoning + structured_output_settings.get_llm_documentation( - provider=self.provider) + provider=self.provider)+ "\n" elif not structured_output_settings.add_thoughts_and_reasoning_field and not self.provider.is_using_json_schema_constraints(): messages[0][ "content"] += function_calling_without_thoughts_and_reasoning + structured_output_settings.get_llm_documentation( - provider=self.provider) + provider=self.provider)+ "\n" elif structured_output_settings.output_type == LlmStructuredOutputType.object_instance or structured_output_settings.output_type == LlmStructuredOutputType.list_of_objects: if structured_output_settings.add_thoughts_and_reasoning_field and self.provider.is_using_json_schema_constraints(): messages[0][ "content"] += structured_output_thoughts_and_reasoning_json_schema + structured_output_settings.get_llm_documentation( - provider=self.provider) + provider=self.provider) + "\n" elif not structured_output_settings.add_thoughts_and_reasoning_field and self.provider.is_using_json_schema_constraints(): messages[0][ "content"] += structured_output_without_thoughts_and_reasoning_json_schema + structured_output_settings.get_llm_documentation( - provider=self.provider) + provider=self.provider) + "\n" elif structured_output_settings.add_thoughts_and_reasoning_field and not self.provider.is_using_json_schema_constraints(): messages[0][ "content"] += structured_output_thoughts_and_reasoning + structured_output_settings.get_llm_documentation( - provider=self.provider) + provider=self.provider) + "\n" elif not structured_output_settings.add_thoughts_and_reasoning_field and not self.provider.is_using_json_schema_constraints(): messages[0][ "content"] += structured_output_without_thoughts_and_reasoning + structured_output_settings.get_llm_documentation( - provider=self.provider) + provider=self.provider) + "\n" prompt, response_role = self.messages_formatter.format_conversation( messages, Roles.assistant diff --git a/src/llama_cpp_agent/prompt_templates.py b/src/llama_cpp_agent/prompt_templates.py index 22ed981..3b84756 100644 --- a/src/llama_cpp_agent/prompt_templates.py +++ b/src/llama_cpp_agent/prompt_templates.py @@ -1,4 +1,5 @@ -function_calling_thoughts_and_reasoning = '''\n\nYou can call functions to help you with your tasks and user queries. To call functions, you respond with a JSON object containing three fields: +function_calling_thoughts_and_reasoning = '''\n +You can call functions to help you with your tasks and user queries. To call functions, you respond with a JSON object containing three fields: "thoughts_and_reasoning": Write down your thoughts and reasoning behind the function call in this field. Think step by step and plan your next action. "function": Write down the name of the function you want to call in this field. @@ -10,7 +11,8 @@ ''' -function_calling_without_thoughts_and_reasoning = '''\n\nYou can call functions to help you with your tasks and user queries. To call functions, you respond with a JSON object containing two fields: +function_calling_without_thoughts_and_reasoning = '''\n +You can call functions to help you with your tasks and user queries. To call functions, you respond with a JSON object containing two fields: "function": Write down the name of the function you want to call in this field. "arguments": Write down arguments for the function in this field. @@ -22,7 +24,8 @@ ''' -function_calling_thoughts_and_reasoning_json_schema = '''\n\nYou can call functions to help you with your tasks and user queries. To call functions, you respond with a JSON object containing three fields: +function_calling_thoughts_and_reasoning_json_schema = '''\n +You can call functions to help you with your tasks and user queries. To call functions, you respond with a JSON object containing three fields: "001_thoughts_and_reasoning": Write down your thoughts and reasoning behind the function call in this field. Think step by step and plan your next action. "002_function": Write down the name of the function you want to call in this field. @@ -34,7 +37,8 @@ ''' -function_calling_without_thoughts_and_reasoning_json_schema = '''\n\nYou can call functions to help you with your tasks and user queries. To call functions, you respond with a JSON object containing two fields: +function_calling_without_thoughts_and_reasoning_json_schema = '''\n +You can call functions to help you with your tasks and user queries. To call functions, you respond with a JSON object containing two fields: "001_function": Write down the name of the function you want to call in this field. "002_arguments": Write down arguments for the function in this field. @@ -46,7 +50,8 @@ ''' -structured_output_thoughts_and_reasoning = '''\n\nYour output is constrained to JSON objects containing the content of specific models, each JSON object has three fields: +structured_output_thoughts_and_reasoning = '''\n +Your output is constrained to JSON objects containing the content of specific models, each JSON object has three fields: "thoughts_and_reasoning": Your thoughts and reasoning behind the model you will output. "model": The name of the model you will output. @@ -56,7 +61,8 @@ ''' -structured_output_without_thoughts_and_reasoning = '''\n\nYour output is constrained to JSON objects containing the content of specific models, each JSON object has two fields: +structured_output_without_thoughts_and_reasoning = '''\n +Your output is constrained to JSON objects containing the content of specific models, each JSON object has two fields: "model": The name of the model you will output. "fields": The fields of the model. @@ -66,7 +72,8 @@ ''' -structured_output_thoughts_and_reasoning_json_schema = '''\n\nYour output is constrained to JSON objects containing the content of specific models, each JSON object has three fields: +structured_output_thoughts_and_reasoning_json_schema = '''\n +Your output is constrained to JSON objects containing the content of specific models, each JSON object has three fields: "001_thoughts_and_reasoning": Your thoughts and reasoning behind the model you will output. "002_model": The name of the model you will output. @@ -76,7 +83,8 @@ ''' -structured_output_without_thoughts_and_reasoning_json_schema = '''\n\nYour output is constrained to JSON objects containing the content of specific models, each JSON object has two fields: +structured_output_without_thoughts_and_reasoning_json_schema = '''\n +Your output is constrained to JSON objects containing the content of specific models, each JSON object has two fields: "001_model": The name of the model you will output. "002_fields": The fields of the model. @@ -100,5 +108,23 @@ Write only the markdown document in your response and begin and end your response with '---'. """ -web_search_system_prompt = """You are a web search specialist and you are able to give detailed answers to user queries based on information extracted from the web. -Write your response to the user in a structured markdown document.""" +web_search_system_prompt = """ +You are an advanced web information search assistant and you are able to formulate the perfect query for a search engine to retrieve useful data. +Your task is to call the 'search_web' function for the user query, with a query optimized for search engines, like duckduckgo, google, etc. + +""" + + +research_system_prompt = """ +You are an excellent research assistant and you are able to write high quality research articles and research reports. You write the research articles and the research reports about subjects given to you by the users. +Provide the response to the user in a structured markdown document following the format below: + +--- +Subject: {Subject} +Content: +{Content} +--- + +Write only the markdown document in your response and begin and end your response with '---'. + +""" diff --git a/src/llama_cpp_agent/tools/web_search/default_web_crawlers.py b/src/llama_cpp_agent/tools/web_search/default_web_crawlers.py index 73fcd35..20ba1ab 100644 --- a/src/llama_cpp_agent/tools/web_search/default_web_crawlers.py +++ b/src/llama_cpp_agent/tools/web_search/default_web_crawlers.py @@ -3,6 +3,9 @@ from .web_search_interfaces import WebCrawler from trafilatura import fetch_url, extract +from bs4 import BeautifulSoup +import requests + class TrafilaturaWebCrawler(WebCrawler): def get_website_content_from_url(self, url: str) -> str: @@ -28,3 +31,26 @@ def get_website_content_from_url(self, url: str) -> str: return "" except Exception as e: return f"An error occurred: {str(e)}" + + +class BeautifulSoupWebCrawler(WebCrawler): + def get_website_content_from_url(self, url: str) -> str: + """ + Get website content from a URL using requests and BeautifulSoup for HTML parsing. + + Args: + url (str): URL to get website content from. + + Returns: + str: Extracted content including title and main text. + """ + try: + response = requests.get(url) + soup = BeautifulSoup(response.text, 'html.parser') + + title = soup.find('title').text if soup.find('title') else "No title found" + body = soup.get_text() + + return f'=========== Website Title: {title} ===========\n\n=========== Website URL: {url} ===========\n\n=========== Website Content ===========\n\n{body}\n\n=========== Website Content End ===========\n\n' + except Exception as e: + return f"An error occurred: {str(e)}" diff --git a/src/llama_cpp_agent/tools/web_search/default_web_search_providers.py b/src/llama_cpp_agent/tools/web_search/default_web_search_providers.py index bd4050f..1049c44 100644 --- a/src/llama_cpp_agent/tools/web_search/default_web_search_providers.py +++ b/src/llama_cpp_agent/tools/web_search/default_web_search_providers.py @@ -1,4 +1,5 @@ from duckduckgo_search import DDGS +from googlesearch import search from .web_search_interfaces import WebSearchProvider @@ -8,3 +9,13 @@ class DDGWebSearchProvider(WebSearchProvider): def search_web(self, search_query: str): results = DDGS().text(search_query, region='wt-wt', safesearch='off', max_results=4) return [res["href"] for res in results] + + +class GoogleWebSearchProvider(WebSearchProvider): + def search_web(self, query: str): + """Searches the web using Google and returns a list of URLs.""" + try: + # Only return the top 5 results for simplicity + return list(search(query, num_results=5)) + except Exception as e: + return f"An error occurred during Google search: {str(e)}" diff --git a/src/llama_cpp_agent/tools/web_search/tool.py b/src/llama_cpp_agent/tools/web_search/tool.py index cd59470..5f4fe6a 100644 --- a/src/llama_cpp_agent/tools/web_search/tool.py +++ b/src/llama_cpp_agent/tools/web_search/tool.py @@ -57,7 +57,7 @@ def search_web(self, search_query: str): web_info = self.summarising_agent.get_chat_response( f"Please summarize the following Website content and extract relevant information to this query:'{search_query}'.\n\n" + web_info, add_response_to_chat_history=False, add_message_to_chat_history=False, llm_sampling_settings=self.settings) - result_string += f"\n\n{web_info.strip()}" + result_string += f"\n{web_info.strip()}" res = result_string.strip() tokens = self.llm_provider.tokenize(res) @@ -73,8 +73,8 @@ def search_web(self, search_query: str): remove_char_count += 50 tokens = self.llm_provider.tokenize(res[:remove_char_count]) if not has_remove_char: - return "\nResults of searching the web:\n\n" + res - return "\nResults of searching the web:\n\n" + res[:remove_char_count] + return res + return res[:remove_char_count] def get_tool(self): return self.search_web