LLM的应用开发框架——Langchain

LLM的应用开发框架——Langchain

一. Langchain是什么

Langchain 官网文档:https://python.langchain.com/v0.2/docs/introduction/

LLM崛起出现了哪些需求?
  • 格式化输出:希望给的输出格式是json、csv、db格式

  • 输出很长的提示词文本:如何总结一本书的内容?

  • 多次API调用:两次调用api,前后两次需要结合的

  • 外部调用:比如需要进行web 搜索

  • 标准化开发

  • 快速切换模型:有多个大模型可用,支持代码不变,快速切换

二. Langchain支撑LLM的应用

2.1 支持多种LLM

无论是国外的GPT4、LLaMa,还是国内的ChatGLM、Baichuan,都支持调用api和huggingface模型的使用,下面主要介绍HF模型的下载使用。

from huggingface_hub import snapshot_download  
from langchain.llms.base import LLM   
# 指定下载目录(在当前文件夹下)  
snapshot_download(repo_id="baichuan-inc/Baichuan2-7B-Chat-4bits", local_dir="baichuan-inc/Baichuan2-7B-Chat-4bits")  
  
classbaichuan2_LLM(LLM):  
# 基于本地 Baichuan 自定义 LLM 类  
     tokenizer:AutoTokenizer=None  
     model:AutoModelForCausalLM=None  
def__init__(self, model_path: str, dtype = torch.bfloat16):  
# model_path: Baichuan-7B-chat模型路径  
# 从本地初始化模型  
super().__init__()  
print("正在从本地加载模型...")  
         self.tokenizer =AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)  
         self.model =AutoModelForCausalLM.from_pretrained(model_path, trust_remote_code=True,  
         torch_dtype=dtype, device_map="auto")  
         self.model.generation_config =GenerationConfig.from_pretrained(model_path)  
         self.model = self.model.eval()  
print("完成本地模型的加载")  
  
def_call(self, prompt: str, stop: Optional[List[str]] = None,  
         run_manager: Optional[CallbackManagerForLLMRun] = None,  
         **kwargs: Any):  
# 重写调用函数  
         messages =[  
{"role":"user","content": prompt}  
]  
# 重写调用函数  
         response = self.model.chat(self.tokenizer, messages)  
return response   
  
    @property  
def_llm_type(self)->str:  
return "baichuan2_LLM"
2.2 零样本少样本提示

对于LLM来说,尽可能的使用prompt来尝试解决问题,而非直接对LLM进行训练。

因此对于少数据或者没有数据来利用prompt来解决问题:

  • zero-shot prompting:直接问模型,最低成本获取答案

  • few-shot prompting:给模型几个例子,引导它做的更好

在Langchain中已经集成了few-shot prompting,如下例子

from langchain.prompts.few_shot importFewShotPromptTemplate  
examples =[  
{  
"question":"你好吗?",  
"answer":"帅哥,我很好"  
},  
{  
"question":"今天周几?",  
"answer":"帅哥,今天周日"  
},  
{  
"question":"天气好吗?",  
"answer":"帅哥,是的,今天天气很不错"  
}  
]  
  
    example_prompt =PromptTemplate(input_variables=["question","answer"], template="提问: {question}\n回答:{answer}")  
    prompt =FewShotPromptTemplate(examples=examples,  
                                   example_prompt=example_prompt,  
                                   suffix="提问: {input}\n",  
                                   input_variables=["input"])  
print(prompt.format(input="我怎么这么丑"))  
  
# 这里相当于将前缀的这些少样本和当前问题全部放入llm,让其知道前后关系或者学习规则  
print(llm.predict((prompt.format(input="我怎么这么丑"))))
2.3 文档问答

方案:LangChain + ChatGLM,GitHub - chatchat-space/Langchain-Chatchat

在这里插入图片描述

整体流程:

  • 本地文档通过Loader读入

  • 利用文档分割器对其文字进行分割,不然文字太长无法输入llm

  • 利用Embedding Model(直接利用Huggingface下载相关词嵌入模型)对分割后的文档chunk进行向量化操作

  • 利用VectorStore(可以选择Chroma或者FAISS作为DB)对向量进行存储

  • 对于新的query而言,向量化后对Vecorstore进行搜索其相似度高的chunk

  • 最后将chunk文档同样放入prompt中,输入模型,得到结果

Langchain支持的优势:

  • 支持相关模块,比如TextSplitter、Loader、Embedding、Vector

  • 在文档中直接找到相似度最关联的chunk,减少输入llm的文字,从而缩短推理时长

从 LangChain + LLM 的流程图可以看出,embedding 的召回率、LLM 的回答能力都会影响到最终回答的准确率。所以,要如果你遇到了 bad case ,你应该先确认这个 bad case 是召回错误,还是模型回答错误。

下面使用庆余年这本书示范的例子,这里的LLM和Embedding模型均来自于Huggingface。

from langchain.indexes.vectorstore importVectorStoreIndexWrapper  
from langchain.text_splitter importRecursiveCharacterTextSplitter  
from langchain_community.vectorstores import FAISS  
from langchain_community.document_loaders importTextLoader  
  
  
defload_text_save_index(file_path,index_name):  
    loader =TextLoader(file_path)  
    text_qyn = loader.load()  
  
    splitter =RecursiveCharacterTextSplitter(  
            chunk_size=100,# 分割出来的文本长度  
            chunk_overlap=10,# 块之间的重叠文字  
            length_function=len,# 计算每个块的长度  
            add_start_index=True# 决定是否在metadata中包含每个块在原始文档中的起始位置  
)  
  
    texts = splitter.split_documents(text_qyn)[:100]  
  
    faiss_db = FAISS.from_documents(texts, hf_embeddings)  
    faiss_db.save_local(os.path.join(local_persist_path,index_name))  
print('faiss db saved !')  
  
  
  
defload_index(index_name):  
    index_path = os.path.join(local_persist_path, index_name)  
    faiss_db = FAISS.load_local(index_path,embeddings=hf_embeddings,allow_dangerous_deserialization=True)  
    index =VectorStoreIndexWrapper(vectorstore = faiss_db)  
return index  
  
  
file_path ='庆余年.txt'  
local_persist_path ='./vector_store'  
  
# load_text_save_index(file_path,index_name='庆余年')  
index = load_index(index_name='庆余年')  
result = index.query("五竹是谁", llm=llm)  
print(result)
2.4 搜索助手

对于Langchain而言,有专门的Agent模块,其支持对指令进行外部搜索。因此需要申请google搜索的APIkey,这里推荐SerpAPI的key。

from langchain.agents import initialize_agent  
from langchain_community.agent_toolkits.load_tools import load_tools  
  
  
tools = load_tools(['serpapi','llm-math'],llm=llm)  
print(tools[1].name,tools[1].description)  
  
agent = initialize_agent(tools,llm,agent = 'zero-shot-react-description',verbose=True)  
  
agent.run('苹果的CEO,他10年后多少岁?')
2.5 文章总结

对于一个大型的文章而言,如果直接全部扔给LLM,无疑是会报显存错误的,因此需要将大的文本切分成小的docs,Langchain支持一下三种总结链方式将docs输入给LLM:

  • stuff:将所有的docs汇总成一个总的提示直接塞给LLM,这里可能会字数太长而报错。

  • map_reduce:每个docs 依次输入LLM进行总结,并将每个总结的结果拼接后再次输入LLM作为汇总。

  • refine:通过循环遍历输入doc并逐步更新其答案来构建响应。对于每个doc,它将所有非文档输入、当前文档和最新的中间答案传递给LLM链以获得新的答案。

下面是对URL网页的文章做一个汇总的过程

from langchain_community.document_loaders importUnstructuredURLLoader  
from langchain.text_splitter importRecursiveCharacterTextSplitter  
from langchain.chains.summarize import load_summarize_chain  
from langchain_core.prompts importPromptTemplate  
  
defload_news(url):  
    text_splitter =RecursiveCharacterTextSplitter(  
# separators=['正文','撰稿'],  
                                                   chunk_size=300,  
                                                   chunk_overlap=10)  
    loader =UnstructuredURLLoader([url])  
    data = loader.load_and_split(text_splitter)  
print(f'doc lenth is {len(data)}')  
return data  
  
  
defsummary_news():  
    map_prompt_temp ="""总结这段新闻的内容在50字以内:{text}, 总结:"""  
    ch_prompt =PromptTemplate(template=map_prompt_temp, input_variables=['text'])  
    chain = load_summarize_chain(llm, chain_type='map_reduce', map_prompt=ch_prompt, combine_prompt=ch_prompt)  
# summary = chain_ch.run(doc)  
    summary = chain.invoke({"input_documents": doc})['output_text']  
print(summary)  # 这里展示的是中文的总结
2.6 输出解析

往往我们希望对于LLM生成的结果,能输出成我们预先定义好的格式。下面是对文章指定生成剧本的形式的过程。

from langchain.output_parsers importPydanticOutputParser  
from pydantic importBaseModel,Field  
from typing importList  
  
# 注意:这里的BaseModel的类的description 不能用中文,因为到template的prompt的时候,会乱码,导致模型不懂描述的是什么  
classLine(BaseModel):  
# character:str = Field(description=u"说这句台词的角色名字",)  
    character:str=Field(description=u"The name of the character who said this line",)  
# content:str = Field(description=u"台词的具体内容,其中不再包含角色的名字")  
    content:str=Field(description=u"The specific content of the line, which no longer contains the character's name")  
  
  
classJuBen(BaseModel):  
# script: List[Line] = Field(description=u"一段的台词剧本")  
    script:List[Line]=Field(description=u"A talk script")  
  
  
defparse_process():  
    temp ="""我将给你一段文章,请按照要求把这段文章改写成一个电视剧的剧本。  
  
                    文章:"{docs}"  
                    要求:"{request}"  
                    {output_instructions}  
  
            """  
    parser =PydanticOutputParser(pydantic_object=JuBen)  
  
    prompt =PromptTemplate(template=temp,  
                               input_variables=['docs','request'],  
                               partial_variables={"output_instructions": parser.get_format_instructions()},  
# pattern = re.compile('\n')  
)  
    jb_content = prompt.format_prompt(docs=docs, request="风格大胆悲情,剧本对话角色不少于三个人,以他们的自我介绍为开头")  
  
# msg = [HumanMessage(content=jb_content)]  
# rs = llm.predict_messages(msg)  
    rs = llm(jb_content.to_string())  
    jb = parser.parse(rs)  
  
  
# chain = jb_prompt | llm | parser  
# xiangsheng = chain.invoke({  
#     "docs" : docs,  
#     "request" : "风格大胆悲情,剧本对话角色不少于三个人,以他们的自我介绍为开头"  
# })  
# print(jb)  
return jb

如何学习大模型 AI ?

由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。

但是具体到个人,只能说是:

“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。

这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。

我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

在这里插入图片描述

第一阶段(10天):初阶应用

该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。

  • 大模型 AI 能干什么?
  • 大模型是怎样获得「智能」的?
  • 用好 AI 的核心心法
  • 大模型应用业务架构
  • 大模型应用技术架构
  • 代码示例:向 GPT-3.5 灌入新知识
  • 提示工程的意义和核心思想
  • Prompt 典型构成
  • 指令调优方法论
  • 思维链和思维树
  • Prompt 攻击和防范

第二阶段(30天):高阶应用

该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。

  • 为什么要做 RAG
  • 搭建一个简单的 ChatPDF
  • 检索的基础概念
  • 什么是向量表示(Embeddings)
  • 向量数据库与向量检索
  • 基于向量检索的 RAG
  • 搭建 RAG 系统的扩展知识
  • 混合检索与 RAG-Fusion 简介
  • 向量模型本地部署

第三阶段(30天):模型训练

恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。

到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?

  • 为什么要做 RAG
  • 什么是模型
  • 什么是模型训练
  • 求解器 & 损失函数简介
  • 小实验2:手写一个简单的神经网络并训练它
  • 什么是训练/预训练/微调/轻量化微调
  • Transformer结构简介
  • 轻量化微调
  • 实验数据集的构建

第四阶段(20天):商业闭环

对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。

  • 硬件选型
  • 带你了解全球大模型
  • 使用国产大模型服务
  • 搭建 OpenAI 代理
  • 热身:基于阿里云 PAI 部署 Stable Diffusion
  • 在本地计算机运行大模型
  • 大模型的私有化部署
  • 基于 vLLM 部署大模型
  • 案例:如何优雅地在阿里云私有部署开源大模型
  • 部署一套开源 LLM 项目
  • 内容安全
  • 互联网信息服务算法备案

学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。

如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值