资讯详情

资讯详情

LangChain 科普(一):从“和大模型说话“到拼出第一条链

前言这篇写给刚接触大模型、想搞懂 LangChain 到底是什么的人。不预设你会任何框架知识只需要会一点 Python。我们会从最笨的办法开始一步步拆出 LangChain 的三个核心零件最后把它们拼成一条流水线。1. 和大模型聊天最笨的办法先想一个问题程序和大模型之间到底是什么关系其实很简单——发一条消息过去收一条回复回来。和你用微信聊天没什么两样只不过对面不是人是一个跑在服务器上的程序。最原始的实现长这样importrequests# 把发消息、收消息这一步用最原始的方式实现resprequests.post(https://api.deepseek.com/v1/chat/completions,# 大模型的接口地址headers{Authorization:Bearer 你的密钥},# 带上你的身份凭证json{# 请求体告诉模型用哪个模型、聊什么model:deepseek-chat,messages:[{role:system,content:你是一个耐心的老师。},# 系统消息设定人设{role:user,content:用一句话解释什么是 Python。},# 用户消息提问],},)# 从层层嵌套的返回结构里把模型回答的文字取出来这一步最容易写错answerresp.json()[choices][0][message][content]print(answer)这段代码能跑但你先别急着复制停下来观察几件事第一它很脆。回复藏在choices[0][message][content]这个很深的位置模型接口稍微变一变你的代码就要跟着改。第二换模型很麻烦。今天用 DeepSeek明天想换通义千问URL、请求格式、字段名可能全不一样这段代码几乎要重写。第三每个功能都要重写一遍。你要做翻译、做总结、做聊天机器人都得把这段拼请求、发请求、拆响应的代码抄一遍。这时候你大概会想能不能有人把这些重复劳动统一好我只要专心写该让模型干什么有就是 LangChain。2. 一个贯穿全文的比喻工厂流水线在动手写代码前先建立一个画面后面所有章节都会回到这个画面上来。想象一家做菜的中央厨房里面有一条流水线零件是流水线上负责单项工作的机器比如洗菜机“切菜机”“装盘机”传送带负责把上一个机器的产出送到下一个机器手里图纸是你要生产什么菜决定流水线怎么组装。在 LangChain 里这三样东西都有对应零件就是一个个组件提示词、模型、输出解析器每个只干一件事传送带是代码里的|符号把上一个组件的输出递给下一个图纸就是我们写的代码决定组件怎么拼。很多人学 LangChain 被RunnableLCEL这些词吓到其实它们翻译过来就是能上流水线的零件和拼流水线的方式。记住这个画面后面每一个零件你都能对号入座。3. 第一个程序让模型回一句话先安装工具。打开终端运行pipinstalllangchain langchain-openai python-dotenv# 安装核心库和读取 .env 的库然后准备一个密钥。DeepSeek 也好、OpenAI 也好都可以这里以 DeepSeek 为例。把密钥放在项目根目录的.env文件里DEEPSEEK_API_KEY你的密钥# 密钥本身永远不要写进代码DEEPSEEK_BASE_URLhttps://api.deepseek.com# 模型服务的地址接着写我们的第一个程序。它做的事和上一节最笨的办法一模一样但代码干净得多importosfromdotenvimportload_dotenvfromlangchain_openaiimportChatOpenAI load_dotenv()# 读 .env 文件把密钥加载进来# 造一个聊天模型零件后面所有对话都由它负责llmChatOpenAI(modeldeepseek-chat,# 用哪个模型base_urlos.getenv(DEEPSEEK_BASE_URL),# 模型服务的地址api_keyos.getenv(DEEPSEEK_API_KEY),# 从环境变量读密钥不写死在代码里)# invoke 就是把话递进去把回答拿回来answerllm.invoke(用一句话解释什么是 Python)print(answer.content)# content 才是回复的文字和第一版对比一下不用自己拼 JSON 请求不用自己从深层结构里取答案invoke就是把话递进去把回答拿回来。现在让我们把这段代码解剖开找出里面藏着的零件。你会发现其实只有三个提示词——用一句话解释什么是 Python这句话模型——llm这个聊天模型输出——打印出来的answer.content。下面三章我们把这三个零件挨个拆开看。4. 零件一提示词怎么跟模型说清楚先做一个实验。同样一个问题“明天穿什么”如果你直接问模型它会给你一个泛泛而谈的答案因为它不知道你在哪里、要去干嘛、有什么衣服。但如果你把问题换成这样“我在上海明天要参加一个互联网公司的面试偏休闲风格我衣柜里有一件白衬衫和一条卡其裤。明天穿什么”模型的回答会完全不一样具体、可用、像人话。这说明一件事模型回答的质量很大程度取决于你怎么提问。而怎么提问这件事值得被当成一个正式的零件来管理而不是随手写死在代码里。LangChain 提供了提示词模板ChatPromptTemplate它解决两个问题第一变量。提问里经常有会变化的部分城市、场合、衣服把它们挖出来当成变量模板就可以复用fromlangchain_core.promptsimportChatPromptTemplate# 用占位符 {city} {occasion} {style} 挖出会变化的部分做成模板promptChatPromptTemplate.from_template(我在{city}明天要参加{occasion}风格偏{style}帮我推荐穿搭。)# 填上不同的变量得到不同的提问format_messages 返回的是消息列表msg1prompt.format_messages(city上海,occasion面试,style休闲)msg2prompt.format_messages(city北京,occasion朋友聚会,style潮流)第二结构。模板不只是拼字符串它生成的是消息这是下一个零件的事。5. 零件二消息聊天和填空题的区别你有没有想过为什么叫聊天模型不叫问答模型因为聊天是有来有回的。你问一句它答一句你再追问它得记得刚才你们聊到哪。LangChain 把对话里的每句话都包装成一种消息一共三种系统消息SystemMessage开场白告诉模型你是谁、按什么规矩说话。可以理解成面试前人事先告诉你面试官的性格用户消息HumanMessage用户说的话助手消息AIMessage模型之前说过的话代表你们已经聊到哪了。多轮对话就是把这三种消息按顺序排成一列一起发给模型。比如# 沿用第 3 节创建的 llm如果你单独运行本节先把第 3 节创建 llm 的代码带上fromlangchain_core.messagesimportAIMessage,HumanMessage,SystemMessage# 把这一轮对话的完整聊天记录排成一列messages[SystemMessage(content你是一个耐心的穿搭顾问。),# 系统消息设定人设和规矩HumanMessage(content我下周要面试帮我选套穿搭。),# 用户消息用户的提问AIMessage(content先问一下面试是什么风格的岗位),# 助手消息上一轮模型说的话HumanMessage(content互联网产品岗偏休闲。),# 用户消息继续追问]# 把整个聊天记录发给模型它才知道上下文answerllm.invoke(messages)print(answer.content)注意看我们把助手上一轮的回复也放进去了。这就是模型能记得上文的全部秘密——它自己并没有记忆只是我们每次把聊天记录重新递给它一遍。6. 零件三模型和输出解析模型回答的是文字但程序需要的是数据。打个比方你让朋友帮你记下今晚聚会的人他可能给你一段流畅的描述张三到了李四在路上了王五说要晚点来……“你当然能听懂但如果你要写程序统计来了几个人”这段文字就没法直接用。你更希望他填一张表姓名状态张三已到李四路上王五晚点到填表这件事在 LangChain 里叫输出解析。它负责把模型的自由文字变成程序能直接用的结构。最简单的解析器叫字符串解析器StrOutputParser它的工作就是把模型的回答拆包装成纯文本fromlangchain_core.output_parsersimportStrOutputParser# 解析器零件把模型的回复拆包装成纯文本parserStrOutputParser()# 先让模型回答得到的是一个消息对象里面除了文字还有附加信息answerllm.invoke(用一句话介绍 LangChain)# 再用解析器取出纯文字程序后面就能直接用了textparser.invoke(answer)print(text)这里有个容易绕晕的点换个角度看就清楚了llm.invoke返回的是一个消息对象里面除了文字还带各种附加信息而我们的程序大多只需要里面的文字。解析器干的就是这件事——把消息对象里的文字取出来。更进阶的用法是让模型按固定结构输出比如我们规定它必须返回评分 建议两个字段这样程序就能直接取用。这一块上篇先点到为止下篇我们会专门展开。7. 把零件拼起来第一条链三个零件都认识完了现在把它们放上流水线。回想厨房的比喻提示词是洗菜机模型是炒菜机解析器是装盘机。LangChain 用|这个符号把它们串起来左边零件的输出自动变成右边零件的输入fromlangchain_core.output_parsersimportStrOutputParserfromlangchain_core.promptsimportChatPromptTemplate# 零件一提示词模板负责生成提问promptChatPromptTemplate.from_template(把下面这句话扩写成 30~60 字的详细描述\n{query})parserStrOutputParser()# 零件三解析器负责把回答变成纯文本# 用 | 把零件拼成一条链提示词 → 模型 → 解析器模型沿用第 3 节的 llmchainprompt|llm|parser# 往链的入口丢一个变量链会自己把零件一个个走完resultchain.invoke({query:红色连衣裙})print(result)这条链读起来就是一句人话“先按模板生成提示词再让模型回答最后解析成纯文本”。拼好的链会自动获得三种能力这是 LangChain 最巧妙的设计# 一次执行处理一个问题chain.invoke({query:红色连衣裙})# 批量执行把多个问题一起丢进去一次处理完chain.batch([{query:蓝色牛仔裤},{query:白色T恤}])# 流式执行答案一个字一个字地蹦出来end 让它们接在一起不换行forchunkinchain.stream({query:黑色风衣}):print(chunk,end,flushTrue)# flushTrue 让文字立刻显示注意我们没有多写任何代码只是把零件拼成了链这三种能力就自动都有了。这就是把问题交给流水线的价值你不用管传送带怎么转只需要把零件接好。上篇小结我们这一篇其实只做了一件事把和大模型说话这件事从手写请求变成了拼流水线。你学会了程序和大模型之间本质就是发消息、收消息流水线比喻零件、传送带、图纸分别对应组件、|、代码三个基础零件提示词怎么问、消息聊天上下文、模型与解析器怎么把回答变成数据用|拼成链自动获得单次、批量、流式三种执行方式。到这一步你已经能搭一条提问 → 回答 → 解析的小流水线了。但它还只会说话。下篇我们给它装四样东西一双手调用工具查天气、查数据、一个记事本记住上下文、一本书能查资料回答问题、一个大脑自己决定下一步做什么。到那时候它就从会聊天变成会干活了。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →