欢迎光临
我们一直在努力

LangChain初入门 - 简化LLM开发难度的利器

前言

随着大语言模型(LLM)的快速发展,越来越多的开发者希望将AI能力集成到自己的应用中。然而,直接调用LLM API面临着诸多工程挑战:如何保存聊天上下文?如何进行网络检索?如何加载本地数据?如何便捷管理Prompt?更重要的是,当需要切换不同的LLM时,模型的输入输出结构差异巨大,微小的需求变更可能导致大量代码修改。

LangChain应运而生,作为当前最热门的AI应用开发框架,它提供了一站式解决方案,让开发者能够像搭积木一样快速构建LLM应用。本文将从零开始,带你深入理解LangChain的核心组件与最佳实践。

一、为什么选择LangChain

1.1 为什么需要大模型应用框架

大模型一般有两种形态呈现给我们:一种是训练好的二进制文件,另一种是部署后暴露的接口。无论哪种形式,LLM只提供了非常基础的调用方式。当我们要构建复杂的Chat Bot时,需要考虑大量工程问题。

传统开发模式的痛点:

  • 上下文管理复杂:需要自行处理聊天历史存储
  • 模型切换成本高:不同LLM的接口差异巨大
  • 缺乏可观测性:难以监控运行流程、Token消耗、错误追踪
  • 功能重复开发:网络检索、本地数据加载等功能需要反复实现
  • 以OpenAI和文心一言为例,两者的SDK调用方式截然不同:

    # OpenAI调用方式
    from openai import OpenAI
    client = OpenAI()
    completion = client.chat.completions.create(
    model="gpt-3.5-turbo",
    messages=[{"role": "user", "content": "你好"}]
    )
    content = completion.choices[0].message.content

    # 文心一言调用方式
    import qianfan
    client = qianfan.ChatCompletion()
    resp = client.do(messages=[{"role": "user", "content": "你好"}])
    content = resp["body"]

    1.2 LangChain的核心优势

    优势维度说明
    认可度 GitHub 86.3k+ star,最热门的AI应用开发框架
    持续维护 累计融资数千万美金,10000+次commit更新
    易用性 屏蔽不同LLM的接口差异,统一调用方式
    可扩展性 近700种集成,涵盖LLM、向量存储、工具等
    可观测性 提供LangSmith平台,实现全链路监控
    稳定性 2024年1月发布0.1.0稳定版本

    使用LangChain后的代码对比:

    from langchain_openai import ChatOpenAI
    from langchain_community.chat_models.baidu_qianfan_endpoint import QianfanChatEndpoint
    from langchain_core.output_parsers import StrOutputParser

    # 切换LLM只需修改一行代码
    llm = ChatOpenAI() # OpenAI
    # llm = QianfanChatEndpoint() # 文心一言

    chain = llm | StrOutputParser()
    content = chain.invoke("你好")

    二、LangChain核心概念

    2.1 六大核心组件

    LangChain提供了6大核心组件,帮助开发者更好地使用大语言模型:

    ┌─────────────────────────────────────────────────────────┐
    │ LangChain 核心组件 │
    ├─────────────┬─────────────┬─────────────┬───────────────┤
    │ Models │ Prompts │ Indexes │ Memory │
    │ 模型组件 │ 提示组件 │ 索引组件 │ 记忆组件 │
    ├─────────────┼─────────────┼─────────────┼───────────────┤
    │ Chains │ Agents │ │ │
    │ 链组件 │ 代理组件 │ │ │
    └─────────────┴─────────────┴─────────────┴───────────────┘

    • Models(模型):封装不同类型的LLM,包括文本生成模型和聊天模型
    • Prompts(提示):管理和优化提示词,支持模板化和动态变量
    • Indexes(索引):处理文档索引和检索
    • Memory(记忆):管理对话历史和状态
    • Chains(链):组合多个组件形成处理流程
    • Agents(代理):让LLM自主决策使用哪些工具

    2.2 框架安装

    # 安装LangChain核心包
    pip install langchain langchain-community

    # 指定版本安装(课程使用0.2.1版本)
    pip install langchain==0.2.1 langchain-community==0.2.1

    安装后会自动包含以下扩展包:

    • langchain-core:基础抽象和表达式语言
    • langchain-community:第三方集成
    • langchain-text-splitters:文本分割器
    • langsmith:调试、测试、监控平台

    三、Prompt组件详解

    3.1 Prompt组件的基本组成

    Prompt是所有AI应用交互的起点。LangChain封装了高可移植性的Prompt组件,同一个Prompt可以支持各种LLM,切换模型时无需修改Prompt。

    Prompt组件分类:

    组件类型说明
    PromptTemplate 文本消息提示模板
    ChatPromptTemplate 聊天消息提示模板
    SystemMessagePromptTemplate 系统消息模板
    HumanMessagePromptTemplate 用户消息模板
    AIMessagePromptTemplate AI消息模板
    MessagesPlaceholder 消息占位符

    3.2 基础用法示例

    from datetime import datetime
    from langchain_core.prompts import (
    PromptTemplate,
    ChatPromptTemplate,
    HumanMessagePromptTemplate,
    MessagesPlaceholder,
    )
    from langchain_core.messages import AIMessage

    # 文本提示模板
    prompt = PromptTemplate.from_template("请讲一个关于{subject}的冷笑话")
    prompt_str = prompt.format(subject="程序员")
    print(prompt_str) # 输出: 请讲一个关于程序员的冷笑话

    # 聊天提示模板
    chat_prompt = ChatPromptTemplate.from_messages([
    ("system", "你是OpenAI开发的聊天机器人,当前时间:{now}"),
    MessagesPlaceholder("chat_history"),
    HumanMessagePromptTemplate.from_template("请讲一个关于{subject}的冷笑话"),
    ]).partial(now=datetime.now())

    # 调用模板
    chat_prompt_value = chat_prompt.invoke({
    "subject": "程序员",
    "chat_history": [
    ("human", "我叫慕小课"),
    AIMessage("我是ChatGPT, 有什么能帮到你的么?")
    ],
    })
    print(chat_prompt_value.to_string())

    3.3 字符串提示拼接

    LangChain支持使用+运算符拼接提示模板:

    from langchain_core.prompts import PromptTemplate

    prompt = (
    PromptTemplate.from_template("请讲一个关于{subject}的冷笑话")
    + ",让我开心下"
    + "\\n使用{language}语言。"
    )
    print(prompt.format(subject="程序员", language="中文"))

    3.4 复用提示模板

    使用PipelinePromptTemplate可以实现提示模板的复用:

    from langchain_core.prompts import PipelinePromptTemplate, PromptTemplate

    # 完整模板
    full_template = """{instruction}
    {example}
    {start}"""

    full_prompt = PromptTemplate.from_template(full_template)

    # 子模板
    instruction_prompt = PromptTemplate.from_template("你正在模拟{person}。")
    example_prompt = PromptTemplate.from_template("""
    下面是一个交互例子:
    Q: {example_q}
    A: {example_a}"""
    )
    start_prompt = PromptTemplate.from_template("""
    现在,你是一个真实的人,请回答用户的问题!
    Q: {input}
    A:"""
    )

    # 组装管道提示
    pipeline_prompt = PipelinePromptTemplate(
    final_prompt=full_prompt,
    pipeline_prompts=[
    ("instruction", instruction_prompt),
    ("example", example_prompt),
    ("start", start_prompt),
    ]
    )

    print(pipeline_prompt.format(
    person="雷军",
    example_q="你最喜欢的汽车是什么?",
    example_a="小米su7",
    input="你最喜欢的手机是什么?"
    ))

    四、Model组件详解

    4.1 Model组件概述

    LangChain为两种类型的模型提供接口:

    • LLM:使用纯文本作为输入输出的大语言模型
    • Chat Model:使用聊天消息列表作为输入输出的聊天模型

    核心调用方法:

    方法说明
    invoke 单次调用,生成内容
    batch 批量调用,一次生成多个内容
    stream 流式输出,实时返回生成内容

    4.2 基础使用示例

    import dotenv
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_openai import ChatOpenAI

    dotenv.load_dotenv()

    # 1. 编排Prompt
    prompt = ChatPromptTemplate.from_messages([
    ("system", "你是OpenAI开发的聊天机器人,现在的时间是{now}"),
    ("human", "{query}"),
    ]).partial(now=datetime.now())

    # 2. 创建大语言模型
    llm = ChatOpenAI(model="gpt-3.5-turbo-16k")

    # 3. 生成内容
    prompt_value = prompt.invoke({"query": "请讲一个关于程序员的冷笑话"})
    ai_message = llm.invoke(prompt_value)

    # 4. 提取内容
    print("type:", ai_message.type)
    print("content:", ai_message.content)
    print("response_metadata:", ai_message.response_metadata)

    4.3 批处理与流式输出

    # 批处理示例
    prompt = ChatPromptTemplate.from_template("请讲一个关于{subject}的冷笑话")
    llm = ChatOpenAI(model="gpt-3.5-turbo-16k")

    ai_messages = llm.batch([
    prompt.invoke({"subject": "程序员"}),
    prompt.invoke({"subject": "Python"}),
    ])

    for ai_message in ai_messages:
    print(ai_message.content)

    # 流式输出示例
    response = llm.stream(prompt.invoke({"subject": "LLM和LLMOps"}))
    for chunk in response:
    print(chunk.content, flush=True, end="")

    五、OutputParser组件详解

    5.1 为什么需要输出解析器

    LLM的输出通常是字符串,但在实际应用中,我们往往需要结构化的数据:

    llm = ChatOpenAI()
    llm.invoke("告诉我3个动物的名字。")
    # 输出: 好的,这里有三种动物的名字:\\n\\n1. 狮子\\n2. 大熊猫\\n3. 斑马

    输出解析器可以将非结构化文本转换为结构化数据:

    输出解析器 = 预设提示 + 解析功能

    5.2 StrOutputParser

    最简单的输出解析器,将内容原样返回:

    from langchain_core.output_parsers import StrOutputParser

    parser = StrOutputParser()
    result = parser.parse("程序员的梦工厂") # 输出: 程序员的梦工厂

    5.3 JsonOutputParser

    将LLM输出转换为JSON格式:

    from langchain_core.output_parsers import JsonOutputParser
    from langchain_core.pydantic_v1 import BaseModel, Field

    # 定义输出结构
    class Joke(BaseModel):
    joke: str = Field(description="回答用户的冷笑话")
    punchline: str = Field(description="冷笑话的笑点")

    # 创建解析器
    parser = JsonOutputParser(pydantic_object=Joke)
    prompt = ChatPromptTemplate.from_template(
    "回答用户的问题。\\n{format_instructions}\\n{query}\\n"
    ).partial(format_instructions=parser.get_format_instructions())

    # 构建链
    llm = ChatOpenAI(model="gpt-3.5-turbo-16k")
    chain = prompt | llm | parser

    # 调用
    result = chain.invoke({"query": "请讲一个关于程序员的冷笑话"})
    # 输出: {'joke': '为什么程序员总是冷静的?', 'punchline': '因为他们总是有一堆bug在身边。'}

    六、LCEL表达式与Runnable协议

    6.1 传统嵌套写法的问题

    # 嵌套式写法(不推荐)
    content = parser.invoke(
    llm.invoke(
    prompt.invoke({"query": req.query.data})
    )
    )

    问题:

  • 可读性差,维护困难
  • 无法得知每步执行进度
  • 组件增多时代码变成"一次性"代码
  • 6.2 Runnable协议

    LangChain实现了Runnable协议,所有组件支持统一的调用方式:

    方法说明
    invoke 单次调用
    batch 批量调用
    stream 流式输出
    ainvoke 异步单次调用
    abatch 异步批量调用
    astream 异步流式输出

    6.3 使用管道操作符

    from langchain_core.output_parsers import StrOutputParser
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_openai import ChatOpenAI

    prompt = ChatPromptTemplate.from_template("{query}")
    llm = ChatOpenAI(model="gpt-3.5-turbo-16k")
    parser = StrOutputParser()

    # 使用 | 操作符连接组件
    chain = prompt | llm | parser

    # 等价于
    composed_chain = (
    RunnableParallel({"query": RunnablePassthrough()})
    .pipe(prompt)
    .pipe(llm)
    .pipe(parser)
    )

    # 调用
    content = chain.invoke({"query": "你好,你是?"})

    6.4 RunnableParallel并行运行

    from langchain_core.runnables import RunnableParallel

    # 创建两条链
    joke_chain = joke_prompt | llm | parser
    poem_chain = poem_prompt | llm | parser

    # 并行执行
    map_chain = RunnableParallel(joke=joke_chain, poem=poem_chain)
    result = map_chain.invoke({"subject": "程序员"})
    # 输出: {'joke': '…', 'poem': '…'}

    6.5 RunnablePassthrough透传数据

    from langchain_core.runnables import RunnablePassthrough

    # 透传输入数据
    chain = {"query": RunnablePassthrough()} | prompt | llm | StrOutputParser()
    content = chain.invoke("你好,你是")

    七、Callback回调机制

    7.1 Callback功能介绍

    Callback是LangChain提供的回调机制,允许在LLM应用程序的各个阶段使用钩子(hook),对于记录日志、监控、流式传输等任务非常有用。

    主要事件:

    事件触发时机方法
    on_chat_model_start 聊天模型启动时
    on_llm_start 大语言模型启动时
    on_llm_new_token 生成新token时
    on_llm_end 模型结束时
    on_chain_start 链开始运行时
    on_chain_end 链结束时
    on_tool_start 工具开始运行时

    7.2 使用StdOutCallbackHandler

    from langchain_core.callbacks import StdOutCallbackHandler

    chain = {"query": RunnablePassthrough()} | prompt | llm | StrOutputParser()

    # 使用回调
    content = chain.stream(
    "你好,你是?",
    config={"callbacks": [StdOutCallbackHandler()]}
    )

    7.3 自定义回调处理器

    from langchain_core.callbacks import BaseCallbackHandler
    from typing import Dict, Any, List, Optional
    from uuid import UUID

    class LLMOpsCallbackHandler(BaseCallbackHandler):
    """自定义回调处理器"""

    def on_llm_start(
    self,
    serialized: Dict[str, Any],
    prompts: List[str],
    *,
    run_id: UUID,
    **kwargs: Any,
    ) > Any:
    print("LLM开始执行:", prompts)

    def on_llm_new_token(self, token: str, **kwargs) > Any:
    print("新token:", token)

    def on_llm_end(self, response, **kwargs) > Any:
    print("LLM执行完成")

    总结

    本文深入介绍了LangChain框架的核心组件与使用技巧:

  • Prompt组件:支持模板化、拼接、复用,提高提示词管理效率
  • Model组件:统一不同LLM的调用方式,支持批处理和流式输出
  • OutputParser组件:将非结构化输出转换为结构化数据
  • LCEL表达式:使用管道操作符优雅地组合组件
  • Runnable协议:提供统一的调用接口和并行执行能力
  • Callback机制:实现全链路监控和调试
  • 赞(0)
    未经允许不得转载:171主机测评 » LangChain初入门 - 简化LLM开发难度的利器
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址