共计 7055 个字符,预计需要花费 18 分钟才能阅读完成。
先来个舒适的小揭示:
这篇文章尽管较为全面地介绍了 LangChain,但都是点到为止,只是让你理解一下它的皮毛而已,适宜小白选手。
So,如果你是 LangChain 的小白,看完之后还是一头雾水,那就请毫不留情地,狠狠地 ……………………………… 给我点赞吧!有了你的激励,我会再接再厉的!(ง •\_•)ง
What?
丹尼尔:蛋兄,刚刚听到他人在说 LangChain,你晓得是啥玩意吗?
蛋学生:哦,LangChain 啊,一个开发框架
丹尼尔:开发啥的框架?
蛋学生:一个用于开发语言模型驱动的利用的框架
丹尼尔:哦,开发这种利用,不就是写写 Prompt 提醒语,调调语言模型 API 的事么?
蛋学生:没错。但 LangChain 使得 Prompt 的编写,API 的调用更加标准化
丹尼尔:就这样吗?
蛋学生:当然不止,它还有很多很酷的性能
丹尼尔:比方?
蛋学生:它能够连贯内部数据源,依据输出检索相干数据作为上下文给到语言模型,使得语言模型能够答复训练数据之外的问题。这是由 LangChain 的 Retrieval
来实现的
丹尼尔:太酷了,我想到了一个场景,比方通过它来连贯客服的答复话术库,这样就能够让语言模型摇身一变,变成一个业余的客服了
蛋学生:恩,这是一个很好的场景
丹尼尔:还有其它更酷的性能吗?
蛋学生:它能够让语言模型来自行决定采取哪些口头
丹尼尔:这个就不是很明确了
蛋学生:接着你那个客服的例子持续说。如果用户问的问题是对于公司产品的,咱们就想让语言模型应用客服的话术库来答复;如果是其它问题,就让语言模型用它本人的常识来间接答复。如果是你,你会怎么实现?
丹尼尔:我想我会先通过语言模型来判断用户的问题是否对于公司产品。如果是,就走连贯话术库的逻辑;如果不是,就走让语言模型间接答复的逻辑
蛋学生:恩,你这种就是 hardcode 逻辑的形式。还有一种更加 amazing 的 形式,就是让语言模型自行决定采取哪种行为。这个由 LangChain 的 Agent
来实现。
丹尼尔:听下来太酷了,怎么用呢?
蛋学生:莫急,待我缓缓道来
Why?
丹尼尔:蛋兄,你刚刚说到 LangChain 使得 Prompt 的编写,API 的调用更加标准化,标准化了必定是好的,但益处很大吗?我用语言模型的 SDK 不也用得好好的吗?
蛋学生:那你先给一个应用 SDK 与语言模型交互的例子呗
丹尼尔:这还不简略,我就用这个吧:fireworks.ai(注:这个平台提供收费的资源,拜访也不便 )
from fireworks.client import Fireworks
client = Fireworks(api_key="<FIREWORKS_API_KEY>")
response = client.chat.completions.create(
model="accounts/fireworks/models/llama-v2-7b-chat",
messages=[{
"role": "user",
"content": "Who are you?",
}],
)
print(response.choices[0].message.content)
输入:Hello! I'm just an AI assistant, here to help you in any way I can. My purpose is to provide helpful and respectful responses, always being safe and socially unbiased. I'm here to assist you in a positive and ethical manner, and I'm happy to help you with any questions or tasks you may have. Is there anything specific you would like me to help you with?
蛋学生:很好,再给另外一个语言模型的例子呗
丹尼尔:额,一样的操作啊,你这是在消遣我吗?好吧,那我就再给一个百度的文心一言的例子
import os
import qianfan
os.environ["QIANFAN_AK"] = "<QIANFAN_AK>"
os.environ["QIANFAN_SK"] = "<QIANFAN_SK>"
chat_comp = qianfan.ChatCompletion()
resp = chat_comp.do(messages=[{
"role": "user",
"content": "Who are you?"
}])
print(resp.body['result'])
输入:您好,我是百度研发的常识加强大语言模型,中文名是文心一言,英文名是 ERNIE Bot。我可能与人对话互动,答复问题,帮助创作,高效便捷地帮忙人们获取信息、常识和灵感。
蛋学生:Good,当初假如我一开始应用 fireworks 来开发利用,过程中发现成果不太现实,想换成文心一言呢?
丹尼尔:Oh\~,各个语言模型的 SDK 的接口定义是不一样的,替换起来的确麻烦。来吧,是时候开始你的表演了
蛋学生:咱们间接来看下通过 LangChain 应用 fireworks 和 文心一言 的代码示例吧,毕竟 No Code No BB 嘛
- fireworks LangChain 示例
import os
from langchain_community.chat_models.fireworks import ChatFireworks
os.environ["FIREWORKS_API_KEY"] = '<FIREWORKS_API_KEY>'
model = ChatFireworks(model="accounts/fireworks/models/llama-v2-13b-chat")
res = model.invoke("Who are you?")
print(res.content)
- 文心一言 LangChain 示例
import os
from langchain_community.chat_models import QianfanChatEndpoint
os.environ["QIANFAN_AK"] = "<QIANFAN_AK>"
os.environ["QIANFAN_SK"] = "<QIANFAN_SK>"
model = QianfanChatEndpoint(model="ERNIE-Bot-turbo")
res = model.invoke("Who are you?")
print(res.content)
丹尼尔:如同看进去了,标准化之后,要更换语言模型变得十分不便了,只须要更换下 model 的实例化就行了
蛋学生:是的,这只是个最简略的例子,LangChain 还有很多种优雅的形式来切换不同的模型。从此以后咱们就能够专一于 Prompt 的开发了。语言模型嘛,哪个适合换哪个
How?
丹尼尔:好了,我决定入坑 LangChain 了,那咱们进一步聊聊?
蛋学生:当然能够!咱们从简略到简单,联合代码和流程图来展现 LangChain 的一些用法。先来最简略的代替 SDK 的用法,这个上边曾经有提到了
res = model.invoke("tell me a short joke about a cat")
print(res.content)
丹尼尔:恩,这个 so easy,一瞄就懂
蛋学生:OK,那接下来咱们来应用 PromptTemplate,通过变量的形式来管制模板里的局部内容
from langchain_core.prompts import ChatPromptTemplate
...
prompt = ChatPromptTemplate.from_template("tell me a short joke about {topic}")
chain = prompt | model
res = chain.invoke({"topic": "a cat"})
print(res.content)
丹尼尔:应用 PromptTemplate 的形式来写 prompt,的确比字符串的拼接要优雅不少
蛋学生:再加个简略的输入转换吧
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
...
prompt = ChatPromptTemplate.from_template("tell me a short joke about {topic}")
output_parser = StrOutputParser()
chain = prompt | model | output_parser
res = chain.invoke({"topic": "a cat"})
print(res)
丹尼尔:终于晓得为啥叫 chain
了
蛋学生:持续?
丹尼尔:持续 …
蛋学生:接下来这段代码可能有点长哦
from langchain_community.embeddings import QianfanEmbeddingsEndpoint
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import faiss
from langchain_community.chat_models import QianfanChatEndpoint
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableParallel, RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
# 1
docs = WebBaseLoader("https://docs.smith.langchain.com").load()
embeddings = QianfanEmbeddingsEndpoint()
documents = RecursiveCharacterTextSplitter(chunk_size=900).split_documents(docs)
vector = faiss.FAISS.from_documents(documents, embeddings)
retriever = vector.as_retriever(search_kwargs={'k': 4})
# 2
setup_and_retrieval = RunnableParallel({"context": retriever, "input": RunnablePassthrough()}
)
prompt = ChatPromptTemplate.from_template("""Answer the following question based only on the provided context:
<context>
{context}
</context>
Question: {input}""")
model = QianfanChatEndpoint(streaming=False, model="ERNIE-Bot-turbo")
output_parser = StrOutputParser()
# 3
retrieval_chain = setup_and_retrieval | prompt | model | output_parser
res = retrieval_chain.invoke("how can langsmith help with testing?")
print(res)
丹尼尔:请把“可能”去掉,谢谢
蛋学生:但逻辑其实并不简单,次要分为三块
1)加载网页文档,通过 Embeddings
将文档内容转成向量并存储在向量数据库 FAISS
中,retriever
就是一个能够依据输出从向量数据库获取相干文档的检索工具
2)申明 chain 的各个步骤
3)将各个步骤按程序 chain 起来
丹尼尔:等等,看着有点脑壳疼。Embeddings?向量?向量数据库?
蛋学生:咱们明天是“初探”,所以也只能简略讲讲,不然很多同学就要昏昏欲睡了
丹尼尔:没问题,有个大略印象也好
蛋学生:首先,为什么要将文本转成向量呢?因为通过计算两个向量的间隔,咱们就能够量化地评估它们的相关性。间隔越小,通常意味着文本之间的相关性越高。咱们这里是须要检索与输出相干的文档内容,将其作为会话上下文提供给语言模型。如果是整个文档都传过来,是不是就太大了呢?
丹尼尔:哦,原来向量有这么高级的性能啊
蛋学生:没错。而后要将文本转成向量,就须要用到 Embeddings(词嵌入)技术。Embeddings 在历史上有过多种办法,如基于统计的计数办法,基于神经网络的推理方法等。QianfanEmbeddingsEndpoint
正是一个利用深度学习训练失去的 Embeddings 模型服务,输出为文本,输入为向量
丹尼尔:大略有点明确了
蛋学生:那咱们接着看下流程图
丹尼尔:是否为小弟我解释一下下面这个流程图的前半部分
蛋学生:当然!首先输出是 “how can langsmith help with testing?”;接着有个并行的逻辑,一个是通过 Retriever 依据输出检索相干的文档内容作为 context 的值,另一个则是间接 pass 将输出作为 input 的值;而后就是将数据传给 Prompt 模板,最终就能够失去传给语言模型的 PromptValue 了
丹尼尔:Soga
蛋学生:留神,压轴要退场了哦,当初让咱们来请出赫赫有名的 Agent 吧
from langchain import hub
from langchain.agents import AgentExecutor, create_json_chat_agent
from langchain.tools import tool
from langchain_community.chat_models.fireworks import ChatFireworks
@tool
def leng(word: str) -> str:
"""Please use this tool if you want to find the length of the word."""
return len(word)
@tool
def lower(word: str) -> str:
"""Please use this tool if you need to change the word to lowercase."""
return f'dx_{word.lower()}'
tools = [leng, lower]
model = ChatFireworks(model="accounts/fireworks/models/llama-v2-70b-chat")
prompt = hub.pull("hwchase17/react-chat-json")
agent = create_json_chat_agent(model, tools, prompt, stop_sequence=False)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, handle_parsing_errors=True, max_iterations=5)
res = agent_executor.invoke({"input": "Make this word lowercase:'Daniel'"})
print(res)
{'input': "Make this word lowercase:'Daniel'",'output':"The lowercase version of 'Daniel' is 'dx_daniel'"}
丹尼尔:好耶,快点解说一下吧
蛋学生:首先咱们申明了两个工具:一个是 leng(用于求字符串长度),一个是 lower(用于将字符串变成小写)。这里为了证实后果是通过咱们的工具来失去后果的,所以特意在 lower 的实现中加了个 dx\_ 前缀
丹尼尔:等等,hub.pull("hwchase17/react-chat-json")
是什么神秘代码?
蛋学生:这是 LangChain hub 社区上共享的用于实现 Agent 的泛滥 Prompt 中的一个,你能够在这里找到很多有用的 Prompt。毕竟,语言工程也是一种艺术,也是须要实际积攒的。
丹尼尔:明确,请持续
蛋学生:通过 Agent,语言模型就能够依据输出自行判断应该应用哪个工具了
丹尼尔:哇,这太神奇了!我对它是怎么自行判断很感兴趣
蛋学生:简略来说,语言模型能够依据输出,再依据各个工具的形容,来判断哪个工具更适宜,而后将后果输入为能够让 LangChain 了解的执行指令(比方 JSON)
丹尼尔:太棒了!当初我对 LangChain 有了一个大抵的理解,心愿当前还能跟你持续深入探讨
蛋学生:机会有滴是,咱们后会有期!ヾ (~▽~)Bye~Bye~