目录

一、核心桥梁:Model I/O 三大组件全景

二、实战准备:环境搭建与Ollama初始化

三、实战演练一:构建专业文本问答系统

四、实战演练二:释放多模态模型潜力

五、进阶技巧:让输出高度结构化

总结与避坑指南


告别繁琐的API调用和提示词拼接,LangChain Model I/O正为你构建一条标准、高效的对话管道。

        LangChain的核心价值之一,就是将与大语言模型(LLM)的交互过程标准化、模块化。而Model I/O模块,正是这条标准化管道的入口和出口,它负责处理你给模型的输入,并解析模型给你的输出。无论是商业API(如GPT-4)还是本地开源模型(通过Ollama运行),Model I/O都提供了一套统一的接口,让你能“一次编码,多模型适配”。

        今天,我们就以风靡的本地模型工具Ollama及其搭载的开源模型(如qwen3:8bgemma3: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的核心。最后,分享几个关键点:

  1. 模型选择:根据任务选择OllamaLLM(通用文本生成)或ChatOllama(多轮对话)。多模态任务必须使用支持此功能的模型(如gemma3:4b)。

  2. 流式输出:对于长文本,使用 model.stream() 或链的 .stream() 方法,可以逐词返回,提升用户体验。

  3. 常见错误

    • 连接拒绝:确认Ollama服务是否运行(ollama serve),且base_url(默认为 http://localhost:11434)正确。

    • 模型不存在:确认已用 ollama pull <model-name> 下载模型。

    • 提示词无效:检查模板变量名是否与传入的字典键名匹配。

  4. 深入方向:掌握LCEL后,你可以轻松地将这个Model I/O链与LangChain的记忆(Memory)检索(Retrieval) 和代理(Agents) 模块结合,构建出功能极其强大的AI应用。

        Model I/O是LangChain世界的基石。理解并熟练运用它,你就拿到了解锁大模型能力的万能钥匙。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐