Source code for langchain.agents.self_ask_with_search.base

"""链式结构,执行自我询问并进行搜索。"""
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): """自问自答搜索论文的代理。""" 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 AgentType.SELF_ASK_WITH_SEARCH
[docs] @classmethod def create_prompt(cls, tools: Sequence[BaseTool]) -> BasePromptTemplate: """提示不依赖于工具。""" 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: """要附加到观测值前面的前缀。""" return "Intermediate answer: " @property def llm_prefix(self) -> str: """LLM调用前要添加的前缀。""" return ""
[docs]@deprecated("0.1.0", removal="0.3.0") class SelfAskWithSearchChain(AgentExecutor): """[已弃用] 执行自我询问并进行搜索的链。""" def __init__( self, llm: BaseLanguageModel, search_chain: Union[ GoogleSerperAPIWrapper, SearchApiAPIWrapper, SerpAPIWrapper ], **kwargs: Any, ): """仅使用LLM和搜索链进行初始化。""" 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: """创建一个使用自我询问和搜索提示的代理。 参数: llm:用作代理的LLM。 tools:工具列表。应该只有一个工具,该工具的名称为`Intermediate Answer`。 prompt:要使用的提示,必须具有输入键`agent_scratchpad`,其中将包含代理操作和工具输出。 返回: 代表代理的可运行序列。它接受与传入的提示相同的所有输入变量。它返回一个AgentAction或AgentFinish。 示例: .. 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 = [...] # 应该只有一个名称为`Intermediate Answer`的工具 agent = create_self_ask_with_search_agent(model, tools, prompt) agent_executor = AgentExecutor(agent=agent, tools=tools) agent_executor.invoke({"input": "hi"}) 提示: 提示必须具有输入键`agent_scratchpad`,其中将包含代理操作和工具输出的字符串。 这是一个示例: .. code-block:: python from langchain_core.prompts import PromptTemplate template = '''问题:谁活得更久,穆罕默德·阿里还是艾伦·图灵? 这里是否需要后续问题:是的。 后续问题:穆罕默德·阿里去世时多大年纪? 中间答案:穆罕默德·阿里去世时74岁。 后续问题:艾伦·图灵去世时多大年纪? 中间答案:艾伦·图灵去世时41岁。 因此最终答案是:穆罕默德·阿里 问题:craigslist的创始人是什么时候出生的? 这里是否需要后续问题:是的。 后续问题:craigslist的创始人是谁? 中间答案:craigslist的创始人是Craig Newmark。 后续问题:Craig Newmark是什么时候出生的? 中间答案:Craig Newmark于1952年12月6日出生。 因此最终答案是:1952年12月6日 问题:乔治·华盛顿的外祖父是谁? 这里是否需要后续问题:是的。 后续问题:乔治·华盛顿的母亲是谁? 中间答案:乔治·华盛顿的母亲是玛丽·波尔·华盛顿。 后续问题:玛丽·波尔·华盛顿的父亲是谁? 中间答案:玛丽·波尔·华盛顿的父亲是约瑟夫·波尔。 因此最终答案是:约瑟夫·波尔 问题:《大白鲨》和《皇家赌场》的导演都来自同一个国家吗? 这里是否需要后续问题:是的。 后续问题:《大白鲨》的导演是谁? 中间答案:《大白鲨》的导演是史蒂文·斯皮尔伯格。 后续问题:史蒂文·斯皮尔伯格来自哪里? 中间答案:美国。 后续问题:《皇家赌场》的导演是谁? 中间答案:《皇家赌场》的导演是马丁·坎贝尔。 后续问题:马丁·坎贝尔来自哪里? 中间答案:新西兰。 因此最终答案是:不是 问题:{input} 这里是否需要后续问题:{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