langchain

1.概念

什么是LangChain?

源起:LangChain产生源于Harrison与领域内的一些人交谈,这些人正在构建复杂的LLM应用,他在开发方式

上看到了一些可以抽象的部分。一个应用可能需要多次提示LLM并解析其输出,因此需要编写大量的复制粘贴。

LangChain使这个开发过程更加简单。一经推出后,在社区被广泛采纳,不仅有众多用户,还有许多贡献者参

与开源工作。

还有大模型本身的问题,无法感知实时数据,无法和当前世界进行交互。

LangChain是一个用于开发大语言模型的框架。

主要特性:

\1. 数据感知:能够将语⾔模型与其他数据源进⾏连接。

\2. 代理性:允许语⾔模型与其环境进⾏交互。可以通过写⼯具的⽅式做各种事情,数据的写⼊更新。

主要价值:

1、组件化了需要开发LLM所需要的功能,提供了很多工具,方便使用。

2、有一些现成的可以完整特定功能的链,也可以理解为提高了工具方便使用。

2.主要模块

LangChain 为以下模块提供了标准、可扩展的接口和外部集成,按照复杂程度从低到高列出:

模型输入/输出 (Model I/O)

与语言模型进行接口交互

数据连接 (Data connection)

与特定于应用程序的数据进行接口交互

链式组装 (Chains)

构造调用序列

代理 (Agents)

根据高级指令让链式组装选择要使用的工具

内存 (Memory)

在链式组装的多次运行之间持久化应用程序状态

回调 (Callbacks)

记录和流式传输任何链式组装的中间步骤

3.链

链允许我们将多个组件组合在一起,创建一个单一而连贯的应用程序。例如,我们可以创建一个链,接受用户输入,使用 PromptTemplate 进行格式化,然后将格式化的响应传递给 LLM。我们可以通过将多个链组合在一起或将链与其他组件组合来构建更复杂的链。

目前内置的几种常用Chain:

• LLMChain:

这是一个简单的链,由PromptTemplate和LLM组成,它使用提供的输入键值格式化提示模板,将格式化的字符串传递给LLM,并返回LLM的输出。

1.模型配置

from langchain.chat_models import ChatOpenAI
from langchain import LLMChain
from langchain.prompts.chat import (
    ChatPromptTemplate,
    HumanMessagePromptTemplate,
)

api_base_url = "http://192.168.175.6:8000/v1"  
api_key= "EMPTY"
LLM_MODEL = "Baichuan-13b-Chat"
model = ChatOpenAI(
    streaming=False,
    verbose=True,
    # callbacks=[callback],
    openai_api_key=api_key,
    openai_api_base=api_base_url,
    model_name=LLM_MODEL
)
 

2.设置模板

from langchain import PromptTemplate

template = """\
你是一个新公司的命名咨询顾问.
为制作 {product} 的公司起好的名字? 使用中文回答问题,不少于5个名字
"""

chat_prompt = PromptTemplate.from_template(template)
chain = LLMChain(prompt=chat_prompt, llm=model)
print(chain.run("五颜六色的袜子"))

  1. 彩虹袜坊 2. 缤纷袜艺 3. 七彩袜语 4. 多彩袜尚 5. 绚丽袜裳

• SimpleSequentialChain

每个步骤都有一个单一的输入/输出,一个步骤的输出是下一个步骤的输入。

from langchain.prompts import ChatPromptTemplate
from langchain.chains import SimpleSequentialChain

# 第一个Prompt和Chain
first_prompt = ChatPromptTemplate.from_template(
    "你是一个新公司的命名咨询顾问.为制作 {product} 的公司起2个好的名字? 使用中文回答问题"
)
chain_one = LLMChain(llm=model, prompt=first_prompt)

# 第二个Prompt和Chain
second_prompt = ChatPromptTemplate.from_template(
    "为下面的公司写一个20字的简短描述:{company_name}"
)
chain_two = LLMChain(llm=model, prompt=second_prompt)

# 把第一个Chain和第二个Chain合在一起
overall_simple_chain = SimpleSequentialChain(chains=[chain_one, chain_two],
                                             verbose=True
                                            )
overall_simple_chain.run("智能手机")

• Sequential Chains:

不是所有的链都是有固定的输入和输出,有时候中间的链需要多个输入,最终也有多个输出,这个时候考虑用SequentialChain

# 这是一个LLMChain,给定一个剧本的标题和它所处的时代,它的任务是写一个概要。
template = """你是一位剧作家。给定剧本的标题和它所处的时代,你的任务是为该标题写一个概要。

标题: {title}
时代: {era}
剧作家: 这是上述剧本的概要:"""
prompt_template = PromptTemplate(input_variables=["title", "era"], template=template)
synopsis_chain = LLMChain(llm=model, prompt=prompt_template, output_key="synopsis")

# 这是一个LLMChain,给定一个剧本的概要,它的任务是写一个剧本的评论。
template = """你是一位专业的剧本评论家。给定剧本的概要,你的任务是为该剧本写一篇评论。

剧本概要:
{synopsis}
你对上述剧本的评论:"""
prompt_template = PromptTemplate(input_variables=["synopsis"], template=template)
review_chain = LLMChain(llm=model, prompt=prompt_template, output_key="review")


# 这是整体链,我们按顺序运行这两个链。
from langchain.chains import SequentialChain
overall_chain = SequentialChain(
    chains=[synopsis_chain, review_chain],
    input_variables=["era", "title"],
    # 这里我们返回多个变量
    output_variables=["synopsis", "review"],
    verbose=True)

overall_chain({"title":"海滩上的日落悲剧", "era": "维多利亚时代的英格兰"})
 

• RouterChain:

有时候单个串行的Chain不能满足我们的诉求,这个时候考虑使用RouterChain

它在一系列的链(Chain)中动态地选择下一个要执行的链。这种模式通常用于处理复杂的逻辑流程,其中下一个执行的步骤取决于当前的输入或状态。


#例如,如果你正在构建一个问题回答系统,你可能有多个链,每个链专门处理一种类型的问题
# (例如,一个处理物理问题,一个处理数学问题等)。
# 然后,你可以使用一个"RouterChain"来检查每个问题的特性,并将问题路由到最适合处理该问题的链。
from langchain.chains.router import MultiPromptChain
from langchain.chains import ConversationChain
from langchain.chains.llm import LLMChain
from langchain.prompts import PromptTemplate

physics_template = """你是一位非常聪明的物理教授。 \
你擅长以简洁易懂的方式回答物理问题。 \
当你不知道问题的答案时,你会承认你不知道。

这是一个问题:
{input}"""

math_template = """你是一位非常好的数学家。你擅长回答数学问题。 \
你之所以这么好,是因为你能够将难题分解成各个组成部分, \
回答组成部分,然后将它们组合起来回答更广泛的问题。

这是一个问题:
{input}"""

prompt_infos = [
    {  "name": "物理", "description": "适合回答物理问题","prompt_template": physics_template,},
    {  "name": "数学", "description": "适合回答数学问题","prompt_template": math_template,},
]


destination_chains = {}
for p_info in prompt_infos:
    name = p_info["name"]
    prompt_template = p_info["prompt_template"]
    prompt = PromptTemplate(template=prompt_template, input_variables=["input"])
    chain = LLMChain(llm=model, prompt=prompt)
    destination_chains[name] = chain

# 默认的Chain
default_chain = ConversationChain(llm=model, output_key="text")

destinations = [f"{p['name']}: {p['description']}" for p in prompt_infos]
print(destination_chains.keys())
print(destinations)

dict_keys([‘物理’, ‘数学’]) [‘物理: 适合回答物理问题’, ‘数学: 适合回答数学问题’]


from langchain.chains.router.llm_router import LLMRouterChain, RouterOutputParser
# 物理: 适合回答物理问题', '数学: 适合回答数学问题
destinations = [f"{p['name']}: {p['description']}" for p in prompt_infos]
destinations_str = "\n".join(destinations)

router_prompt_template = """\
给定一个原始的文本输入到语言模型中,选择最适合输入的模型提示。
你将得到可用提示的名称和提示最适合的描述。如果你认为修改原始输入最终会得到更好的语言模型响应,你也可以修改原始输入。

<< 格式化 >>
返回一个markdown代码片段,其中包含一个格式化为如下样式的JSON对象:
​```json
{{{{
    "destination": string \\ 使用的提示名称或"DEFAULT"
    "next_inputs": string \\ 可能修改过的原始输入
}}}}
​```

记住:"destination" 必须是下面指定的候选提示名称之一,或者如果输入不适合任何候选提示,它可以是"DEFAULT"。
记住:"next_inputs" 可以是原始输入,如果你认为不需要任何修改。

<< 候选提示 >>
{destinations}

<< 输入 >>
{{input}}

<< 输出 >>
"""
router_template = router_prompt_template.format(destinations=destinations_str)
router_prompt = PromptTemplate(
    template=router_template,
    input_variables=["input"],
    output_parser=RouterOutputParser(),
)
router_chain = LLMRouterChain.from_llm(model, router_prompt)

# 构建RouterChains
chain = MultiPromptChain(
    router_chain=router_chain,
    destination_chains=destination_chains,
    default_chain=default_chain,
    verbose=True,
)
print(chain.run("什么是黑体辐射?"))

print(chain.run("计算下7乘以24,然后再乘以60等于多少?"))

print(chain.run("什么是彩虹?"))