LangChain-Models IO | 豆包MarsCode AI刷题

85 阅读11分钟

一、Models IO 组成及其说明

与语言模型的交互,比如在线GPT系列,或各种离线模型 任何语言模型应用程序的核心元素都是XXX模型。LangChain 提供了与任何语言模型交互的构建块。

image.png

从案例理解prompt,下面是一个内容,占位字符串为 foo 跟 bar,在python中可以根据format,将需要传入的内容代入

   我最喜欢的运动是{args1},平时最喜欢去{args2}
"""
template = template.format(args1='foo',args2='bar')

我最喜欢的运动是foo,平时最喜欢去bar

langchain,提供了prompt工程,可以通过规定风格,将提示内容代入到各类模型,以便告知模型,你的想法或执行内容;

对于LLM的输出,我们也能进行解析,比如,要求输出为json格式,等等格式要求

其主要交互组件如下图

注意 内容所出现的代码,我都忽略了key的配置,你们可以自己加

os.environ["OPENAI_API_KEY"] = ''

二、Prompt 提示工程

image.png

语言模型的提示是用户提供的一组指令或输入,用于指导模型的响应,帮助模型理解上下文并生成相关且连贯的基于语言的输出,例如回答问题、完成句子或参与某项活动。对话。 LangChain 提供了几个类和函数来帮助构建和使用提示。

1、提示模板:参数化模型输入

2、示例选择器:动态选择要包含在提示中的示例

1、关于模板的使用 (1)基础模板 PromptTemplate (1.1)使用预设值实例化

from langchain.prompts.prompt import PromptTemplate

prompt_template = PromptTemplate(input_variables=["content"], template="告诉我一个笑话关于 {content}")
prompt = prompt_template.format(content="兔子")
print(prompt_template)
print(type(prompt_template))
print(prompt)
print(type(prompt))

--------打印结果----------
input_variables=['content'] template='告诉我一个笑话关于 {content}'
<class 'langchain.prompts.prompt.PromptTemplate'>
告诉我一个笑话关于 兔子
<class 'str'>



1.2)使用from_template实例化
from langchain.prompts.prompt import PromptTemplate

prompt_template = PromptTemplate.from_template(
    template="告诉我一个笑话 {adjective} 关于 {content}."
)
prompt = prompt_template.format(adjective="funny", content="chickens")
print(prompt_template)
print(type(prompt_template))
print(prompt)
print(type(prompt))
--------打印结果----------
input_variables=['adjective', 'content'] template='告诉我一个笑话 {adjective} 关于 {content}.'
<class 'langchain.prompts.prompt.PromptTemplate'>
告诉我一个笑话 funny 关于 chickens.
<class 'str'>
from langchain import PromptTemplate
from langchain.llms import OpenAI

invalid_prompt = PromptTemplate(
    input_variables=["adjective"],
    template="Tell me a {adjective} joke about cat. 中文回复我"
)

# llm = ChatOpenAI()
llm = OpenAI()
# 定义一个函数来生成回答
def generate_joke(adjective, content):
    prompt = invalid_prompt.format(adjective=adjective)
    response = llm(prompt)
    print(response)
    # return response.choices[0].text.strip()

# 调用函数生成一个笑话
joke = generate_joke("funny", "cats")
print(joke)
"""
这只猫真有趣:它把自己看作是一只狗!
"""
(1.4)源码

从源码层来说,都是源自这边,并且函数也说明了,是所有模板的基础,建议去看看源码 这里不过多说明

image.png

从源码可以看继承于StringPromptTemplate 字符串模板

而它又继承于 BasePromptTemplate,拥有如下功能函数

往往有时候,提供的基础模板不够我们使用场景,就需要我们自己创建自定义模板

image.png

(2) 自定义模板

举例使用场景:

用于某些业务实现 在自定义模板中,我们可以实现很多功能,比如加上日志记录 要创建自定义字符串提示模板,有两个要求:

1、它有一个 input_variables 属性,该属性公开提示模板所需的输入变量。 2、定义了一个 format 方法,该方法接受与预期的 input_variables 相对应的关键字参数并返回格式化的提示。

通过如下案例说明:

第一步,创建一个获取函数源码的函数

import inspect
def get_source_code(function_name):
    return inspect.getsource(function_name)

第二步,构建自定义提示模板

from langchain.prompts import StringPromptTemplate
from pydantic import BaseModel, validator

PROMPT = """
	给定函数名称和源代码,生成该函数的中文解释。.
	函数名称: {function_name}
	源代码:
	{source_code}
	解释:
"""

class CustomPromptTemplate(StringPromptTemplate, BaseModel):
    """ 自定义提示模板,
        将函数名称作为输入,
        并格式化提示模板以提供函数的源代码。
    """

    @validator("input_variables")
    def validate_input_variables(cls, v):
        """验证输入变量是否正确。"""
        if len(v) != 1 or "function_name" not in v:
            raise ValueError("function_name must be the only input_variable.")
        return v

    def format(self, **kwargs) -> str:
        # 获取源码
        source_code = get_source_code(kwargs["function_name"])
        # 生成要发送到语言模型的提示
        prompt = PROMPT.format(
            function_name=kwargs["function_name"].__name__, source_code=source_code
        )
        return prompt

    def _prompt_type(self):
        return "function-explainer"

第四步,使用它

fn_explainer = CustomPromptTemplate(input_variables=["function_name"])

prompt = fn_explainer.format(function_name=get_source_code)
print(prompt)

(3)基础可选模板

可以实现从一组示例或示例选择器对象构建少量提示模板。 在实现这个前,先补充一下知识

Few-shot和zero-shot是机器学习中常用的术语,用于描述模型在处理新任务或新类别时的能力。

Few-shot(少样本学习):Few-shot学习是指在面对只有很少样本的新任务或新类别时,模型能够通过利用有限的样本进行学习和泛化。这意味着模型可以从少量的训练样本中快速学习,并在面对新任务时进行适应。

Zero-shot(零样本学习):Zero-shot学习是指在面对完全没有见过的新任务或新类别时,模型能够进行推理和泛化。这意味着模型可以在没有任何训练样本的情况下,通过利用先验知识或外部信息来进行预测和分类。

这些概念在自然语言处理、计算机视觉和强化学习等领域中被广泛应用,旨在提高模型的泛化能力和适应性,使其能够处理新领域、新任务或新类别的数据。

为此langchain也提供了,few-shot模板操作

from langchain.prompts.few_shot import FewShotPromptTemplate
from langchain.prompts.prompt import PromptTemplate
from langchain.prompts.example_selector import SemanticSimilarityExampleSelector
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings

examples = [
    {
        "question": "你是谁?",
        "answer":
            """
            我是wenwenc9
            """
    },
    {
        "question": "爸爸叫什么",
        "answer":
            """
            叫爷爷
            """
    },
    {
        "question": "朱自清是谁",
        "answer":
            """
            朱自清是文学家
            """
    }
]
example_prompt = PromptTemplate(input_variables=["question", "answer"], template="Question: {question}\n{answer}")
prompt = FewShotPromptTemplate(
    examples=examples,  # 少量示例
    example_prompt=example_prompt,  # 用户格式化单个示例
    suffix="Question: {input}",  # 前缀,也有后缀prefix
    input_variables=["input"]
)

example_selector = SemanticSimilarityExampleSelector.from_examples(
    # 可供选择的示例模板
    examples,
    # 用于生成用于测量语义相似性的嵌入的嵌入类。
    OpenAIEmbeddings(),
    # 向量数据库
    Chroma,
    # 这是要生成的实例数目
    k=1
)

构建问题,基于用户问题,选择合适的提示模板

# 创建一个问题
question = "朱自清"

# 根据向量引擎从中选择一个符合
selected_examples = example_selector.select_examples({"question": question})
print(selected_examples)

执行输出结果

[{'answer': '\n            朱自清是文学家\n            ', 'question': '朱自清是谁'}]
1

(4)聊天模板 ChatPromptTemplate

from_messages from_template 在langchain中,ChatPromptTemplate是一个模版方法,它具有两个功能:from_messages和from_template。

from_messages:这个功能用于从一系列对话消息中创建聊天模版。您可以提供多个消息作为输入,并将它们转换为聊天模版,以便用于后续的对话生成。

from_template:这个功能用于从现有的聊天模版中创建新的聊天模版。您可以基于现有的模版进行修改或扩展,以便生成更多的对话内容。

from_messages用于从消息创建模版,而from_template用于基于现有模版创建新的模版。

(4.1) 二元数组方式
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate

template = ChatPromptTemplate.from_messages([
    ("system", "你是一个优秀的助手. 你的名字是 {name}."),
    ("human", "你好,你最近过的好嘛?"),
    ("ai", "最近过的不错"),
    ("human", "{user_input}"),
])

messages = template.format_messages(
    name="小爱同学",
    user_input="你的名字是什么?"
)
# 实例化AI对象
llm = ChatOpenAI()

# 响应
res = llm(messages)

print(res)

content='我的名字是小爱同学。有什么我可以帮助你的吗?'
(4.2)BaseMessage 或者 MessagePromptTemplate
from langchain.callbacks import get_openai_callback
from langchain.prompts import ChatPromptTemplate
from langchain.prompts.chat import SystemMessage, HumanMessagePromptTemplate, HumanMessage
from langchain.chat_models import ChatOpenAI

template = ChatPromptTemplate.from_messages(
    [
        SystemMessage(
            content=(
                "你是一个乐于助人的助手,可以将用户的文本重新写入"
                "听起来更乐观"
                "用中文回复我"
            )
        ),
        # HumanMessagePromptTemplate.from_template("{text}"), # 这里对应本小节前面,所示的from_template可以从已有模板进行修改
        HumanMessage(
            content=(
                "{text}"
            )
        ),
    ]
)

llm = ChatOpenAI()
# 使用上下文管理器获取 token 使用情况
with get_openai_callback() as cb:
    res = llm(template.format_messages(text='我很难过'))
    print(res)
# 获取 token 使用总数
total_tokens = cb.total_tokens
print(f"Total tokens used: {total_tokens}")

content='亲爱的用户,你的文本让我感到有些沮丧。但是,无论遇到什么困难,我们都应该保持积极乐观的态度。相信自己,相信明天会更好。让我们一起努力,面对挑战,充满希望地前进吧!加油!'
Total tokens used: 155

(5)聊天Few-shot模板

关于few-shot的含义本文基础可选模板已经提到过

前面的few-shot 并没有带上chat二字

创建多个提示案例

from langchain.prompts import (
    FewShotChatMessagePromptTemplate,
    ChatPromptTemplate,
)

# 生成具体回复
examples = [
    {"input": "2+2", "output": "4"},
    {"input": "2+3", "output": "5"},
]

example_prompt = ChatPromptTemplate.from_messages(
    [        ("human", "{input}"),        ("ai", "{output}"),    ]
)

few_shot_prompt = FewShotChatMessagePromptTemplate(
    example_prompt=example_prompt,
    examples=examples,
)


final_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "You are a wondrous wizard of math."),
        few_shot_prompt,
        ("human", "{input}"),
    ]
)


查看一下final_prompt 内容,可以看到下图,由3个消息提示模板

  1. SystemMessagePromptTemplate
  2. FewShotChatMessagePromptTemplate
  3. HumanMessagePromptTemplate

image.png

展开FewShotChatMessagePromptTemplate ,可以看到是通过2,实例出2个ChatPromptTemplate

image.png

进行使用

方式一:

from langchain.chains import LLMChain
from langchain.chat_models import ChatOpenAI
# 创建LLM模型
llm = ChatOpenAI()
# 创建执行链
chain = LLMChain(
    llm=llm,
    prompt=final_prompt
)
res = chain.invoke({"input": "2+2 等于多少"})
res2 = chain.invoke({"input": "2+6 等于多少"})
print(res)
print(res2)

方式二:

from langchain.chat_models import ChatOpenAI

# 创建执行链
chain = final_prompt | ChatOpenAI()

res = chain.invoke({"input": "2+2 等于多少"})
res2 = chain.invoke({"input": "2+2 等于多少"})
print(res)

{'input': '2+2 等于多少', 'text': '2+2等于4。'}
{'input': '2+6 等于多少', 'text': '2 + 6 等于 8。'}

Model I/O 的template以及批量运用

这段代码演示了如何使用一个预定义的文案模板,结合OpenAI的GPT-3模型,为不同的水果和它们的价格生成吸引人的描述。以下是对代码的逐行注释和解释model_io_for_loop.py:

from langchain.llms import OpenAI  

# 导入Langchain库中的PromptTemplate模块,用于创建和管理提示模板
from langchain.prompts import PromptTemplate  

# 导入Langchain库中的LLMChain模块,它允许构建基于大型语言模型的处理链
from langchain.chains import LLMChain  

# 导入dotenv库,用于从.env文件加载环境变量,这对于管理敏感数据如API密钥很有用
from dotenv import load_dotenv  

# 调用load_dotenv函数来加载.env文件中的环境变量
load_dotenv()  

# 定义一个文案模板,用于生成关于水果的描述。
# {price} 和 {fruit_name} 是占位符,稍后将被具体的价格和水果名称替换。
template = """您是一位专业的水果店文案撰写员。\n
对于售价为 {price} 元的 {fruit_name} ,您能提供一个吸引人的简短描述吗?
"""

# 使用PromptTemplate类的from_template方法创建一个基于上述模板的prompt对象。
# 这个对象可以用来格式化具体的水果名称和价格。
prompt = PromptTemplate.from_template(template)
print(prompt)

# 使用OpenAI类创建一个名为llm的实例。
# 这个实例配置了用于生成文本的模型参数:"text-davinci-003"是一个高级的GPT-3模型。
# temperature设置为0.5,用于控制生成文本的随机性和创造性。
# max_tokens设置为60,用于限制生成文本的最大长度。
llm = OpenAI(
    model="text-davinci-003",
    temperature=0.5,
    max_tokens=60
)

# 定义两个列表:fruits包含不同的水果名称,prices包含相应的价格。
fruits = ["葡萄", "草莓", "樱桃"]
prices = ["10", "20", "30"]

# 使用zip函数将fruits和prices中的元素配对,并在每一对元素上迭代。
for fruit, price in zip(fruits, prices):
    # 使用format方法和prompt模板生成具体的输入文本。
    # 这里将占位符替换为具体的水果名称和价格。
    input_prompt = prompt.format(price=price, fruit_name=fruit)
    # 使用llm实例生成文案描述。
    response = llm(input_prompt)
    # 打印生成的文案描述。
    print(response)

这里LLM也可以替换为别的,比如HuggingFaceHub

# 导入LangChain中的OpenAI模型接口
from langchain import HuggingFaceHub
# 创建模型实例
llm= HuggingFaceHub(repo_id="google/flan-t5-large")

运行结果

input_variables=['fruit_name', 'price'] template='您是一位专业的水果店文案撰写员。\n\n对于售价为 {price} 元的 {fruit_name} ,您能提供一个吸引人的简短描述吗?\n'

「精选葡萄,香甜多汁,仅 10 元,让你尝遍天下美味!」

草莓:甜美多汁,满口芬芳,令人回味无穷,20元即可

精选樱桃,甜蜜爽口,香甜可口,仅售30元!