本文主要讲述了在LangChain中如何正确给agent增加记忆和共享记忆的方法以及Tool的使用方法。同时,本文也阐述了LCEL的概念以及在Agents中的使用,并利用一个完整的项目案例贯穿全流程。
agent中添加memory的方式
Agents增加记忆的正确做法:
- 将memory插入到提示词模板中
- 定义大模型
- 构建agent可用工具
- 增加memory组件
- 定义agent
- 需要使用agent_kwargs传递参数,将chat_history传入
from langchain.agents import Tool
from langchain.agents import AgentType
from langchain.memory import ConversationBufferMemory
from langchain.chat_models import ChatOpenAI
from langchain.utilities import SerpAPIWrapper
from langchain.agents import initialize_agent
from langchain.chains import LLMMathChain
from langchain.prompts import MessagesPlaceholder
## 定义大模型
llm=ChatOpenAI(
temperature=0,
model="gpt-4-1106-preview",
)
## 构建agent可用工具
import os
os.environ["SERPAPI_API_KEY"] = "f265b8d9834ed7692cba6db6618e2a8a9b24ed6964c457296a2626026e8ed594"
#构建一个搜索工具
search = SerpAPIWrapper()
#创建一个数学计算工具
llm_math_chain = LLMMathChain(
llm=llm,
verbose=True
)
tools = [
Tool(
name = "Search",
func=search.run,
description="useful for when you need to answer questions about current events or the current state of the world"
),
Tool(
name="Calculator",
func=llm_math_chain.run,
description="useful for when you need to answer questions about math"
),
]
# print(tools)
## **增加memory组件**
#记忆组件
memory = ConversationBufferMemory(
memory_key="chat_history",
return_messages=True
)
# 查看默认的agents prompt是什么样的
print(agent_chain.agent.prompt.messages)
print(agent_chain.agent.prompt.messages[0])
print(agent_chain.agent.prompt.messages[1])
print(agent_chain.agent.prompt.messages[2])
agent_chain.run("我叫什么名字?")
## 需要使用agent_kwargs传递参数,将chat_history传入
agent_chain = initialize_agent(
tools,
llm,
agent=AgentType.OPENAI_FUNCTIONS,
verbose=True,
handle_parsing_errors=True,#处理解析错误
agent_kwargs={
"extra_prompt_messages":[MessagesPlaceholder(variable_name="chat_history"),MessagesPlaceholder(variable_name="agent_scratchpad")]
},
memory=memory #记忆组件
)
## 查看重新渲染后的提示词模版
print(agent_chain.agent.prompt.messages)
print(agent_chain.agent.prompt.messages[0])
print(agent_chain.agent.prompt.messages[1])
print(agent_chain.agent.prompt.messages[2])
agent_chain.run("好厉害,刚才我们都聊了什么?")
图1 默认agents prompt
图2 优化后agents prompt
agents与tool共享记忆
在agent与tool之间共享记忆,使任务更精准。对此,采用以下步骤:
- 自定义一个工具以便于LLMChain总结内容
- 使用readonlymemory来共享记忆
- 观察共享与不共享的区别
from langchain.agents import initialize_agent, Tool, AgentType
from langchain.memory import ConversationBufferMemory, ReadOnlySharedMemory
from langchain.llms import OpenAI
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate,MessagesPlaceholder
from langchain.utilities import SerpAPIWrapper
## 创建llm
llm = OpenAI(
temperature = 0,
model="gpt-3.5-turbo-instruct",
)
## 创建一条链用于总结对话
template = """以下是一段AI机器人和人类的对话:
{chat_history}
根据输入和上面的对话记录写一份对话总结.
输入: {input}"""
prompt = PromptTemplate(
input_variables=["input","chat_history"],
template=template,
)
memory = ConversationBufferMemory(
memory_key="chat_history",
return_messages=True,
)
readonlymemory = ReadOnlySharedMemory(memory=memory)
summary_chain = LLMChain(
llm=llm,
prompt=prompt,
verbose=True,
memory=readonlymemory,
)
## 构建工具
import os
os.environ["SERPAPI_API_KEY"] = "f265b8d9834ed7692cba6db6618e2a8a9b24ed6964c457296a2626026e8ed594"
#搜索工具
search = SerpAPIWrapper()
#总结工具
def SummaryChainFun(history):
print("\n==============总结链开始运行==============")
print("输入历史: ",history)
summary_chain.run(history)
tools = [
Tool(
name="Search",
func=search.run,
description="当需要了解实时的信息或者你不知道的事时候可以使用搜索工具",
),
Tool(
name="Summary",
func=SummaryChainFun,
description="当你被要求总结一段对话的时候可以使用这个工具,工具输入必须为字符串,只在必要时使用",
),
]
print(tools)
## 创建记忆组件
memory = ConversationBufferMemory(
memory_key="chat_history",
return_messages=True,
)
## 创建agent
agent_chain = initialize_agent(
tools,
llm,
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
verbose=True,
handle_parsing_errors=True,
memory=memory,
)
print(agent_chain.agent.llm_chain.prompt.template)
由于输出结果不符合预期,本文采用修改前、后缀的方式对初始提示词进行覆盖。
prefix = """Have a conversation with a human, answering the following questions as best you can. You have access to the following tools:"""
suffix = """Begin!"
{chat_history}
Question: {input}
{agent_scratchpad}"""
agent_chain = initialize_agent(
tools,
llm,
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
verbose=True,
handle_parsing_errors=True,
agent_kwargs={
"prefix":prefix,
"suffix":suffix,
"agent_scratchpad":MessagesPlaceholder("agent_scratchpad"),
"chat_history":MessagesPlaceholder("chat_history"),
"input":MessagesPlaceholder("input"),
},
memory=memory,
)
print(agent_chain.agent.llm_chain.prompt.template)
prefix = """Have a conversation with a human, answering the following questions as best you can. You have access to the following tools:"""
suffix = """Begin!"
{chat_history}
Question: {input}
{agent_scratchpad}"""
agent_chain = initialize_agent(
tools,
llm,
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
verbose=True,
handle_parsing_errors=True,
agent_kwargs={
"prefix":prefix,
"suffix":suffix,
"agent_scratchpad":MessagesPlaceholder("agent_scratchpad"),
"chat_history":MessagesPlaceholder("chat_history"),
"input":MessagesPlaceholder("input"),
},
memory=memory,
)
print(agent_chain.agent.llm_chain.prompt.template)
多次使用后,输出共享记忆内容。
Tool和Tookit的使用
工具分为两类:单体工具tool能独立完成任务,工具集tookit则由多个工具组成,可配合完成特定任务。langchain预制了大量的tools,这些工具基本能满足大部分需求:工具集链接
Tool
#添加预制工具的方法很简单
#添加预制工具的方法很简单
from langchain.agents import load_tools
tool_names = [...]
tools = load_tools(tool_names) #使用load方法
#有些tool需要单独设置llm
from langchain.agents import load_tools
tool_names = [...]
llm = ...
tools = load_tools(tool_names, llm=llm) #在load的时候指定llm
SerpAPI
最常见的聚合搜索引擎: 聚合搜索引擎
from langchain.utilities import SerpAPIWrapper
#serpapi的api key
import os
os.environ["SERPAPI_API_KEY"] = "f265b8d9834ed7692cba6db6618e2a8a9b24ed6964c457296a2626026e8ed594"
search = SerpAPIWrapper()
search.run("Obama's first name?")
支持自定义参数,比如将引擎切换到bing,设置搜索语言等。
params = {
"engine": "bing",
"gl": "us",
"hl": "en",
}
search = SerpAPIWrapper(params=params)
search.run("Obama's first name?")
使用Dall-E
all-E是openai出品的文到图AI大模型。
ps:必须科学用网,不然无法访问。
from langchain.agents import initialize_agent, load_tools
tool_names = (["dalle-image-generator"])
tools = load_tools(tool_names) #使用load方法
agent = initialize_agent(
tools,
llm,
agent="zero-shot-react-description",
verbose=True
)
output = agent.run("Create an image of a halloween night at a haunted museum")
Eleven Labs Text2Speech
ElevenLabs 是非常优秀的TTS合成API,登录elevenlabs官方网站。
pip install elevenlabs
## 插入APIKEY
import os
os.environ["ELEVEN_API_KEY"] = "23261e4a3b79697822252a505a169863"
## 工具使用
from langchain.tools import ElevenLabsText2SpeechTool
text_to_speak = "Hello! 你好! Hola! नमस्ते! Bonjour! こんにちは! مرحبا! 안녕하세요! Ciao! Cześć! Привіт! வணக்கம்!"
tts = ElevenLabsText2SpeechTool(
voice="Bella",
text_to_speak=text_to_speak,
verbose=True
)
tts.name
speech_file = tts.run(text_to_speak)
#tts.play(speech_file)
tts.stream_speech(text_to_speak)
GraphQL
一种api查询语言,类似sql,我们用它来查询奈飞的数据库,查找一下和星球大战相关的电影,API地址
## 安装包: httpx gql > /dev/null, gql, requests_toolbelt
from langchain.chat_models import ChatOpenAI
from langchain.agents import load_tools, initialize_agent, AgentType
from langchain.utilities import GraphQLAPIWrapper
llm = ChatOpenAI(
temperature=0,
model="gpt-4",
)
tools = load_tools(
["graphql"],
graphql_endpoint="https://swapi-graphql.netlify.app/.netlify/functions/index",
)
agent = initialize_agent(
tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True
)
graphql_fields = """allFilms {
films {
title
director
releaseDate
speciesConnection {
species {
name
classification
homeworld {
name
}
}
}
}
}
"""
suffix = "Search for the titles of all the stawars films stored in the graphql database that has this schema,and answer in chinese:"
agent.run(suffix + graphql_fields)
Tookit
tookit是langchain已经封装好的一系列工具,一个工具包是一组工具来组合完成特定的任务。
Azure认知服务
- AzureCogsFormRecognizerTool:从文档里提取文本
- AzureCogsSpeech2TextTool:语音到文本
- AzureCogsText2SpeechTool:文本到语音
## 提前安装包pip install azure-ai-textanalytics
#创建toolkit
from langchain.agents.agent_toolkits import AzureCognitiveServicesToolkit
toolkit = AzureCognitiveServicesToolkit()
# [tool.name for tool in toolkit.get_tools()]
#agent使用
from langchain.chat_models import ChatOpenAI
from langchain.agents import initialize_agent, AgentType
llm = ChatOpenAI(temperature=0,model="gpt-4-1106-preview")
agent = initialize_agent(
tools=toolkit.get_tools(),
llm=llm,
agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
verbose=True
)
audio_file = agent.run("Tell me a joke and read it out for me.")
python
一个python代码机器人
## 安装包 pip install langchain_experimental
from langchain_experimental.agents.agent_toolkits import create_python_agent
from langchain_experimental.tools import PythonREPLTool
from langchain_experimental.utilities import PythonREPL
from langchain.llms.openai import OpenAI
from langchain.agents.agent_types import AgentType
from langchain.chat_models import ChatOpenAI
agent_executor = create_python_agent(
llm=ChatOpenAI(temperature=0, model="gpt-4-1106-preview"),
tool=PythonREPLTool(),
verbose=True,
agent_type=AgentType.OPENAI_FUNCTIONS,
agent_executor_kwargs={"handle_parsing_errors": True}
)
# agent_executor.run("What is the 10th fibonacci number?")
agent_executor.run(
"""Understand, write a single neuron neural network in PyTorch.
Take synthetic data for y=2x. Train for 1000 epochs and print every 100 epochs.
Return prediction for x = 5"""
)
SQL Database
使用SQLDatabaseChain构建的agent,用来根据数据库回答一般行动饿问题
from langchain.agents import create_sql_agent
from langchain.agents.agent_toolkits import SQLDatabaseToolkit
from langchain.sql_database import SQLDatabase
from langchain.llms.openai import OpenAI
from langchain.agents import AgentExecutor
from langchain.agents.agent_types import AgentType
from langchain.chat_models import ChatOpenAI
db = SQLDatabase.from_uri("sqlite:///Chinook.db")
toolkit = SQLDatabaseToolkit(db=db, llm=OpenAI(temperature=0))
agent_executor = create_sql_agent(
llm=ChatOpenAI(temperature=0, model="gpt-4-1106-preview"),
toolkit=toolkit,
verbose=True,
agent_type=AgentType.OPENAI_FUNCTIONS
)
# agent_executor.run("Describe the playlisttrack table")
## 用自然语言描述一个数据库表
# agent_executor.run("Describe the Playlist table")
## 用自然语言跑sql查询
agent_executor.run(
"List the total sales per country. Which country's customers spent the most?"
)
agent_executor.run(
"Show the total number of tracks in each playlist. The Playlist name should be included in the result."
)
## agent可以自动修复不存在的键值
agent_executor.run("Who are the top 3 best selling artists?")
LCEL(LangChain Expression Language): LangChain表达式语言
LCEL是一种在LangChain之上封装的高级解释语言,用于简化链条开发,支持真实生产环境而发明。
特点:
- Runnable接口: 为了方便自定义链,创造了runnable协议。它适用于大多数组件,是一个标准接口,可以轻松地定义自定义链并以标准方式调用它们。
组件内部关系以及执行流程如下:
- Prompt: 组件内部关系以及执行流程如下:
最简单示例
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
prompt = ChatPromptTemplate.from_template("给我讲一个关于 {topic}的笑话")
model = ChatOpenAI(model="gpt-4")
output_parser = StrOutputParser()
chain = prompt | model | output_parser
chain.invoke({"topic": "冰激凌"})
输出结果1
## Prompt
prompt_value = prompt.invoke({"topic": "刺猬"})
prompt_value
prompt_value.to_messages()
prompt_value.to_string()
输出结果2
## 使用llm的区别
from langchain_openai.llms import OpenAI
llm = OpenAI(model="gpt-3.5-turbo-instruct")
llm.invoke(prompt_value)
# Output parser
output_parser.invoke(message)
输出结果3
RAG Search Exampl
- 建立向量数据
- 使用RAG增强
## 安装langchain-openai faiss-cpu tiktoken等工具包
from operator import itemgetter
from langchain_community.vectorstores import FAISS
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableLambda, RunnablePassthrough
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
vectorstore = FAISS.from_texts(
["harrison worked at kensho"], embedding=OpenAIEmbeddings()
)
retriever = vectorstore.as_retriever()
template = """Answer the question based only on the following context:
{context}
Question: {question}
"""
prompt = ChatPromptTemplate.from_template(template)
model = ChatOpenAI()
chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| model
| StrOutputParser()
)
chain.invoke("where did harrison work?")
输出如下:
## 自定义
template = """Answer the question based only on the following context:
{context}
Question: {question}
Answer in the following language: {language}
"""
prompt = ChatPromptTemplate.from_template(template)
chain = (
{
"context": itemgetter("question") | retriever,
"question": itemgetter("question"),
"language": itemgetter("language"),
}
| prompt
| model
| StrOutputParser()
)
chain.invoke({"question": "where did harrison work", "language": "chinese"})
输出如下:
LCEL的Pipeline
LCEL接口
主要介绍8种不同的接口方式的输入格式、输出格式。
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
model = ChatOpenAI(
model="gpt-3.5-turbo",
)
prompt = ChatPromptTemplate.from_template("给我讲一个关于{topic}的笑话")
chain = prompt | model
- input schema
- Output Schema
- Stream(流式)
- Invoke
- Batch
- Async Stream 异步
- Async Invoke
- Async Batch
## 异步获取中间步骤
from langchain_community.vectorstores import FAISS
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import OpenAIEmbeddings
template = """基于下面的上下文来回答问题:
{context}
Question: {question}
"""
prompt = ChatPromptTemplate.from_template(template)
vectorstore = FAISS.from_texts(
["柯基犬是一种中型家庭宠物犬"], embedding=OpenAIEmbeddings()
)
retriever = vectorstore.as_retriever()
retrieval_chain = (
{
"context": retriever.with_config(run_name="Docs"),
"question": RunnablePassthrough(),
}
| prompt
| model
| StrOutputParser()
)
async for chunk in retrieval_chain.astream_log(
"柯基是什么?", include_names=["Docs"]
):
print("-" * 40)
print(chunk)
## 只看状态值
async for chunk in retrieval_chain.astream_log(
"柯基是什么?", include_names=["Docs"], diff=False
):
print("-" * 70)
print(chunk)
## 并行支持
from langchain_core.runnables import RunnableParallel
chain1 = ChatPromptTemplate.from_template("给我讲一个关于{topic}的笑话") | model
chain2 = (
ChatPromptTemplate.from_template("写两行关于{topic}的诗歌")
| model
)
combined = RunnableParallel(joke=chain1, poem=chain2)
%%time
chain1.invoke({"topic": "熊"})
## 并行执行
%%time
combined.invoke({"topic": "熊"})
## 并行批处理,适用于大量生成
%%time
chain1.batch([{"topic": "熊"}, {"topic": "猫"}])
%%time
chain2.batch([{"topic": "熊"}, {"topic": "猫"}])
## 并行执行
%%time
combined.batch([{"topic": "熊"}, {"topic": "猫"}])
Prompt+LLM
基本构成: PromptTemplate / ChatPromptTemplate -> LLM / ChatModel -> OutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
prompt = ChatPromptTemplate.from_template("给我讲一个关于{foo}的笑话")
model = ChatOpenAI(
temperature=0,
model="gpt-3.5-turbo",
)
chain = prompt | model
- 自定义停止输出符
chain = prompt | model.bind(stop=["\n"])
- 兼容openai函数调用的方式
functions = [
{
"name": "joke",
"description": "讲笑话",
"parameters": {
"type": "object",
"properties": {
"setup": {"type": "string", "description": "笑话的开头"},
"punchline": {
"type": "string",
"description": "爆梗的结尾",
},
},
"required": ["setup", "punchline"],
},
}
]
chain = prompt | model.bind(function_call={"name": "joke"}, functions=functions)
- 输出解析器
from langchain_core.output_parsers import StrOutputParser
chain = prompt | model | StrOutputParser()
- 与函数调用混合使用
from langchain.output_parsers.openai_functions import JsonOutputFunctionsParser
chain = (
prompt
| model.bind(function_call={"name": "joke"}, functions=functions)
| JsonOutputFunctionsParser()
)
#只输出setup
from langchain.output_parsers.openai_functions import JsonKeyOutputFunctionsParser
from langchain_core.runnables import RunnableParallel, RunnablePassthrough
chain = (
{"foo": RunnablePassthrough()} #使用RunnablePassthrough()跳过prompt
| prompt
| model.bind(function_call={"name": "joke"}, functions=functions)
| JsonKeyOutputFunctionsParser(key_name="punchline") # 定义输出的key
)
#使用RunnablePassthrough()跳过prompt
使用Runnables来连接多链结构
from langchain_core.runnables import RunnablePassthrough
prompt1 = ChatPromptTemplate.from_template(
"生成一个{attribute}属性的颜色。除了返回这个颜色的名字不要做其他事:"
)
prompt2 = ChatPromptTemplate.from_template(
"什么水果是这个颜色:{color},只返回这个水果的名字不要做其他事情:"
)
prompt3 = ChatPromptTemplate.from_template(
"哪个国家的国旗有这个颜色:{color},只返回这个国家的名字不要做其他事情:"
)
prompt4 = ChatPromptTemplate.from_template(
"有这个颜色的水果是{fruit},有这个颜色的国旗是{country}?"
)
model_parser = model | StrOutputParser()
# 生成一个颜色
color_generator = (
{"attribute": RunnablePassthrough()} | prompt1 | {"color": model_parser}
)
color_to_fruit = prompt2 | model_parser
color_to_country = prompt3 | model_parser
question_generator = (
color_generator | {"fruit": color_to_fruit, "country": color_to_country} | prompt4
)
- 多链执行与结果合并
输入
/ \
/ \
分支1 分支2
\ /
\ /
合并结果
- 唯物辩证链
planner = (
ChatPromptTemplate.from_template("生成一个关于{input}的论点")
| ChatOpenAI()
| StrOutputParser()
| {"base_response": RunnablePassthrough()}
)
arguments_for = (
ChatPromptTemplate.from_template(
"列出以下内容的优点或积极方面:{base_response}"
)
| ChatOpenAI()
| StrOutputParser()
)
arguments_against = (
ChatPromptTemplate.from_template(
"列出以下内容的缺点或消极方面:{base_response}"
)
| ChatOpenAI()
| StrOutputParser()
)
final_responder = (
ChatPromptTemplate.from_messages(
[
("ai", "{original_response}"),
("human", "积极:\n{results_1}\n\n消极:\n{results_2}"),
("system", "根据评论生成最终的回复"),
]
)
| ChatOpenAI()
| StrOutputParser()
)
chain = (
planner
| {
"results_1": arguments_for,
"results_2": arguments_against,
"original_response": itemgetter("base_response"),
}
| final_responder
)
自定义输出解析器
python编程助手
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import (
ChatPromptTemplate,
)
from langchain_experimental.utilities import PythonREPL
from langchain_openai import ChatOpenAI
template = """根据用户需求帮助用户编写python代码.
只需要返回makedown格式的python代码, 比如:
```python
....
```"""
prompt = ChatPromptTemplate.from_messages([("system", template), ("human", "{input}")])
model = ChatOpenAI(
model="gpt-4",
temperature=0,
)
#自定义输出解析,只返回python代码
def _sanitize_output(text: str):
_, after = text.split("```python")
return after.split("```")[0]
#定义链
chain = prompt | model | StrOutputParser() | _sanitize_output | PythonREPL().run
memory添加方式
from operator import itemgetter
from langchain.memory import ConversationBufferMemory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import RunnableLambda, RunnablePassthrough
from langchain_openai import ChatOpenAI
model = ChatOpenAI()
prompt = ChatPromptTemplate.from_messages(
[
("system", "你是一个乐于助人的机器人"),
MessagesPlaceholder(variable_name="history"),
("human", "{input}"),
]
)
memory = ConversationBufferMemory(return_messages=True)
- 增加一条链
chain = (
RunnablePassthrough.assign(
history=RunnableLambda(memory.load_memory_variables) | itemgetter("history")
)
| prompt
| model
)
inputs = {"input": "你好我是tomie"}
response = chain.invoke(inputs)
response
#保存记忆
memory.save_context(inputs, {"output": response.content})
memory.load_memory_variables({})
inputs = {"input": "我叫什么名字?"}
response = chain.invoke(inputs)
response
- 使用Redis来实现长时记忆
# pip install redis
from typing import Optional
from langchain_community.chat_message_histories import RedisChatMessageHistory
from langchain_community.chat_models import ChatOpenAI
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables.history import RunnableWithMessageHistory
prompt = ChatPromptTemplate.from_messages(
[
("system", "你是一个擅长{ability}的助手"),
MessagesPlaceholder(variable_name="history"),
("human", "{question}"),
]
)
chain = prompt | ChatOpenAI(model="gpt-4-1106-preview",temperature=0)
chain_with_history = RunnableWithMessageHistory(
chain,
#使用redis存储聊天记录
lambda session_id: RedisChatMessageHistory(session_id, url="redis://localhost:6379/0"),
input_messages_key="question",
history_messages_key="history",
)
- 每次调用都会保存聊天记录,需要有对应的session_id
chain_with_history.invoke(
{"ability": "历史", "question": "中国建都时间最长的城市是哪个?"},
config={"configurable": {"session_id": "tomiezhang"}},
)
chain_with_history.invoke(
{"ability": "历史", "question": "它有多少年建都历史?"},
config={"configurable": {"session_id": "tomiezhang"}},
)
Agent完整实践
- step1:中间步骤处理
- step2:定义提示词
- step3:模型配置(停止符必要的话)
- step4:输出解析器
from langchain import hub
from langchain.agents import AgentExecutor, tool
from langchain.agents.output_parsers import XMLAgentOutputParser
from langchain_openai import ChatOpenAI
#配置模型
model = ChatOpenAI(
model="gpt-4-1106-preview",
temperature=0,
)
#可用工具
@tool
def search(query: str) -> str:
"""当需要了解最新的天气信息的时候才会使用这个工具。"""
return "晴朗,32摄氏度,无风"
tool_list = [search]
#提示词模版
# https://smith.langchain.com/hub
# Get the prompt to use - you can modify this!
prompt = hub.pull("hwchase17/xml-agent-convo")
#中间步骤,实现一个log
def convert_intermediate_steps(intermediate_steps):
log = ""
for action, observation in intermediate_steps:
log += (
f"<tool>{action.tool}</tool><tool_input>{action.tool_input}"
f"</tool_input><observation>{observation}</observation>"
)
return log
#将工具列表插入到模版中
def convert_tools(tools):
return "\n".join([f"{tool.name}: {tool.description}" for tool in tools])
step2:定义agent
agent = (
{
"input": lambda x: x["input"],
"agent_scratchpad": lambda x: convert_intermediate_steps(
x["intermediate_steps"]
),
}
| prompt.partial(tools=convert_tools(tool_list))
| model.bind(stop=["</tool_input>", "</final_answer>"])
| XMLAgentOutputParser()
)
#执行agent
agent_executor = AgentExecutor(agent=agent, tools=tool_list, verbose=True)
#定义工具
import os
os.environ["SERPAPI_API_KEY"] = "f265b8d9834ed7692cba6db6618e2a8a9b24ed6964c457296a2626026e8ed594"
# Import things that are needed generically
from langchain.pydantic_v1 import BaseModel, Field
from langchain.tools import BaseTool, StructuredTool, tool
from langchain_community.utilities import SerpAPIWrapper
@tool
def search(query: str) -> str:
"""当需要查找实时信息的时候才会使用这个工具."""
serp = SerpAPIWrapper()
return serp.run(query)
#RAG增强生成
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
loader = WebBaseLoader("https://docs.smith.langchain.com/user_guide")
docs = loader.load()
print(f"Loaded {len(docs)} documents")
documents = RecursiveCharacterTextSplitter(
chunk_size=1000, chunk_overlap=200
).split_documents(docs)
vector = FAISS.from_documents(documents, OpenAIEmbeddings())
retriever = vector.as_retriever()
#搜索匹配文档块
retriever.get_relevant_documents("如何debug?")[0]
#把检索器加入到工具中
from langchain.tools.retriever import create_retriever_tool
retriever_tool = create_retriever_tool(
retriever,
"langsmith_search",
"搜索有关 LangSmith 的信息。关于LangSmith的任何问题,你一定要使用这个工具!",
)
retriever_tool
#可用工具集
tools = [search, retriever_tool]
step3:定义模型
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4-1106-preview", temperature=0)
#从hub中获取模版
from langchain import hub
# 一个最简单的模版,带记忆
prompt = hub.pull("hwchase17/openai-functions-agent")
#创建agent
from langchain.agents import create_openai_functions_agent
agent = create_openai_functions_agent(llm, tools, prompt)
#创建agent执行器AgentExecutor
from langchain.agents import AgentExecutor
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
#执行
agent_executor.invoke({"input": "hi!"})
#交互时添加记忆
agent_executor.invoke({"input": "hi! 我叫bob", "chat_history": []})
#手动构造记忆数据
from langchain_core.messages import AIMessage, HumanMessage
agent_executor.invoke(
{
"chat_history": [
HumanMessage(content="hi! 我叫bob"),
AIMessage(content="你好,Bob!很高兴认识你。有什么我可以帮助你的吗?"),
],
"input": "我叫什么名字?",
}
)
step4:输出解析器
#使用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,
#注意此处session_id没有用到,因为我们没用类似redis的存储,只是用了一个变量
lambda session_id: message_history,
input_messages_key="input",
history_messages_key="chat_history",
)
#调用方式
agent_with_chat_history.invoke(
{"input": "hi! 我是tomie"},
#注意此处session_id没有用到,因为我们没用类似redis的存储,只是用了一个变量
config={"configurable": {"session_id": "foo_001"}},
)
#调用方式
agent_with_chat_history.invoke(
{"input": "截止目前我们都聊了什么?"},
#注意此处session_id没有用到,因为我们没用类似redis的存储,只是用了一个变量
config={"configurable": {"session_id": "foo_001"}},
)
备注:
- open_ai_tooth:主要用于聊天记忆、批处理和并行函数,适合使用最新open ai模型。
- open_AI_function:类似open i的function call微调开源模型,不支持并行函数但需函数回调。
- XM_R:文本处理型LLM模型,仅支持聊天记忆。
- structure_chat:支持结构化数据输出,适用于需要处理多个输入项工具的情况。
- json_chat:处理json数据友好模型,适合使用开源模型处理json数据。
- react:处理文档型文本,适用于简单开源模型。
- self_ask_with_search:支持简单模型和实时搜索工具,不支持聊天记忆。