Appearance
LangChain初入门-简化LLM开发难度
章节简介
本章核心围绕LangChain框架展开,从框架认知、核心组件使用、项目集成,到LCEL表达式、回调系统、LangSmith平台监控,逐步讲解如何通过LangChain简化LLM应用开发难度,同时涵盖Git版本控制、API文档撰写规范等工程化开发内容,实现从基础使用到项目落地的全流程学习。
一、为什么选择LangChain及框架简介
1.1 为什么需要大模型应用开发框架
原生LLM仅提供基础调用接口,构建复杂AI应用时需解决诸多工程问题:
- 聊天上下文保存、网络检索、本地数据加载、Prompt管理;
- 不同LLM的输入/输出结构差异大,切换模型需修改大量代码,可维护性差;
- 原生LLM无运行流程监控、输出统计、费用统计、错误监控等功能,需开发者自行开发。
而LangChain作为AI应用开发框架,可一站式解决上述问题,同时提供Python/JavaScript两个版本,适配主流开发语言。
- LangChain-Python:https://github.com/langchain-ai/langchain
- LangChain-JavaScript:https://github.com/langchain-ai/langchainjs
1.2 选择LangChain的核心原因
- 高认可度:Github获86.3k+ star,是星数最多的AI应用开发框架,社区活跃、文档丰富;
- 持续维护:累计融资数千万美金,团队一年提交近10000次commit,版本更新快,被Dify、MetaGPT、百度智能云千帆等知名项目采用;
- 易用性:屏蔽不同LLM的调用/输入/输出差异,切换模型无需修改业务代码;
- 高可扩展性:集成近700种工具,涵盖LLM、向量存储、Agent等,支持本地大模型/工具/数据库快速接入;
- 稳定版本:2024年1月发布0.1.0稳定版,完全向后兼容,推出LangGraph、LangSmith;
- 可观测性:配套LangSmith平台,提供一流的调试、监控、评估能力,记录每一步执行数据。
LangChain适配多模型示例:切换OpenAI和文心一言仅需替换模型实例,业务代码无改动
python
from langchain_community.chat_models.baidu_qianfan_endpoint import QianfanChatEndpoint
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
def completion():
req = CompletionReq()
if not req.validate():
return validate_error_json(req.errors)
# 切换模型仅修改这一行
llm = ChatOpenAI()
# llm = QianfanChatEndpoint()
chain = llm | StrOutputParser()
content = chain.invoke(req.query.data)
return success_json({"content": content})1.3 LangChain基本概念
LangChain如同数字世界的乐高工具箱,将提示词、工具、大语言模型作为“积木块”,帮助开发者快速搭建LLM应用。其核心包含6大组件,集成数十种LLM、上百种工具,实现大模型应用的快速开发与部署:
- Models(模型):封装各类LLM/聊天模型,提供统一调用接口;
- Prompts(提示):提示词模板、选择器,实现Prompt的标准化与复用;
- Indexes(索引):文档加载、文本分割、向量存储,支撑知识库检索;
- Memory(记忆):聊天上下文管理,实现会话记忆功能;
- Chains(链):将多个组件按逻辑组合,实现复杂业务流程;
- Agents(代理):结合工具与LLM,实现自主决策、工具调用。
二、LangChain框架安装及文档介绍
2.1 框架安装及组成
2.1.1 安装命令
bash
# 安装最新版核心包+社区包
pip install langchain langchain-community
# 安装课程指定版本(0.2.1)
pip install langchain==0.2.1 langchain-community==0.2.1
# 升级旧版本
pip install -U langchain==0.2.1 langchain-community==0.2.12.1.2 LangChain v0.2.1核心组成
| 包名 | 核心功能 |
|---|---|
| langchain-core | 基础抽象、LangChain表达式语言(LCEL) |
| langchain-community | 第三方集成、合作伙伴包(如langchain-openai) |
| langchain | 链、代理、检索策略的认知架构构建 |
| langgraph | 基于图结构构建有状态的多参与者LLM应用 |
| langserve | 将LangChain链部署为REST API |
| langsmith | LLM应用调试、测试、评估、监控平台 |
2.2 文档重构与解读(Diátaxis哲学)
LangChain v0.2.0后基于Diátaxis(https://diataxis.fr/)重构文档,将内容划分为4类,LangGraph、LangSmith也采用此规范,提升文档可读性:
- Tutorials(教程):从零到一的实操案例,结合概念与实践(如构建本地RAG应用);
- How-to Guides(操作指南):解决具体单点问题(如实现应用流式输出);
- Explanation(解释):深入讲解概念/原理(如LCEL基础类型、框架架构);
- Reference(参考):技术性详细描述(如Runnable接口、类/参数说明)。
2.3 版本文档与辅助工具
- 版本变化说明:包含新版本功能、未来更新路线、版本对比;
- v0.2升级指南:提供旧版本迁移步骤、包导入方式修改、配置更新等;
- Chat LangChain:官方文档问答机器人,可快速查询组件使用方法,支持多模型适配。
三、LangChain核心组件及使用技巧
3.1 Prompt组件
Prompt是LLM应用交互的起点,LangChain封装Prompt组件实现标准化、可复用、跨模型适配,切换LLM无需修改Prompt。
3.1.1 核心分类
- Prompt Template:主流使用,对Prompt进行格式化、变量处理、提示词组合,包含多个子组件;
- Selectors:根据条件选择不同Prompt/示例,使用场景较窄,基于Prompt Template二次封装。
3.1.2 Prompt Template核心子组件
| 组件 | 功能 |
|---|---|
| PromptTemplate | 文本消息提示模板,适配纯文本输出的LLM |
| ChatPromptTemplate | 聊天消息提示模板,适配聊天模型(如GPT-3.5/4) |
| MessagePlaceholder | 聊天模型的消息占位符,处理不确定的上下文消息 |
| SystemMessagePromptTemplate | 系统角色提示模板 |
| HumanMessagePromptTemplate | 人类角色提示模板 |
| AIMessagePromptTemplate | AI角色提示模板 |
| PipelinePromptTemplate | 管道提示模板,实现Prompt的复用与组合 |
3.1.3 核心方法与运算符
- 方法:
partial()(格式化部分变量)、format()(传递变量生成文本)、invoke()(传递变量生成提示值)、to_string()/to_messages()(格式转换); - 重载运算符:
+,支持所有Prompt组件的拼接组装。
3.1.4 格式化方式
- f-string(默认):Python3.6+特性,语法简洁、性能好,仅适用于py文件,用
{variable_name}定义变量; - Jinja2:支持循环/条件语句、自定义过滤器,需安装依赖,用
定义变量。
3.1.5 常用示例
(1)基础文本Prompt
python
from langchain_core.prompts import PromptTemplate
prompt = PromptTemplate.from_template("请讲一个关于{subject}的冷笑话")
print(prompt.format(subject="程序员")) # 请讲一个关于程序员的冷笑话(2)聊天Prompt(含上下文、部分变量)
python
from datetime import datetime
from langchain_core.prompts import ChatPromptTemplate, HumanMessagePromptTemplate, MessagesPlaceholder
from langchain_core.messages import AIMessage
chat_prompt = ChatPromptTemplate.from_messages([
("system", "你是OpenAI开发的聊天机器人,当前时间是:{now}"),
MessagesPlaceholder("chat_history"),
HumanMessagePromptTemplate.from_template("请讲一个关于{subject}的冷笑话")
]).partial(now=datetime.now())
prompt_value = chat_prompt.invoke({
"subject": "程序员",
"chat_history": [("human", "我叫慕小课"), AIMessage("我是ChatGPT,有什么能帮到你的么")]
})
print(prompt_value.to_string())(3)Prompt拼接与复用
python
# 字符串拼接
prompt = PromptTemplate.from_template("请讲一个关于{subject}的冷笑话") + ",让我开心下\n使用{language}语言。"
print(prompt.format(subject="程序员", language="中文"))
# 管道复用
from langchain_core.prompts import PipelinePromptTemplate
full_template = "{instruction}\n{example}\n{start}"
full_prompt = PromptTemplate.from_template(full_template)
instruction_prompt = PromptTemplate.from_template("你正在模拟{person}。")
example_prompt = PromptTemplate.from_template("示例:Q:{example_q}\nA:{example_a}")
start_prompt = PromptTemplate.from_template("Q:{input}\nA:")
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="你最喜欢的手机是什么"))3.2 Model组件
Model是LangChain的核心组件,不提供自研LLM,而是封装各类第三方LLM/聊天模型,提供统一调用接口,屏蔽模型差异。
3.2.1 核心分类
- LLM:纯文本输入/输出的大语言模型;
- Chat Model:聊天消息列表输入/输出的模型(如GPT-3.5-turbo、文心一言)。
LangChain内部会自动将PromptValue/字符串/消息列表转换为模型适配的输入格式,开发者无需关注细节。
3.2.2 核心调用方法
| 方法 | 功能 |
|---|---|
| invoke() | 单次调用,生成单条结果 |
| batch() | 批量调用,一次性生成多个结果 |
| stream() | 流式输出,逐字符返回结果 |
| ainvoke()/abatch()/astream() | 上述方法的异步版本 |
3.2.3 Message组件
封装聊天消息,所有消息包含type(类型)、content(内容)、response_metadata(响应元数据),共5种类型: SystemMessage(系统)、HumanMessage(人类)、AIMessage(AI)、FunctionMessage(函数)、ToolMessage(工具)。
3.2.4 常用示例
python
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)
print(ai_message.content)
# 4. 批量调用
batch_prompt = ChatPromptTemplate.from_template("讲一个关于{subject}的冷笑话")
ai_messages = llm.batch([
batch_prompt.invoke({"subject": "程序员"}),
batch_prompt.invoke({"subject": "Python"})
])
for msg in ai_messages:
print(msg.content)
# 5. 流式输出
stream_prompt = ChatPromptTemplate.from_template("介绍下{subject}")
response = llm.stream(stream_prompt.invoke({"subject": "LLMOps"}))
for chunk in response:
print(chunk.content, end="", flush=True)3.3 OutputParser组件
解决LLM输出非结构化问题,将模型的字符串输出转换为结构化数据,核心逻辑:输出解析器 = 预设提示 + 解析功能。
3.3.1 解析场景
- 非结构化文本转结构化数据(JSON/字典/Pydantic模型);
- 截取部分文本,仅保留核心结果;
- 自定义处理,按约定格式解析输出。
3.3.2 核心抽象方法
自定义解析器需实现以下两个方法:
get_format_instructions():约定输出格式,转换为描述文本并嵌入Prompt;parse():解析LLM输出为约定格式。
实际使用中直接调用invoke(),底层会自动执行parse()。
3.3.3 常用解析器
| 解析器 | 功能 |
|---|---|
| StrOutputParser | 字符串输出解析器,原样返回文本,使用频率最高 |
| JsonOutputParser | 转JSON/Python字典,需配合Pydantic BaseModel定义结构 |
| SimpleJsonOutputParser | 简易JSON解析器,无需定义Pydantic模型 |
| PydanticOutputParser | 转Pydantic模型,强类型校验 |
| CommaSeparatedListOutputParser | 逗号分隔的列表解析器 |
| MarkdownListOutputParser | Markdown列表解析器 |
3.3.4 常用示例
(1)StrOutputParser(基础)
python
from langchain_core.output_parsers import StrOutputParser
parser = StrOutputParser()
print(parser.parse("程序员的梦工厂")) # 程序员的梦工厂(2)JsonOutputParser(结构化)
python
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.pydantic_v1 import BaseModel, Field
# 定义Pydantic模型(JSON结构)
class Joke(BaseModel):
joke: str = Field(description="冷笑话内容")
punchline: str = Field(description="冷笑话笑点")
# 初始化解析器
parser = JsonOutputParser(pydantic_object=Joke)
# 将格式说明嵌入Prompt
prompt = ChatPromptTemplate.from_template("回答用户问题\n{format_instructions}\n{query}")
prompt = prompt.partial(format_instructions=parser.get_format_instructions())
# 调用模型并解析
prompt_value = prompt.invoke({"query": "讲一个程序员的冷笑话"})
ai_message = llm.invoke(prompt_value)
joke_json = parser.invoke(ai_message)
print(joke_json) # {'joke': '为什么程序员总冷静?', 'punchline': '因为有一堆bug在身边'}3.3.5 替代方案
若模型支持Function Calling/Tool Calling,可直接定义函数并强制模型调用,无需解析器,实现精准结构化输出。
四、LCEL表达式与Runnable可运行协议
4.1 传统invoke嵌套的缺点
直接嵌套各组件的invoke()方法构建应用,存在以下问题:
- 代码可读性、维护性差,修改组件困难;
- 无法监控每一步执行结果,出错难以排查;
- 组件增多时代码变成“一次性代码”,无法扩展。
4.2 LCEL与Runnable协议简介
LangChain Expression Language (LCEL) 是基于Runnable可运行协议的表达式语言,是LangChain的核心设计,解决组件嵌套问题。
4.2.1 Runnable协议核心能力
所有LangChain组件均实现Runnable协议,提供统一的标准接口和运算符重载,支持:
- 基础方法:
invoke()/batch()/stream()(及异步版本)、astream_log()(流式返回中间步骤); - 运算符重载:
|(管道符),支持组件拼接为链; - 辅助方法:
pipe(),等价于|,实现组件拼接。
4.2.2 组件输入/输出类型约定
各组件的输入为上一个组件的输出,形成流水线式执行,核心组件输入输出类型:
| 组件 | 输入类型 | 输出类型 |
|---|---|---|
| Prompt | 字典 | PromptValue |
| ChatModel | 字符串/消息列表/PromptValue | ChatMessage |
| LLM | 字符串/消息列表/PromptValue | 字符串 |
| OutputParser | LLM/ChatModel输出 | 自定义结构化数据 |
| Retriever | 字符串 | 文档列表 |
| Tool | 字符串/字典 | 自定义结果 |
4.2.3 基础链构建(LCEL核心用法)
通过|拼接组件,替代嵌套调用,代码简洁、可扩展:
python
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
# 构建链:Prompt → ChatModel → StrOutputParser
chain = ChatPromptTemplate.from_template("{query}") | ChatOpenAI() | StrOutputParser()
# 调用链
result = chain.invoke({"query": "你好,你是?"})
print(result)4.3 两个核心Runnable类
4.3.1 RunnableParallel(并行运行)
功能:并行执行多个Runnable/函数,以字典形式返回结果,可用于:
- 并行执行多条链,提升效率;
- 格式化上游数据,匹配下游组件的输入要求。
示例1:并行执行多条链
python
from langchain_core.runnables import RunnableParallel
# 构建两条独立链
joke_chain = ChatPromptTemplate.from_template("讲一个{subject}的冷笑话") | ChatOpenAI() | StrOutputParser()
poem_chain = ChatPromptTemplate.from_template("写一首{subject}的小诗") | ChatOpenAI() | StrOutputParser()
# 并行运行
parallel_chain = RunnableParallel(joke=joke_chain, poem=poem_chain)
result = parallel_chain.invoke({"subject": "程序员"})
print(result) # {'joke': 'xxx', 'poem': 'xxx'}示例2:格式化数据(检索+Prompt)
python
def retrieval(query):
"""模拟检索器"""
return "我叫慕小课,AI开发工程师"
# 并行获取query和检索结果,传递给Prompt
chain = RunnableParallel(
context=retrieval,
query=lambda x: x
) | ChatPromptTemplate.from_template("根据上下文{context}回答问题:{query}") | ChatOpenAI() | StrOutputParser()
result = chain.invoke("你好,我叫什么?")
print(result) # 你好,你叫慕小课4.3.2 RunnablePassthrough(数据透传)
功能:透传上游输入数据,可保持数据不变或新增键,常与RunnableParallel配合使用,核心用法:
RunnablePassthrough():透传整个输入(字符串/字典);RunnablePassthrough.assign():透传数据并新增键值对;- 结合
operator.itemgetter:透传字典中的指定字段。
示例1:基础透传
python
from langchain_core.runnables import RunnablePassthrough
from operator import itemgetter
# 方式1:透传整个输入
chain = RunnablePassthrough() | ChatPromptTemplate.from_template("{query}") | ChatOpenAI() | StrOutputParser()
result = chain.invoke({"query": "你好"})
# 方式2:透传字典指定字段
chain = itemgetter("query") | ChatPromptTemplate.from_template("{query}") | ChatOpenAI() | StrOutputParser()
result = chain.invoke({"query": "你好"})示例2:透传并新增字段
python
# 透传query,并新增context字段(检索结果)
chain = RunnablePassthrough.assign(context=retrieval) | ChatPromptTemplate.from_template("根据{context}回答{query}") | ChatOpenAI() | StrOutputParser()
result = chain.invoke("我叫什么?")五、Callback回调系统与LangSmith平台
5.1 Callback回调系统
5.1.1 核心功能
Callback是LangChain的钩子机制,在LLM应用的关键执行阶段截获事件,实现:
- 运行流程监控、日志记录;
- 流式传输、错误追踪;
- 统计模型调用次数、Token使用、执行耗时;
- 自定义业务逻辑(如告警、数据存储)。
5.1.2 核心组成
- CallbackHandler:对特定场景(Chain/LLM/Tool/Agent)的事件进行记录,实现具体的钩子方法;
- CallbackManager:管理多个CallbackHandler,统一封装、触发钩子事件。
5.1.3 核心钩子方法(事件触发)
| 事件 | 触发时机 | 钩子方法 |
|---|---|---|
| LLM/ChatModel启动/结束/错误 | 模型调用阶段 | on_llm_start/on_llm_end/on_llm_error |
| Chain启动/结束/错误 | 链执行阶段 | on_chain_start/on_chain_end/on_chain_error |
| Tool启动/结束/错误 | 工具调用阶段 | on_tool_start/on_tool_end/on_tool_error |
| Agent动作/结束 | 代理执行阶段 | on_agent_action/on_agent_finish |
| 模型生成新Token | 流式输出阶段 | on_llm_new_token |
5.1.4 内置CallbackHandler
- StdOutCallbackHandler:将日志输出到控制台,用于调试;
- FileCallbackHandler:将日志输出到本地文件,用于持久化。
5.1.5 使用方式(推荐前两种)
- 调用
invoke/stream时通过config传递:chain.invoke(input, config={"callbacks": [StdOutCallbackHandler()]}); - 链上调用
with_config方法:chain.with_config({"callbacks": [StdOutCallbackHandler()]}); - 构建模型时传递
callbacks参数(不推荐,耦合度高)。
5.1.6 自定义CallbackHandler
继承BaseCallbackHandler,实现所需的钩子方法即可:
python
from langchain_core.callbacks import BaseCallbackHandler
import uuid
from typing import Dict, List, Any
class LLMOpsCallbackHandler(BaseCallbackHandler):
"""自定义回调处理器"""
def on_llm_start(
self,
serialized: Dict[str, Any],
prompts: List[str],
run_id: uuid.UUID,
**kwargs: Any
) -> Any:
print(f"LLM启动,请求参数:{serialized},提示词:{prompts}")
def on_llm_new_token(
self,
token: str,
**kwargs: Any
) -> Any:
print(f"生成新Token:{token}")5.2 LangSmith平台
5.2.1 核心定位
LangSmith是LangChain官方的LLM应用开发平台,从原型到生产全流程支撑,与LangChain无缝衔接,解决LLM应用可观测、可调试、可评估的问题。
5.2.2 核心功能
- 全流程监控:记录Chain/LLM/Tool的每一步执行数据、输入输出、耗时、Token使用;
- 调试能力:可视化执行流程,定位出错步骤,支持重跑测试;
- 评估能力:自定义评估指标,对LLM应用的效果进行量化评估;
- Prompt管理:集中管理Prompt,支持版本控制、A/B测试;
- LangChain Hub:共享/复用Chain、Prompt、模型配置;
- Playground:可视化调试Chain/Prompt,快速验证效果。
5.2.3 项目集成
注册LangSmith账号并获取密钥,配置到项目环境变量中,结合Callback系统即可实现应用的全流程监控,快速定位聊天机器人的执行问题、优化性能。
六、项目集成与工程化开发
6.1 项目接入LangChain
- 将原有LLM对话接口改写为LangChain Chain形式,利用LCEL实现组件解耦;
- 遵循项目API文档规范,统一接口输入输出;
- 集成Callback系统,实现本地日志记录;
- 配置LangSmith,实现应用的远程监控、调试、评估。
6.2 Git版本控制
- 仓库配置:初始化本地Git仓库,关联远程仓库(Gitee/GitHub);
- 版本约定:每周课程内容对应一个Git版本,做好版本标签;
- 操作技巧:掌握不同迭代模式(分支开发、合并)、不同IDE的Git操作、提交/拉取/推送/回滚等核心命令;
- 团队规范:约定分支命名、提交信息规范,避免代码冲突。
6.3 API接口文档规范
6.3.1 接口文档核心要素
一份标准的API文档包含:接口描述、接口地址、请求方法、请求参数、响应内容、错误代码、请求/响应示例。
6.3.2 接口规范选择
- RESTful API:以“资源”为核心,结合HTTP方法(GET/POST/PUT/DELETE)实现增删改查,适用于Web应用、对外开放API、单体服务;
- RPC:以“动作”为核心,像调用本地函数一样调用远程接口,适用于微服务架构。
6.3.3 LLMOps项目API规范
- 模块划分:按功能模块组织接口(授权认证、AI应用、工作流、知识库);
- 路径约定:资源有多种操作时路径为复数,否则为单数;
- 授权校验:需授权的接口在Header中添加
Authorization: Bearer access_token; - 响应状态:统一6种业务状态码:
success/fail/not_found/unauthorized/forbidden/validate_error; - 响应字段:固定包含
code(业务状态码)、data(业务数据)、message(附加信息)。
七、本章核心总结
- 框架价值:LangChain解决原生LLM开发的工程化问题,实现模型适配、组件复用、流程标准化,大幅降低LLM应用开发难度;
- 核心组件:掌握Prompt/Model/OutputParser三大核心组件的使用,实现Prompt标准化、模型统一调用、输出结构化;
- 核心语法:熟练使用LCEL表达式(Runnable协议),通过
|拼接组件构建链,替代传统嵌套调用,提升代码可维护性和扩展性; - 可观测性:通过Callback回调系统实现本地日志监控,结合LangSmith平台实现LLM应用的全流程调试、监控、评估;
- 工程化落地:掌握LangChain项目集成、Git版本控制、API文档撰写规范,实现从基础组件使用到企业级项目开发的落地。