LangChain 入门与实战

想要构建自己的Agent,你一定无法避免和LangChain打交道。

所以这篇文章将给大家介绍一下LangChain,知道它是什么,怎么用,基于LangChain能做了,并且它有什么问题。

LangChain的文字资料和视频资料挺多的,我就不重复了,我会在文末添加相关的资料,有兴趣的朋友自行学习。

我相信很多人是因为不了解LangChain,不了解Agent,才来看我的文章,而且大部分人时间也很宝贵,没有这么时间花几天时间来系统化学习,而且主要也不确定学习LangChain对自己的工作有没有帮助。

所以我尽量用最简单的语言把事情说情况。

我们要构建一个自己的AI系统(Agent)

假设我们要构建一个自己的问答系统,问答系统需要使用我们私有的数据库。

我们应该怎么做,我们可以先考虑一个成本最低的POC方案。

  • 使用最强的大语言模型ChatGPT的openapi
  • 自己用Python写一个代码,基于openai为基础,然后自己做知识库问答,自己多文件切片和向量化,自己做RAG

思路是这个思路,但是全都自己写,除非你是大牛,还是有些吃力的,而且随着项目的复杂度增加,整个项目的架构将会非常关键。

这时候可能有杠精要说,老师你就用GPTs或其他的一些Agent平台就可以无代码完成一个自己知识库的问答系统了。

没错,但是这里只是想大家都懂的问答系统作为案例进行举例,因为你用友LangChain可以构建比这个复杂的多的系统,比如你可以基于LangChain构建自己的Agent平台。

[图片上传失败...(image-d8ecaf-1714307532853)]

这里给出一个例子,使用可以本地部署的大语言模型ChatGLM-6b,和本地知识库test.txt,简单的十几行代码就完成了我们要做的问答系统,分别是加载文件、文件内容切割、向量化,然后根据向量化的内容和Query进行Prompt组装,就是这么简单。

当然引入LangChain也有带来很多副作用,开发过低代码的朋友肯定知道,虽然使用图形化的方式可以降低系统开发的复杂度,但是同时也降低了系统的性能,原来最精简的代码只要3分钟的执行时长可能会被拉长到数倍。

LangChain核心功能

好了,有了信心之后,我们学习一下LangChain有哪些能力。

模板 PromptTemplate

<pre style="box-sizing: border-box; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 13.6px; margin-top: 0px; margin-bottom: 16px; overflow-wrap: normal; padding: 16px; overflow: auto; line-height: 1.45; background-color: rgb(255, 255, 255); border-radius: 6px;">

1from langchain.prompts import PromptTemplate 2 3prompt = PromptTemplate( 4 input_variables=["product"], 5 template="What is a good name for a company that makes {product}?", 6) 7print(prompt.format(product="colorful socks"))

</pre>

输出:What is a good name for a company that makes colorful socks?

通过PromptTemplate就可以给Prompt添加变量了,很简单吧

链 Chain

<pre style="box-sizing: border-box; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 13.6px; margin-top: 0px; margin-bottom: 16px; overflow-wrap: normal; padding: 16px; overflow: auto; line-height: 1.45; background-color: rgb(255, 255, 255); border-radius: 6px;">

1from langchain.prompts import PromptTemplate 2from langchain.llms import OpenAI 3 4llm = OpenAI(temperature=0.9) 5prompt = PromptTemplate( 6 input_variables=["product"], 7 template="What is a good name for a company that makes {product}?", 8) 9 10from langchain.chains import LLMChain 11chain = LLMChain(llm=llm, prompt=prompt) 12 13chain.run("colorful socks")

</pre>

输出:# -> '\n\nSocktastic!'

这样就使用了LangChain的Chain了,还是很简单吧。

代理Agent

为了用好代理,需要理解以下概念:

  • 工具(tools): 执行特定任务的功能。这可以是: Google 搜索、数据库查找、 Python REPL、其他链。工具的接口目前是一个函数,预计将有一个字符串作为输入,一个字符串作为输出。

<pre style="box-sizing: border-box; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 13.6px; margin-top: 0px; margin-bottom: 16px; overflow-wrap: normal; padding: 16px; overflow: auto; line-height: 1.45; background-color: rgb(255, 255, 255); border-radius: 6px;">

1from langchain.agents import load_tools 2from langchain.agents import initialize_agent 3from langchain.agents import AgentType 4from langchain.llms import OpenAI 5 6# 首先,让我们加载用于控制代理的语言模型。 7llm = OpenAI(temperature=0) 8 9# 接下来,让我们加载一些要使用的工具。请注意,“llm-math”工具使用 LLM,因此我们需要将其传入。 10tools = load_tools(["serpapi", "llm-math"], llm=llm) 11 12# 最后,让我们用工具、语言模型和我们想要使用的代理类型来初始化一个代理。 13agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True) 14 15# 现在让我们测试一下! 16agent.run("What was the high temperature in SF yesterday in Fahrenheit? What is that number raised to the .023 power?")

</pre>

输出:

Entering new AgentExecutor chain...

I need to find the temperature first, then use the calculator to raise it to the .023 power.

Action: Search

Action Input: "High temperature in SF yesterday"

Observation: San Francisco Temperature Yesterday. Maximum temperature yesterday: 57 °F (at 1:56 pm) Minimum temperature yesterday: 49 °F (at 1:56 am) Average temperature ...

Thought: I now have the temperature, so I can use the calculator to raise it to the .023 power.

Action: Calculator

Action Input: 57^.023

Observation: Answer: 1.0974509573251117

Thought: I now know the final answer

Final Answer: The high temperature in SF yesterday in Fahrenheit raised to the .023 power is 1.0974509573251117.

Finished chain.

加入Agent之后,是不是有点智能的味道了,它知道自己去搜索,观察,使用工具,最后形成结论。

内存Memory

到目前为止,我们经历过的所有工具和代理都是无状态的的。

但是通常,您可能希望链或代理具有某种“内存”概念,以便它可以记住关于其以前的交互的信息。

最简单明了的例子就是在设计一个聊天机器人时——你想让它记住之前的消息,这样它就可以利用这些消息的上下文来进行更好的对话。

ConversationChain 这是一种“短期记忆”。

输入:

<pre style="box-sizing: border-box; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 13.6px; margin-top: 0px; margin-bottom: 16px; overflow-wrap: normal; padding: 16px; overflow: auto; line-height: 1.45; background-color: rgb(255, 255, 255); border-radius: 6px;">

1from langchain import OpenAI, ConversationChain 2llm = OpenAI(temperature=0) 3# 让我们看一下如何使用这个链(设置 verbose=True,这样我们就可以看到提示符)。 4conversation = ConversationChain(llm=llm, verbose=True) 5output = conversation.predict(input="Hi there!") 6print(output) 7

</pre>

输出

<pre style="box-sizing: border-box; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 13.6px; margin-top: 0px; margin-bottom: 16px; overflow-wrap: normal; padding: 16px; overflow: auto; line-height: 1.45; background-color: rgb(255, 255, 255); border-radius: 6px;">

1> Entering new chain... 2Prompt after formatting: 3The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know. 4Current conversation: 5Human: Hi there! 6AI: 7> Finished chain. 8' Hello! How are you today?'

</pre>

输入

<pre style="box-sizing: border-box; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 13.6px; margin-top: 0px; margin-bottom: 16px; overflow-wrap: normal; padding: 16px; overflow: auto; line-height: 1.45; background-color: rgb(255, 255, 255); border-radius: 6px;">

1output = conversation.predict(input="I'm doing well! Just having a conversation with an AI.") 2print(output)

</pre>

输出

<pre style="box-sizing: border-box; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 13.6px; margin-top: 0px; margin-bottom: 16px; overflow-wrap: normal; padding: 16px; overflow: auto; line-height: 1.45; background-color: rgb(255, 255, 255); border-radius: 6px;">

1> Entering new chain... 2Prompt after formatting: 3The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know. 4Current conversation: 5Human: Hi there! 6AI: Hello! How are you today? 7Human: I'm doing well! Just having a conversation with an AI. 8AI: 9> Finished chain. 10" That's great! What would you like to talk about?"

</pre>

LangChain专门对于聊天模型进行了封装

聊天模型

LangChain 中当前支持的消息类型是 AIMessage , HumanMessage , SystemMessage , 和 ChatMessage , ChatMessage 接受任意角色参数。大多数时候,您只需要处理 HumanMessage , AIMessage , 和 SystemMessage .

<pre style="box-sizing: border-box; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 13.6px; margin-top: 0px; margin-bottom: 16px; overflow-wrap: normal; padding: 16px; overflow: auto; line-height: 1.45; background-color: rgb(255, 255, 255); border-radius: 6px;">

1from langchain.chat_models import ChatOpenAI 2from langchain.schema import ( 3 AIMessage, 4 HumanMessage, 5 SystemMessage 6) 7chat = ChatOpenAI(temperature=0) 8chat([HumanMessage(content="Translate this sentence from English to French. I love programming.")]) 9# -> AIMessage(content="J'aime programmer.", additional_kwargs={})

</pre>

您还可以为 OpenAI 的 gpt-3.5-turbo 和 gpt-4型号传递多条消息。

<pre style="box-sizing: border-box; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 13.6px; margin-top: 0px; margin-bottom: 16px; overflow-wrap: normal; padding: 16px; overflow: auto; line-height: 1.45; background-color: rgb(255, 255, 255); border-radius: 6px;">

1messages = [ 2 SystemMessage(content="You are a helpful assistant that translates English to French."), 3 HumanMessage(content="Translate this sentence from English to French. I love programming.") 4] 5chat(messages) 6# -> AIMessage(content="J'aime programmer.", additional_kwargs={})

</pre>

您可以更进一步,使用generate为多组消息生成完成。

这将返回一个带有附加message参数的 LLMResult

<pre style="box-sizing: border-box; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 13.6px; margin-top: 0px; margin-bottom: 16px; overflow-wrap: normal; padding: 16px; overflow: auto; line-height: 1.45; background-color: rgb(255, 255, 255); border-radius: 6px;">

1batch_messages = [ 2 [ 3 SystemMessage(content="You are a helpful assistant that translates English to French."), 4 HumanMessage(content="Translate this sentence from English to French. I love programming.") 5 ], 6 [ 7 SystemMessage(content="You are a helpful assistant that translates English to French."), 8 HumanMessage(content="Translate this sentence from English to French. I love artificial intelligence.") 9 ], 10] 11result = chat.generate(batch_messages) 12 13result 14# -> LLMResult(generations=[[ChatGeneration(text="J'aime programmer.", generation_info=None, message=AIMessage(content="J'aime programmer.", additional_kwargs={}))], [ChatGeneration(text="J'aime l'intelligence artificielle.", generation_info=None, message=AIMessage(content="J'aime l'intelligence artificielle.", additional_kwargs={}))]], llm_output={'token_usage': {'prompt_tokens': 71, 'completion_tokens': 18, 'total_tokens': 89}})

</pre>

有点聊天历史的感觉

同样的,聊天也有链 ,也有聊天代理

索引 Indexes

还记得我们一开始举的例子吗,我们要怎么切分、存储、检索自己的文档呢?

加载文件后有三个主要步骤:

  1. 将文档分割成块
  2. 为每个文档创建嵌入
  3. 在向量库中存储文档和嵌入

<pre style="box-sizing: border-box; font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: 13.6px; margin-top: 0px; margin-bottom: 16px; overflow-wrap: normal; padding: 16px; overflow: auto; line-height: 1.45; background-color: rgb(255, 255, 255); border-radius: 6px;">

1# 加载文档 2documents = loader.load() 3# 将文档切块 4from langchain.text_splitter import CharacterTextSplitter 5text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0) 6texts = text_splitter.split_documents(documents) 7# 然后,我们将选择要使用的嵌入 8from langchain.embeddings import OpenAIEmbeddings 9embeddings = OpenAIEmbeddings() 10# 最后,我们创建用作索引的向量存储 11from langchain.vectorstores import Chroma 12db = Chroma.from_documents(texts, embeddings) 13# 这就是创建索引的过程,然后,我们在一个检索接口中公开这个索引 14retriever = db.as_retriever() 15# 像以前一样,我们创建一个链,并使用它来回答问题 16qa = RetrievalQA.from_chain_type(llm=OpenAI(), chain_type="stuff", retriever=retriever) 17query = "What did the president say about Ketanji Brown Jackson" 18qa.run(query)

</pre>

默认情况下,LangChain 使用 Chroma 作为向量存储来索引和搜索嵌入

好了,你已经学完了LangChain最核心的功能,接下来可以自己动手了。

LangChain官网文档

LangChain中文教程

原创声明:本文为本人原创作品,首发于AI ONES https://wuxiongwei.com,如果转载,请保留本文链接,谢谢。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容