Google Search API integration with serper.dev (wrapper, tests, docs, … (#909)

Adds Google Search integration with [Serper](https://serper.dev) a
low-cost alternative to SerpAPI (10x cheaper + generous free tier).
Includes documentation, tests and examples. Hopefully I am not missing
anything.

Developers can sign up for a free account at
[serper.dev](https://serper.dev) and obtain an api key.

## Usage

```python
from langchain.utilities import GoogleSerperAPIWrapper
from langchain.llms.openai import OpenAI
from langchain.agents import initialize_agent, Tool

import os
os.environ["SERPER_API_KEY"] = ""
os.environ['OPENAI_API_KEY'] = ""

llm = OpenAI(temperature=0)
search = GoogleSerperAPIWrapper()
tools = [
    Tool(
        name="Intermediate Answer",
        func=search.run
    )
]

self_ask_with_search = initialize_agent(tools, llm, agent="self-ask-with-search", verbose=True)
self_ask_with_search.run("What is the hometown of the reigning men's U.S. Open champion?")
```

### Output
```
Entering new AgentExecutor chain...
 Yes.
Follow up: Who is the reigning men's U.S. Open champion?
Intermediate answer: Current champions Carlos Alcaraz, 2022 men's singles champion.
Follow up: Where is Carlos Alcaraz from?
Intermediate answer: El Palmar, Spain
So the final answer is: El Palmar, Spain

> Finished chain.

'El Palmar, Spain'
```
searx-api
rogerserper 1 year ago committed by GitHub
parent 52753066ef
commit e46cd3b7db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,70 @@
# Google Serper Wrapper
This page covers how to use the [Serper](https://serper.dev) Google Search API within LangChain. Serper is a low-cost Google Search API that can be used to add answer box, knowledge graph, and organic results data from Google Search.
It is broken into two parts: setup, and then references to the specific Google Serper wrapper.
## Setup
- Go to [serper.dev](https://serper.dev) to sign up for a free account
- Get the api key and set it as an environment variable (`SERPER_API_KEY`)
## Wrappers
### Utility
There exists a GoogleSerperAPIWrapper utility which wraps this API. To import this utility:
```python
from langchain.utilities import GoogleSerperAPIWrapper
```
You can use it as part of a Self Ask chain:
```python
from langchain.utilities import GoogleSerperAPIWrapper
from langchain.llms.openai import OpenAI
from langchain.agents import initialize_agent, Tool
import os
os.environ["SERPER_API_KEY"] = ""
os.environ['OPENAI_API_KEY'] = ""
llm = OpenAI(temperature=0)
search = GoogleSerperAPIWrapper()
tools = [
Tool(
name="Intermediate Answer",
func=search.run
)
]
self_ask_with_search = initialize_agent(tools, llm, agent="self-ask-with-search", verbose=True)
self_ask_with_search.run("What is the hometown of the reigning men's U.S. Open champion?")
```
#### Output
```
Entering new AgentExecutor chain...
Yes.
Follow up: Who is the reigning men's U.S. Open champion?
Intermediate answer: Current champions Carlos Alcaraz, 2022 men's singles champion.
Follow up: Where is Carlos Alcaraz from?
Intermediate answer: El Palmar, Spain
So the final answer is: El Palmar, Spain
> Finished chain.
'El Palmar, Spain'
```
For a more detailed walkthrough of this wrapper, see [this notebook](../modules/utils/examples/google_serper.ipynb).
### Tool
You can also easily load this wrapper as a Tool (to use with an Agent).
You can do this with:
```python
from langchain.agents import load_tools
tools = load_tools(["google-serper"])
```
For more information on this, see [this page](../modules/agents/tools.md)

@ -12,9 +12,13 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 1, "execution_count": null,
"id": "e6860c2d", "id": "e6860c2d",
"metadata": {}, "metadata": {
"pycharm": {
"is_executing": true
}
},
"outputs": [], "outputs": [],
"source": [ "source": [
"from langchain.agents import load_tools\n", "from langchain.agents import load_tools\n",
@ -34,39 +38,111 @@
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "a09ca013", "source": [
"metadata": {}, "## Google Serper API Wrapper\n",
"\n",
"First, let's try to use the Google Serper API tool."
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 4,
"outputs": [],
"source": [
"tools = load_tools([\"google-serper\"], llm=llm)"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 5,
"outputs": [],
"source": [
"agent = initialize_agent(tools, llm, agent=\"zero-shot-react-description\", verbose=True)"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 6,
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\n",
"\u001B[1m> Entering new AgentExecutor chain...\u001B[0m\n",
"\u001B[32;1m\u001B[1;3m I need to find out what the current weather is in Pomfret.\n",
"Action: Search\n",
"Action Input: \"weather in Pomfret\"\u001B[0m\n",
"Observation: \u001B[36;1m\u001B[1;3mShowers early becoming a steady light rain later in the day. Near record high temperatures. High around 60F. Winds SW at 10 to 15 mph. Chance of rain 60%.\u001B[0m\n",
"Thought:\u001B[32;1m\u001B[1;3m I now know the current weather in Pomfret.\n",
"Final Answer: Showers early becoming a steady light rain later in the day. Near record high temperatures. High around 60F. Winds SW at 10 to 15 mph. Chance of rain 60%.\u001B[0m\n",
"\u001B[1m> Finished AgentExecutor chain.\u001B[0m\n"
]
},
{
"data": {
"text/plain": [
"'Showers early becoming a steady light rain later in the day. Near record high temperatures. High around 60F. Winds SW at 10 to 15 mph. Chance of rain 60%.'"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"agent.run(\"What is the weather in Pomfret?\")"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "markdown",
"source": [ "source": [
"## SerpAPI\n", "## SerpAPI\n",
"\n", "\n",
"First, let's use the SerpAPI tool." "Now, let's use the SerpAPI tool."
] ],
"metadata": {
"collapsed": false
}
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 4, "execution_count": 4,
"id": "dd4ce6d9",
"metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"tools = load_tools([\"serpapi\"], llm=llm)" "tools = load_tools([\"serpapi\"], llm=llm)"
] ],
"metadata": {
"collapsed": false
}
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 5, "execution_count": 5,
"id": "ef63bb84",
"metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"agent = initialize_agent(tools, llm, agent=\"zero-shot-react-description\", verbose=True)" "agent = initialize_agent(tools, llm, agent=\"zero-shot-react-description\", verbose=True)"
] ],
"metadata": {
"collapsed": false
}
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 6, "execution_count": 6,
"id": "53e24f5d",
"metadata": {},
"outputs": [ "outputs": [
{ {
"name": "stdout", "name": "stdout",
@ -74,14 +150,14 @@
"text": [ "text": [
"\n", "\n",
"\n", "\n",
"\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", "\u001B[1m> Entering new AgentExecutor chain...\u001B[0m\n",
"\u001b[32;1m\u001b[1;3m I need to find out what the current weather is in Pomfret.\n", "\u001B[32;1m\u001B[1;3m I need to find out what the current weather is in Pomfret.\n",
"Action: Search\n", "Action: Search\n",
"Action Input: \"weather in Pomfret\"\u001b[0m\n", "Action Input: \"weather in Pomfret\"\u001B[0m\n",
"Observation: \u001b[36;1m\u001b[1;3mShowers early becoming a steady light rain later in the day. Near record high temperatures. High around 60F. Winds SW at 10 to 15 mph. Chance of rain 60%.\u001b[0m\n", "Observation: \u001B[36;1m\u001B[1;3mShowers early becoming a steady light rain later in the day. Near record high temperatures. High around 60F. Winds SW at 10 to 15 mph. Chance of rain 60%.\u001B[0m\n",
"Thought:\u001b[32;1m\u001b[1;3m I now know the current weather in Pomfret.\n", "Thought:\u001B[32;1m\u001B[1;3m I now know the current weather in Pomfret.\n",
"Final Answer: Showers early becoming a steady light rain later in the day. Near record high temperatures. High around 60F. Winds SW at 10 to 15 mph. Chance of rain 60%.\u001b[0m\n", "Final Answer: Showers early becoming a steady light rain later in the day. Near record high temperatures. High around 60F. Winds SW at 10 to 15 mph. Chance of rain 60%.\u001B[0m\n",
"\u001b[1m> Finished AgentExecutor chain.\u001b[0m\n" "\u001B[1m> Finished AgentExecutor chain.\u001B[0m\n"
] ]
}, },
{ {
@ -97,43 +173,47 @@
], ],
"source": [ "source": [
"agent.run(\"What is the weather in Pomfret?\")" "agent.run(\"What is the weather in Pomfret?\")"
] ],
"metadata": {
"collapsed": false
}
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"id": "8ef49137",
"metadata": {},
"source": [ "source": [
"## GoogleSearchAPIWrapper\n", "## GoogleSearchAPIWrapper\n",
"\n", "\n",
"Now, let's use the official Google Search API Wrapper." "Now, let's use the official Google Search API Wrapper."
] ],
"metadata": {
"collapsed": false
}
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 13, "execution_count": 13,
"id": "3e9c7c20",
"metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"tools = load_tools([\"google-search\"], llm=llm)" "tools = load_tools([\"google-search\"], llm=llm)"
] ],
"metadata": {
"collapsed": false
}
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 14, "execution_count": 14,
"id": "b83624dc",
"metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"agent = initialize_agent(tools, llm, agent=\"zero-shot-react-description\", verbose=True)" "agent = initialize_agent(tools, llm, agent=\"zero-shot-react-description\", verbose=True)"
] ],
"metadata": {
"collapsed": false
}
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 17, "execution_count": 17,
"id": "9d5835e2",
"metadata": {},
"outputs": [ "outputs": [
{ {
"name": "stdout", "name": "stdout",
@ -141,14 +221,14 @@
"text": [ "text": [
"\n", "\n",
"\n", "\n",
"\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", "\u001B[1m> Entering new AgentExecutor chain...\u001B[0m\n",
"\u001b[32;1m\u001b[1;3m I should look up the current weather conditions.\n", "\u001B[32;1m\u001B[1;3m I should look up the current weather conditions.\n",
"Action: Google Search\n", "Action: Google Search\n",
"Action Input: \"weather in Pomfret\"\u001b[0m\n", "Action Input: \"weather in Pomfret\"\u001B[0m\n",
"Observation: \u001b[36;1m\u001b[1;3mShowers early becoming a steady light rain later in the day. Near record high temperatures. High around 60F. Winds SW at 10 to 15 mph. Chance of rain 60%. Pomfret, CT Weather Forecast, with current conditions, wind, air quality, and what to expect for the next 3 days. Hourly Weather-Pomfret, CT. As of 12:52 am EST. Special Weather Statement +2 ... Hazardous Weather Conditions. Special Weather Statement ... Pomfret CT. Tonight ... National Digital Forecast Database Maximum Temperature Forecast. Pomfret Center Weather Forecasts. Weather Underground provides local & long-range weather forecasts, weatherreports, maps & tropical weather conditions for ... Pomfret, CT 12 hour by hour weather forecast includes precipitation, temperatures, sky conditions, rain chance, dew-point, relative humidity, wind direction ... North Pomfret Weather Forecasts. Weather Underground provides local & long-range weather forecasts, weatherreports, maps & tropical weather conditions for ... Today's Weather - Pomfret, CT. Dec 31, 2022 4:00 PM. Putnam MS. --. Weather forecast icon. Feels like --. Hi --. Lo --. Pomfret, CT temperature trend for the next 14 Days. Find daytime highs and nighttime lows from TheWeatherNetwork.com. Pomfret, MD Weather Forecast Date: 332 PM EST Wed Dec 28 2022. The area/counties/county of: Charles, including the cites of: St. Charles and Waldorf.\u001b[0m\n", "Observation: \u001B[36;1m\u001B[1;3mShowers early becoming a steady light rain later in the day. Near record high temperatures. High around 60F. Winds SW at 10 to 15 mph. Chance of rain 60%. Pomfret, CT Weather Forecast, with current conditions, wind, air quality, and what to expect for the next 3 days. Hourly Weather-Pomfret, CT. As of 12:52 am EST. Special Weather Statement +2 ... Hazardous Weather Conditions. Special Weather Statement ... Pomfret CT. Tonight ... National Digital Forecast Database Maximum Temperature Forecast. Pomfret Center Weather Forecasts. Weather Underground provides local & long-range weather forecasts, weatherreports, maps & tropical weather conditions for ... Pomfret, CT 12 hour by hour weather forecast includes precipitation, temperatures, sky conditions, rain chance, dew-point, relative humidity, wind direction ... North Pomfret Weather Forecasts. Weather Underground provides local & long-range weather forecasts, weatherreports, maps & tropical weather conditions for ... Today's Weather - Pomfret, CT. Dec 31, 2022 4:00 PM. Putnam MS. --. Weather forecast icon. Feels like --. Hi --. Lo --. Pomfret, CT temperature trend for the next 14 Days. Find daytime highs and nighttime lows from TheWeatherNetwork.com. Pomfret, MD Weather Forecast Date: 332 PM EST Wed Dec 28 2022. The area/counties/county of: Charles, including the cites of: St. Charles and Waldorf.\u001B[0m\n",
"Thought:\u001b[32;1m\u001b[1;3m I now know the current weather conditions in Pomfret.\n", "Thought:\u001B[32;1m\u001B[1;3m I now know the current weather conditions in Pomfret.\n",
"Final Answer: Showers early becoming a steady light rain later in the day. Near record high temperatures. High around 60F. Winds SW at 10 to 15 mph. Chance of rain 60%.\u001b[0m\n", "Final Answer: Showers early becoming a steady light rain later in the day. Near record high temperatures. High around 60F. Winds SW at 10 to 15 mph. Chance of rain 60%.\u001B[0m\n",
"\u001b[1m> Finished AgentExecutor chain.\u001b[0m\n" "\u001B[1m> Finished AgentExecutor chain.\u001B[0m\n"
] ]
}, },
{ {
@ -164,7 +244,10 @@
], ],
"source": [ "source": [
"agent.run(\"What is the weather in Pomfret?\")" "agent.run(\"What is the weather in Pomfret?\")"
] ],
"metadata": {
"collapsed": false
}
} }
], ],
"metadata": { "metadata": {

@ -119,3 +119,12 @@ Below is a list of all supported tools and relevant information:
- Requires LLM: No - Requires LLM: No
- Extra Parameters: `google_api_key`, `google_cse_id` - Extra Parameters: `google_api_key`, `google_cse_id`
- For more information on this, see [this page](../../ecosystem/google_search.md) - For more information on this, see [this page](../../ecosystem/google_search.md)
**google-serper**
- Tool Name: Search
- Tool Description: A low-cost Google Search API. Useful for when you need to answer questions about current events. Input should be a search query.
- Notes: Calls the [serper.dev](https://serper.dev) Google Search API and then parses results.
- Requires LLM: No
- Extra Parameters: `serper_api_key`
- For more information on this, see [this page](../../ecosystem/google_serper.md)

@ -0,0 +1,157 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "dc23c48e",
"metadata": {},
"source": [
"# Google Serper API\n",
"\n",
"This notebook goes over how to use the Google Serper component to search the web. First you need to sign up for a free account at [serper.dev](https://serper.dev) and get your api key."
]
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"import os\n",
"os.environ[\"SERPER_API_KEY\"] = \"\""
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 2,
"id": "54bf5afd",
"metadata": {},
"outputs": [],
"source": [
"from langchain.utilities import GoogleSerperAPIWrapper"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "31f8f382",
"metadata": {},
"outputs": [],
"source": [
"search = GoogleSerperAPIWrapper()"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "25ce0225",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": "'Barack Hussein Obama II'"
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"search.run(\"Obama's first name?\")"
]
},
{
"cell_type": "markdown",
"source": [
"## As part of a Self Ask With Search Chain"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"os.environ['OPENAI_API_KEY'] = \"\""
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 5,
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\n",
"\u001B[1m> Entering new AgentExecutor chain...\u001B[0m\n",
"\u001B[32;1m\u001B[1;3m Yes.\n",
"Follow up: Who is the reigning men's U.S. Open champion?\u001B[0m\n",
"Intermediate answer: \u001B[36;1m\u001B[1;3mCurrent champions Carlos Alcaraz, 2022 men's singles champion.\u001B[0m\n",
"\u001B[32;1m\u001B[1;3mFollow up: Where is Carlos Alcaraz from?\u001B[0m\n",
"Intermediate answer: \u001B[36;1m\u001B[1;3mEl Palmar, Spain\u001B[0m\n",
"\u001B[32;1m\u001B[1;3mSo the final answer is: El Palmar, Spain\u001B[0m\n",
"\n",
"\u001B[1m> Finished chain.\u001B[0m\n"
]
},
{
"data": {
"text/plain": "'El Palmar, Spain'"
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from langchain.utilities import GoogleSerperAPIWrapper\n",
"from langchain.llms.openai import OpenAI\n",
"from langchain.agents import initialize_agent, Tool\n",
"\n",
"llm = OpenAI(temperature=0)\n",
"search = GoogleSerperAPIWrapper()\n",
"tools = [\n",
" Tool(\n",
" name=\"Intermediate Answer\",\n",
" func=search.run\n",
" )\n",
"]\n",
"\n",
"self_ask_with_search = initialize_agent(tools, llm, agent=\"self-ask-with-search\", verbose=True)\n",
"self_ask_with_search.run(\"What is the hometown of the reigning men's U.S. Open champion?\")"
],
"metadata": {
"collapsed": false
}
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.9"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -42,6 +42,7 @@ from langchain.prompts import (
from langchain.serpapi import SerpAPIChain, SerpAPIWrapper from langchain.serpapi import SerpAPIChain, SerpAPIWrapper
from langchain.sql_database import SQLDatabase from langchain.sql_database import SQLDatabase
from langchain.utilities.google_search import GoogleSearchAPIWrapper from langchain.utilities.google_search import GoogleSearchAPIWrapper
from langchain.utilities.google_serper import GoogleSerperAPIWrapper
from langchain.utilities.wolfram_alpha import WolframAlphaAPIWrapper from langchain.utilities.wolfram_alpha import WolframAlphaAPIWrapper
from langchain.vectorstores import FAISS, ElasticVectorSearch from langchain.vectorstores import FAISS, ElasticVectorSearch
@ -58,6 +59,7 @@ __all__ = [
"SerpAPIWrapper", "SerpAPIWrapper",
"SerpAPIChain", "SerpAPIChain",
"GoogleSearchAPIWrapper", "GoogleSearchAPIWrapper",
"GoogleSerperAPIWrapper",
"WolframAlphaAPIWrapper", "WolframAlphaAPIWrapper",
"Anthropic", "Anthropic",
"CerebriumAI", "CerebriumAI",

@ -13,6 +13,7 @@ from langchain.requests import RequestsWrapper
from langchain.serpapi import SerpAPIWrapper from langchain.serpapi import SerpAPIWrapper
from langchain.utilities.bash import BashProcess from langchain.utilities.bash import BashProcess
from langchain.utilities.google_search import GoogleSearchAPIWrapper from langchain.utilities.google_search import GoogleSearchAPIWrapper
from langchain.utilities.google_serper import GoogleSerperAPIWrapper
from langchain.utilities.wolfram_alpha import WolframAlphaAPIWrapper from langchain.utilities.wolfram_alpha import WolframAlphaAPIWrapper
@ -131,6 +132,14 @@ def _get_google_search(**kwargs: Any) -> Tool:
) )
def _get_google_serper(**kwargs: Any) -> Tool:
return Tool(
"Search",
GoogleSerperAPIWrapper(**kwargs).run,
"A low-cost Google Search API. Useful for when you need to answer questions about current events. Input should be a search query.",
)
def _get_serpapi(**kwargs: Any) -> Tool: def _get_serpapi(**kwargs: Any) -> Tool:
return Tool( return Tool(
name="Search", name="Search",
@ -147,6 +156,7 @@ _EXTRA_LLM_TOOLS = {
_EXTRA_OPTIONAL_TOOLS = { _EXTRA_OPTIONAL_TOOLS = {
"wolfram-alpha": (_get_wolfram_alpha, ["wolfram_alpha_appid"]), "wolfram-alpha": (_get_wolfram_alpha, ["wolfram_alpha_appid"]),
"google-search": (_get_google_search, ["google_api_key", "google_cse_id"]), "google-search": (_get_google_search, ["google_api_key", "google_cse_id"]),
"google-serper": (_get_google_serper, ["serper_api_key"]),
"serpapi": (_get_serpapi, ["serpapi_api_key", "aiosession"]), "serpapi": (_get_serpapi, ["serpapi_api_key", "aiosession"]),
} }

@ -1,5 +1,5 @@
"""Chain that does self ask with search.""" """Chain that does self ask with search."""
from typing import Any, List, Optional, Tuple from typing import Any, List, Optional, Tuple, Union
from langchain.agents.agent import Agent, AgentExecutor from langchain.agents.agent import Agent, AgentExecutor
from langchain.agents.self_ask_with_search.prompt import PROMPT from langchain.agents.self_ask_with_search.prompt import PROMPT
@ -7,6 +7,7 @@ from langchain.agents.tools import Tool
from langchain.llms.base import BaseLLM from langchain.llms.base import BaseLLM
from langchain.prompts.base import BasePromptTemplate from langchain.prompts.base import BasePromptTemplate
from langchain.serpapi import SerpAPIWrapper from langchain.serpapi import SerpAPIWrapper
from langchain.utilities.google_serper import GoogleSerperAPIWrapper
class SelfAskWithSearchAgent(Agent): class SelfAskWithSearchAgent(Agent):
@ -74,12 +75,17 @@ class SelfAskWithSearchChain(AgentExecutor):
Example: Example:
.. code-block:: python .. code-block:: python
from langchain import SelfAskWithSearchChain, OpenAI, SerpAPIWrapper from langchain import SelfAskWithSearchChain, OpenAI, GoogleSerperAPIWrapper
search_chain = SerpAPIWrapper() search_chain = GoogleSerperAPIWrapper()
self_ask = SelfAskWithSearchChain(llm=OpenAI(), search_chain=search_chain) self_ask = SelfAskWithSearchChain(llm=OpenAI(), search_chain=search_chain)
""" """
def __init__(self, llm: BaseLLM, search_chain: SerpAPIWrapper, **kwargs: Any): def __init__(
self,
llm: BaseLLM,
search_chain: Union[GoogleSerperAPIWrapper, SerpAPIWrapper],
**kwargs: Any,
):
"""Initialize with just an LLM and a search chain.""" """Initialize with just an LLM and a search chain."""
search_tool = Tool(name="Intermediate Answer", func=search_chain.run) search_tool = Tool(name="Intermediate Answer", func=search_chain.run)
agent = SelfAskWithSearchAgent.from_llm_and_tools(llm, [search_tool]) agent = SelfAskWithSearchAgent.from_llm_and_tools(llm, [search_tool])

@ -5,6 +5,7 @@ from langchain.serpapi import SerpAPIWrapper
from langchain.utilities.bash import BashProcess from langchain.utilities.bash import BashProcess
from langchain.utilities.bing_search import BingSearchAPIWrapper from langchain.utilities.bing_search import BingSearchAPIWrapper
from langchain.utilities.google_search import GoogleSearchAPIWrapper from langchain.utilities.google_search import GoogleSearchAPIWrapper
from langchain.utilities.google_serper import GoogleSerperAPIWrapper
from langchain.utilities.wolfram_alpha import WolframAlphaAPIWrapper from langchain.utilities.wolfram_alpha import WolframAlphaAPIWrapper
__all__ = [ __all__ = [
@ -12,6 +13,7 @@ __all__ = [
"RequestsWrapper", "RequestsWrapper",
"PythonREPL", "PythonREPL",
"GoogleSearchAPIWrapper", "GoogleSearchAPIWrapper",
"GoogleSerperAPIWrapper",
"WolframAlphaAPIWrapper", "WolframAlphaAPIWrapper",
"SerpAPIWrapper", "SerpAPIWrapper",
"BingSearchAPIWrapper", "BingSearchAPIWrapper",

@ -0,0 +1,94 @@
"""Util that calls Google Search using the Serper.dev API."""
from typing import Dict, Optional
import requests
from pydantic.class_validators import root_validator
from pydantic.main import BaseModel
from langchain.utils import get_from_dict_or_env
class GoogleSerperAPIWrapper(BaseModel):
"""Wrapper around the Serper.dev Google Search API.
You can create a free API key at https://serper.dev.
To use, you should have the environment variable ``SERPER_API_KEY``
set with your API key, or pass `serper_api_key` as a named parameter
to the constructor.
Example:
.. code-block:: python
from langchain import GoogleSerperAPIWrapper
google_serper = GoogleSerperAPIWrapper()
"""
k: int = 10
gl: str = "us"
hl: str = "en"
serper_api_key: Optional[str] = None
@root_validator()
def validate_environment(cls, values: Dict) -> Dict:
"""Validate that api key exists in environment."""
serper_api_key = get_from_dict_or_env(
values, "serper_api_key", "SERPER_API_KEY"
)
values["serper_api_key"] = serper_api_key
return values
def run(self, query: str) -> str:
"""Run query through GoogleSearch and parse result."""
results = self._google_serper_search_results(query, gl=self.gl, hl=self.hl)
return self._parse_results(results)
def _parse_results(self, results: dict) -> str:
snippets = []
if results.get("answerBox"):
answer_box = results.get("answerBox", {})
if answer_box.get("answer"):
return answer_box.get("answer")
elif answer_box.get("snippet"):
return answer_box.get("snippet").replace("\n", " ")
elif answer_box.get("snippetHighlighted"):
return ", ".join(answer_box.get("snippetHighlighted"))
if results.get("knowledgeGraph"):
kg = results.get("knowledgeGraph", {})
title = kg.get("title")
entity_type = kg.get("type")
if entity_type:
snippets.append(f"{title}: {entity_type}.")
description = kg.get("description")
if description:
snippets.append(description)
for attribute, value in kg.get("attributes", {}).items():
snippets.append(f"{title} {attribute}: {value}.")
for result in results["organic"][: self.k]:
if "snippet" in result:
snippets.append(result["snippet"])
for attribute, value in result.get("attributes", {}).items():
snippets.append(f"{attribute}: {value}.")
if len(snippets) == 0:
return "No good Google Search Result was found"
return " ".join(snippets)
def _google_serper_search_results(self, search_term: str, gl: str, hl: str) -> dict:
headers = {
"X-API-KEY": self.serper_api_key or "",
"Content-Type": "application/json",
}
params = {"q": search_term, "gl": gl, "hl": hl}
response = requests.post(
"https://google.serper.dev/search", headers=headers, params=params
)
response.raise_for_status()
search_results = response.json()
return search_results

@ -1,7 +1,7 @@
"""Integration test for self ask with search.""" """Integration test for self ask with search."""
from langchain.agents.self_ask_with_search.base import SelfAskWithSearchChain from langchain.agents.self_ask_with_search.base import SelfAskWithSearchChain
from langchain.llms.openai import OpenAI from langchain.llms.openai import OpenAI
from langchain.serpapi import SerpAPIWrapper from langchain.utilities.google_serper import GoogleSerperAPIWrapper
def test_self_ask_with_search() -> None: def test_self_ask_with_search() -> None:
@ -9,7 +9,7 @@ def test_self_ask_with_search() -> None:
question = "What is the hometown of the reigning men's U.S. Open champion?" question = "What is the hometown of the reigning men's U.S. Open champion?"
chain = SelfAskWithSearchChain( chain = SelfAskWithSearchChain(
llm=OpenAI(temperature=0), llm=OpenAI(temperature=0),
search_chain=SerpAPIWrapper(), search_chain=GoogleSerperAPIWrapper(),
input_key="q", input_key="q",
output_key="a", output_key="a",
) )

@ -0,0 +1,9 @@
"""Integration test for Serper.dev's Google Search API Wrapper."""
from langchain.utilities.google_serper import GoogleSerperAPIWrapper
def test_call() -> None:
"""Test that call gives the correct answer."""
search = GoogleSerperAPIWrapper()
output = search.run("What was Obama's first name?")
assert "Barack Hussein Obama II" in output
Loading…
Cancel
Save