LangChain补充四:Agent知识点和案例补充

山上有风景 / 2024-07-18 / 原文

https://www.alang.ai/langchain/101/lc07

一:基本流程和概念

(一)概念

LangChain Agent的核心思想是,使用大语言模型选择一系列要执行的动作
在Chain中,一系列动作是硬编码在代码中的。
在Agent中,大语言模型被用作推理引擎,以确定要采取的动作及其顺序。
它包括 3 个组件:
  • 规划:将任务分解为较小的子目标
  • 记忆:短期(聊天历史)/ 长期(向量存储)
  • 工具使用:可以利用不同的工具来扩展其功能。

(二)基本流程

工具定义--->定义工具集--->定义prompt--->创建模型实例--->创建Agent(传递进入llm、tools、prompt)--->创建Agent Executor
补充:OpenAI API已弃用functions,而推荐使用tools。两者之间的区别是,tools API允许一次模型请求使用多个函数调用,这可以减少响应时间。

1.工具定义(这里以Serp搜索工具、自定义工具、RAG检索工具为例)

serp搜索需要用到ApiKey,参考https://serpapi.com/manage-api-key
  • Serp搜索
SerpAPI使用案例:
from langchain_community.utilities import SerpAPIWrapper
search = SerpAPIWrapper()
res= search.run("周星驰生日那天是星期几")
print(res)
SerpAPI工具定义:通过第三方api调用内部封装方式(下面就是工具集合)
#三方api
search = SerpAPIWrapper() #搜索引擎
Tool.from_function(
    func=search.run,
    name="Search",
    description="useful for when you need to answer questions about current events" #问一下实时的事情
)
#内部的封装好的
tool = load_tools(["serpapi"])[0]
  • 自定义tool:最常用扩展方式
from datetime import datetime
from langchain_core.tools import tool

#函数自定义
@tool("weekday")
def weekday(date_str:str) -> str:
    """Convert date to weekday name"""
    date_object = datetime.strptime(date_str, '%Y-%m-%d')
    weekday_number = date_object.weekday()
    weekdays = ['星期一','星期二','星期三','星期四','星期五','星期六','星期日']
    return weekdays[weekday_number]

print(weekday("2024-07-08"))
  • 创建RAG检索工具(里面有调用大模型编码api
RAG使用:


添加RAG工具:
from langchain.tools.retriever import create_retriever_tool

retriever_tool = create_retriever_tool(
    retriever=retriever,
    name="movie_search",
    description="按电影发布年份搜索关于周星驰的电影,并且查询关于周星驰扮演的角色,对于这种类型的问题,you must use this tool!",
)

2.定义工具集

tools = [searchTool,weekday,retriever_tool]

3.定义prompt

#pip install langchainhub
from langchain import hub
prompt = hub.pull("hwchase17/openai-functions-agent")
[
    SystemMessagePromptTemplate(
        prompt=PromptTemplate(input_variables=[], template='You are a helpful assistant')),
    MessagesPlaceholder(
        variable_name='chat_history', optional=True),
    HumanMessagePromptTemplate(
        prompt=PromptTemplate(input_variables=['input'], template='{input}')),
    MessagesPlaceholder(variable_name='agent_scratchpad')
]
注意:这里prompt里面指定了我们传递的变量key是input,如果后续需要传递历史消息,变量是chat_history

4.创建模型实例

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)

5.创建Agent(传递进入llm、tools、prompt):这里以create_openai_tools_agent为例

from langchain.agents import create_openai_tools_agent

agent = create_openai_tools_agent(llm, tools, prompt)

6.创建Agent Executor

from langchain.agents import AgentExecutor

agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
#设置 verbose=True,以观察 Agent 调用过程

(三)调用实例


1.不需要调用tool

res = agent_executor.invoke({"input": "hi!"})
print(res)

2.调用search工具

res = agent_executor.invoke({"input": "周星驰是谁?"})
print(res)

3.调用search和weekday工具

res = agent_executor.invoke({"input": "周星驰的生日是星期几"})
print(res)

4.调用 RAG 检索工具

res = agent_executor.invoke({"input": "周星驰在2004年主演了什么电影?饰演的角色叫什么?"})
print(res)

二:增加记忆力

class Runnable(Generic[Input, Output], ABC)
/ \
 |
 |
 |
class RunnableSerializable(Serializable, Runnable[Input, Output]) 
/ \
 |
 |
 |
class Chain(RunnableSerializable[Dict[str, Any], Dict[str, Any]], ABC)
/ \
 |
 |
 |
 class AgentExecutor(Chain)

我们可以看到agent继承chain,两者都是Runnable的子类,可以参考LangChain补充二:LCEL和Runnable更加方便的创建调用链RunnableWithMessageHistory,实现上下文的处理

from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory

message_history = ChatMessageHistory()
agent_with_chat_history = RunnableWithMessageHistory(
    agent_executor,
    lambda session_id: message_history,    #这里没有考虑session_id,对话只维护一个历史
    input_messages_key="input",
    history_messages_key="chat_history",    #prompt指定了占位
)

agent_with_chat_history.invoke(
    {"input": "周星驰在2004年主演了什么电影"},
    config={"configurable": {"session_id": "1"}},
)


agent_with_chat_history.invoke(
    {"input": "饰演了什么角色呢?"},
    config={"configurable": {"session_id": "1"}},
)

三:LangChain的代理对比

https://python.langchain.com/v0.1/docs/modules/agents/agent_types/
https://cloud.tencent.com/developer/article/2404258
LLM Completions 已经被标记为 Legacy(不建议使用),所以在实际应用中,建议使用 Chat Model 类型的 Agent 就可以了
代理类型 模型类型 历史对话支持 支持多输入的工具 支持同时调用多工具 使用时机
Tool Calling Chat 使用tool-calling模型时
OpenAI Tools Chat 废弃,tool-calling代替
OpenAI Functions Chat   废弃,tool-calling代替可以并发调用
XML LLM     使用擅长XML的模型时(比如:Anthropic),就可以用到这个agent
Structured Chat Chat   需要支持多输入的工具时
JSON Chat Chat     擅长json的模型,普通chat模型加上prompt即可
ReAct LLM     简单的模型
Self Ask With Search LLM       简单的模型,只有一个搜索工具

补充:Structured Chat 和 JSON Chat 的区别在于对 tool 入参类型的支持上,参数越多,LLM 对工具的学习成本就会越高,Agent 有可能会越不稳定。

Json chat只支持单参数的tool
@tool
def search(query: str) -> str:
    """Look up things online."""
    return "LangChain"
Structured chat支持多参数tool,可以替代json chat
@tool
def multiply(a: int, b: int) -> int:
    """Multiply two numbers."""
    return a * b
Tool calling更🐮,都替代