LangChain Model I/O:与LLMs对话的桥梁(Ollama实战篇)
目录
告别繁琐的API调用和提示词拼接,LangChain Model I/O正为你构建一条标准、高效的对话管道。
LangChain的核心价值之一,就是将与大语言模型(LLM)的交互过程标准化、模块化。而Model I/O模块,正是这条标准化管道的入口和出口,它负责处理你给模型的输入,并解析模型给你的输出。无论是商业API(如GPT-4)还是本地开源模型(通过Ollama运行),Model I/O都提供了一套统一的接口,让你能“一次编码,多模型适配”。
今天,我们就以风靡的本地模型工具Ollama及其搭载的开源模型(如qwen3:8b和gemma3:4b)为例,手把手带你搭建这条“对话高速公路”。
一、核心桥梁:Model I/O 三大组件全景
在深入代码前,我们先通过一张表,快速理解Model I/O的三大支柱及其作用:
| 组件 | 核心职责 | 类比 |
|---|---|---|
| 提示词模板 (Prompt Templates) | 告别硬编码,将用户输入、指令、示例等动态组合成模型能理解的提示文本。 | 邮件的标准格式和信封,确保信息以正确结构送达。 |
| 语言模型 (LLMs & Chat Models) | 连接各类大模型的核心包装器。LLM用于文本补全,ChatModel专为多轮对话设计。 | 负责处理信件的邮局和邮递员。 |
| 输出解析器 (Output Parsers) | 将模型返回的非结构化文本,转换为程序可用的结构化数据(如JSON、列表)。 | 拆开信封并分类归档信件内容的秘书。 |
这三个组件通过 LCEL(LangChain Expression Language) 优雅地串联起来,形成 Prompt | Model | Parser 的处理链,这是现代LangChain应用开发的推荐模式。
二、实战准备:环境搭建与Ollama初始化
一切从环境开始。我们假设你已有Python环境,并已安装Ollama且拉取了所需模型。
1. 安装LangChain及相关库
打开终端,执行以下命令安装核心库。langchain-community包含Ollama集成,langchain-core则是新版本的核心。
pip install langchain langchain-ollama
2. 启动并验证Ollama模型
确保Ollama服务在后台运行(默认地址 http://localhost:11434)。然后,在终端拉取我们即将使用的两个模型:
# 拉取一个纯文本模型和一个多模态模型
ollama pull qwen3:8b
ollama pull gemma3:4b
三、实战演练一:构建专业文本问答系统
我们的第一个目标是构建一个系统,让模型扮演特定角色(如“算法专家”)来回答问题。
1. 构建提示词模板
我们使用 ChatPromptTemplate 来定义一个结构化的对话模板。它将系统指令和用户问题组合起来。
from langchain_core.prompts import ChatPromptTemplate
# 1. 定义提示词模板:角色设定 + 用户问题
prompt_template = ChatPromptTemplate.from_messages([
("system", "你是一位资深{role},请用严谨专业且简洁的中文回答。"),
("human", "{question}")
])
# 预览填充后的模板
test_prompt = prompt_template.invoke({"role": "算法工程师", "question": "解释一下动态规划的核心思想。"})
print(test_prompt.to_string())
输出预览:
System: 你是一位资深算法工程师,请用严谨专业且简洁的中文回答。
Human: 解释一下动态规划的核心思想。
2. 连接模型与解析输出
接下来,我们初始化聊天模型,并创建一个完整的处理链。
from langchain_ollama import ChatOllama
# 2. 初始化聊天模型 (连接Ollama)
chat_model = ChatOllama(
base_url="http://localhost:11434",
model="qwen3:8b", # 指定使用的模型
temperature=0.3, # 控制创造性,越低越确定
num_ctx=4096 # 上下文窗口大小
)
# 3. 创建处理链:模板 -> 模型 -> 字符串解析器
from langchain_core.output_parsers import StrOutputParser
text_qa_chain = prompt_template | chat_model | StrOutputParser()
# 4. 调用链并获取答案
response = text_qa_chain.invoke({
"role": "算法工程师",
"question": "解释一下动态规划的核心思想。"
})
print("AI回答:", response)
输出预览(AI回答可能不一定相同):
AI回答: 动态规划(Dynamic Programming, DP)的核心思想是通过**将原问题分解为重叠的子问题**,并**存储子问题的最优解**,从而避免重复计算,最终求得原问题的最优解。其本质是**利用最优子结构**和**状态转移方程**,结合**记忆化**或**表格化**策略实现高效求解。
### 核心要素:
1. **最优子结构**
原问题的最优解包含子问题的最优解。例如,最长公共子序列(LCS)问题中,整体最优解依赖于子序列的最优解。
2. **重叠子问题**
子问题会被多次重复计算(如斐波那契数列递归解法),动态规划通过**记忆化**(Memoization)或**表格化**(Tabulation)存储子问题解,避免重复计算。
3. **状态转移方程**
定义状态(如 `dp[i][j]` 表示前 `i` 个字符和前 `j` 个字符的LCS长度),并建立状态之间的递推关系(如 `dp[i][j] = max(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]+1)`)。
4. **初始条件与边界**
明确状态的初始值(如 `dp[0][j] = 0` 表示空序列与任意序列的LCS长度为0),确保状态转移的正确性。
### 关键区别:
- **分治法**:将问题分解为独立子问题(如归并排序),不重复计算。
- **动态规划**:针对**重叠子问题**,通过存储子问题解优化时间复杂度(如从指数级降至多项式级)。
### 应用场景:
适用于具有**最优子结构**和**重叠子问题**的优化问题(如背包问题、最短路径、编辑距离等)。
至此,一个最简单的Model I/O流水线就完成了。LCEL的 | 操作符让整个流程清晰直观。
3. 流式输出
采用invoke方式需要等待模型完整生成结果后才能输出。为了提升用户体验,我们增加了流式输出功能,让您可以实时看到文字逐字显示的效果。这种即时反馈机制不仅能让您了解系统正在运行中,避免误认为程序卡顿,还能让您直观地观察到语言模型"思考"和"生成"内容的整个过程。
# 注意:这里使用的是 .stream() 方法,而不是 .invoke()
for chunk in text_qa_chain.stream({
"role": "算法工程师",
"question": "解释一下动态规划的核心思想。"
}):
print(chunk, end="", flush=True) # end="" 确保不换行,flush=True 确保立即显示
核心要点与注意事项
-
.stream()方法:这是开启流式输出的关键。绝大多数 LangChain 的可运行对象(Runnable,如你创建的 Chain)都支持.invoke()(同步调用)和.stream()(流式调用)。 -
逐块处理:流式返回的不是最终完整的字符串,而是一个“数据流”。你需要通过
for循环来遍历接收每一个数据块chunk。这个块的大小取决于模型和底层的实现。 -
前端应用:在 Web 应用(如使用 FastAPI 或 Flask)中,流式输出通常结合 Server-Sent Events 技术,将每个
chunk实时推送到网页前端,从而实现类似 ChatGPT 的打字机效果。
四、实战演练二:释放多模态模型潜力
gemma3:4b这类多模态模型能理解图像。我们来看看如何通过Model I/O处理图片问答。
加载并编码图片
LangChain与Ollama配合,可以通过绑定(bind)图片数据来调用多模态模型。
import base64
from langchain_core.messages import HumanMessage
from langchain_ollama import ChatOllama
from langchain_core.output_parsers import StrOutputParser
def encode_image(image_path):
"""
将图片文件转换为base64编码的字符串
参数:
image_path (str): 图片文件的路径
返回:
str: base64编码的图片数据字符串
"""
with open(image_path, "rb") as image_file:
return base64.b64encode(image_file.read()).decode('utf-8')
def prompt_func(data):
"""
构造包含文本和图片的多模态消息内容
参数:
data (dict): 包含"text"和"image"键的字典
- "text": 要发送的文本内容
- "image": base64编码的图片数据
返回:
list: 包含HumanMessage对象的列表,其中content为包含图片和文本的混合内容
"""
text = data["text"]
image = data["image"]
# 构造图片内容部分
image_part = {
"type": "image_url",
"image_url": f"data:image/jpeg;base64,{image}",
}
content_parts = []
# 构造文本内容部分
text_part = {"type": "text", "text": text}
content_parts.append(image_part)
content_parts.append(text_part)
return [HumanMessage(content=content_parts)]
# 假设有一张图片 landscape.jpg
image_base64 = encode_image("./image.png")
# 初始化多模态模型,并绑定图片
chat_model = ChatOllama(
model="gemma3:4b"
)
# 组合prompt处理函数、聊天模型和输出解析器构建多模态处理链
multimodal_model = prompt_func | chat_model | StrOutputParser()
# 直接调用模型提问
response = multimodal_model.invoke({"text": "请描述图片中的内容。", "image": image_base64})
print(response)
五、进阶技巧:让输出高度结构化
对于摘要、信息提取等任务,我们需要JSON或特定对象格式的输出。
PydanticOutputParser是解决此问题的利器。
假设我们要从一段技术描述中提取关键信息。
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_ollama import ChatOllama
from pydantic import BaseModel, Field
from typing import List
# 1. 定义我们期望的、结构化的数据模型
class TechnologySummary(BaseModel):
core_tech: List[str] = Field(description="提到的核心技术点", min_items=1)
difficulty: str = Field(description="学习难度,分为:入门、中等、困难")
application: str = Field(description="主要应用领域")
# 2. 创建解析器,并获取格式指令
parser = PydanticOutputParser(pydantic_object=TechnologySummary)
format_instructions = parser.get_format_instructions()
# 3. 创建提示词模板,注入格式指令
template = """
请从以下技术描述中提取信息。
{format_instructions}
描述内容:
{description}
"""
prompt = PromptTemplate(
template=template,
input_variables=["description"],
partial_variables={"format_instructions": format_instructions}
)
# 4. 初始化模型并构建链
llm = ChatOllama(model="qwen3:8b")
chain = prompt | llm | parser
# 5. 执行
description_text = "LangChain是一个用于构建LLM应用的框架,它抽象了Model I/O、检索、代理等核心概念。学习曲线中等,主要用于快速开发AI智能体应用。"
result = chain.invoke({"description": description_text})
print("提取的结构化数据:")
print(f" 核心技术:{result.core_tech}")
print(f" 学习难度:{result.difficulty}")
print(f" 应用领域:{result.application}")
输出预览:
提取的结构化数据:
核心技术:['Model I/O', '检索', '代理']
学习难度:中等
应用领域:快速开发AI智能体应用
通过PydanticOutputParser,我们将一段模糊的描述,精准地转换为了一个结构化的TechnologySummary对象,后续可以直接用result.core_tech等属性进行编程处理。
总结与避坑指南
通过以上实战,你已经掌握了Model I/O的核心。最后,分享几个关键点:
-
模型选择:根据任务选择
OllamaLLM(通用文本生成)或ChatOllama(多轮对话)。多模态任务必须使用支持此功能的模型(如gemma3:4b)。 -
流式输出:对于长文本,使用
model.stream()或链的.stream()方法,可以逐词返回,提升用户体验。 -
常见错误:
-
连接拒绝:确认Ollama服务是否运行(
ollama serve),且base_url(默认为http://localhost:11434)正确。 -
模型不存在:确认已用
ollama pull <model-name>下载模型。 -
提示词无效:检查模板变量名是否与传入的字典键名匹配。
-
-
深入方向:掌握LCEL后,你可以轻松地将这个Model I/O链与LangChain的记忆(Memory)、检索(Retrieval) 和代理(Agents) 模块结合,构建出功能极其强大的AI应用。
Model I/O是LangChain世界的基石。理解并熟练运用它,你就拿到了解锁大模型能力的万能钥匙。
更多推荐


所有评论(0)