Source code for langchain.agents.self_ask_with_search.base

"""Chain that does self-ask with search."""

from __future__ import annotations

from typing import TYPE_CHECKING, Any, Sequence, Union

from langchain_core._api import deprecated
from langchain_core.language_models import BaseLanguageModel
from langchain_core.prompts import BasePromptTemplate
from langchain_core.pydantic_v1 import Field
from langchain_core.runnables import Runnable, RunnablePassthrough
from langchain_core.tools import BaseTool, Tool

from langchain.agents.agent import Agent, AgentExecutor, AgentOutputParser
from langchain.agents.agent_types import AgentType
from langchain.agents.format_scratchpad import format_log_to_str
from langchain.agents.self_ask_with_search.output_parser import SelfAskOutputParser
from langchain.agents.self_ask_with_search.prompt import PROMPT
from langchain.agents.utils import validate_tools_single_input

if TYPE_CHECKING:
    from langchain_community.utilities.google_serper import GoogleSerperAPIWrapper
    from langchain_community.utilities.searchapi import SearchApiAPIWrapper
    from langchain_community.utilities.serpapi import SerpAPIWrapper


[docs] @deprecated("0.1.0", alternative="create_self_ask_with_search", removal="0.3.0") class SelfAskWithSearchAgent(Agent): """Agent for the self-ask-with-search paper.""" output_parser: AgentOutputParser = Field(default_factory=SelfAskOutputParser) @classmethod def _get_default_output_parser(cls, **kwargs: Any) -> AgentOutputParser: return SelfAskOutputParser() @property def _agent_type(self) -> str: """Return Identifier of an agent type.""" return AgentType.SELF_ASK_WITH_SEARCH
[docs] @classmethod def create_prompt(cls, tools: Sequence[BaseTool]) -> BasePromptTemplate: """Prompt does not depend on tools.""" return PROMPT
@classmethod def _validate_tools(cls, tools: Sequence[BaseTool]) -> None: validate_tools_single_input(cls.__name__, tools) super()._validate_tools(tools) if len(tools) != 1: raise ValueError(f"Exactly one tool must be specified, but got {tools}") tool_names = {tool.name for tool in tools} if tool_names != {"Intermediate Answer"}: raise ValueError( f"Tool name should be Intermediate Answer, got {tool_names}" ) @property def observation_prefix(self) -> str: """Prefix to append the observation with.""" return "Intermediate answer: " @property def llm_prefix(self) -> str: """Prefix to append the LLM call with.""" return ""
[docs] @deprecated("0.1.0", removal="0.3.0") class SelfAskWithSearchChain(AgentExecutor): """[Deprecated] Chain that does self-ask with search.""" def __init__( self, llm: BaseLanguageModel, search_chain: Union[ GoogleSerperAPIWrapper, SearchApiAPIWrapper, SerpAPIWrapper ], **kwargs: Any, ): """Initialize only with an LLM and a search chain.""" search_tool = Tool( name="Intermediate Answer", func=search_chain.run, coroutine=search_chain.arun, description="Search", ) agent = SelfAskWithSearchAgent.from_llm_and_tools(llm, [search_tool]) super().__init__(agent=agent, tools=[search_tool], **kwargs)
[docs] def create_self_ask_with_search_agent( llm: BaseLanguageModel, tools: Sequence[BaseTool], prompt: BasePromptTemplate ) -> Runnable: """Create an agent that uses self-ask with search prompting. Args: llm: LLM to use as the agent. tools: List of tools. Should just be of length 1, with that tool having name `Intermediate Answer` prompt: The prompt to use, must have input key `agent_scratchpad` which will contain agent actions and tool outputs. Returns: A Runnable sequence representing an agent. It takes as input all the same input variables as the prompt passed in does. It returns as output either an AgentAction or AgentFinish. Examples: .. code-block:: python from langchain import hub from langchain_community.chat_models import ChatAnthropic from langchain.agents import ( AgentExecutor, create_self_ask_with_search_agent ) prompt = hub.pull("hwchase17/self-ask-with-search") model = ChatAnthropic(model="claude-3-haiku-20240307") tools = [...] # Should just be one tool with name `Intermediate Answer` agent = create_self_ask_with_search_agent(model, tools, prompt) agent_executor = AgentExecutor(agent=agent, tools=tools) agent_executor.invoke({"input": "hi"}) Prompt: The prompt must have input key `agent_scratchpad` which will contain agent actions and tool outputs as a string. Here's an example: .. code-block:: python from langchain_core.prompts import PromptTemplate template = '''Question: Who lived longer, Muhammad Ali or Alan Turing? Are follow up questions needed here: Yes. Follow up: How old was Muhammad Ali when he died? Intermediate answer: Muhammad Ali was 74 years old when he died. Follow up: How old was Alan Turing when he died? Intermediate answer: Alan Turing was 41 years old when he died. So the final answer is: Muhammad Ali Question: When was the founder of craigslist born? Are follow up questions needed here: Yes. Follow up: Who was the founder of craigslist? Intermediate answer: Craigslist was founded by Craig Newmark. Follow up: When was Craig Newmark born? Intermediate answer: Craig Newmark was born on December 6, 1952. So the final answer is: December 6, 1952 Question: Who was the maternal grandfather of George Washington? Are follow up questions needed here: Yes. Follow up: Who was the mother of George Washington? Intermediate answer: The mother of George Washington was Mary Ball Washington. Follow up: Who was the father of Mary Ball Washington? Intermediate answer: The father of Mary Ball Washington was Joseph Ball. So the final answer is: Joseph Ball Question: Are both the directors of Jaws and Casino Royale from the same country? Are follow up questions needed here: Yes. Follow up: Who is the director of Jaws? Intermediate answer: The director of Jaws is Steven Spielberg. Follow up: Where is Steven Spielberg from? Intermediate answer: The United States. Follow up: Who is the director of Casino Royale? Intermediate answer: The director of Casino Royale is Martin Campbell. Follow up: Where is Martin Campbell from? Intermediate answer: New Zealand. So the final answer is: No Question: {input} Are followup questions needed here:{agent_scratchpad}''' prompt = PromptTemplate.from_template(template) """ # noqa: E501 missing_vars = {"agent_scratchpad"}.difference( prompt.input_variables + list(prompt.partial_variables) ) if missing_vars: raise ValueError(f"Prompt missing required variables: {missing_vars}") if len(tools) != 1: raise ValueError("This agent expects exactly one tool") tool = list(tools)[0] if tool.name != "Intermediate Answer": raise ValueError( "This agent expects the tool to be named `Intermediate Answer`" ) llm_with_stop = llm.bind(stop=["\nIntermediate answer:"]) agent = ( RunnablePassthrough.assign( agent_scratchpad=lambda x: format_log_to_str( x["intermediate_steps"], observation_prefix="\nIntermediate answer: ", llm_prefix="", ), # Give it a default chat_history=lambda x: x.get("chat_history", ""), ) | prompt | llm_with_stop | SelfAskOutputParser() ) return agent