LangChain
LangChain 是什么
LangChain 可以用于多种常见场景: LLM -> 大语言模型 开发者希望灵活集成、工具调用(Tool Calling)、检索增强生成(RAG)等功能
补充整理:LangChain 的本质是一层“胶水”——它不训练模型,而是把 「提示词 → 模型 → 工具 → 记忆 → 检索」这些环节抽象成统一接口, 让你可以换模型不改代码。它的价值在标准化,不在性能。
langchain 包含的包
langchain:上层封装,包含链、Agent 等“开箱即用”的组装件langchain-core:核心抽象(消息、Runnable、输出解析器),最轻最稳定langchain-openai:厂商适配包(还有-anthropic、-ollama等)
补充整理:装包时别再无脑
pip install langchain一把梭。按需装:bashpip install langchain-core langchain-openai # 用 OpenAI 系模型时 pip install langchain-community # 用第三方向量库/工具时拆包是为了让依赖变轻——你不会想在只调 OpenAI 的项目里被迫装上一堆用不到的 SDK。
核心模块划分
- Model I/O:标准化大模型输入输出,包含提示模板、模型调用和格式化输出 Format(格式化)→ Predict(预测)→ Parse(解析)
- Chains:链条,将多个组件组合成一个完整流程,方便链式调用
- Retrieval:对应 RAG,检索外部数据作为参考信息输入 LLM 辅助生成答案
- Agents:自主规划执行步骤并使用工具来完成任务
在代码中通过 os 读取环境变量
调用在线模型
调用 load_dotenv 方法加载 .env 文件
from dotenv import load_dotenv
import os
load_dotenv()
api_key = os.getenv("OPENAI_API_KEY")
print(api_key)补充整理:
.env文件必须加进.gitignore。密钥一旦提交到公开仓库, 几小时内就会被扫描机器人抓取并刷爆账单——这是新手最常犯、代价最高的错误。 建议用python-dotenv只在本地开发时加载,生产环境直接用系统环境变量。
OpenAI 官方接口调用方式
ChatCompletions API
from openai import OpenAI
import os
from dotenv import load_dotenv
load_dotenv()
client = OpenAI(
base_url=os.getenv("OPENAI_BASE_URL"),
api_key=os.getenv("OPENAI_API_KEY"),
)
completion = client.chat.completions.create(
model="gpt-4o", # 模型名称
messages=[{"role": "user", "content": "你好,翻译成意大利语"}],
)
print(completion.choices[0].message.content)补充整理:原稿这里有 3 处会直接报错——
cilent拼错(后面却用client)、两个参数之间少了逗号 (Python 会把相邻字符串隐式拼接,报难以理解的错)、message=应为messages=(复数)。
Responses API 调用示例(较新的接口)
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-4o",
input="中国国内今天发生了哪些大事",
tools=[{"type": "web_search"}], # 可以自动调用内置工具
)
print(response.output_text)补充整理:两个接口的关系——
接口 特点 适用 Chat Completions 事实标准,生态最全,所有兼容 OpenAI 的服务都支持 默认选它 Responses 官方新推,内置了会话状态、内置工具、推理项 只调 OpenAI 且想用新特性时 用 LangChain 或接国内模型(DeepSeek、通义、vLLM)时,走 Chat Completions 兼容性最好。 注意:
base_url换成国内/自建服务时,模型名也要跟着换,不能沿用 gpt-4o。
LangChain API 调用模型
- 构造 LLM 实例
- 传递 Message 对象或普通字符串,调用 LLM 实例
- 解析调用结果
方式一:init_chat_model(按厂商名动态创建)
import os
from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
load_dotenv()
def get_model_from_init():
llm = init_chat_model(
model="gpt-4o",
model_provider="openai",
base_url=os.getenv("OPENAI_BASE_URL"),
api_key=os.getenv("OPENAI_API_KEY"),
)
resp = llm.invoke("你好啊")
print(resp.content)补充整理:
init_chat_model的好处是换厂商只改model_provider字符串 ("openai"/"anthropic"/"ollama"),适合要支持多模型的场景。
方式二:直接用厂商包
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
load_dotenv()
llm = ChatOpenAI(
model="gpt-4o",
base_url=os.getenv("OPENAI_BASE_URL"),
api_key=os.getenv("OPENAI_API_KEY"),
)
res = llm.invoke("你好")
print(type(res)) # <class 'langchain_core.messages.ai.AIMessage'>
print(res.content)补充整理:原稿此处有 4 处笔误——
llm.invole→invoke;res.connect→res.content;base_key→api_key;两参数之间缺逗号。
除了传入字符串,还可以传入消息列表
import os
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage, AIMessage
def invoke_llm_with_message_list():
llm = ChatOpenAI(
model="gpt-4o",
base_url=os.getenv("OPENAI_BASE_URL"),
api_key=os.getenv("OPENAI_API_KEY"),
)
resp = llm.invoke([
SystemMessage(content="你是一个专业的数学助手"),
HumanMessage(content="你好,你是谁"),
])
print(resp.content)补充整理:
message应为messages(复数)——这是 LangChain 最常见的导入错误。三种等价的消息写法,按场景选:
写法 示例 适用 消息类 SystemMessage(content="...")推荐,类型明确、可读性好 元组 ("system", "...")最简洁,写提示模板时常用 字典 {"role": "system", "content": "..."}和 OpenAI 原生格式一致 python# 元组形式 messages = [("system", "你是一个专业的数学助手"), ("user", "你好,你是谁")] # 字典形式 messages = [ {"role": "system", "content": "你是一个专业的数学助手"}, {"role": "user", "content": "你好,你是谁"}, ]三种角色:
system定行为基调、user是用户输入、assistant是模型历史回复 (多轮对话时要把历史 assistant 消息一起传,模型才“记得”说过什么)。
异步调用
import asyncio
from langchain_openai import ChatOpenAI
async def call_llm_async():
llm = ChatOpenAI(model="gpt-4o")
response = await llm.ainvoke([("user", "什么是 LangChain")])
print(response.content)
asyncio.run(call_llm_async())补充整理:异步调用在生产环境非常实用。IO 等待期间事件循环可以去处理别的请求, 同样一台机器能扛的并发量是同步的数倍。
但要注意:异步只有在真正并发时才有意义。如果只是
await一个接一个地调, 和同步没有区别。批量并发要用asyncio.gather:pythonresults = await asyncio.gather(*[llm.ainvoke(q) for q in questions])更省事的写法是直接用
abatch。
流式调用
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o")
for chunk in llm.stream([("user", "什么是 LangChain")]):
print(chunk.content, end="")补充整理:原稿
input[("user", ...)]应为input=[...](少了等号), 且函数体没有缩进。流式不只是“打字机效果”这么简单。生产上它的意义是:
- 首字延迟(TTFT)从几秒降到几百毫秒,体验提升巨大
- 长回答不用让用户干等
- 服务端能边生成边处理(如提前做安全过滤)
调 OpenAI 时要
stream=True(LangChain 已封装好);自建 vLLM 服务同样支持。 异步流式用astream。
批量调用
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o")
responses = llm.batch([
[("user", "什么是 LangChain")],
[("user", "LangChain 的核心价值是什么")],
])
for r in responses:
print(r.content)补充整理:原稿有 3 处笔误——
ChatopenAI→ChatOpenAI、responce→response、inputs多套了一层方括号(这会让两个问题被当成 “一次对话的两条消息”,而不是“两个独立请求”,结果完全不对)。
batch的正确输入是请求列表:每个元素是一次完整的对话。 想控制并发上限(避免触发限流)用:pythonllm.batch(requests, config={"max_concurrency": 5})对海量任务,用
abatch+max_concurrency效果最好。
通过 Prompt 约束输出格式
要让模型稳定输出 JSON,用 LangChain 提供的 JsonOutputParser:
- 通过 Pydantic 定义 JSON Schema(约束)
- 用构造好的 Schema 构造
JsonOutputParser实例json_parser - 调用
json_parser.get_format_instructions(),把格式要求放进 SystemMessage - 将 SystemMessage 和 HumanMessage 一起给 LLM,结果用
json_parser.parse()解析成 Python dict
import os
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import ChatPromptTemplate
from pydantic import BaseModel, Field
class Prime(BaseModel):
prime: int = Field(description="一个素数")
count: int = Field(description="小于该素数的素数个数")
parser = JsonOutputParser(pydantic_object=Prime)
prompt = ChatPromptTemplate.from_messages([
("system", "严格按以下格式输出 JSON,不要有多余文字。\n{format_instructions}"),
("human", "{question}"),
])
llm = ChatOpenAI(model="gpt-4o")
chain = prompt | llm | parser
result = chain.invoke({
"question": "请给我一个大于 20 的素数",
"format_instructions": parser.get_format_instructions(),
})
print(result) # {'prime': 23, 'count': 9}
print(type(result)) # dict补充整理:原稿这段代码有 3 处错误且没有写完——
count : int - Field(...)的-应为=(这是语法错误);Field(discriminator=...)没有这个参数,应为description;model_name="gpt_5"的横杠应为点号(且新版推荐用model=)。 上面是补全后可直接运行的版本。几个实战经验:
- 光靠提示词约束还不够稳。生产环境优先用模型厂商的 结构化输出 / JSON Schema 模式(OpenAI 的
response_format、 各家都有的 JSON mode),LangChain 里是llm.with_structured_output(Prime)get_format_instructions()生成的说明会占不少 token, 字段多时考虑手写更精简的示例(few-shot)替代- 解析失败要兜底:模型偶尔会输出带 ```json 标记的文本, 用
try/except重试一次而不是直接崩掉prompt | llm | parser这个管道写法就是 LCEL, 它自动支持invoke/stream/batch/ainvoke,不用自己写多套
