LangChain Prompt Engineering和LLMs:如何让语言模型更好地理解和生成文本

一、前言

在机器学习中,我们一直依赖不同的模型来完成不同的任务。随着多模态和大型语言模型 (LLM)的引入,这种情况发生了变化。过去,我们需要为分类、命名实体识别(NER)、问答(QA)等任务使用不同的模型。

 随着 transformers 和迁移学习的引入,只需在网络的末尾(或者头部)添加几个较小的层,并进行微调,就可以将语言模型适应于不同的任务。这意味着使用预训练的语言模型,如 transformers,可以通过在网络的最后一层或者最前一层进行微调或者扩展来适应特定的任务,而无需从头开始训练一个全新的模型。这种方法可以提高模型的效率和灵活性。

 Transformer 和迁移学习的思想使我们能够通过切换模型“头”并执行微调,将预训练 Transformer 模型的相同核心组件重复用于不同的任务。

然而,即便这种方法也已经过时了。当你可以通过提示模型进行分类或问答时,为什么还要改最后几层模型并经历整个微调过程呢?

 大型语言模型(LLMs)可以执行所有这些任务以及更多。这些模型经过训练,输入一个文本序列,模型输出一个文本序列。唯一的变量是输入文本,即Prompt。

在这个新时代的LLMs中,提示至关重要。糟糕的提示会产生糟糕的输出,而好的提示则具有非常强大的能力。构建好的提示是那些使用LLMs的人必备的关键技能。

LangChain库认识到了提示的重要性,并为其构建了一整套对象。在本文中,我们将学习有关PromptTemplates的一切,并有效地实施它们。

二、Prompt工程

在深入了解LangChain的PromptTemplate之前,我们需要更好地理解提示和提示工程的原理。

一个提示通常由多个部分组成:

 并非所有的提示都使用这些组件,但是一个好的提示通常会使用两个或更多。让我们更加准确地定义它们。

指令:告诉模型要做什么,如何使用外部信息(如果提供),如何处理查询,并构建输出。

外部信息或上下文:作为模型的额外知识来源。这些可以手动插入到提示中,通过向量数据库检索获取(检索增强),或通过其他方式获取(API、计算等)。

用户输入或查询:通常是由人类用户(即提示者)输入到系统中的查询。

输出指示器:标志着即将生成的文本的开头。如果生成Python代码,我们可以使用import来告诉模型提示它必须开始编写Python代码(因为大多数Python脚本以import开头)。

每个组件通常按照这个顺序放置在提示中。从指令开始,外部信息(如果适用),提示者输入,最后是输出指示器。

让我们看看如何使用 LangChain 将其输入到 OpenAI 模型:

prompt = """根据下面的上下文回答问题。如果无法使用提供的信息回答问题,请回答“我不知道”".

Context: 大型语言模型(LLMs)是自然语言处理中使用的最新模型。 它们相对于较小的模型具有卓越的性能,使它们对于构建支持自然语言处理的应用程序的开发人员非常有用。这些模型可以通过Hugging Face的“transformers”库,通过OpenAI的“openai”库以及通过Cohere的“cohere”库进行访问.

Question: 哪些库和模型提供商提供LLMs?

Answer: """

from langchain.llms import OpenAI

# 初始化OpenAI模型
openai = OpenAI(
    model_name="text-davinci-003",
    openai_api_key="YOUR_API_KEY"
)

print(openai(prompt))

输出内容:

 Hugging Face的“transformers”库、OpenAI的“openai”库和Cohere的“cohere”库提供LLMs。

实际上,我们不太可能对上下文和用户问题进行硬编码。我们通过模板将它们输入——这就是 LangChain 的PromptTemplate用武之地。

三、Prompt模板

LangChain 中的提示模板类旨在使构建具有动态输入的提示更加容易。其中最简单的是PromptTemplate。我们将通过向之前的提示添加一个动态输入(用户查询)来测试这一点。

from langchain import PromptTemplate

template = """根据下面的上下文回答问题。如果无法使用提供的信息回答问题,请回答"我不知道".

Context: 大型语言模型(LLMs)是自然语言处理中使用的最新模型。 它们相对于较小的模型具有卓越的性能,使它们对于构建支持自然语言处理的应用程序的开发人员非常有用。这些模型可以通过Hugging Face的“transformers”库,通过OpenAI的“openai”库以及通过Cohere的“cohere”库进行访问.

Question: {query}

Answer: """

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

有了这个,我们可以使用prompt_templateformat方法来查看将查询传递给模板的效果。

print(
    prompt_template.format(
        query="哪些库和模型提供商提供LLMs?"
    )
)

输出内容:

根据下面的上下文回答问题。如果无法使用提供的信息回答问题,请回答"我不知道".

Context: 大型语言模型(LLMs)是自然语言处理中使用的最新模型。 它们相对于较小的模型具有卓
越的性能,使它们对于构建支持自然语言处理的应用程序的开发人员非常有用。这些模型可以通过Hugging Face
的“transformers”库,通过OpenAI的“openai”库以及通过Cohere的“cohere”库进行访问.

Question: 哪些库和模型提供商提供LLMs?

Answer: 

当然,我们可以直接将这个输出传递给LLM对象,如下所示:

print(openai(
    prompt_template.format(
        query="哪些库和模型提供商提供LLMs?"
    )
))

输出内容:

Hugging Face的“transformers”库,OpenAI的“openai”库和Cohere的“cohere”库提供LLMs。

这只是一个简单的实现,可以很容易地用f-strings(例如f"插入一些自定义文本 '{custom_text}' etc")来替代。然而,使用LangChain的PromptTemplate对象,我们可以规范化这个过程,添加多个参数,并以面向对象的方式构建提示。

这些都是LangChain为帮助我们处理提示提供的重要优势之一。

四、Few Shot Prompt模板

LLMs(Language Model)的成功来自于它们的庞大规模和在模型训练过程中学习到的“知识”存储在模型参数中的能力。然而,还有其他方法可以将知识传递给LLM。主要的两种方法是:

参数知识(Parametric knowledge)——上述提到的知识是指模型在训练过程中学习到的任何东西,并存储在模型的权重(或参数)中。

源知识(Source knowledge)——通过输入提示(input prompt)在推理过程中提供给模型的任何知识。

LangChainFewShotPromptTemplate适用于源头知识输入。其思想是在几个示例上“训练”模型——我们称之为少样本学习——并且这些例子在提示中提供给模型。

当我们的模型需要帮助理解我们要求它做什么时,少样本学习非常有效。我们可以在以下示例中看到这一点:

prompt = """以下是与AI助手的对话。 助手通常是讽刺和机智的,对用户的问题产生创造性和有趣的回答。以下是一些例子:

User: 生活的意义是什么?
AI: """

openai.temperature = 1.0  # 增加创造力/随机性的输出

print(openai(prompt))

输出内容:

人们对生活的意义往往有不同的看法。可以说生活的意义就在于你怎么对待它,它可以是充满希望,也可以是一种挑战。你的价值观和理念决定了你的生活。

在这种情况下,我们要求得到一个有趣的笑话作为对我们严肃问题的回答。然而,即使将temperature设置为1.0(增加随机性/创造力),我们仍然得到了一个严肃的回答。

为了帮助模型,我们可以给出一些我们想要的答案类型的示例:

prompt = """以下是与AI助手进行对话的摘录。助手通常很讽刺和机智,对用户的问题产生创造性和有趣的回应。这里有一些例子:

User: 你好吗?
AI: 我不能抱怨,但有时候我还是会这么做。

User: 现在几点了?
AI:  是时候买个手表了。

User: 生活的意义是什么?
AI: """

print(openai(prompt))

输出内容:

从我未来观测的角度来看,每个人的意义可能会有所不同!

通过示例来强化我们在提示中传递的指令,我们更有可能得到一个更有趣的回答。然后,我们可以使用 LangChain 的FewShotPromptTemplate来规范这个过程:

from langchain import FewShotPromptTemplate

# 创建一个示例
examples = [
    {
        "query": "你好吗?",
        "answer": "我不能抱怨,但有时候我还是会这么做。"
    }, {
        "query": "现在几点了?",
        "answer": "是时候买个手表了。"
    }
]

# 创建一个示例模版
example_template = """
User: {query}
AI: {answer}
"""

# 使用上面的模板创建一个提示示例。
example_prompt = PromptTemplate(
    input_variables=["query", "answer"],
    template=example_template
)

# 把我们之前的提示分成前缀和后缀
# 前缀是我们的说明
prefix = """以下是与AI助手进行对话的摘录。助手通常很讽刺和机智,对用户的问题产生创造性和有趣的回应。这里有一些例子:
"""
#  后缀是我们的用户输入和输出指示符
suffix = """
User: {query}
AI: """

# 现在创建few-shot提示模板
few_shot_prompt_template = FewShotPromptTemplate(
    examples=examples,
    example_prompt=example_prompt,
    prefix=prefix,
    suffix=suffix,
    input_variables=["query"],
    example_separator="\n\n"
)

如果我们将 examples 和 user query 传递进去,我们将得到以下结果:

query = "生活的意义是什么?"

print(few_shot_prompt_template.format(query=query))

输出内容:

以下是与AI助手进行对话的摘录。助手通常很讽刺和机智,对用户的问题产生创造性和有趣的回应。这里有一些例子:

User: 你好吗?
AI: 我不能抱怨,但有时候我还是会这么做。

User: 现在几点了?
AI: 是时候买个手表了。

User: 生活的意义是什么?
AI: 

这个过程可能看起来有点复杂。为什么要使用FewShotPromptTemplate对象、examples字典等来完成所有这些呢 —— 当我们可以用几行代码和一个f字符串来做同样的事情呢?

再次强调,这种方法更加正式化,并且与 LangChain 中的其他功能(如chains)很好地集成在一起,并且具有多种功能。其中之一是可以根据查询长度来调整要包含的示例数量。

动态数量的示例非常重要,因为我们的提示和完成输出的最大长度是有限的。这个限制是通过最大上下文窗口来衡量的。 同时,我们可以最大化给模型提供的示例数量,以进行少样本学习。

context window = input tokens + output tokens

同时,我们可以给模型提供最大化的示例数量,以进行Few Shot学习。

考虑到这一点,我们需要在包含的示例数量和提示大小之间取得平衡。我们的硬性限制是最大上下文大小,但我们还必须考虑通过LLM处理更多token的成本。较少的token意味着更便宜的服务和从LLM获得更快的完成。

FewShotPromptTemplate允许我们根据这些变量来调整包含的示例数量。首先,我们创建一个更广泛的examples列表:

examples = [
    {
        "query": "你好吗?",
        "answer": "我不能抱怨,但有时还是会这样做。"
    }, {
        "query": "现在几点了?",
        "answer": "是时候去买个手表了。"
    }, {
        "query": "生命的意义是什么?",
        "answer": "42"
    }, {
        "query": "今天的天气如何?",
        "answer": "多云,有一些梗的机会。"
    }, {
        "query": "你最喜欢的电影是什么?",
        "answer": "终结者"
    }, {
        "query": "你最好的朋友是谁?",
        "answer": "Siri。我们对生命的意义进行激烈的辩论。"
    }, {
        "query": "今天我应该做什么?",
        "answer": "别在网上和聊天机器人聊天了,出去走走吧。"
    }
]

然后,我们实际上使用LengthBasedExampleSelector来传递示例,如下所示:

from langchain.prompts.example_selector import LengthBasedExampleSelector

example_selector = LengthBasedExampleSelector(
    examples=examples,
    example_prompt=example_prompt,
    max_length=50  # 设置示例的最大长度
)

需要注意的是,我们将max_length定义为通过空格和换行符拆分字符串后的单词数。具体的逻辑如下:

import re

some_text = "There are a total of 8 words here.\nPlus 6 here, totaling 14 words."

words = re.split('[\n ]', some_text)
print(words, len(words))

输出内容:

['There', 'are', 'a', 'total', 'of', '8', 'words', 'here.', 'Plus', '6', 'here,', 'totaling', '14', 'words.'] 14

然后,我们将其传递example_selectorFewShotPromptTemplate创建一个新的动态提示模板:

# 现在创建少样本提示模板
dynamic_prompt_template = FewShotPromptTemplate(
    example_selector=example_selector,  # 使用example_selector而不是examples
    example_prompt=example_prompt,
    prefix=prefix,
    suffix=suffix,
    input_variables=["query"],
    example_separator="\n"
)

现在,如果我们输入一个较短或较长的查询,我们会发现包含的示例数量会有所变化。

print(dynamic_prompt_template.format(query="鸟是如何飞行的?"))

输出内容:

以下是与AI助手进行对话的摘录。助手通常很讽刺和机智,对用户的问题产生创造性和有趣的回应。这里有一些例子:

User: 你好吗?
AI: 我不能抱怨,但有时还是会这样做。

User: 现在几点了?
AI: 是时候去买个手表了。

User: 生命的意义是什么?
AI: 42

User: 今天的天气如何?
AI: 多云,有一些梗的机会。

User: 你最喜欢的电影是什么?
AI: 终结者

User: 你最好的朋友是谁?
AI: Siri。我们对生命的意义进行激烈的辩论。

User: 今天我应该做什么?
AI: 别在网上和聊天机器人聊天了,出去走走吧。

User: 鸟是如何飞行的?
AI: 

提供一个较长的问题将导致包含的示例数量减少:

query = """如果我在中国,想给另一个国家的人打电话,我在考虑可能是欧洲,可能是西欧国家,比如法国、德国或英国,最好的方式是什么?"""

print(dynamic_prompt_template.format(query=query))

输出内容:

以下是与AI助手进行对话的摘录。助手通常很讽刺和机智,对用户的问题产生创造性和有趣的回应。这里有一些例子:

User: 你好吗?
AI: 我不能抱怨,但有时还是会这样做。

User: 现在几点了?
AI: 是时候去买个手表了。

User: 生命的意义是什么?
AI: 42

User: 今天的天气如何?
AI: 多云,有一些梗的机会。

User: 你最喜欢的电影是什么?
AI: 终结者

User: 你最好的朋友是谁?
AI: Siri。我们对生命的意义进行激烈的辩论。

User: 今天我应该做什么?
AI: 别在网上和聊天机器人聊天了,出去走走吧。

User: 如果我在中国,想给另一个国家的人打电话,我在考虑可能是欧洲,可能是西欧国家,比如法国、德国或英国,最好的方式是什么?
AI: 

通过这样做,我们在prompt变量中返回较少的示例。这样可以限制过多的标记使用,并避免超过LLM的最大上下文窗口引起的错误。

Jupyter Notebook 的完整代码

https://github.com/Crossme0809/langchain-tutorials/blob/main/LangChain_Prompt_Templates.ipynb

总结

自然地,提示是LLM新世界中的重要组成部分。值得探索LangChain提供的工具,并熟悉不同的提示工程技术。

在这里,我们只介绍了LangChain中可用的一些提示工具,并对它们的使用进行了有限的探索。后面我们会继续探讨LangChain的其它重要组成部分,并进一步研究提示模板的应用以及它们如何与库提供的更广泛的工具集相结合。

<think>好的,我现在需要帮助用户了解提示工程(prompt engineering)、代理(agent)函数调用(function call)在AI或编程中的概念实现方法。首先,我需要理清每个概念的定义它们之间的关系。 首先,提示工程是指设计优化输入提示,以引导AI模型生成更准确的输出。根据引用[1],分解复杂问题为更简单的子问题可以提升大语言模型(LLM)的解决能力,这可能涉及到像思维链(CoT)这样的技术。用户提到的提示工程结合代理函数调用,可能需要将这些技术结合起来,使AI系统更有效地处理任务。 接下来是代理(agent)的概念。在AI中,代理通常指能够自主执行任务或决策的实体。比如,一个代理可能需要调用工具或函数来完成目标,这涉及到函数调用。例如,一个代理可能需要使用搜索API获取实时数据,或者执行代码进行计算,这都需要函数调用的支持。 然后是函数调用,这允许AI模型与外部系统或API交互,执行具体操作。例如,当模型需要获取天气数据时,可以调用相应的API函数。结合提示工程,可以设计提示让模型明确何时以及如何调用这些函数,从而增强其功能。 现在需要将这些概念结合起来,思考它们如何协同工作。比如,提示工程可以设计特定的指令,让代理根据输入的提示分解任务,然后通过函数调用执行具体步骤。这可能涉及到分步提示(Step-by-Step Prompting),将复杂任务拆解,并指导代理逐步完成,同时适时调用必要的函数。 此外,引用[1]提到的Least-to-Most提示方法,可能类似于逐步引导模型解决子问题,再整合结果。这可能应用于代理的工作流程中,先分解问题,再通过函数调用处理每个子问题,最后汇总结果。 用户还可能需要了解具体的实现方法,比如在编程中如何将提示工程与函数调用结合。例如,使用LangChain框架,可以创建代理,根据提示解析用户请求,决定调用哪个工具,执行函数,并返回结果。代码示例可能包括定义工具、设置提示模板、初始化代理并运行。 最后,需要考虑可能的相关问题,如不同提示技术的对比、代理设计的最佳实践、函数调用的安全性效率等。这些都需要在回答中提及,以帮助用户全面理解。 总结,我需要分步骤解释每个概念,结合引用中的研究,给出实际示例,并生成相关问题供用户深入探索。</think>### 提示工程与代理、函数调用的结合 #### 1. **核心概念** - **提示工程(Prompt Engineering)** 通过设计特定结构的输入提示(prompt),引导大语言模型(LLM)生成更符合目标的输出。例如: - **思维链(CoT)**:要求模型分步骤推理,如“请逐步解释如何计算圆的面积”[^1]。 - **函数调用提示**:明确指示模型调用外部工具,如“调用天气API获取北京今日温度”。 - **代理(Agent)** 代理是能够自主决策并执行任务的AI实体。例如: - **工具调用代理**:根据输入决定调用搜索引擎、计算器或数据库。 - **多步任务代理**:分解复杂任务为多个子步骤,如“规划旅行路线”需依次处理交通、住宿等。 - **函数调用(Function Call)** 允许AI模型与外部系统交互,执行具体操作。例如: - 调用Python函数处理数据:`calculate_average([1,2,3])`。 - 调用API获取实时信息:`get_stock_price("AAPL")`。 #### 2. **结合方式与实现** - **步骤分解与函数调用** 1. **提示设计**:通过CoT将任务分解为子问题,例如: ``` 用户输入:“帮我分析某公司近三年营收增长率。” 提示模板: 1. 调用API获取2019-2021年财务数据。 2. 计算每年增长率:(当年营收 - 前年营收)/前年营收。 3. 生成可视化图表。 ``` 2. **代理决策**:代理根据提示选择需调用的函数(如财务API、计算函数、绘图库)。 3. **执行与整合**:依次执行函数并汇总结果。 - **代码示例(基于LangChain框架)** ```python from langchain.agents import initialize_agent, Tool from langchain.llms import OpenAI # 定义工具函数 def fetch_financial_data(company: str) -> dict: # 模拟API调用 return {"2019": 100, "2020": 120, "2021": 150} def calculate_growth(data: dict) -> float: return (data["2021"] - data["2019"]) / data["2019"] # 创建工具列表 tools = [ Tool(name="FinancialDataFetcher", func=fetch_financial_data, description="获取公司财务数据"), Tool(name="GrowthCalculator", func=calculate_growth, description="计算增长率") ] # 初始化代理 agent = initialize_agent(tools, OpenAI(), agent="zero-shot-react-description", verbose=True) agent.run("请分析公司XYZ的三年营收增长率。") ``` 输出可能为: ``` 1. 调用FinancialDataFetcher获取数据 → {2019:100, 2020:120, 2021:150} 2. 调用GrowthCalculator → 增长率=(150-100)/100=50% ``` #### 3. **应用场景** - **自动化数据分析**:代理根据自然语言指令调用清洗、计算、可视化函数[^1]。 - **智能客服**:通过函数调用查询订单、生成解决方案,如“查询订单123状态 → 调用`get_order_status(123)`”。 - **科研辅助**:分解复杂问题为文献检索、公式推导、图表生成等步骤。 #### 4. **优化策略** - **精准提示设计**:明确函数调用条件参数,例如: “若用户需要实时数据,调用`search_web(query)`;若需要计算,调用`math_solver(expression)`。” - **错误处理机制**:在提示中要求代理验证函数输出,如“检查API返回是否为空,若空则重试”。 - **多模态扩展**:结合图像处理函数,如“调用`ocr(image_path)`提取文本,再调用`translate(text)`翻译”。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

技术狂潮AI

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值