langchain 大模型学习

183 阅读10分钟

1. 学习资料

langchain 快速开始:python.langchain.com/docs/get_st…

这个讲的特别好

www.bilibili.com/video/BV1Lu…

python 版本要求:>= 3.8.1, 推荐使用3.10.12 pip install jupyter

jupyter notebook

1.1 概念解释

什么是 Chain

通过 “Chain” 来链接LangChain的各个组件和功能——模型之间彼此链接,或模型与其他组件链接。

LLMChain:最简单的链

LLMChain围绕着语言模型推理功能又添加了一些功能,整合了PromptTemplate、语言模型(LLM或聊天模型)和 Output Parser,相当于把Model I/O放在一个链中整体操作。它使用提示模板格式化输入,将格式化的字符串传递给 LLM,并返回 LLM 输出。

1.2 提示模板的demo

提示模板: langchain对提示词的封装,可以接受一个或者多个参数,当然不接受参数就退化成提示词本身了

接受部分参数的提示模板

  • 使用场景:所有的参数无法同步获得,当在链式结构中传递提示模板的时候,不需要一次性提供所有参数
  • 有的参数可以由方法提供
  • 两种形式,都可以使用
from datetime import datetime

from langchain.prompts import PromptTemplate

prompt_template = PromptTemplate.from_template("给我一个很土但是听起来很好听好养活的{对象}小名")
prompt = prompt_template.format(对象 = "小女孩")
print(prompt)



no_input_prompt_template = PromptTemplate(input_variables =[], template="给我讲个故事")
prompt = no_input_prompt_template.format()
print(prompt)

multi_input_prompt_template = PromptTemplate(input_variables =["类型","故事"], template="给我讲个{类型}的{故事}")
prompt = multi_input_prompt_template.format(类型 = "讽刺好笑", 故事 = "笑话")
print(prompt)



multi_input_prompt_template = PromptTemplate(input_variables =["类型","故事"], template="给我讲个{类型}的{故事}, 当前时间:{日期}")
partial_prompt = multi_input_prompt_template.partial(类型 = "讽刺好笑", 故事 = "笑话")
print(partial_prompt)
print(partial_prompt.format(日期 = "1998-03-23"))

def _get_datetime():
    now = datetime.now()
    return now.strftime("%m/%d/%Y,%H:%M:%S")


multi_input_prompt_template = PromptTemplate(input_variables =["类型","故事"], template="给我讲个{类型}的{故事}, 当前时间:{日期}")
partial_prompt = multi_input_prompt_template.partial(类型 = "讽刺好笑", 故事 = "笑话")
print(partial_prompt)
print(partial_prompt.format(日期 = _get_datetime()))

少样本学习:对语言模型进行本身的微调

我是chatgpt3.5 做的测试,没咋生效,后续继续追踪

examples = [
    {
        "question":"你好吗?",
        "answer":"帅哥,我很好"
    },
    {
        "question":"今天周几?",
        "answer":"帅哥,今天周末"
    },
    {
        "question":"天气好么?",
        "answer":"帅哥,是的,今天天气很好"
    }
]
example_prompt_template = PromptTemplate(input_variables =["question","answer"], template="Question:{question}\n{answer}")
prompt = FewShotPromptTemplate(
    examples = examples,
    example_prompt = example_prompt_template,
    suffix = "Question:{input}",
    input_variables = ["input"]
)
prompt_value = prompt.format(input = "你好吗")
print(prompt_value)

result = chat_model.predict(prompt_value)
print(result)

利用样本选择器筛选样本

  • 样本数量太多的时候,并不是所有的样本都起到提升输出质量的作用

  • SemanticSimilarityExampleSelector,语义相似度选择器

  • 给的样本数量特别多的时候,就可以使用样本选择器帮助我们筛选最合适的样本,筛选出来的是一个子集,子集会作为最终提示词里面的样本

  • 使用场景:

    当你的样本数量特别多,又没有必要把所有的样本数量全部喂给大语言模型来回答你的一个可能小小的问题,这样就会造成一些浪费

    用筛选器可以帮你缩短发送给大语言模型的提示词的总长度,提示词越长,你需要花的钱越多,所以变相省钱

1.3 简单应用-链式结构

为什么需要链式结构

  • 方便连接多个llm模块
  • 构造模块包括:llm模块 + 提示模板

image.png

image.png

如何避免重复定义功能相似的llm模块

1.4 llm 模块

基本用法

  • 直接调用:predict
  • 批量生成:generate, 我用的azure,没有跑通

自定义llm模块

  • 用于封装langchain尚未支持的大语言模型
  • 用来做模拟测试
  • 你来定义llm模块被调用的时候,如何根据输入的文本来输出

自己定义一个llm

import os
from typing import Optional

from langchain.llms.base import LLM
from langchain_openai import AzureChatOpenAI
from langchain.callbacks.manager import CallbackManagerForLLMRun


class ExtAI(LLM):

    @property
    def _llm_type(self) -> str:
        return "ExtAI"

    def _call(self,
              prompt: str,
              stop: Optional[list[str]] = None,
              run_manager:Optional[CallbackManagerForLLMRun] = None) -> str:
        if stop is not None:
            raise ValueError("stop kwags are not permitted")
        pd = prompt.find("吗")
        if pd >= 0:
            return prompt[0:pd] + "。"
        return "嗷。"

llm = ExtAI()
print(llm)
result = llm("你好吗")
print(result)

1.5 数据连接

一些大模型经常用到模型数据集中没有的数据,langchain提供了一系列的工具可以让你从各种数据源中加载新的数据源,转换数据,存储数据以及访问数据

文档加载器(Document loaders)、文档转换器、文本embedding模型、向量存储站、检索器

1.5.1 CSV数据加载器

结构化CSV文件解析

from langchain.document_loaders.csv_loader import CSVLoader

loader = CSVLoader(file_path="./data.csv")
data = loader.load()
print(data)
print(len(data))
print(data[0].page_content)

非结构化文件解析

  • 一旦加载了文档,就会对其进行转换来适应应用场景
  • 将长文档拆分成较小的块,避免大模型提示词文本长度的限制
  • 嵌入模型导出的向量需要放到一个地方便于存储查询
  • 检索器是一种根据非结构查询语句返回对应文档的接口

image.png

image.png

1.6 链结构

链结构分类 基础链解构

  • LLM chain
  • Router chain
  • Sequential chain
  • Transfermation chain

应用链结构实例

  • Document chains
  • Retrieval QA

LLM chain(单链结构)

步骤

定义prompt、定义llm、定义chain、运行predict

from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
import os

from langchain_openai import AzureChatOpenAI

prompt_template = "给我一个很土但是听起来好养活的男孩小名"
prompt = PromptTemplate.from_template(prompt_template)
llm_chain = LLMChain(llm=chat_model, prompt= prompt)
result = llm_chain.predict()
print(result)

Router chain(多链结构)

执行两次llm,先决定走哪个llm,然后再执行

步骤

定义prompts,定义llm、embeddings, 定义chain(多链并行,选择一条链处理)、运行predict

from langchain.chains import LLMChain
from langchain.chains import ConversationChain
from langchain.chains.router import MultiPromptChain
from langchain.chains.router.llm_router import LLMRouterChain, RouterOutputParser
from langchain.chains.router.multi_prompt import MULTI_PROMPT_ROUTER_TEMPLATE



from langchain.prompts import PromptTemplate
import os

from langchain_openai import AzureChatOpenAI


naming_template = '''
    你是一个非常有创意的起名字专家。你非常擅长给新生儿们起好听的,富含寓意的名字。
    用中文给出回答。
    这里是问题:
    {input}
    '''

math_template = '''
    you are a very good mathematician, You are great at answering math questions.
    you are so good because you are able to break down hard problems into their component parts,
    answer the component parts, and then put them together to answer the broader question.

    Here is a question:{input}

'''

prompt_info = [
    {
        "name": "naming",
        "description": "擅长用中文起名字",
        "prompt_template": naming_template
    },
    {
        "name": "math",
        "description": "Good for answering math questions",
        "prompt_template": math_template
    },

]

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

default_chain = ConversationChain(llm=llm, output_key="text")

destinations = [f"{p['name']}:{p['description']}" for p in prompt_info]
destinations_str = "\n".join(destinations)
router_template = MULTI_PROMPT_ROUTER_TEMPLATE.format(destinations=destinations_str)
router_prompt = PromptTemplate(
    template=router_template,
    input_variables=["input"],
    output_parser=RouterOutputParser(),
)
router_chain = LLMRouterChain.from_llm(llm, router_prompt)
chain = MultiPromptChain(
    router_chain=router_chain,
    destination_chains=destination_chains,
    default_chain=default_chain,
    verbose=True
)

result = chain.run("给我一个很土但听起来好听好养活的小名")
print(result)

Sequential chain(串行链)

串行的上一个大模型调用的结果是下一步大模型调用的输入之一,从而变成一个串行的多链结构

定义prompts, 定义llm、embeddings,定义chain(多链串行,按照顺序执行), 运行predict

from langchain.chains import LLMChain
from langchain.chains import SimpleSequentialChain




from langchain.prompts import PromptTemplate
import os

from langchain_openai import AzureChatOpenAI

os.environ["AZURE_OPENAI_API_KEY"] =  "58b974229abc4ec1a311fd151aea94f1"
os.environ["AZURE_OPENAI_ENDPOINT"] = "https://IBU02.openai.azure.com/"
os.environ["OPENAI_API_VERSION"] = "2024-02-15-preview"


# 并行的多链结构
llm = AzureChatOpenAI(
    openai_api_version="2024-02-15-preview",
    azure_deployment="gpt-4-0125-Preview",
)




template ="""
        你是一个非常棒的起名字专家。你非常擅长给新生儿们起好听的的名字。用中文给出3种回答。
        这里是问题:
        {input}
        """

prompt_template = PromptTemplate(input_variables= ["input"], template=template)

synopsis_chain =LLMChain(llm=llm,prompt=prompt_template)

template = """
    你是一个名字专家评论家,你非常擅长给名字做分析,并且判断哪个名字更好.
    名字分析:
    {synopsis}
    对上面的名字进行分析,判断哪个更好:
    """

prompt_template = PromptTemplate(input_variables=["synopsis"], template=template)
review_chain = LLMChain(llm=llm, prompt = prompt_template)
overall_chain = SimpleSequentialChain(chains=[synopsis_chain, review_chain], verbose=True)

while True:
    result = overall_chain.run(input = "给我一个很土但是听起来好养活的男孩小名")
    print(result)

1.7 Langchain模块拆解

记忆模块/历史记录

  • 可以用于chain、agent等结构
  • 可以对memory进行修改,总结,或者自定义操作
  • 可以利用数据库对历史记录进行存取

代理人模块(agent)

  • agent的重要性,代表未来
  • agent拓展了LLM的边界,从基础设施到应用
  • agent还在早期

1.8 基于Langchain的PDF文档检索助手

应用目标

  • 上传本地PDF内容
  • 针对PDF内容进行提问式检索

应用框架

image.png

image.png

image.png

image.png

1.9 模型的使用拆解demo

把对模型的使用过程拆解成三块,分别是 输入提示、调用模型和 输出解析。这三块形成了一个整体,因此在LangChain中这个过程被统称为 Model I/O(Input/Output)

重点演示怎么把结果格式化输出和保存到csv当中

# 导入LangChain中的提示模板
from langchain import PromptTemplate
import os
from langchain_community.chat_models import AzureChatOpenAI
# 导入结构化输出解析器和ResponseSchema
from langchain.output_parsers import StructuredOutputParser, ResponseSchema

# 创建原始模板
prompt_template = """您是一位专业的鲜花店文案撰写员。
对于售价为 {price} 元的 {flower_name} ,您能提供一个吸引人的简短描述吗?
{format_instructions}"""

prompt = PromptTemplate.from_template(prompt_template)


# 定义我们想要接收的响应模式
response_schemas = [
    ResponseSchema(name="description", description="鲜花的描述文案"),
    ResponseSchema(name="reason", description="问什么要这样写这个文案")
]
# 创建输出解析器
output_parser = StructuredOutputParser.from_response_schemas(response_schemas)

# 获取格式指示
format_instructions = output_parser.get_format_instructions()

# 根据原始模板创建提示,同时在提示中加入输出解析器的说明
prompt = PromptTemplate.from_template(prompt_template, partial_variables={"format_instructions": format_instructions})



# 多种花的列表
flowers = ["玫瑰", "百合", "康乃馨"]
prices = ["50", "30", "20"]


# 创建一个空的DataFrame用于存储结果
import pandas as pd

# 先声明列名
df = pd.DataFrame(columns=["flower", "price", "description", "reason"])

for flower, price in zip(flowers, prices):
    # 根据提示准备模型的输入
    input = prompt.format(flower_name=flower, price=price)

    #获取模型的输出
    output = chat_model.predict(input)
    #解析模型的输出(这是一个字典结构)
    parsed_output = output_parser.parse(output)
    #在解析后的输出中添加“flower”和“price”
    parsed_output['flower'] = flower
    parsed_output['price'] = price

    # 将解析后的输出添加到DataFrame中
    df.loc[len(df)] = parsed_output

# 打印字典
print(df.to_dict(orient='records'))

# 保存DataFrame到CSV文件
df.to_csv("flowers_with_descriptions.csv", index=False)

2.0 LangChain的ChatPromptTemplate模板

# 导入聊天消息类模板
from langchain.prompts import (
 ChatPromptTemplate,
 SystemMessagePromptTemplate,
 HumanMessagePromptTemplate,
)
from langchain_community.chat_models import AzureChatOpenAI
import os
# 模板的构建
template="你是一位专业顾问,负责为专注于{product}的公司起名。"
system_message_prompt = SystemMessagePromptTemplate.from_template(template)

human_template="公司主打产品是{product_detail}。"
human_message_prompt = HumanMessagePromptTemplate.from_template(human_template)

prompt_template = ChatPromptTemplate.from_messages([system_message_prompt, human_message_prompt])

# 格式化提示消息生成提示
prompt = prompt_template.format_prompt(product="鲜花装饰", product_detail="创新的鲜花设计。").to_messages()


result = chat_model(prompt)
print(result)

2.1 解析器规范chatgpt输出

# 创建一个空的DataFrame用于存储结果
import pandas as pd
import os

from langchain_openai import AzureChatOpenAI

# 下面调用模型,把提示传入模型,生成结果

df = pd.DataFrame(columns=["flower_type", "price", "description", "reason"])

# 数据准备
flowers = ["玫瑰", "百合", "康乃馨"]
prices = ["50", "30", "20"]

# 定义我们想要接收的数据格式
from pydantic import BaseModel, Field

class FlowerDescription(BaseModel):
    flower_type: str = Field(description="鲜花的种类")
    price: int = Field(description="鲜花的价格")
    description: str = Field(description="鲜花的描述文案")
    reason: str = Field(description="为什么要这样写这个文案")

# 创建输出解析器
from langchain.output_parsers import PydanticOutputParser
output_parser = PydanticOutputParser(pydantic_object=FlowerDescription)

# 获取输出格式指示
format_instructions = output_parser.get_format_instructions()
# 打印提示
print("输出格式:",format_instructions)


# ------Part 4
# 创建提示模板
from langchain import PromptTemplate
prompt_template = """您是一位专业的鲜花店文案撰写员。
对于售价为 {price} 元的 {flower} ,您能提供一个吸引人的简短中文描述吗?
{format_instructions}"""

# 根据模板创建提示,同时在提示中加入输出解析器的说明
prompt = PromptTemplate.from_template(prompt_template, 
       partial_variables={"format_instructions": format_instructions}) 

# 打印提示
print("提示:", prompt)

# ------Part 5
for flower, price in zip(flowers, prices):
    # 根据提示准备模型的输入
    input = prompt.format(flower=flower, price=price)
    # 打印提示
    print("提示:", input)
    # 获取模型的输出
    output = chat_model.predict(input)

    # 解析模型的输出
    parsed_output = output_parser.parse(output)
    parsed_output_dict = parsed_output.dict()
    # 将Pydantic格式转换为字典

    # 将解析后的输出添加到DataFrame中
    df.loc[len(df)] = parsed_output_dict


# 打印字典
print("输出的数据:", df.to_dict(orient='records'))