LangChain 调大模型:模板拼接 + invoke / stream / batch
读完你会什么
跟着本文走一遍,你能掌握 LangChain 调大模型的完整链路:
- 拼消息 --- 用
ChatPromptTemplate模板拼接(本文重点,生产最常用) - 调模型 --- 用
invoke/stream/batch三种方式发出请求(日常必会前两个)
文末附有完整可运行脚本,复制即可跑通全文示例。
记忆口诀 :先
from_messages建模板 →format_messages填变量 →llm.invoke(messages)调模型。要做聊天 UI 就把invoke换成stream。
一、模板拼接(重点掌握)
为什么要用模板
直接手写 SystemMessage + HumanMessage 能跑,但问题一多、变量一多,字符串拼接很快变得难维护。
模板的做法 :在 Prompt 里留 {变量名} 占位,运行时填值,自动生成消息列表。
python
# ❌ 手写拼接:难维护
f"你是{role},请回答:{question}"
# ✅ 模板:结构清晰、可复用
("system", "你是{role}"),
("human", "{question}"),
聊天模型用 ChatPromptTemplate (产出 SystemMessage / HumanMessage 等)。老式纯文本补全的 PromptTemplate 本文不涉及。
标准写法:from_messages + format_messages
日常只用这一套,记住 三步:
python
import os
from langchain.chat_models import init_chat_model
from langchain_core.prompts import ChatPromptTemplate
os.environ["OPENAI_API_KEY"] = os.getenv("SILICON_KEY")
os.environ["OPENAI_BASE_URL"] = os.getenv("SILICON_BASE_URL")
llm = init_chat_model("openai:deepseek-ai/DeepSeek-V3")
# ① 创建模板
prompt = ChatPromptTemplate.from_messages([
("system", "你是{role},回答要简洁"),
("human", "{question}"),
])
# ② 填值 → 消息列表
messages = prompt.format_messages(role="数学老师", question="什么是余数?")
# ③ 调模型
answer = llm.invoke(messages)
print(answer.content)
输出(示例,模型每次略有不同):
ini
余数是整数除法后剩下的部分。例如 7÷3=2......1,这里的 1 就是余数。
| 步骤 | 做什么 | 产出 |
|---|---|---|
① from_messages |
定义 Prompt 结构 | 模板对象 prompt |
② format_messages |
填入 {role}、{question} |
消息列表 messages |
③ llm.invoke |
发给大模型 | AIMessage 回复 |
元组第一个位置常用 "system" / "human" / "ai",对应系统指令、用户输入、模型示例回复。
占位符语法同 Python f-string :{变量名}。模板里要写 literal 花括号用 {{ / }}。
其他模板写法(遇到再查)
| 写法 | 适用场景 |
|---|---|
from_template("{question}") |
只有一条 human 消息 |
MessagesPlaceholder("history") |
多轮聊天,历史条数不固定 |
prompt.partial(role="...") |
角色固定,只换 {question} |
prompt.invoke({...}) 与 format_messages 效果相同,用在chain的链式调用里,后续再说;直接调模型时 用 format_messages 更直观。
二、三种调用方式(总览)
模板拼好 messages 之后,用 llm.xxx(...) 发出。LangChain 提供三种同步调用方式,第一个参数的写法规则相同,区别只在「怎么等结果」。
| 方法 | 等什么 | 返回什么 | 要不要学 |
|---|---|---|---|
invoke |
全文生成完 | 1 个 AIMessage |
⭐ 必学 |
stream |
边生成边返回 | 多个 AIMessageChunk |
⭐ 必学 |
batch |
多个问题并行跑 | N 个 AIMessage |
特定场景再学 |
| 典型场景 | 用哪个 |
|---|---|
| 脚本调试、后端 API、只要最终结果 | invoke |
| Web / App 聊天、打字机效果 | stream |
| 批量翻译 / 摘要、离线跑几百条 | batch |
同一批 messages,换方法名即可,输入不用改:
python
llm.invoke(messages) # 同步,一次拿全文
llm.stream(messages) # 流式,边生成边输出
llm.batch([messages1, messages2]) # 批量,两个独立问题
异步版本(FastAPI 等场景,了解即可)
ainvoke / astream / abatch 与上表一一对应,用于 async/await 项目,本文不展开。
第一个参数怎么传
关键区别:invoke / stream 是一维,batch 是二维。
| 方法 | 维度 | 含义 |
|---|---|---|
invoke / stream |
一维 | 一个聊天框 |
batch |
二维 | 多个聊天框,各聊各的 |
css
invoke / stream: [消息, 消息, 消息] → 1 条回复
batch: [[框1...], [框2...], ...] → N 条回复
一维示例 --- 一个聊天框,消息之间有上下文:
python
messages = [
("system", "你是数学老师"),
("human", "什么是余数?"),
("ai", "除法剩下的部分......"),
("human", "再举个例子"),
]
llm.invoke(messages) # 1 个框 → 1 条回复
二维示例 --- 多个聊天框,互不相干:
python
llm.batch([
[("system", "你是诗人"), ("human", "写首诗")],
[("system", "你是程序员"), ("human", "解释变量")],
"1+1等于几?",
])
# 3 个框 → 3 条回复
一维参数的五种写法
batch 里每个聊天框也适用以下任意一种。
| # | 写法 | 示例 | 什么时候用 |
|---|---|---|---|
| 1 | 字符串 | llm.invoke("问题") |
调试、单条提问 |
| 2 | 模板填值 | llm.invoke(messages) |
⭐ 生产默认 |
| 3 | Message 对象 | llm.invoke([SystemMessage(...), HumanMessage(...)]) |
封装层、类型要明确 |
| 4 | 元组 | llm.invoke([("system", "..."), ("human", "...")]) |
和模板风格一致 |
| 5 | 字典 | llm.invoke([{"role": "user", "content": "..."}]) |
对齐 OpenAI API 格式 |
python
# 1. 字符串 --- 自动当 HumanMessage
llm.invoke("小学生怎么学余数?")
# 2. 模板填值 --- 第一节的标准链路
messages = prompt.format_messages(role="数学老师", question="什么是余数?")
llm.invoke(messages)
# 3. Message 对象 --- 最显式
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
llm.invoke([
SystemMessage(content="你是数学老师"),
HumanMessage(content="什么是余数?"),
AIMessage(content="除法剩下的部分......"),
HumanMessage(content="再举个例子"),
])
# 4. 元组 --- 和 from_messages 写法一样
llm.invoke([("system", "你是助手"), ("human", "你好")])
# 5. 字典 --- 和 OpenAI JSON 一致
llm.invoke([{"role": "system", "content": "你是助手"}, {"role": "user", "content": "你好"}])
写法 3 / 4 / 5 本质相同,LangChain 都会转成
SystemMessage/HumanMessage/AIMessage。新手优先记 1(调试)和 2(生产)。
三、invoke --- 同步拿全文
场景:脚本、后端 API、不需要打字机效果。
python
# 最简
llm.invoke("用一句话介绍你自己").content
# 配合模板(推荐)
messages = prompt.format_messages(role="数学老师", question="什么是余数?")
answer = llm.invoke(messages)
print(answer.content)
输出:
shell
# 最简 → '我是 DeepSeek,由深度求索打造的 AI 助手......'
# 模板 → '余数是除法运算后剩下的部分,例如 7÷3=2 余 1......'
invoke = 给一个输入,同步等待,返回完整结果。
返回值 :AIMessage,取文本用 .content:
python
response = llm.invoke("你好")
response.content # 回复文本
response.response_metadata # token 用量等
四、stream --- 流式打字机
场景:Web / App 聊天窗口,边生成边展示。
第一个参数与 invoke 相同(一维、一个聊天框),区别在怎么读返回值。
python
for chunk in llm.stream("用一句话介绍你自己"):
print(chunk.content, end="", flush=True)
输出(逐字打印,不换行直到结束):
我是 DeepSeek,由深度求索公司打造的 AI 助手,乐意为你解答问题!
配合模板:
python
messages = prompt.format_messages(role="数学老师", question="什么是余数?")
for chunk in llm.stream(messages):
print(chunk.content, end="", flush=True)
输出:
ini
余数就是除法里除不尽剩下的那部分。比如 10÷3=3 余 1......
返回值 :每次迭代一个 AIMessageChunk ,同样用 .content 取文本。部分 chunk 可能为空,打印前建议判断:
python
full_text = ""
for chunk in llm.stream(messages):
if chunk.content:
full_text += chunk.content
print(chunk.content, end="", flush=True)
五、batch --- 批量多个问题
场景 :批量翻译、批量摘要、离线一次跑很多条。日常开发用得少,需要时再查本节。
第一个参数是二维 的:外层 list 的每个元素 = 一个独立聊天框 = 一次 invoke。
python
answers = llm.batch([
"1+1等于几?",
"北京是哪个国家的首都?",
"用一句话介绍 Python",
])
for i, ans in enumerate(answers):
print(f"Q{i + 1}: {ans.content}")
输出:
makefile
Q1: 1+1 等于 2。
Q2: 北京是中华人民共和国的首都。
Q3: Python 是一门简洁易读、用途广泛的编程语言。
每个框也可以是一组 messages:
python
answers = llm.batch([
[("system", "你是诗人"), ("human", "写一首短诗")],
[("system", "你是程序员"), ("human", "解释变量")],
])
配合模板 --- 每个问题各填一次值:
python
questions = ["什么是余数?", "什么是分数?", "什么是小数?"]
message_lists = [prompt.format_messages(role="数学老师", question=q) for q in questions]
answers = llm.batch(message_lists)
LangChain 默认用线程池并行 执行,返回 list[AIMessage],顺序与输入一一对应。
小结
scss
拼消息(重点) 调模型(按场景选)
───────────────── ─────────────────
ChatPromptTemplate invoke → 同步,拿全文 ⭐ 必学
.from_messages([...]) stream → 流式,打字机 ⭐ 必学
.format_messages(...) → batch → 批量,多框并行 特定场景
messages
| 方法 | 参数维度 | 返回 | 一句话 |
|---|---|---|---|
invoke |
一维(一个聊天框) | 1 个 AIMessage |
等全文,一次拿完 |
stream |
一维(一个聊天框) | 多个 AIMessageChunk |
边生成边输出 |
batch |
二维(多个聊天框) | N 个 AIMessage |
多个独立问题并行 |
新手路径 :先把第一节三步链路跑通 → 会用 invoke → 做聊天 UI 时换 stream → 遇到批处理再看 batch。
附录:完整可运行脚本
前文有怎么在系统上配置硅基流动的秘钥和baseUrl
复制保存为 demo.py,配置好环境变量后执行 python demo.py。
环境准备 (写入 ~/.zshrc 或 .env):
bash
export SILICON_KEY="sk-xxx"
export SILICON_BASE_URL="https://api.siliconflow.cn/v1"
依赖安装:
bash
pip install langchain langchain-openai
完整代码:
python
import os
import sys
from langchain.chat_models import init_chat_model
from langchain_core.prompts import ChatPromptTemplate
api_key = os.getenv("SILICON_KEY")
base_url = os.getenv("SILICON_BASE_URL", "https://api.siliconflow.cn/v1")
if not api_key:
sys.exit("缺少 SILICON_KEY,请配置环境变量")
os.environ["OPENAI_API_KEY"] = api_key
os.environ["OPENAI_BASE_URL"] = base_url
# 本机若开了系统代理,访问国内 API 时建议 trust_env=False
llm = init_chat_model(
"openai:deepseek-ai/DeepSeek-V3",
)
# ── 1. 模板拼接 + invoke ──
prompt = ChatPromptTemplate.from_messages([
("system", "你是{role},回答要简洁"),
("human", "{question}"),
])
messages = prompt.format_messages(role="数学老师", question="什么是余数?")
print("【invoke】")
answer = llm.invoke(messages)
print(answer.content)
# ── 2. stream ──
print("\n【stream】")
for chunk in llm.stream("用一句话介绍你自己"):
print(chunk.content, end="", flush=True)
print()
# ── 3. batch ──
print("\n【batch】")
answers = llm.batch([
"1+1等于几?",
"北京是哪个国家的首都?",
])
for i, ans in enumerate(answers):
print(f"Q{i + 1}: {ans.content}")
运行结果(示例):
ini
【invoke】
余数是整数除法后剩下的部分。例如 7÷3=2......1,这里的 1 就是余数。
【stream】
我是 DeepSeek,由深度求索公司打造的 AI 助手,乐意为你解答问题!
【batch】
Q1: 1+1 等于 2。
Q2: 北京是中华人民共和国的首都。
实际输出因模型随机性会略有不同,看到类似结构即表示跑通。