02 Agent内部结构解析和实践

本文主要讲述了在LangChain中如何正确给agent增加记忆和共享记忆的方法以及Tool的使用方法。同时,本文也阐述了LCEL的概念以及在Agents中的使用,并利用一个完整的项目案例贯穿全流程。

agent中添加memory的方式

Agents增加记忆的正确做法:

  • 将memory插入到提示词模板中
  • 定义大模型
  • 构建agent可用工具
  • 增加memory组件
  • 定义agent
  • 需要使用agent_kwargs传递参数,将chat_history传入

Agent增加记忆的正确做法

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("好厉害,刚才我们都聊了什么?")

默认的agents prompt
图1 默认agents prompt
渲染后的agents prompt
图2 优化后agents prompt

agents与tool共享记忆

在agent与tool之间共享记忆,使任务更精准。对此,采用以下步骤:

  • 自定义一个工具以便于LLMChain总结内容
  • 使用readonlymemory来共享记忆
  • 观察共享与不共享的区别

readonly

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

#添加预制工具的方法很简单
Tool&Toolkit

#添加预制工具的方法很简单
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之上封装的高级解释语言,用于简化链条开发,支持真实生产环境而发明。
特点:
LCEL的特点

  • Runnable接口: 为了方便自定义链,创造了runnable协议。它适用于大多数组件,是一个标准接口,可以轻松地定义自定义链并以标准方式调用它们。
    组件内部关系以及执行流程如下:
    组件原理
  • Prompt: 组件内部关系以及执行流程如下:
    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
输出结果1

## Prompt
prompt_value = prompt.invoke({"topic": "刺猬"})
prompt_value
prompt_value.to_messages()
prompt_value.to_string()

输出结果2
输出结果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
输出结果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?")    

输出如下:
结果1

## 自定义
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的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

1

  • Output Schema

2

  • Stream(流式)

3

  • Invoke

4

  • Batch

5

  • Async Stream 异步

6

  • Async Invoke

7

  • Async Batch

8

## 异步获取中间步骤
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": "熊"})

输出1

## 并行执行
%%time
combined.invoke({"topic": "熊"})

## 并行批处理,适用于大量生成
%%time
chain1.batch([{"topic": "熊"}, {"topic": "猫"}])

%%time
chain2.batch([{"topic": "熊"}, {"topic": "猫"}])

输出2

## 并行执行
%%time
combined.batch([{"topic": "熊"}, {"topic": "猫"}])

输出3

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
  1. 自定义停止输出符
chain = prompt | model.bind(stop=["\n"])
  1. 兼容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)
  1. 输出解析器
from langchain_core.output_parsers import StrOutputParser

chain = prompt | model | StrOutputParser()
  1. 与函数调用混合使用
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
)

2

  1. 多链执行与结果合并
      输入
      / \
     /   \
 分支1   分支2
     \   /
      \ /
    合并结果
  1. 唯物辩证链
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
)

3

自定义输出解析器

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)
  1. 增加一条链
chain = (
    RunnablePassthrough.assign(
        history=RunnableLambda(memory.load_memory_variables) | itemgetter("history")
    )
    | prompt
    | model
)

inputs = {"input": "你好我是tomie"}
response = chain.invoke(inputs)
response

1

#保存记忆
memory.save_context(inputs, {"output": response.content})
memory.load_memory_variables({})

2

inputs = {"input": "我叫什么名字?"}
response = chain.invoke(inputs)
response

3

  1. 使用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"}},
)

1

chain_with_history.invoke(
    {"ability": "历史", "question": "它有多少年建都历史?"},
    config={"configurable": {"session_id": "tomiezhang"}},
)

2

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()

05

#搜索匹配文档块
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

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!"})

05

#交互时添加记忆
agent_executor.invoke({"input": "hi! 我叫bob", "chat_history": []})

04

#手动构造记忆数据
from langchain_core.messages import AIMessage, HumanMessage
agent_executor.invoke(
    {
        "chat_history": [
            HumanMessage(content="hi! 我叫bob"),
            AIMessage(content="你好,Bob!很高兴认识你。有什么我可以帮助你的吗?"),
        ],
        "input": "我叫什么名字?",
    }
)

03

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"}},
)

02

#调用方式
agent_with_chat_history.invoke(
    {"input": "截止目前我们都聊了什么?"},
    #注意此处session_id没有用到,因为我们没用类似redis的存储,只是用了一个变量
    config={"configurable": {"session_id": "foo_001"}},
)

01备注:

  1. open_ai_tooth:主要用于聊天记忆、批处理和并行函数,适合使用最新open ai模型。
  2. open_AI_function:类似open i的function call微调开源模型,不支持并行函数但需函数回调。
  3. XM_R:文本处理型LLM模型,仅支持聊天记忆。
  4. structure_chat:支持结构化数据输出,适用于需要处理多个输入项工具的情况。
  5. json_chat:处理json数据友好模型,适合使用开源模型处理json数据。
  6. react:处理文档型文本,适用于简单开源模型。
  7. self_ask_with_search:支持简单模型和实时搜索工具,不支持聊天记忆。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Whitney_mao

您的认可和鼓励是持续分享的动力

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

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

打赏作者

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

抵扣说明:

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

余额充值