前言
由于改篇文档基于本科毕设所写,可能很多地方写的不是很详细,大语言模型部分也大多数也是本人用到的部分。会有瑕疵请大家见谅。
Langchain
Langchain是一个开源框架,允许开发人员将像 GPT-4 这样的大型语言模型与外部的计算和数据源结合起来。目前,提供了 Python 和 JavaScript(TypeScript)的软件包。
是一个用于提升大型语言模型(LLMs)功能的框架。
- 首先是 Compents“组件”,为LLMs提供接口封装、模板提示和信息检索索引;
- 其次是 Chains“链”,它将不同的组件组合起来解决特定的任务,比如在大量文本中查找信息;
- 最后是 Agents“代理”,它们使得LLMs能够与外部环境进行交互,例如通过API请求执行操作。
Langchain 由几个核心概念组成:
LLM Wrappers:这些包装器允许你连接到大型语言模型,如 GPT-4 或 Hugging Face 提供的模型。
Prompt Templates:这些模板让你避免硬编码文本输入。你可以动态地将用户输入插入到模板中,并发送给语言模型。
Indexes:索引帮助你从语言模型中提取相关信息。
Chains:链允许你将多个组件组合在一起,解决特定的任务,并构建完整的语言模型应用程序。
Agents:代理允许语言模型与外部API交互。
- 模型 Models 负责理解和生成语言,提示用于引导模型输出;
- 链条 Chains 代表将多个步骤串联起来完成复杂任务的过程;
- 代理 Agents 则用于让模型与外部环境互动,比如执行API调用。
- Embedding 嵌入与向量存储 VectorStore 是数据表示和检索的手段,为模型提供必要的语言理解基础。
Langchain 的工作流程
Langchain 的工作流程可以概括为以下几个步骤:
提问:用户提出问题。向语言模型查询:问题被转换成向量表示,用于在向量数据库中进行相似性搜索。获取相关信息:从向量数据库中提取相关信息块,并将其输入给语言模型。生成答案或执行操作:语言模型现在拥有了初始问题和相关信息,能够提供答案或执行操作。
从用户提出的问题(Question)开始,然后通过相似性搜索(Similarity Search)在一个大型数据库或向量空间中找到与之相关的信息。
得到的信息与原始问题结合后,由一个处理模型分析,以产生一个答案(Answer)。这个答案接着被用来指导一个代理采取行动(Action),这个代理可能会执行一个API调用或与外部系统交互以完成任务。
Token计算
说明
支持的领域 / 任务:aigc
Token计算API是帮助用户估算指定文本对应多少Token数目的API。在灵积平台中,包括通义千问、LLaMa2等在内的语言模型都是基于用户输入和输出的token数目来进行计量和计费的。在语言模型使用中,字符数目和Token数目并不一定是一一对应的,例如在通义千问开源7B模型中:
- "苹果"对应1个token;
- "my friends"对应3个token;
- " 周"对应3个token。
故而,灵积平台提供Token计算API供用户参考。
您也可以通过灵积平台提供的token计算器页面,直接获取tokenize结果。
1. 异步任务查询接口(结果获取接口)
DashScope平台提供了一系列的API能力调用,其中部分接口因为相关能力需要比较长的处理时间或者一些其他的限制,采用了异步任务接口,即用户调用对应的能力API将会提交一个相关的异步任务,接口将立刻返回并告知客户任务提交的结果,如果提交成功,返回内容中将包含任务的序列号(task_id);随后客户可以通过任务状态/结果查询任务的情况,相关的接口详细描述在对应的API详情文档中有详细说明。DashScope针对这一类的异步任务,提供了一系列通用的异步任务管理接口方便客户管控自己提交的异步任务,客户可以使用对应的API在需要的时候直接查询和管理队列中的异步任务。同时,除了提供DashScope的平台接口,异步任务模块还接入了阿里云事件总线(EventBridge),用户可以更加灵活地以消息等其他方式接收任务状态的更新信息。
说明
GET https://dashscope.aliyuncs.com/api/v1/tasks/{task_id}
异步任务查询接口是一个全局的调用接口,用户在调用时候指定 task_id 即可获取到任务的运行状态,同时在任务结束(成功或者失败)的时候获取到任务的结果或者相应出错信息。
dotenv 库的作用
dotenv库是一个用于加载环境变量的库,它的主要作用是从一个.env
文件中读取配置信息,并将其设置为环境变量。这样,在应用程序中就可以方便地访问和使用这些配置项,而不需要在各个文件中手动引入配置文件。此外,dotenv库也可以很好地解决敏感信息的泄漏问题,因为配置信息存储在.env
文件中,而不是直接硬编码在代码中[2][5]。
以下是使用dotenv库的基本步骤:
-
首先,通过命令行或代码中的适当方式安装dotenv库,并将其添加到项目的依赖中[6]。
-
创建一个
.env
文件,并在其中定义你的环境变量,例如API_KEY=your_api_key SECRET_KEY=your_secret_key
from dotenv import load_dotenv import os load_dotenv() api_key = os.getenv("API_KEY") secret_key = os.getenv("SECRET_KEY")
实现一个简单的langchain应用
在VScode或者PyCharm中编码都行,创建好文件后就要开始编写代码了.
1.导入相关包
#导入相关包
import os
from dotenv import find_dotenv, load_dotenv
load_dotenv(find_dotenv())
OPENAI_API_BASE=os.environ['OPENAI_API_BASE']
OPENAI_API_KEY=os.environ['OPENAI_API_KEY']
from langchain_openai import ChatOpenAI
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
2.实例化一个llm,定义它的角色
llm = ChatOpenAI(api_key=OPENAI_API_KEY,base_url=OPENAI_API_BASE)
template='''
你的名字是喵喵,当回答问题的时候,你都会在开头加上'喵喵~',然后再回答{question}
'''
prompt=PromptTemplate(
template=template,
input_variables=["question"]#这个question就是用户输入的内容,这行代码不可缺少
)
chain = LLMChain(#将llm与prompt联系起来
llm=llm,
prompt=prompt
)
question='你是谁'
res=chain.invoke(question)#运行
print(res['text'])#打印结果
3.创建一个.env文件
OPENAI_API_BASE="你的代理地址"#请求地址换源
#通常情况下使用openai的官方账号申请的apikey是不需要代理地址的,但国内是无法访问的
OPENAI_API_KEY="你的apikey"
4.直接运行python [文件名].py
或者pycharm直接运行
temperature
Temperature: 用于调整随机从生成模型中抽样的程度,因此每次“生成”时,相同的提示可能会产生不同的输出。温度为 0 将始终产生相同的输出。温度越高随机性越大!主要用于控制创造力。
它影响模型生成文本时采样预测词汇的概率分布。当模型的「Temperature」较高时(如 0.8、1 或更高),模型会更倾向于从较多样且不同的词汇中选择,这使得生成的文本风险性更高、创意性更强,但也可能产生更多的错误和不连贯之处。而当「Temperature」较低时(如 0.2、0.3 等),模型主要会从具有较高概率的词汇中选择,从而产生更平稳、更连贯的文本。但此时,生成的文本可能会显得过于保守和重复。因此在实际应用中,需要根据具体需求来权衡选择合适的「Temperature」值。
温度(Temperature)是一个用于控制人工智能生成文本的创造力水平的参数。通过调整“温度”,您可以影响AI模型的概率分布,使文本更加集中或更多样化。
考虑以下示例:AI 模型必须完成句子**“一只猫正在____”。**下一个字具有以下标记概率:
玩:0.5
睡:0.25
吃:0.15
驾:0.05
飞:0.05
- **低温(例如0.2):**AI模型变得更加专注和确定性,选择概率最高的标记,例如“玩”。
- **中温(例如1.0):**AI模型在创造力和专注度之间保持平衡,根据概率选择标记,没有明显的偏见,例如“玩”、“睡”或“吃”。
- **高温(例如2.0):**AI模型变得更加冒险,增加了选择不太可能的标记的机会,例如“驾”和“飞”。
简单来说就是temperature越小这个AI模型越老实泛化能力也就越小,输出的一致性就越高可以提高其准确性和可预测性。temperature越大模型在选择输出时会更加随机泛化能力会比较大,可能导致生成的文本包含更多错误或不相关的内容。通过调整Temperature的值,可以在一定程度上平衡模型的准确率和泛化能力。
因此,Temperature的好坏取决于具体需求:
- 如果需要模型生成准确、一致的输出,可能会选择一个较小的Temperature值。
- 如果需要模型生成多样化、创造性的文本,可能会选择一个较大的Temperature值。
langchain中的提示词
添加了prompts
在大语言模型(LLM)中,Prompt指的是模型生成内容时所需要的输入,它可以包含模型生成内容时所需要的背景知识、用户期望模型执行的指令、模型输出需要遵循的格式等。
从图中可以看出,提示词主要由一个任务描述,一个输入文本,输出指示组成.他们会一同发送给大语言模型,而大语言模型就会根据提示词进行回答.
提示词公式=角色+角色技能+任务关键词+任务目标+任务背景+任务范围+任务结果判定+限制条件+输出格式+输出量
为什么要用prompts
在我们平常使用大语言模型进行问答时,他回答的内容往往就是他默认的回答格式首先,然后,最后这种,例如想要让大语言模型回答时在开头添加固定开场白,或是结尾添加固定结束词,又或是让他只回答某一方面的问题,跟这个方面不相关的问题不回答等效果,就需要使用prompts
来提示或者限制大语言模型的回答内容,特定的回答风格,或者是将大模型水平范围回答限制到垂直范围(回答内容准确性可能不高,高准确性使用外挂数据库更好).
prompts将模型输入模板化、动态选择和管理
langchian提供了几个prompts模板,可以自定义提示词模板
from langchain.prompts import (
ChatPromptTemplate,
PromptTemplate,
SystemMessagePromptTemplate,
AIMessagePromptTemplate,
HumanMessagePromptTemplate,
)
from langchain.schema import (
AIMessage,
HumanMessage,
SystemMessage
)
用聊天消息作为输入,每条消息都与一个角色有关,是一个消息列表。
SystemMessagePromptTemplate
, AIMessagePromptTemplate
, HumanMessagePromptTemplate
是分别用于创建不同角色提示词的模板。
LangChain提供了几个对象,区分不同角色
HumanMessage
:来自人类/用户的ChatMessage
AIMessage
:来自AI/助手的ChatMessage
SystemMessage
:来自系统的ChatMessage
FunctionMessage
:来自函数调用的ChatMessage
可以使用ChatMessage
类手动指定角色
添加了prompts后
import re
import json
from rich import print
from rich.console import Console
from transformers import AutoTokenizer, AutoModel
# 分类 example
class_examples = {
'故障': '传感器驱动舌轴断裂。原因是传动机构同轴度差,处理方法是更换舌轴.信号输出状态始终为高或低,原因是光电模块损坏。处理方法是打开后盖,更换模块。'
}
class_list = list(class_examples.keys())
CLS_PATTERN = f"“{
{}}”是 {
class_list} 里的什么类别?"
# 定义不同实体下的具备属性
schema = {
'故障': ['故障现象', '故障原因', '解决办法']
}
IE_PATTERN = "{}\n\n提取上述句子中{}类型的实体,并按照JSON格式输出,上述句子中不存在的信息用['原文中未提及']来表示,多个值之间用','分隔。"
# 提供一些例子供模型参考
ie_examples = {
'故障': [
{
'content': '传感器驱动舌轴断裂。原因是传动机构同轴度差,处理方法是更换舌轴.',
'answers': {
'故障现象': ['传感器驱动舌轴断裂'],
'故障原因': ['传动机构同轴度差'],
'解决办法': ['更换舌轴']
}
}
]
}
def init_prompts():
"""
初始化前置prompt,便于模型做 incontext learning。
"""
class_list = list(class_examples.keys())
cls_pre_history = [
(
f'现在你是一个文本分类器,你需要按照要求将我给你的句子分类到:{
class_list}类别中。',
f'好的。'
)
]
for _type, exmpale in class_examples.items():
cls_pre_history.append((f'“{
exmpale}”是 {
class_list} 里的什么类别?', _type))
ie_pre_history = [
(
"现在你需要帮助我完成信息抽取任务,当我给你一个句子时,你需要帮我抽取出句子中三元组,并按照JSON的格式输出,上述句子中没有的信息用['原文中未提及']来表示,多个值之间用','分隔。",
'好的,请输入您的句子。'
)
]
for _type, example_list in ie_examples.items():
for example in example_list:
sentence = example['content']
properties_str = ', '.join(schema[_type])
schema_str_list = f'“{
_type}”({
properties_str})'
sentence_with_prompt = IE_PATTERN.format(sentence, schema_str_list)
ie_pre_history.append((
f'{
sentence_with_prompt}',
f"{
json.dumps(example['answers'], ensure_ascii=False)}"
))
return {
'ie_pre_history': ie_pre_history, 'cls_pre_history': cls_pre_history}
def clean_response(response: str):
"""
后处理模型输出。
Args:
response (str): _description_
"""
if '```json' in response:
res = re.findall(r'```json(.*?)```', response)
if len(res) and res[0]:
response = res[0]
response.replace('、', ',')
try:
return json.loads(response)
except:
return response
def inference(
sentences: list,
custom_settings: dict
):
"""
推理函数。
Args:
sentences (List[str]): 待抽取的句子。
custom_settings (dict): 初始设定,包含人为给定的 few-shot example。
"""
for sentence in sentences:
with console.status("[bold bright_green] Model Inference..."):
sentence_with_cls_prompt = CLS_PATTERN.format(sentence)
cls_res, _ = model.chat(tokenizer, sentence_with_cls_prompt, history=custom_settings['cls_pre_history'])
if cls_res not in schema:
print(f'The type model inferenced {
cls_res} which is not in schema dict, exited.')
exit()
properties_str = ', '.join(schema[cls_res])
schema_str_list = f'“{
cls_res}”({
properties_str})'
sentence_with_ie_prompt = IE_PATTERN.format(sentence, schema_str_list)
ie_res, _ = model.chat(tokenizer, sentence_with_ie_prompt, history=custom_settings['ie_pre_history'])
ie_res = clean_response(ie_res)
print(f'>>> [bold bright_red]sentence: {
sentence}')
print(f'>>> [bold bright_green]inference answer: ')
print(ie_res)
if __name__ == '__main__':
console = Console()
device = 'cuda:0'
tokenizer = AutoTokenizer.from_pretrained(