精通LangGraph-Tools call-06

文摘   2025-01-19 10:00   四川  
如何处理大量tools
可供调用的工具子集通常由model决定(尽管许多提供商也允许用户指定或限制工具的选择)。随着可用tool数量的增加,您可能希望限制 LLM 的选择范围,以减少token消耗并帮助管理 LLM 推理中的错误源。
在这里,我们将演示如何动态调整模型可用的工具。首先要说的是:与RAG和类似方法一样,我们通过检索可用工具来为模型调用添加前缀。虽然我们演示了一种搜索工具描述的实现,但可以根据需要自定义工具选择的细节。
定义tools
让我们考虑一个小例子,其中我们为标准普尔 500 指数中的每一家上市公司提供了一个工具。每个工具根据作为参数提供的年份获取公司特定的信息。
我们首先构建一个注册表,将每个工具的唯一标识符与架构关联起来。我们将使用 JSON 架构表示工具,该架构可以直接绑定到支持工具调用的聊天模型。
import reimport uuidfrom langchain_core.tools import StructuredTool
def create_tool(company: str) -> dict:    """为占位工具创建模式。"""    # 删除非字母数字字符并将空格替换为下划线以生成工具名称    formatted_company = re.sub(r"[^\w\s]""", company).replace(" ""_")    def company_tool(year: int) -> str:        # 占位函数,返回公司和年份的静态收入信息        return f"{company} had revenues of $100 in {year}."    return StructuredTool.from_function(        company_tool,        name=formatted_company,        description=f"Information about {company}",    )
# 用于演示的标准普尔500指数公司简化列表s_and_p_500_companies = [    "3M",    "A.O. Smith",    "Abbott",    "Accenture",    "Advanced Micro Devices",    "Yum! Brands",    "Zebra Technologies",    "Zimmer Biomet",    "Zoetis",]# 为每家公司创建一个工具,并使用唯一的UUID作为键将其存储在dict中tool_registry = {    str(uuid.uuid4()): create_tool(company) for company in s_and_p_500_companies}
工具选择
我们将构建一个节点,根据状态中的信息(例如最近的用户消息)检索可用工具的子集。一般来说,此步骤可以使用所有检索解决方案。
作为一种简单的解决方案,我们在向量存储中索引工具描述的嵌入,并通过语义搜索将用户查询与工具关联起来。
from langchain_core.documents import Documentfrom langchain_core.vectorstores import InMemoryVectorStorefrom langchain_ollama import OllamaEmbeddingsimport base_conftool_documents = [    Document(        page_content=tool.description,        id=id,        metadata={"tool_name": tool.name},    )    for id, tool in tool_registry.items()]vector_store = InMemoryVectorStore(embedding=OllamaEmbeddings(base_url=base_conf.base_url,                                                              model=base_conf.embedding_model_name_en))# 可以看到,我们把tools的描述和name内容放到了store中document_ids = vector_store.add_documents(tool_documents)
结合agent
我们将使用典型的 React agent graph,并进行一些修改:
  1. selected_tools我们向状态添加一个key,用于存储我们选择的工具子集;
  2. 我们将graph的入口点设置为一个select_tools节点,它填充状态的select_tools key;
  3. 我们将选定的工具子集绑定到节点内的聊天模型agent。
from typing import Annotatedfrom langchain_ollama import ChatOllamafrom typing_extensions import TypedDictfrom langgraph.graph import StateGraph, STARTfrom langgraph.graph.message import add_messagesfrom langgraph.prebuilt import ToolNode, tools_condition
# 使用TypedDict定义状态结构。# 它包括一个消息列表(由add_messages处理)# 和一个选定工具ID的列表。class State(TypedDict):    messages: Annotated[list, add_messages]    # 选定的工具ID列表    selected_tools: list[str]
builder = StateGraph(State)# 从工具dictionary中检索所有可用工具。tools = list(tool_registry.values())llm = ChatOllama(base_url=base_conf.base_url,model=base_conf.model_name)
# 代理函数通过将选定的工具绑定到LLM来处理当前状态。def agent(state: State):    # 根据状态的selected_tools列表将工具ID映射到实际工具。    selected_tools = [tool_registry[id] for id in state["selected_tools"]]    # 将选定的工具绑定到当前交互的LLM。    llm_with_tools = llm.bind_tools(selected_tools)    # 使用当前消息调用LLM并返回更新的消息列表。    return {"messages": [llm_with_tools.invoke(state["messages"])]}
# select_tools函数根据用户的最后一条消息内容选择工具。def select_tools(state: State):    last_user_message = state["messages"][-1]    query = last_user_message.content    tool_documents = vector_store.similarity_search(query)    return {"selected_tools": [document.id for document in tool_documents]}
builder.add_node("agent", agent)builder.add_node("select_tools", select_tools)tool_node = ToolNode(tools=tools)builder.add_node("tools", tool_node)builder.add_conditional_edges("agent", tools_condition, path_map=["tools""__end__"])builder.add_edge("tools""agent")builder.add_edge("select_tools""agent")
# 先从store中通过query找到相似的tools,将这些tools添加到state中的selected_tools中builder.add_edge(START, "select_tools")graph = builder.compile()
user_input = "Can you give me some information about AMD in 2022?"result = graph.invoke({"messages": [("user", user_input)]})print(result["selected_tools"])for message in result["messages"]:    message.pretty_print()
输出:
['abd4721b-80b3-4e76-82b1-f59efbde9d9a''26ef59cd-8e93-4915-88dc-b0e5e5c11471''a1bd1bae-dbfa-4894-b4a4-aa21985ac8a4''48d1ccdf-c17c-405f-a127-b56f590c0fcb']================================ Human Message =================================
Can you give me some information about AMD in 2022?================================== Ai Message ==================================Tool Calls:  Advanced_Micro_Devices (cbf17b76-4393-47db-b95a-70965dd25322) Call ID: cbf17b76-4393-47db-b95a-70965dd25322  Args:    year2022================================= Tool Message =================================Name: Advanced_Micro_Devices
Advanced Micro Devices had revenues of $100 in 2022.================================== Ai Message ==================================
In 2022, Advanced Micro Devices (AMD) reported revenues of $100 billion.
agent的流程图是这样的:

重复工具选择
为了管理因工具选择不正确而导致的错误,我们可以重新访问 select_tools 节点。
实现此功能的一个选项是修改 select_tools 以使用状态中的所有消息(例如,使用聊天模型)生成向量存储查询,并在graph添加从tools到 select_tools 的路由边。
核心点在于我们要理解,一个node是可以反复去跑的,因为连接了LLM,经过多次尝试,它是有可能从错误中恢复过来的,这是跟传统开发不一样的一个思考问题的方式。

半夏决明
读书,摄影,随笔
 最新文章