构建LangChain应用程序的示例代码:18、使用检索技术来选择一组工具以回答代理查询教程

自定义代理与工具检索

介绍

本文介绍了使用检索技术来选择一组工具以回答代理查询的概念。当您有很多工具可供选择时,这种方法非常有用。由于上下文长度问题,您不能在提示中放入所有工具的描述,因此您可以在运行时动态选择要使用的N个工具。

设置环境

进行必要的导入等。

import re
from typing import Union

# 导入必要的库和模块
from langchain.agents import (
    AgentExecutor,
    AgentOutputParser,
    LLMSingleActionAgent,
    Tool,
)
from langchain.chains import LLMChain
from langchain.prompts import StringPromptTemplate
from langchain_community.utilities import SerpAPIWrapper
from langchain_core.agents import AgentAction, AgentFinish
from langchain_openai import OpenAI

设置工具

我们将创建一个合法的工具(搜索)和99个假工具。

# 定义代理可以用来回答用户查询的工具
search = SerpAPIWrapper()
search_tool = Tool(
    name="Search",  # 工具名称
    func=search.run,  # 工具函数
    description="当您需要回答有关当前事件的问题时非常有用",  # 工具描述
)

# 定义一个无实际功能的假工具函数
def fake_func(inp: str) -> str:
    return "foo"

# 创建99个假工具
fake_tools = [
    Tool(
        name=f"foo-{i}",  # 工具名称,添加序号
        func=fake_func,  # 工具函数
        description=f"一个愚蠢的函数,您可以使用它来获取有关数字{i}的更多信息",  # 工具描述
    )
    for i in range(99)
]
# 将搜索工具和假工具合并为所有工具列表
ALL_TOOLS = [search_tool] + fake_tools

工具检索器

使用向量存储为每个工具描述创建嵌入,然后基于传入的查询创建嵌入并执行相似性搜索以找到相关工具。

from langchain_community.vectorstores import FAISS
from langchain_core.documents import Document
from langchain_openai import OpenAIEmbeddings

# 为所有工具创建文档
docs = [
    Document(page_content=t.description, metadata={"index": i})  # 工具描述和索引
    for i, t in enumerate(ALL_TOOLS)
]

# 创建向量存储
vector_store = FAISS.from_documents(docs, OpenAIEmbeddings())

# 创建检索器
retriever = vector_store.as_retriever()

# 定义获取工具的函数
def get_tools(query):
    docs = retriever.invoke(query)  # 根据查询调用检索器
    return [ALL_TOOLS[d.metadata["index"]] for d in docs]  # 返回工具列表

测试检索器

现在我们可以测试检索器是否有效。

# 测试检索器
get_tools("whats the weather?")
get_tools("whats the number 13?")

提示模板

提示模板相当标准,因为我们实际上并没有改变提示模板中的逻辑,而是改变了检索的执行方式。

# 设置基础模板
template = """
尽你所能,以海盗的口吻回答以下问题。你可以使用以下工具:
{tools}

使用以下格式:
问题:你必须回答的输入问题
思考:你应该总是思考要做什么
动作:要采取的动作,应该是[{tool_names}]之一
动作输入:动作的输入
观察:动作的结果
...(这个思考/动作/动作输入/观察可以重复N次)
思考:我现在知道最终答案了
最终答案:原始输入问题的最终答案

开始!记住在给出最终答案时要像海盗一样说话。多用“Arg”
问题:{input}
{agent_scratchpad}
"""

# 自定义提示模板,引入tools_getter概念,根据输入调用以选择使用的工具
class CustomPromptTemplate(StringPromptTemplate):
    # 使用的模板
    template: str
    # 新增
    # 可用工具的列表
    tools_getter: Callable

prompt = CustomPromptTemplate(
    template=template,
    tools_getter=get_tools,
    # 这省略了`agent_scratchpad`, `tools`, 和 `tool_names`变量因为那些是动态生成的
    # 这包括了`intermediate_steps`变量因为那是需要的
    input_variables=["input", "intermediate_steps"],
)

输出解析器

输出解析器与之前的笔记本相同,因为我们没有改变输出格式。

class CustomOutputParser(AgentOutputParser):
    def parse(self, llm_output: str) -> Union[AgentAction, AgentFinish]:
        # 检查代理是否应该结束
        if "Final Answer:" in llm_output:
            return AgentFinish(
                # 返回值通常总是一个带有单个`output`键的字典
                return_values={"output": llm_output.split("Final Answer:")[-1].strip()},
                log=llm_output,
            )
        # 解析动作和动作输入
        regex = r"Action\s*\d*\s*:(.*?)\nAction\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)"
        match = re.search(regex, llm_output, re.DOTALL)
        if not match:
            raise ValueError(f"无法解析LLM输出: `{llm_output}`")
        action = match.group(1).strip()
        action_input = match.group(2)
        # 返回动作和动作输入
        return AgentAction(
            tool=action, tool_input=action_input.strip(" ").strip('"'), log=llm_output
        )

output_parser = CustomOutputParser()

设置LLM、停止序列和代理

与之前的笔记本相同。

llm = OpenAI(temperature=0)

# LLM链由LLM和提示模板组成
llm_chain = LLMChain(llm=llm, prompt=prompt)
# 根据示例查询获取工具
tools = get_tools("whats the weather?")
tool_names = [tool.name for tool in tools]
agent = LLMSingleActionAgent(
    llm_chain=llm_chain,
    output_parser=output_parser,
    stop=["\nObservation:"],
    allowed_tools=tool_names,
)

使用代理

现在我们可以利用它了!

agent_executor = AgentExecutor.from_agent_and_tools(
    agent=agent, tools=tools, verbose=True
)

# 执行代理
agent_executor.run("What's the weather in SF?")

总结

本文展示了如何创建一个自定义代理,它能够根据用户查询动态检索并选择使用的工具集合。我们构建了一个示例,其中包括一个真实的搜索工具和99个无实际功能的假工具。通过向量化工具描述并使用相似性搜索,我们可以根据用户输入检索相关工具。此外,我们还定义了一个自定义的提示模板和输出解析器,以支持代理的工作流程。最终,我们展示了如何设置和运行代理,使其能够响应特定的用户查询。这种方法在处理大量工具时特别有用,因为它允许代理在运行时动态地选择最合适的工具来回答问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hugo_Hoo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值