黔西南布依族苗族自治州网站建设_网站建设公司_动画效果_seo优化
2026/1/8 21:25:10 网站建设 项目流程

Chain翻译成中文就是“链”,我们将大模型、相关工具等作为组件,链就是负责将这些组件按照某一种逻辑,顺序组合成一个流水线的方式。比如我们要构建一个简单的问答链,就需要把大模型组件和标准输出组件用链串联起来。

1.简单链

fromlangchain.chat_modelsimportinit_chat_modelfromlangchain_core.output_parsersimportStrOutputParserif__name__=='__main__':# 模型model=init_chat_model(model="ollama:deepseek-v3.1:671b-cloud",base_url="http://127.0.0.1:11434")output_paser=StrOutputParser()# 将它们组装成一个链,_chain=model|output_paser question="写一首唐诗"result=_chain.invoke(question)print(result)

执行后的结果:

《秋夜泊舟》 江阔星垂野,风清月近人。 孤灯寒照水,夜鹊暗栖云。 客路霜钟远,家山晓梦频。 归舟何日稳?一叶落秋津。 注:诗中通过“星垂野”、“月近人”等意象勾勒出秋夜泊舟的静谧画面,后以“孤灯”、“夜鹊”暗合游子孤寂心境。尾联以落叶问归舟,将思乡之情融于秋景,余韵悠长。全诗借鉴唐代山水田园诗风,注重意境的营造与情感的含蓄表达。

可以看到此时的result不再是包含模型各种调用信息的AIMessage对象,而是纯粹的模型响应的字符串结果。

这里用到的StrOutputParser()实际上就是用于构成LangChain中一个链条的一个对象,其核心功能是用于处理模型输出结果。

同时我们也发现LangChain中搭建链的方法十分简单,只需将不同组件通过|符号串联即可。

2.加入提示词模板

在 LangChain 1.x 版本中,提示词模板(Prompt Templates)是用于构建大语言模型(LLM)输入提示(prompts)的核心组件之一。它们提供了一种结构化、可复用的方式来动态生成提示内容,避免硬编码字符串,并支持变量插值、格式控制

fromlangchain.chat_modelsimportinit_chat_modelfromlangchain_core.output_parsersimportStrOutputParserfromlangchain_core.promptsimportChatPromptTemplateif__name__=='__main__':# 模型model=init_chat_model(model="ollama:deepseek-v3.1:671b-cloud",base_url="http://127.0.0.1:11434")# 提示词模板prompt_template=ChatPromptTemplate([("system","你是一个乐意助人的助手,请根据用户的问题给出回答"),("user","这是用户的问题: {topic}, 请用 yes 或 no 来回答")])# 结果解析器output_paser=StrOutputParser()# 将它们组装成一个链_chain=prompt_template|model|output_paser# topic 将会被填充到模板中question={"topic","你是一个真实的人类吗?"}result=_chain.invoke(question)print(result)

借助ChatPromptTemplate非常便捷的将一个提示词模板打造为组件,同样以链的形式加入当前流程中.

查看这个类的源码有关这个类的说明:

fromlangchain_core.promptsimportChatPromptTemplate template=ChatPromptTemplate([("system","You are a helpful AI bot. Your name is {name}."),("human","Hello, how are you doing?"),("ai","I'm doing well, thanks!"),("human","{user_input}"),])
  • system: 表示系统角色,对应SystemMessage
  • human: 表示用户角色 ,对应HumanMessage
  • ai : 表示它是AI回复的。对应AIMessage

3.结构化解析器

LangChain中一个基础的链一般由如下三部分构成:

发人员通过提示词让大模型输出结构化的字符串,然后通过结构化解析器将字符串解析为指定对象:

上面示例中使用了最简单的结构化解释器StrOutputParserLangChain中常用的核心结构化解析器功能如下:

解析器名称功能类型
BooleanOutputParser将LLM输出解析为布尔值基础类型解析
DatetimeOutputParser将LLM输出解析为日期时间基础类型解析
EnumOutputParser解析输出为预定义枚举值之一基础类型解析
RegexParser使用正则表达式解析模式匹配解析
RegexDictParser使用正则表达式将输出解析为字典模式匹配解析
StructuredOutputParser将LLM输出解析为结构化格式结构化解析
YamlOutputParser使用Pydantic模型解析YAML输出结构化解析
PandasDataFrameOutputParser使用Pandas DataFrame格式解析输出数据处理解析
CombiningOutputParser将多个输出解析器组合为一个组合解析器
OutputFixingParser包装解析器并尝试修复解析错误错误处理解析
RetryOutputParser包装解析器并尝试修复解析错误错误处理解析
RetryWithErrorOutputParser包装解析器并尝试修复解析错误错误处理解析
ResponseSchema结构化输出解析器的响应模式辅助类

结构化解析器使用示例:

fromlangchain.chat_modelsimportinit_chat_modelfromlangchain_classic.output_parsersimportResponseSchema,StructuredOutputParserfromlangchain_core.promptsimportPromptTemplateif__name__=='__main__':# 模型model=init_chat_model(model="ollama:deepseek-v3.1:671b-cloud",base_url="http://192.168.6.133:11434")# 提示词模板prompt_template=PromptTemplate.from_template(""" 请根据以下内容提取用户信息,并返回 JSON 格式: 信息: {input} 格式: {format_instructions} """)format_instructions=[# 构建结构化数据模板ResponseSchema(name="name",description="用户的姓名"),ResponseSchema(name="age",description="用户的年龄"),ResponseSchema(name="job",description="用户的职业"),]# 根据模板生成解析器output_paser=StructuredOutputParser.from_response_schemas(format_instructions)# 将它们组装成一个链_chain=(prompt_template.partial(format_instructions=output_paser.get_format_instructions())|model|output_paser)# topic 将会被填充到模板中question={"input","用户张三,今年20岁,是一个工程师"}result=_chain.invoke(question)# 输出: {'name': '张三', 'age': '20', 'job': '工程师'}print(result)

4.链组合

链不但可以将不同的组件组织起来。不同的链又可以作为组件被其它的链串联起来。

if__name__=='__main__':# 模型deepseek_model=init_chat_model(model="ollama:deepseek-v3.1:671b-cloud",base_url="http://192.168.6.133:11434")qwen_model=init_chat_model(model="ollama:qwen3-next:80b-cloud",base_url="http://192.168.6.133:11434")# 1. chain1:提示词模板chain1_prompt_tpl=PromptTemplate.from_template(""" 请根据以下新闻标题撰写一段简短的新闻内容(100字以内): 标题:{title} """)# 2. chain1chain1=chain1_prompt_tpl|deepseek_model# 3. chain2:提示词模板chain2_prompt_tpl=PromptTemplate.from_template(""" 请从下面这段新闻内容中提取关键信息,并返回结构化JSON格式: {news} 格式:{format_instructions} """)# 4. chain2format_instructions=[# 构建结构化数据模板ResponseSchema(name="time",description="事件发生的时间"),ResponseSchema(name="location",description="事件发生的地点"),ResponseSchema(name="event",description="发生的具体事件"),ResponseSchema(name="news",description="完整新闻内容")]# 根据模板生成解析器output_paser=StructuredOutputParser.from_response_schemas(format_instructions)chain2=chain2_prompt_tpl.partial(format_instructions=output_paser.get_format_instructions())|qwen_model|output_paser# 将它们组装成一个链_chain=chain1|chain2# topic 将会被填充到模板中question={"title","OpenAI 在旧金山发布AI笔"}result=_chain.invoke(question)print(result)

代码运行后输出:

{'time': '近期', 'location': '旧金山', 'event': '正式推出AI智能笔', 'news': 'OpenAI在旧金山新品发布会上正式推出AI智能笔。这款设备集成先进AI技术,可实时转录并智能分析书写内容,旨在提升办公与创作效率。产品预计将于下季度上市。'}

两个提示词模板是什么时候被填充的?

模板的填充(即占位符被实际值替换)发生在chain.invoke()调用的过程中。

  • chain1_prompt_tpl的填充

当整个_chain被调用时,_chain.invoke(question)首先会将question({"title": "..."}) 传递给chain1.chain1的定义是chain1_prompt_tpl | deepseek_model。此时,chain1_prompt_tpl接收到question这个字典。因为question中包含键"title",所以模板中的{title}占位符会被question中对应的值填充,生成一个完整的提示字符串,然后传递给deepseek_model进行处理。

  • chain2_prompt_tpl的填充:

deepseek_model生成新闻内容后,这个内容会作为输出返回给chain1。接着,chain1的输出(即生成的新闻内容)被传递给chain2(chain2的输入)。chain2的定义是chain2_prompt_tpl.partial(...) | qwen_model | output_paserchain2_prompt_tpl.partial(...)创建了一个新的提示模板,其中format_instructions部分已经被具体的格式说明填充好了。

chain1的输出(新闻内容)传递给这个部分填充好的模板时,模板会自动查找与输入数据结构匹配的占位符。关键在于:chain1的输出是一个字符串,这个字符串会被自动用来填充chain2_prompt_tpl中名为news的占位符。

chain1的最终输出是一个字符串(由deepseek_model生成的新闻内容)。

当这个输出传递给chain2时,LangChain 会检查chain2(即chain2_prompt_tpl.partial(...))需要什么输入。它发现模板中有一个{news}占位符。

LangChain 会尝试将这个单个字符串值映射到模板中**唯一缺少的、未被 **partial填充的占位符上。因为format_instructions已经通过partial填充了,所以剩下的唯一需要填充的就是{news}。因此,LangChain 框架会自动将chain1的输出字符串填充到{news}位置。

5.自定义组件

上面的 prompt, model, 解析器之所以能够用 管道符|连接起来,是因为 它们都实现了Runnable这个接口,chain的一切基础单元都是Runnable对象,它是一种统一的可调用接口,支持如下形式:

  • .invoke(input):同步调用
  • .stream(input):流式生成
  • .batch(inputs):批量执行

下面修改上面的代码:

# ........前面代码省略output_paser=StructuredOutputParser.from_response_schemas(format_instructions)chain2=chain2_prompt_tpl.partial(format_instructions=output_paser.get_format_instructions())|qwen_model|output_paser# 输出中间结果defmy_debug(x):print(f'中间结果:{x}')# 一定要返回x, 否则结果不会在chain中继续传递returnx debug_content=RunnableLambda(my_debug)# 将它们组装成一个链_chain=chain1|debug_content|chain2# topic 将会被填充到模板中question={"title","OpenAI 在旧金山发布AI笔"}result=_chain.invoke(question)print(result)

RunnableLambda将python函数转换为可运行节点。转化后的节点可以像任何其它Runnable一样组合并与LangChain链无缝集成。(特别注意:RunnableLambda适合非流式输出,如果要流式输出请使用RunnableGenerator

6.总结 LCEL

LCEL,全称为LangChain Expression Language,是一种专为 LangChain 框架设计的表达语言。它通过一种链式组合的方式,允许开发者使用清晰、声明式的语法来构建语言模型驱动的应用流程。 简单来说,LCEL 是一种“函数式管道风格”的组件组合机制,用于连接各种可执行单元(Runnable)。这些单元包括提示模板、语言模型、输出解析器、工具函数等。

LCEL 中的一切都是RunnableRunnable是一个接口(或抽象基类),代表任何可以被调用(.invoke())的东西。

常见Runnable类型:

- `ChatModel` (如 `ChatOpenAI`, `ChatOllama`) - `PromptTemplate` - `Parser` (如 `StrOutputParser`, `StructuredOutputParser`) - `Tool` - `Retriever` - `RunnableLambda` (包装自定义函数) - `RunnablePassthrough` (传递输入) - `RunnableParallel` (并行执行) - `RunnableSequence` (序列执行,由 `|` 创建)

组合操作符:|(管道):

  • |是 LCEL 中连接组件的主要操作符,代表顺序执行(Sequence)。
  • component1 | component2 | component3创建一个RunnableSequence
  • 数据按顺序流经组件:input -> component1 -> output1 -> component2 -> output2 -> component3 -> final_output

简单来说,LCEL 就是用|把各种Runnable(模型、提示词、解析器等)串起来,形成一个数据处理管道,这个管道本身也是一个Runnable,可以接收输入并产生输出。 它强调的是组件的独立性和组合的灵活性。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询