跳至主要內容

LangChain

程序员李某某大约 25 分钟

LangChain

概述

当我们想要实现一个web后端服务时,我们可以选择手动使用Python来编写所有的功能,但这样做通常会非常耗时且容易出错。为了提高效率和稳定性,我们通常会选择使用像Django或Flask这样的web框架。

  • 因为它们提供了许多内置功能,比如路由、中间件、模板引擎和数据库集成,这些都可以帮助我们快速且可靠地搭建 web服务。

  • 并且Django或Flask:变成了Web开发的标准,因此它们有着庞大的社区和丰富的生态系统,这意味着我们可以轻松地找到大量的教程、文档和第三方库。

  • 基于Django:或Flask的标准化开发模式或工程项目,我们可以更容易地与其他开发者合作,或者将我们的项目交给其他开发者维护。

任何大模型应用程序的核心元素是大模型本身。LangChain在构建大模型应用程序方面,扮演着类似Flask和Django在 web开发中的角色。

Langchain的作用就是为了使与大模型的工作变得更容易。

它是一个开发框架,旨在简化和加速基于大模型的应用程序的创建和部署。

Model I/O

image-20240901180321546
image-20240901180321546

这主要涉及明确的模型接口定义、辅助工具用于构建模型输入,以及用于处理模型输出的辅助工具。

模型(Models)

LangChain集成了两种主要类型的模型:LLMs和Chat Models。

这些模型根据它们的输入和输出类型来定义。

  • LLMs:LangChain中的LLMs指的是纯文本完成模型。它们封装的API接受字符串提示作为输入,并输出字符串完成。OpenAl的GPT-3就是作为LLM实现的。
  • Chat Models:Chat models通常由LLMs支持,但专门调整用于进行对话。关键是,它们的提供商API使用与纯文本完成模型不同的接口。它们不是接受单个字符串,而是接受一个聊天消息列表作为输入,并返回一个AI 消息作为输出。GPT-4和Anthropic的Claude-2都是作为chat models实现的。

注意

这两种API类型具有非常不同的输入和输出架构。这意味着与它们交互的最佳方式可能大不相同。

尽管LangChain 使得可以互换地对待它们,但这并不意味着你应该这么做。

特别是,LLMs与ChatModels的提示策略可能大不相同。这意味着你需要确保你使用的提示是为你正在使用的模型类型设计的。

此外,并非所有模型都是相同的。不同的模型有不同的提示策略最适合它们。例如,Anthropic的模型最适合XML,而OpenAl的模型最适合JSON。这意味着你用于一个模型的提示可能不适用于其他模型。LangChain提供了许多默认提示,但这些并不能保证与你正在使用的模型效果很好。从历史上看,大多数提示都适用于OpeAI,但在其他模型上测试不多

消息(Messages)

ChatModels 以消息列表作为输入,并返回一条消息。有几种不同类型的消息。所有消息都有一个角色role和内容content属性。角色描述了WHO在说这条消息。LangChain为不同的角色 role提供了不同的消息类。内容content描述了消息的内容。这可以是几种不同的事情:

  • 字符串(大多数模型都是这样)
  • 字典列表(这用于多模态输入,其中字典包含关于该输入类型和该输入位置的信息)。另外,消息有一个additional_kwargs属性。这是可以传递有关消息的额外信息的地方。这主要用于特定提供商的输入参数,而不是通用的。最著名的例子 是OpenAl 的function_call
  • HumanMessage:这代表了用户的消息。通常只包含内容。
  • AIMessage:这代表了模型的消息。这可能包含additional_kwargs一一例如,如果使用OpenAl函数调用,则为 functional call
  • SystemMessage:这代表了系统消息。只有一些模型支持这一点。这告诉模型如何行为。这通常只包含内容。
  • FunctionMessage:这代表了函数调用的结果。除了角色role和内容content,这条消息还有一个name参数,它传达了产生此结果的函数的名称。
  • ToolMessage:这代表了工具调用的结果。这与FunctionMessage不同,以匹配OpenAl的函数和工具消息类型。除了角色和内容,这条消息还有一个tool_call_id参数,它传达了产生此结果的工具调用的id。

提示(Prompts)

语言模型的输入通常被称为提示prompts

通常情况下,你的应用程序中的用户输入并不是直接输入到模型中的。

相反,他们的输入以某种方式转换,以产生确实进入模型的字符串或消息列表。

将用户输入转换为最终字符串或消息的对象被称为“提示模板”Prompt Templates。LangChain提供了几种抽象概念,以便更容易地处理提示prompts

以下是智能翻译的示例流程图:

from langchain_community.llms.tongyi import Tongyi
from langchain.prompts.prompt import PromptTemplate

def translate():
    model Tongyi(model_name='qwen-max',model_kwargs={'temperature':0.01})
    prompt_template=PromptTemplate.from_template('你是英语翻译官,对用户的输入翻译成英文,不要解释.\n\n {input}')
    user_input=input("请输入要翻译的中文:") 
    print("1.用户输入的内容是:",user_input)
    prompt=prompt_template.format(input=user_input) 
    print(f"2.生成翻译英文的Prompts:\n'''\n{prompt}\n'''") 
    print("3.开始调用大语言模型进行翻译") 
    res=model.invoke(prompt)
    print("4.输出翻译后的英文内容:",res)

translate()

输出解析器(Output Parsers)

模型的输出要么是字符串,要么是消息。

通常,字符串或消息包含以特定格式 格式化的信息,以供下游使用(例如,以逗号分隔的列表或JSON数据块)。

输出解析器负责接收模型的输出,并将其转换为更可用的形式。这些通常作用于输出消息的内容,但偶尔也作用于additional_kwargs字段中的值。

以下是*输出解析器(Output Parsers)*工作流程图:


def output_parser():

    class Location(BaseModel):
        Location:str=Field(description="Location")

    model = Tongyi(model_name='qwen-max',model_kwargs={'temperature':0.01})

    prompt_template=PromptTemplate.from_template('提取用户输入内容中的地点位置。\n{format_instructions}\n')

    parser=JsonOutputParser(pydantic_object=Location)

    user_input=input("User: ")

    print("1.用户输入的内容是:",user_input)

    prompt = prompt_template.format(input=user_input,format_instructions=parser.get_format_instructions())
    print("2.开始调用大语言模型提取位置信息") 
    res = model.invoke(prompt)
    print(f"3.大模型输出的结果为:\n{res}\n,类型:{type(res)}",) 
    parsed_res = parser.parse(res)
    print(f"4.输出解析器解析后的大模型输出结果:{parsed,_res},类型:{type(parsed._res)}") 
    params = {
        'location':parsed_res['location'], 
        'date':'today',
    }
    print(f"5.生成天气API的输入参数:{params}")
    
output_parser()

生态

具体来说,该框架由以下开源库组成:

  • LangChain Libraries:这是Python和JavaScript的库,包含了与大语言模型交互的接口和集成,以及用于将这些组件组合成chains和Agent的基本运行时。

    • langchain-core:关键抽象的基本接口以及将它们组合成Chain的逻辑。
    • langchain-community:各种组件(Components)的第三方集成
      • 合作伙伴包(例如**langchain-openailangchain-anthropic等):一些集成被进一步拆分成自己的仅依赖于的轻量级包langchain-core**。
    • langchain-experimental:组件(Components)和chains是实验性的,无论是因为技术是新颖的且仍在测试中,还是因为它们需要给LLM更多的访问权限,这在大多数生产系统中都是有风险的。
  • LangChain Templates:这是一系列预先设计好的参考架构,用于快速部署各种任务的应用程序。

  • langchain:构成应用程序认知架构的链、代理和检索策略。

  • LangGraphopen in new window:通过将步骤建模为图中的边和节点,使用 LLM 构建强大且有状态的多参与者应用程序。与 LangChain 顺利集成,但可以在没有 LangChain 的情况下使用。

  • LangServeopen in new window:将 LangChain 链部署为 REST API。

  • LangSmithopen in new window:一个开发者平台,提供了调试、测试、评估和监控在任何LLM(Large Language Model,,大型语言模型)框架上构建的chains的工具。

img

通过使用LangChain,开发者可以更加专注于应用程序的核心逻辑,而不是底层的实现细节。

这使得从快速原型制作到生产部署的过程变得更加高效。

它还提供了LangChain表达式语言(LangChain Expression Language,LCEL),这是-种声明式编程语言,旨在简化链的组合过程。

Prompt Template

提示模板是为大模型生成提示的预定义模版。

一个模板可能包括指令、少量示例,以及适合给定任务的特定上下文和问题。

默认情况下,PromptTemplate使用Python的str.format语法进行模板化。

from langchain.prompts import PromptTemplate

prompt_template = PromptTemplate.from_template(
		"给我讲一个关于{topic}的笑话."
)
# str类型
prompt_template.format(topic="足球")

模板支持任意数量的变量,包括没有变量:

from langchain.prompts import PromptTemplate

prompt_template=PromptTemplate.from_template("讲一个笑话") prompt_template.format()

字符串提示组合

在处理字符串提示时,每个模板都会被连接在一起。您可以直接使用提示,也可以使用字符串(列表中的第一个元素需要是一个提示)。

from langchain.prompts import PromptTemplate

prompt = (
	PromptTemplate.from_template("给我讲一个关于{topic}的笑话") 
  	+",确保要好笑"
	+"\n\n使用{language}输出"
)

#类型:langchain_core.prompts.prompt.PromptTemplate 
prompt

prompt.format(topic="足球",language="中文")

整合大模型

from langchain.prompts import PromptTemplate
from langchain_community.llms.tongyi import Tongyi

prompt_template=PromptTemplate.from_template(
  '给我讲一个关于{topic}的笑话'
)
model = Tongyi(model_name='qwen-max')
model.invoke(prompt_template.format(topic="足球"))
# model.invoke('给我讲一个关于{topic}的笑话'.format(topic="足球"))

用 PromptTemplate 主要是使用后面会提到的 chain 的能力

给大模型举例子

使用few-shot Prompt template让大模型更懂你

使用示例集

首先,创建一个少量示例的列表。每个示例应该是一个字典,键是输入变量,值是这些输入变量的值。

examples =[
    {
        "question":"我不会计算1+1",
        "answer":"""'我不会计算1+1'是数学问题,回答:'请联系张老师'""",
    },{
        "question":"足球的英文怎么说",
        "answer":"""'足球的英文怎么说'是英语问题,回答:'请联系李老师'""",
    },{
        "question":"东京是哪个国家的",
        "answer":"""'东京是哪个国家的'是地理问题,回答:'请联系王老师'""", 
    },{
        "question":"介绍下清朝的历史",
        "answer":"""'介绍下清朝的历史'是历史问题,回答:'请联系吴老师'""",
    },{
        "question":"这个游戏怎么玩",
        "answer'":"""'这个游戏怎么玩'这不是学科问题,回答:'你好,请说出具体的学科问题'""", 
    },{
        "question":"你好",
        "answer'":"""'你好'这不是学科问题,回答:'你好,请说出具体的学科问题'""", 
    },
]

配置一个将少量示例格式化为字符串的格式化器。这个格式化器应该是一个PromptTemplate对象。

from langchain.prompts import PromptTemplate

example_prompt = PromptTemplate(
	input_variables=["question","answer"],
  	template="Question:{question}\n{answer}"
)
## 打印看下
example_prompt.format(**examples[0])

最后,创建一个FewShotPromptTemplate对象。这个对象接收少量示例和少量示例的格式化器。

from langchain.prompts.few_shot import FewShotPromptTemplate

all_examples_prompt = FewShotPromptTemplate(
  	examples=examples,
  	example_prompt=example_prompt,
	prefix="参考下面的示例,回答问题:\n<example>:",
  	suffix="</example>\n\nQuestion:{input}\nAI:",
  	input_variables=["input"],
)

question="明朝最出名的历史人物是谁"

## 打印看下
all_examples_prompt.format(input=question)

接入大模型

from langchain.prompts.few_shot import FewShotPromptTemplate
from langchain_community.llms.tongyi import Tongyi

all_examples_prompt = FewShotPromptTemplate(
  	examples=examples,
  	example_prompt=example_prompt,
	prefix="参考下面的示例,回答问题:\n<example>:",
  	suffix="</example>\n\nQuestion:{input}\nAI:",
  	input_variables=["input"],
)

question="明朝最出名的历史人物是谁"

model = Tongyi(model_name='qwen-max')
model.invoke(all_examples_prompt.format(input=question)))

示例选择器

我们不是将示例直接输入到FewShotPromptTemplate对象,而是将它们输入到ExampleSelector对象。

在本教程中,我们将使用SemanticsimilarityExampleSelector类。这个类根据它们与输入的相似度选择少量示例。它使用嵌入模型来计算输入与少量示例之间的语义相似度,并使用向量存储来执行最近邻搜索。

from langchain.prompts.example_selector import SemanticsimilarityExampleSelector 
from langchain_community.embeddings import DashScopeEmbeddings

embeddings = DashScopeEmbeddings(
    model="'text-embedding-v1"
)
example_selector = SemanticSimilarityExampleSelector.from_examples(
    examples,
    embeddings,
    Chroma,
    ## 返回几个
    k=1,
)

selected_examples = example_selector.select_examples({"question":question}) 

print(f"Examples most similar to the input:{question}") 
for example in selected_examples:
    print("\n")
    for k,v in example.items():
        print(f"{k}:{v}")

'''输出结果
Examples most similar to the input:明朝最出名的历史人物是谁

answer:'介绍下清朝的历史'是历史问题,回答:'请联系吴老师'
question:介绍下清朝的历史

'''

接入大模型

from langchain.prompts.few_shot import FewShotPromptTemplate
from langchain_community.llms.tongyi import Tongyi

all_examples_prompt = FewShotPromptTemplate(
  	## 注意这里传入的是example_selector
  	example_selector=example_selector,
  	example_prompt=example_prompt,
	prefix="参考下面的示例,回答问题:\n<example>:",
  	suffix="</example>\n\nQuestion:{input}\nAI:",
  	input_variables=["input"],
)

question="明朝最出名的历史人物是谁"

model = Tongyi(model_name='qwen-max')
model.invoke(all_examples_prompt.format(input=question)))

ExampleSelector适合示例多,并且类别多的场景.

Output Parsers

输出解析器(Output parsers)负责接收大型语言模型(LLM)的输出,并将其转换为更适合的格式。当你使用LLM生成任何形式的结构化数据时,这一点非常有用。

除了拥有大量不同类型的输出解析器之外,LangChain输出解析器的一个显著优势是许多解析器支持流式处理。

JsonOutputParser

from langchain.prompts import PromptTemplate

from langchain_core.output_parsers import JsonOutputParser 
from langchain_community.llms.tongyi import Tongyi

model = Tongyi(model_name='qwen-max',model_kwargs={'temperature':0.01},streaming=True)

parser = JsonOutputParser()

prompt = PromptTemplate(
    template="Answer the user query.\n{format_instructions}\n{query}\n", 
    input_variables=["query"],
    partial_variables={"format_instructions":parser.get_format_instructions()},
)

chain = prompt | model | parser

for s in chain.stream({"query":'给我讲个100字的笑话'}):
    print(s)

'''
{'joke':''}
{'joke“:"为代么熊猫总是抱着竹子?因为他们"}
{'joke':"为什么熊猫总是抱着竹子?因为他们怕被别的动物抢饭碗!"}
'''

输出解析器是帮助将语言模型响应结构化的类。它们必须实现两个主要方法:

  • get_format_instructions(获取格式指令):一个返回字符串的方法,该字符串包含如何格式化语言模型输出的指令。

    parser.get_format_instructions()
    
  • parse (解析):一个方法,它接受一个字符串(假定为来自语言模型的响应)并将其解析成某种结构。

    output=model.invoke(prompt.format(query:="给我讲个l00字的笑"))
    
    parser.parse(output)
    

PydanticOutputParser

from langchain.output_parsers import PydanticOutputParser 
from langchain.prompts import PromptTemplate

from langchain_core.pydantic_v1 import BaseModel,Field,validator 
from langchain_community.llms.tongyi import Tongyi

model = Tongyi(model_name='qwen-max',model_kwargs={'temperature':0.01})

#定义期望的数据结构. 
class Joke(BaseModel):
    content:str=Field(description:="笑话的内容") 
    reason:str=Field(description="为什么好笑")

#设置解析器并将指令注入到提示模板中.
parser = PydanticOutputParser(pydantic_object=Joke)

prompt = PromptTemplate(
    template="请参考下面格式,回答用户问题.\n{format_instructions}\n{query}\n", 
    input_variables=["query"],
    partial_variables={"format_instructions":parser.get_format_instructions()},
)

prompt.format(query="给我讲个笑话.")

#一个旨在提示语言模型填充数据结构的查询.
model.invoke(prompt.format(query="给我讲个笑话."))

joke_obj=parser.parse(model.invoke(prompt.format(query="给我讲个笑话."))) 
## 打印看看
joke_obj
joke_obj.content
joke_obj.reason

LLMs

大型语言模型 (Large Language Models, LLMs) 是LangChain的核心组件。

LangChain并不提供自己的LLMs,而是提供了一个标准接口,用于与许多不同的LLMs进行交互。

具体来说,这个接口接受一个字符串作为输入,并返回一个字符串。

from langchain_openai import ChatOpenAI
from langchain_community.llms.tongyi import Tongyi
model = ChatOpenAI (model_name= 'gpt-3.5-turbo')
model = Tongyi(model_name= 'qwen-max' )
model.invoke( 'say hello')

BaseLLM

@property
@abstractmethod
def _llm_type ( self )->  str :
	"""Return type of llm."""

@abstractmethod
def _generate(
    self,
    prompts: List[str],
    stop: Optional[List[str] ] = None,
    run_manager: Optional[CallbackManagerForLLMRun] = None ,
    **kwargs: Any,
) -> LLMResult:
    """ Run the LLM on the given prompts."""

自定义LLMs

实现 BaseLLM 中的抽象方法

from langchain_core.language_models.llms import BaseLLM
from langchain_core.outputs.generation import Generation
from langchain_core.outputs.llm_result import LLMResult

class Superman(BaseLLM):
    model_name: str = "superman"
    
    @property
    def _llm_type(self) -> str:
    	return "superman"
    
    def _generate(
        self,
        *args,
        **kwargs) -> LLMResult:
        results = []
        for prompt in prompts:
            # result = self.llm.generate(self,*args, **kwargs)
            generation_obj = Generation(text=f'收到了 {prompt} ')
            results.append(generation_obj)
        return LLMResult(results)

测试

model = Superman()
model.invoke('say hi')
## 收到了 say hi

from langchain_core.prompts import PromptTemplate

prompt = PromptTemplate.from_template('say hi')
chain = prompt | model
chain.invoke({})

Fake LLM Models

本地测试大模型

from langchain_community.llms.fake import FakeListLLM
from langchain.prompts import PromptTemplate
responses = [
    "你好",
    "干啥",
]
model = FakeListLLM( responses= responses)
prompt = PromptTemplate.from_template('测试 ')
model.invoke(prompt.format())	# 你好
model.invoke(prompt.format())	# 干啥

调用大模型的结果会从给定的结果集中返回

改造AI聊天机器人

from datetime import datetime

from langchain_community.llms.tongyi import Tongyi
from langchain.prompts import PromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain.output_parsers import PydanticOutputParser

class LogModel(BaseModel):
    user_input: str = Field(description="用户输入的内容")
    llm_output: str = Field(description="大模型输出的内容")
    

prompt_template = PromptTemplate(
    template=" 按照如下格式回答用户问题。\n {format_instructions} \n {query}\n",
    input_variables= ["query"] ,
    partial_variables={"format _instructions": parser.get_format_instructions()},
)

model = Tongyi(model_name= 'qwen-max' , model_kwargs= { 'temperature' : 0.01} )
parser = PydanticOutputParser(pydantic_object=LogModel)

while True:
    user_input = input("User:" )
    if user_input == "quit":
    	break
    response = model.invoke(prompt_template.format(query=user_input) )
    parsed_response = parser.parse(response)
    print (f"AI: {parsed_response.llm_output }")
    # 获取当前时间
    now = datetime.now()
    # 格式化输出小时、分钟和秒
    formatted_time = now.strftime ("%H:%M:%S")
    print(f'{formatted_time}-INFO: User: {parsed_response.user_input}, AI: {parsed_response.llm_output}')

这里可能因为大模型没有按照指定格式返回而报错,重试也许就好了

实际开发中要对此做处理

Chain

Chain的使用

Chains指的是将一系列的调用组装成序列一一包括大语言模型(LLM),工具tool,还有数据预处理步骤的调用等等。目前最新的实现是通过 Lang Chain Expression Language(LCEL)实现的 Chains

LCEL非常适合构建您自己的Chains,也有一些已经封装好的Chains也可以拿来就用。

LangChain支持两种类型的现成的Chains :

  • [推荐]使用LCEL构建的Chains。在这种情况下, LangChain提供了一个更高级别的构造器方法。然而,在底层所做的只是用LCEL构建一个Chain。
  • [遗留] 通过从遗留的Chain类继承来构建的Chain。这些链在底层不使用LCEL,而是独立的类。

LangChain计划将所有Chains的LCEL版本创建出来。 这样做有几个原因:

  • 以这种方式构建的Chain很好,因为如果您想修改链的内部,您可以简单地修改LCEL。
  • 这些Chain天生支持流式处理(stream)、异步(ainvoke)和批量处理(batch)。
  • 这些Chain在每一步自动获得可观察性。
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate

from langchain_core.output_parsers import JsonOutputParser 
from langchain_community.llms.tongyi import Tongyi

model = Tongyi(model_name='qwen-max',model_kwargs={'temperature':0.01},streaming=True)

parser = JsonOutputParser()

prompt = PromptTemplate(
    template="Answer the user query.\n{format_instructions}\n{query}\n", 
    input_variables=["query"],
    partial_variables={"format_instructions":parser.get_format_instructions()},
)
## 遗留的Chain
chain = LLMChain(prompt=prompt,llm=model,output_parser=parser)
## LCEL 方式
chain = prompt | model | parser
resp = chain.invoke({
    'query':'讲一个笑话'
})

自定义Chain

create_stuff_documents_chain

下面给出一个示例:根据文档内容回答用户问题

  • 每个人喜欢的颜色
  • 最喜欢某种颜色的人是谁
  • create_stuff_documents_chain:它接受一个文档列表,并将它们全部格式化为一个提示,然后将该提示传递给LLM (大型语言模型)。它会传递所有文档,因此您应该确保这些文档长度适合您所使用的LLM的上下文窗口限制。
from typing import List
from langchain.prompts import PromptTemplate
from langchain_community.llms.tongyi import Tongyi
from langchain_core.documents import Document
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.runnables import RunnablePassthrough, Runnablelambda
from langchain_core.output_parsers import StroutputParser

model = Tongyi(model_name= 'qwen-max', model_kwargs={ 'temperature' : 0.01})
def get_docs(role: str='student') -> List [Document] :
    docs = []
    if role == 'student' :
        docs = [
            Document(page_content ="李明喜欢红色但不喜欢黄色"),
            Document(page_content ="李华喜欢绿色 但他更喜欢的是橙色")
        ]
    elif role == 'teacher':
        docs = [
            Document(page_content ="张老师喜欢紫色"),
            Document(page_content ="李老师喜欢黄色和蓝色")
        ]
    return docs

prompt_1 = PromptTemplate.from_template(
	'每个人喜欢的颜色是什么:\n\n{context}'
)

answer_favorite_color_based_docs_chain = create_stuff_documents_chain(
    llm=model,
    prompt=prompt_1,
	output_parser=StrOutputParser()
)

answer_favorite_color_based_docs_chain.invoke({
    'context':get_docs('teacher')
})

## 这个 chain需要接收一个角色
answer_favorite_color_based_attached_docs_chain = {'content': get_docs} 
	| answer_favorite_color_based_docs_chain
# student 是 get_docs 函数的参数
answer_favorite_color_based_attached_docs_chain.invoke('student')

prompt_2 = PromptTemplate.from_template(
	'谁喜欢{color}:\n\n{context}'
)
who_like_certain_color_chain = {
    'context': RunableLambda(lambda x: x['role']) | answer_favorite_color_based_docs_chain,
    'color': RunableLambda(lambda x: x['color'])
} | prompt_2 | model

who_like_certain_color_chain.invoke({
    'role': 'teacher',
    'color': '红色'
})

整合搜索

import os
from typing import List
from pydantic import Basemodel
from langchain.prompts import PromptTemplate
from langchain_community.llms.tongyi import Tongyi
from langchain_core.runnables import RunnablePassthrough
from serpapi import Client

# 搜索结果model
class SearchResultItem(BaseModel):
    title: str
    link: str
    snippet: str # 摘要
class SearchResults(BaseModel):
    results: List[SearchResultItem]
    
# 调用 serpapi 获取搜索结果
def get_search_results(query: str) -> SearchResults:
    params = {
        "engine": "google”,
        "q": query,
    }
    client = Client(api_key =os.environ ["SERPAPI_KEY"] )
    results = client.search (params )
    organic_results = results ["organic_results"]
    search_results = SearchResults (
        results= [
            SearchResultItem(
                title=organic_result['title'],
                link=organic_result['link'],
                snippet =organic_result['snippet']
            )
            for organic_result in organic_results
        ]
    )
    return search_results

model = Tongyi(model_name= 'qwen-max', model_kwargs={ 'temperature' : 0.01})

prompt = PromptTemplate.from_template(
	"""根据搜索引擎的结果,回答用户的问题。
	SEARCH_RESULTS:{search_results}
	USER_QUERY:{query}
	"""
)

search_chain = {
    'search_results': get_search_results,
    'query': RunablePassthrough()
} | prompt | model

## 也可以这样写
search_chain = {
    'search_results': lambda x: get_search_results(x),
    'query': lambda x: x,
} | prompt | model

query = '易建联什么时候退役的?'

search_chain.invoke(query)

执行bash、分析、调用API

LLMBashChain

# 功能1: bash_chain --- 查询操作系统的所有磁盘使用情况

from langchain_experimental.llm_bash.base import LLMBashChain
from langchain_community.llms.tongyi import Tongyi

model = Tongyi(model_name= 'qwen-max', model_kwargs={ 'temperature' : 0.01})
# verbose=True 可以打印执行过程
bash_chain = LLMBashChain.from_llm(model,verbose=True)
query = '查询操作系统的所有磁盘使用情况'
bash_chain.invoke(query)

alarm_chain

from langchain.prompts import PromptTemplate
from langchain_experimental.llm_bash.base import LLMBashChain
from langchain_community.llms.tongyi import Tongyi

model = Tongyi(model_name= 'qwen-max', model_kwargs={ 'temperature' : 0.01})
# verbose=True 可以打印执行过程
bash_chain = LLMBashChain.from_llm(model,verbose=True)
query = '查询操作系统的所有磁盘使用情况'


## 功能2:alarm_chain -- 判断是否需要警告
prompt = PromptTemplate.from_template(
	"""{context}\n\n根据如上的查询结果,判断是否需要告警,如果需要的话总结并返回告警内容,否则返回空字符串"""
)

alarm_chain = {
    "context": lambda x: bash_chain.invoke(query)['answer']
} | prompt | model

ararm_chain.invoke({})

APIChain

from langchain.chains import APIChain
from langchain_core.runnables import RunnableLambda

HTTP_DOCS = """
# API 文档

## 概述
此API用于向指定的URL发送告警信息,当系统检测到特定告警条件时,可以通过调用此API以通知管理员或记录系统状态

## API 信息

- **URL**: http://httpbin.org/get
- **业务说明**: 发送告警信息至服务器,用于系统告警通知和日志记录
- **请求方式**: `GET`

## 请求参数

| 参数名 | 类型 |描述 | 是否必须 | 示例 |
|-------|------|----|---------|-----|
|alarm | string | 具体的告警信息描述 | 是 | "alarm information" |

"""

api_chain = APIChain.from_llm_and_api_docs(
	llm=model,
    api_docs=HTTP_DOCS,
    limit_to_domains=['http://httpbin.org'],
    verbose=True,
)

alarm_api_chain = alarm_chain | apichain
alarm_api_chain.invoke({})

这主要涉及明确的模型接口定义、辅助工具用于构建模型输入,以及用于处理模型输出的助工具。 模型(Models) LangChain 集成了两种主要类型的模型: LLMs 和 Chat Models。这些模型根据它们的输入和输出类型来 定义。 · LLMs LangChain 中的 LLMs 指的是纯文本完成模型。它们封装的 API 接受字符串提示作为输入,并 输出字符串完成。OpenAI 的 GPT-3 就是作为 LLM 实现的。 ·Chat Models Chat models通常由 LLMs支持,但专门调整用于进行对话。关键是,它们的提供商 API使用与纯文本完成模型不同的接口。它们不是接受单个字符串,而是接受一个聊天消息列表作为 输入,并 并返回一个 AI 消息 作为输出。 GPT-4 和 Anthropic 的 Claude-2 都是作为 chat models 实现 的。 ·注意这两种 API类型具有非常不同的输入和输出架构。这意味着与它们交互的最佳方式可能大不相 同。尽管 LangChain 使得可以互换地对待它们,但这并不意味着你应该这么做。特别是, LLMs 与 ChatModels 的提示策略可能大不相同。这意味着你需要确保你使用的提示是为你正在使用的模型类 型设计的。 此外,并非所有模型都是相同的。不同的模型有不同的提示策略最适合它们。例如,Anthropic 的模型最适

ChatModels

LLMs 与 chat models对比介绍

LLMs 大型语言模型 (Large Language Models, LLMs) 是Lang Chain的核心组件。 Lang Chain并不提供自己的LLMs,而是提供了一个标准接口,用于与许多不同的LLMs进行交互。

具体来说,这个接口接受一个字符串作为输入,并返回一个字符串。 LLMs Models

from langchain_openai import OpenAI
from langchain_community.llms.tongyi import Tongyi

LLMs Models示例

from langchain_community.llms.tongyi import Tongyi
from lanachain.prompts import PromotTemplate

prompt_template = PromptTemplate("给我讲一个关于{topic}的笑话")
model = Tongyi(model_name='qwen-max')
model.invoke(prompt_template.format(topic='足球'))

ChatModels 聊天模型(ChatModels)是LangChain的核心组件。 LangChain不提供自己的聊天模型, 而是提供了一个标准接口,用于与许多不同的模型进行交互。

具体来说,这个接口接受一个消息列表作为输入,并返回一条消息。

ChatModels

from langchain_openai import ChatOpenAI
from langchain_community.chat_models.tongyi import ChatTongyi

ChatPromptTemplate

聊天模型的提示是一个聊天消息列表。

每个聊天消息都与内容相关联,并且有一个额外的参数称为角色。例如,在 OpenAi 聊天完成 API 中,一个聊天消息可以与 ai 、 human 或 system 角色关联。

这样创建一个聊天提示模板:

from langchain_core.prompts import ChatPromptTemplate

chat_template = ChatPromptTemplate.from_messages(
[
    ('system': 'your name is {name}'),
    ('human': '你好'),
    ('ai': '你好'),
    ('human': '{user_input}'),
])

messages = chat_template.format_messages(name='老王',user_input='你叫啥')

ChatPromptTemplate 除了接收二元组,还可以接收 MessagePromptTemplate 或 BaseMessage 的 实例。这为您构建聊天提示提供了极大的灵活性。

from langchain.prompts import HumanMessagePromptTemplate
from langchain_core.messages import SystemMessage

# langcha in _ core. prompts . chat. HumanMess ageP rompt Temp late

human_message_prompt_template = HumanMessagePromptTemplate.from_template("{text}")
chat_template = ChatPromptTemplate.from_messages(
    [
        SystemMessage(
            content=("你喜欢讲笑话")
        ),
        human_message_prompt_template,
    ]
)
 
messages = chat_template.format_messages(text='今天天气真好')
from langchain.prompts import HumanMessagePromptTemplate
from langchain_core.messages import SystemMessage,HumanMessage,AIMessage
from langchain_core.prompts import ChatPromptTemplate
from langchain_community.chat_models.tongyi import ChatTongyi

human_message_prompt_template = HumanMessagePromptTemplate.from_template("{text}")
chat_template = ChatPromptTemplate.from_messages(
    [
        SystemMessage(
            content=("你是一个翻译官,你的任务是将用户的文本翻译成英语")
        ),
        HumanMessage(content='你好'),
        AIMessage(content='hello'),
        HumanMessagePromptTemplate.from_template("{text}"),
    ]
)

model = ChatTongyi(model_name='qwen-max')
chain = chat_template | model
chain.invoke({
    'text': '我爱你'
})

Memory

帮我维护历史信息

大多数基于大型语言模型 (LLM)的应用都有会话界面。会话中的一个重要组成部分是能够引用之前对话中引入的信息。在最基本的层面上,会话系统应该能够直接访问一些过去的消息。

我们将这种存储过去交互信息的能力称为“记忆(memory)”。LangChain提供了许多工具来为系统添加记忆功能。这些工具可以单独使用,也可以无缝地整合到 Chain中。

简介

记忆系统需要支持两个基本动作:读取和写入。请记住,每个Chain都定义了一些核心执行逻辑,期望某些输入。其中一些输入置接来自用户,但有些输入可以来自记忆。在给定的运行中,Chain将与其记忆系统交互两次。

  • 在接收到初始用户输入之后但在执行核心逻辑之前,Chain将从其记忆系统中读取并增强用户输入。
  • 在执行核心逻辑之后但在返回答案之前,Chain将把当前运行的输入和输出写入记忆,以便将来可以引用。

入门

让我们看看LangChain中的记忆实际上是什么样的。在这里,我们将介绍与任意记忆类交互的基础知识。

让我们看看如何在使用 ConversationBufferMemoryConversationBufferMemory 是一种极其简单的记忆形式, 它只是在缓冲区中保持一系列聊天消息,并将这些消息传递到提示模板中。

from langchain.memory import ConversationBufferMemory
memory = ConversationBufferMemory()
memory.chat_memory.add_user_message("您好")
memory.chat_memory.add_ai_message("您好")

如何定义Memory中保存历史记录的Key

在进入Chain之前,会从记忆中读取各种变量。这些变量有特定的名称,需要与Chain期望的变量对齐。您可以通过调用 memory.load_memory_variables({})来查看这些变量是什么。

请注意,我们传入的空字典只是实际变量的占位符。如果您使用的记忆类型依赖于输入变量,则可能需要传入一些。

memory.load_memory_variables({})
# 输出为 {'history': "Human: 您好\nAI: 您好"}
from langchain.prompts import PromptTemplate
prompt = PromptTemplate.from_template("聊天历史: {history}\n")
prompt.format(**memory.load_memory_variables({}))

在这种情况下,您可以看到 load_memory_variables 返回一个键,即 history 。这意味着您的 Chain (很可能是您的提示(prompt) 应该期望一个名为 history 的输入。您通常可以通过记忆类上的参数来控制这个变量。例如,如果您希望记忆变量以chat_history键返回,您可以这样做:

memory = ConversationBufferMemory(memory_key="chat_history")
memory.chat_memory.add_user_message("您好")
memory.chat_memory.add_ai_message("您好")
memory.load_memory_variables({})
# 输出为 {'chat_history': "Human: 您好\nAI: 您好" }

最常见的一种记忆类型涉及返回聊天消息列表。这些可以作为单个字符串返回,全部连接在一起 (当它们将传递给LLM时很有用)或者是ChatMessages列表 (当传递给ChatModels时很有用)。

默认情况下,它们作为单个字符串返回。为了以消息列表的形式返回,您可以设置return_messages=True

memory = ConversationBufferMemory(return_messages=True)
memory.chat_memory.add_user_message("您好")
memory.chat_memory.add_ai_message("您好")
memory.load_memory_variables({})

'''
{'history': [
HumanMessage(content= '您好', additional_kwargs={}, example=False), 
AIMessage(content= '您好', additional_kwargs={}, example=False) 
]}
'''

改造多轮对话

MessagesPlaceholder

在 prompt 中使用的消息列表占位符。 这时候就会使用 Messages Placeholder 。 这些对象通过一个名为 variable_name 的参数来指定。与这个 variable_name 值相同的输入应该是一个消息列表。

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import SystemMessage,HumanMessage,AIMessage

memory_key='history'
messages_placeholder = MessagesPlaceholder(variable_name=memory_key)
# 与这个 variable_name 值相同的输入应该是一个消息列表
messages_placeholder.format_messages(history=[
    HumanMessage(content='你好'),
    AIMessage(content='你好'),
])

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_community.chat_models.tongyi import ChatTongyi
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationChain

model = ChatTongyi(
    model_name='qwen-max',
	model_kwargs={'temperature': 0.01},
    streaming=True
)
memory_key='history'
messages_placeholder = MessagesPlaceholder(variable_name=memory_key)

prompt = ChatPromptTemplate.from_messages(
[
    ('system': '你是一个很棒的聊天机器人'),
    messages_placeholder,
    ('human': '{input}'),
])
memory = ConversationBufferMemory(memory_key=memory_key, return_messages=True)

conversation_chain = ConversationChain(
	llm=model,
    prompt=prompt,
    memory=memory
)

while True:
    user_input = input("User: ")
    if user_input == "quit":
        break
    print(f'{"-"*50}\n{memory.load_memory_variable({})}\n{"-"*50}')
    for chunk in conversation_chain.stream({"input": user_input}):
        print(chunk["response"],end="",flush=True)
    print()
    
上次编辑于:
贡献者: liyuanhao,李元昊