第2章 Model I/O

一、Model I/O 介绍

Model I/O 部分是与语言模型进行交互的核心组件,包括输入提示(Prompt Template)、调用模型(Model)、输出解析(Output Parser)。简单来说,就是输入、处理、输出这三个步骤。

二、调用在线模型

2.1 大模型服务平台

LangChain作为一个“工具”,依赖于第三方集成各种大模型。有许多提供大模型API服务的平台,使用时只需要注册、充值并创建API-Key,之后即可使用API-Key与URL来调用平台提供的相应的模型的服务。

1)CloseAI:https://platform.closeai-asia.com/

2)OpenRouter:https://openrouter.ai/

3)阿里云百炼:https://bailian.console.aliyun.com/

4)百度千帆:https://console.bce.baidu.com/qianfan/overview

5)硅基流动:https://www.siliconflow.cn/

2.2 OpenAI SDK 调用模型

OpenAI 的 GPT 系列模型影响了大模型技术发展的开发范式和标准。所以无论是 Qwen、ChatGLM 等模型,它们的使用方法和函数调用逻辑基本遵循 OpenAI 定义的规范,没有太大差异。这就使得能够通过一个较为通用的接口来接入和使用不同的模型。

# pip install langchain langchain-openai
from openai import OpenAI

client = OpenAI(
    base_url="https://openrouter.ai/api/v1",  # 平台提供的 URL
    api_key="sk-...",  # 平台提供的 API-Key
)

completion = client.chat.completions.create(
    model="openai/gpt-oss-20b:free",  # 模型名称
    messages=[{"role": "user", "content": "将'你好'翻译成意大利语"}],  # 用户输入
)
print(completion.choices[0].message.content)

案例:以硅基流动为例

from openai import OpenAI

# 创建 OpenAI 客户端
client = OpenAI(
    api_key="sk-vr",
    base_url="https://api.siliconflow.cn/v1"
)

completion = client.chat.completions.create(
    model="Qwen/Qwen3-8B",  # 模型名称
    messages=[{"role": "user", "content": "请将下面内容翻译成英文:\n\n今天北京的苹果价格怎么样?"}],  # 问题
)

# 获取结果并打印
print(completion.choices[0].message.content)

2.3 API-Key 管理

通常有3种方式来管理API-Key:硬编码、写入.env文件、写入环境变量。

前面我们就是将 API-Key 硬编码仅代码中,这仅适用于临时测试,存在密钥泄露风险。

2.3.1.使用 .env 配置文件

使用 python-dotenv 加载本地配置文件,支持多环境管理。该方式有以下优势:

  • 配置文件可加入 .gitignore 避免泄露
  • 支持多环境配置(如 .env.prod 和 .env.dev)
  • 创建 .env 文件(项目根目录):
OPENAI_API_KEY="sk-…"
OPENAI_BASE_URL="https://api.siliconflow.cn/v1"

举例:显式读取 .env 中的环境变量

# pip install python-dotenv
import os
import dotenv
from openai import OpenAI

# 加载环境变量, 默认加载 .env 文件
dotenv.load_dotenv()

# 创建 OpenAI 客户端
client = OpenAI(
    api_key=os.getenv("OPENAI_API_KEY"),
    base_url=os.getenv("OPENAI_BASE_URL")
)

completion = client.chat.completions.create(
    model="Qwen/Qwen3-8B",  # 模型名称
    messages=[{"role": "user", "content": "请将下面内容翻译成英文:\n\n今天北京的苹果价格怎么样?"}],  # 问题
)

# 获取结果并打印
print(completion.choices[0].message.content)

举例:依靠 OpenAI 的默认行为读取 .env 环境变量

OpenAI 在创建时,会自动到环境变量中找 OPENAI_API_KEY 以及OPENAI_BASE_URL。如果在 .env 中配置的名字和上面的两个名字一样,无需再次赋值,只通过 dotenv.load_dotenv() 加载环境配置信息即可。

# pip install python-dotenv
import dotenv
from openai import OpenAI

# 加载环境变量, 默认加载 .env 文件
dotenv.load_dotenv()

# 创建 OpenAI 客户端
client = OpenAI() # 使用默认 .env 的 API 密钥

completion = client.chat.completions.create(
    model="Qwen/Qwen3-8B",  # 模型名称
    messages=[{"role": "user", "content": "请将下面内容翻译成英文:\n\n今天北京的苹果价格怎么样?"}],  # 问题
)

# 获取结果并打印
print(completion.choices[0].message.content)

2.3.2.在环境变量中配置

通过系统环境变量存储 API-Key,避免代码明文暴露。

终端设置变量(临时生效):

export OPENAI_API_KEY="sk-…"  # Linux/Mac
set OPENAI_API_KEY="sk-…"    # Windows CMD

在代码中通过 os.getenv() 读取API-Key:

import os
from openai import OpenAI

client = OpenAI(
    base_url="https://openrouter.ai/api/v1",
    api_key=os.getenv("OPENAI_API_KEY"),
)

completion = client.chat.completions.create(
    model="openai/gpt-oss-20b:free",
    messages=[{"role": "user", "content": "你好"}],
)
print(completion.choices[0].message.content)

2.4 LangChain API 调用模型

通常通过聊天模型接口访问 LLM,该接口通常以消息列表作为输入并返回一条消息作为输出。

  • 输入:接受文本 PromptValue 或消息列表 List[BaseMessage],每条消息需指定角色(如 SystemMessage、HumanMessage、AIMessage)
  • 输出:返回带角色的消息对象(BaseMessage 子类),通常是 AIMessage

举例:

import os
import dotenv
from langchain.chat_models import init_chat_model
from langchain_core.messages import SystemMessage, HumanMessage

dotenv.load_dotenv()

llm = init_chat_model(
    model="Qwen/Qwen3-8B",
    model_provider="openai", # 可选, 默认为 openai,
    base_url=os.getenv("QWEN_BASE_URL"),
    api_key=os.getenv("QWEN_API_KEY"),
)

message = [
    SystemMessage(content="你是一个诗人"),  # 系统提示
    HumanMessage(content="写一首关于夏天的诗")  # 用户问题
]

resp = llm.invoke(message)
print(type(resp))  # <class 'langchain_core.messages.ai.AIMessage'>
print(resp.content)

2.5 模型初始化相关参数

初始化一个模型最简单的方法就是使用 init_chat_model,并设置必要的参数,例如 API-Key 和模型名称。除此之外还有一些其他参数。

参数

说明

model

模型名称或标识符

base_url

发送请求的 API 端点的 URL。常由模型的提供商提供

api_key

与模型提供商进行身份验证所需的 API 密钥

temperature

控制模型输出的随机性。数字越高,回答越有创意;数字越低,回答越确定,

timeout

在取消请求之前,等待模型响应的最大时间(以秒为单位)

max_tokens

限制响应中的总tokens 数量,控制输出长度

max_retries

请求失败时系统尝试重新发送请求的最大次数

Token是什么?

大模型处理的最小单位是 token(相当于自然语言中的词或字),输出时逐个 token 依次生成。模型提供商通常也是以 token 的数量作为其计量或收费的依据。1个中文Token≈1-1.8个汉字,1个英文Token≈3-4字母。

Token与字符转化的可视化工具:

2.6 对话模型的 Message

对话模型的输入可以是文本提示、消息提示或是字典格式。

1)文本提示

文本提示是字符串,适用于不需要保留对话历史的直接生成任务。

resp = llm.invoke("你好")

2)消息提示

将消息对象列表输入模型,方便管理对话历史,包含系统指令以及处理多模态数据。

message = [
    SystemMessage(content="你是一个诗人"),  # 系统提示
    HumanMessage(content="写一首关于夏天的诗")  # 用户问题
]

resp = llm.invoke(message)

3)字典格式

也可以按照 OpenAI 聊天补全格式创建字典列表组成消息。一条消息通常包含 role(角色)、content(内容)、metadata(元数据)。

message = [
    {"role": "system", "content": "你是一个诗人"},
    {"role": "user", "content": "写一首关于夏天的诗"}
]

resp = llm.invoke(message)

4)消息类型

消息类型

描述

SystemMessage

代表一组初始指令,用于引导模型的行为。可以使用系统消息来设定语气、定义模型的角色,并建立响应的指导方针

HumanMessage

表示用户输入

AIMessage

模型生成的响应,包括文本内容、工具调用和元数据

ToolMessage

表示工具调用的输出

HumanMessage、AIMessage 和 SystemMessage 是常用的消息类型。

ToolMessage 是在工具调用场景下才会使用的特殊消息类型。

2.7 调用方法

聊天模型提供了三种主要的调用方法:

invoke / ainvoke

将单个输入转换为输出

batch / abatch

批量将多个输入转换为输出

stream / astream

从单个输入生成流式输出

带有“a”前缀的方法是异步的,需要与 asyncio 和 await 语法一起使用以实现并发。

2.7.1 非流式/流式输出

在Langchain中,语言模型的输出分为了两种主要的模式:流式输出非流式输出

  • 非流式输出:用户提出需求请编写一首诗,系统在静默数秒后突然弹出了完整的诗歌。如同一种“提交请求,等待结果”的流程,实现简单,但体验单调。
  • 流式输出:用户提问,请编写一首诗,当问题刚刚发送,系统就开始一字一句(逐个token)进行回复,更像是“实时对话”,贴近人类交互的习惯。
1)非流式输出:

这是 LangChain 与 LLM 交互时的默认行为,是最简单、最稳定的语言模型调用方式。当用户发出请求后,系统在后台等待模型生成完整响应,然后一次性将全部结果返回。

举例:invoke() 调用

import os
import dotenv
from langchain.chat_models import init_chat_model
from langchain_core.messages import SystemMessage, HumanMessage

dotenv.load_dotenv()

llm = init_chat_model(
    model="Qwen/Qwen3-8B",
    model_provider="openai", # 可选, 默认为 openai,
    base_url=os.getenv("QWEN_BASE_URL"),
    api_key=os.getenv("QWEN_API_KEY"),
    temperature=0.7,
)

message = [
    {"role": "system", "content": "你是一个诗人"},
    {"role": "user", "content": "写一首关于夏天的诗"}
]

resp = llm.invoke(message)
print(type(resp))  # <class 'langchain_core.messages.ai.AIMessage'>
print(resp)
2)流式输出

流式输出是一种更具交互感的模型输出方式,用户不再需要等待完整答案,而是能看到模型逐个 token地实时返回内容。适合构建强调“实时反馈”的应用。

举例:stream() 流式输出

import os
import dotenv
from langchain.chat_models import init_chat_model
dotenv.load_dotenv()

llm = init_chat_model(
    model="Qwen/Qwen3-8B",
    model_provider="openai", # 可选, 默认为 openai,
    base_url=os.getenv("QWEN_BASE_URL"),
    api_key=os.getenv("QWEN_API_KEY"),
    temperature=0.7,
)

message = [
    {"role": "system", "content": "你是一个诗人"},
    {"role": "user", "content": "写一首关于夏天的诗"}
]

# stream()流式方法输出
for chunk in llm.stream(message):
    # 逐个打印内容块,并刷新缓冲区让即时显示内容
    print(chunk.content, end="", flush=True)

2.7.2 批量调用

将一组独立的请求批量发送给模型并行处理。

举例:batch() 批量调用

import os
import dotenv
from langchain.chat_models import init_chat_model

dotenv.load_dotenv()

llm = init_chat_model(
    model="Qwen/Qwen3-8B",
    model_provider="openai", # 可选, 默认为 openai,
    base_url=os.getenv("QWEN_BASE_URL"),
    api_key=os.getenv("QWEN_API_KEY"),
    temperature=0.7,
)

message = [
    [{"role": "system", "content": "你是一个诗人"},{"role": "user", "content": "写一首关于春天的诗"}],
    [{"role": "system", "content": "你是一个诗人"},{"role": "user", "content": "写一首关于夏天的诗"}],
    [{"role": "system", "content": "你是一个诗人"},{"role": "user", "content": "写一首关于秋天的诗"}],
    [{"role": "system", "content": "你是一个诗人"},{"role": "user", "content": "写一首关于冬天的诗"}]
]

resp = llm.batch(message) # 批量调用,返回的结果是一个列表,每个元素是一个AIMessage对象
# 逐个打印结果
for i, item in enumerate(resp):
    print(f"第 {i} 个结果:")
    print(item.content)
    print("+++++++++++++++++++++++++++++++++++++++++++++++++++++")

batch 默认没有依赖底层 API 的原生批量接口,而是使用线程池并行执行多个 invoke()。所以它对 IO 密集型任务(如调用远程 LLM API)很有效。

如果底层 API 自身提供批量接口(一次请求多个 prompt),那么子类可以重写 batch 方法来直接使用批量接口,这样效率会更高。

2.7.3 同步/异步调用

1)同步调用

每个操作依次执行,直到当前操作完成后才开始下一个操作,总的执行时间是各个操作时间的总和。

举例:使用 invoke() 同步调用

import os
import time

import dotenv
from langchain.chat_models import init_chat_model

dotenv.load_dotenv()

llm = init_chat_model(
    model="Qwen/Qwen3-8B",
    model_provider="openai", # 可选, 默认为 openai,
    base_url=os.getenv("QWEN_BASE_URL"),
    api_key=os.getenv("QWEN_API_KEY")
)

messages = [
    [{"role": "system", "content": "你是一个诗人"},{"role": "user", "content": "写一首关于春天的诗"}],
    [{"role": "system", "content": "你是一个诗人"},{"role": "user", "content": "写一首关于夏天的诗"}],
    [{"role": "system", "content": "你是一个诗人"},{"role": "user", "content": "写一首关于秋天的诗"}],
    [{"role": "system", "content": "你是一个诗人"},{"role": "user", "content": "写一首关于冬天的诗"}]
]

start_time = time.time()

resp = [llm.invoke(message) for message in messages]
print(resp)
end_time = time.time()

# 总耗时: 27.14240789413452
print("总耗时:", end_time - start_time) 
2)异步调用

异步调用,允许程序在等待某些操作完成时继续执行其他任务,而不是阻塞等待。这在处理 I/O 操作(如网络请求、文件读写等)时特别有用,可以显著提高程序的效率和响应性。

举例:使用 ainvoke() 异步调用

import asyncio
import os
import dotenv
from langchain.chat_models import init_chat_model

dotenv.load_dotenv()

llm = init_chat_model(
    model="Qwen/Qwen3-8B",
    model_provider="openai",  # 可选, 默认为 openai,
    base_url=os.getenv("QWEN_BASE_URL"),
    api_key=os.getenv("QWEN_API_KEY")
)

messages = [
    [{"role": "system", "content": "你是一个诗人"}, {"role": "user", "content": "写一首关于春天的诗"}],
    [{"role": "system", "content": "你是一个诗人"}, {"role": "user", "content": "写一首关于夏天的诗"}],
    [{"role": "system", "content": "你是一个诗人"}, {"role": "user", "content": "写一首关于秋天的诗"}],
    [{"role": "system", "content": "你是一个诗人"}, {"role": "user", "content": "写一首关于冬天的诗"}]
]


async def async_invoke():
    resp = [llm.ainvoke(message) for message in messages]
    return await asyncio.gather(*resp)  # 使用 asyncio.gather(), 等待所有任务完成

if __name__ == '__main__':
    result = asyncio.run(async_invoke())
    for i, item in enumerate(result):
        print(f"第 {i} 个结果:")
        print(item.content)
        print("+++++++++++++++++++++++++++++++++++++++++++++++++++++")

使用 asyncio.gather() 并行执行时,因为多个任务几乎同时开始,它们的执行时间将重叠。理想情况下,如果多个任务的执行时间相同,那么总执行时间应该接近单个任务的执行时间。

三、调用本地模型

3.1 Ollama介绍

Ollama是一个开源项目,其项目定位是:一个本地运行大模型的集成框架。目前主要针对主流的LlaMA架构的开源大模型设计,可以实现如 Qwen、Deepseek 等主流大模型的下载、启动和本地运行的自动化部署及推理流程。

目前作为一个非常热门的大模型托管平台,已被包括LangChain、Taskweaver等在内的多个热门项目高度集成。

3.2 Ollama安装

Ollama项目支持跨平台部署,目前已兼容Mac、Linux和Windows操作系统。

image

无论使用哪个操作系统,Ollama项目的安装过程都设计得非常简单。

访问 https://ollama.com/download 下载对应系统的安装文件。

  • Windows 系统执行.exe文件安装
  • Linux 系统执行以下命令安装:
curl -fsSL https://ollama.com/install.sh | sh

这行命令的目的是从https://ollama.com/ 网站读取 install.sh脚本,并立即通过 sh 执行该脚本,在安装过程中会包含以下几个主要的操作:

  • 检查当前服务器的基础环境,如系统版本等;
  • 下载Ollama的二进制文件;
  • 配置系统服务,包括创建用户和用户组,添加Ollama的配置信息;
  • 启动Ollama服务;

3.3 模型下载

访问https://ollama.com/search可以查看Ollama支持的模型。使用命令行可以下载并运行模型,例如运行deepseek-r1:7b模型:

ollama run qwen3:8b

1)进入到Settings,可以切换模型目录

image

2)运行模型 ollama run qwen3:8b

当前模型如果不存在,可以联网下载

ollama run qwen3:8b

3)Ollama 常用命令说明

基础命令

  • ollama run <model>: 运行指定模型
  • ollama list: 列出已下载的模型
  • ollama pull <model>: 下载模型
  • ollama push <model>: 上传模型
  • ollama rm <model>: 删除本地模型

模型管理

  • ollama create <model> -f <modelfile>: 从 Modelfile 创建模型
  • ollama show <model>: 显示模型信息
  • ollama cp <source> <destination>: 复制模型

服务管理

  • ollama serve: 启动Ollama服务
  • ollama stop: 停止Ollama服务
  • ollama status: 查看服务状态

其他命令

  • ollama help: 显示帮助信息
  • ollama version: 显示版本信息

3.4 调用本地模型

举例:

# pip install langchain-ollama
from langchain_ollama import ChatOllama

ollama_llm = ChatOllama(
    model="qwen3:8b"
)
messages = {"role": "user", "content": "你好,请介绍一下你自己"}
resp = ollama_llm.invoke([messages])
print(resp.content)

若 Ollama 不在本地默认端口运行,需指定 base_url,即:

# pip install langchain-ollama
from langchain_ollama import ChatOllama

ollama_llm = ChatOllama(
    model="qwen3:8b",
    base_url="http://192.168.130.154:11434",  # 自定义地址
)
messages = {"role": "user", "content": "你好,请介绍一下你自己"}
resp = ollama_llm.invoke([messages])
print(resp.content)

四、Prompt Template

4.1 提示词模板介绍

在应用开发中,固定的提示词限制了模型的灵活性和适用范围。所以,PromptTemplate 是一个模板化的字符串,我们可以将变量插入到模板中,从而创建出不同的提示。PromptTemplate 接收用户输入,返回一个传递给LLM的信息(即提示词 prompt)。

提示模板以字典作为输入,其中每个键代表要填充的提示模板中的变量。并输出一个 PromptValue。这个 PromptValue 可以传递给聊天模型,也可以转换为字符串或消息列表。PromptValue 存在的目的是为了方便在字符串和消息之间切换。

有多种类型的提示模板,常用的有 PromptTemplate(字符串提示模板)和 ChatPromptTemplate(聊天提示模板)。

4.2 PromptTemplate

PromptTemplate 用于快速构建包含变量的提示词模板,并通过传入不同的参数值生成自定义的提示词。

参数

template

提示模板,包括变量占位符

input_variables

需要将其值作为提示输入的变量名称列表

partial_variables

提示模板携带的部分变量的字典。使用部分变量预先填充模板,无需后续在每次调用时再传递这些变量

方法

format()

使用输入格式化提示

4.2.1 实例化

方式1:使用构造方法实例化提示词模板

from langchain_core.prompts import PromptTemplate

template = PromptTemplate(
     template="请评价{product}的优缺点,包含{aspect1}和{aspect2}",
     input_variables=["product", "aspect1", "aspect2"],
)
# 使用模版生成提示词
prompt1 = template.format(product="手机", aspect1="续航", aspect2="拍照")
prompt2 = template.format(product="笔记本电脑", aspect1="性能", aspect2="便携性")
print(prompt1)
print(prompt2)

输出:

请评价手机的优缺点,包含续航和拍照
请评价笔记本电脑的优缺点,包含性能和便携性

方式2:使用 from_template 方法实例化提示词模板

from langchain_core.prompts import PromptTemplate


# 使用from_template方法创建模版
template = PromptTemplate.from_template("请给我一个关于{product}的评价,包含{aspect1}和{aspect2}")
# 使用模版生成提示词
prompt = template.format(product="手机", aspect1="续航", aspect2="拍照")
print(prompt)

输出:

请给我一个关于手机的评价,包含续航和拍照

4.2.2 部分提示模版

在生成提示之前可以赋予部分变量默认值。

  • 方式1:实例化过程中指定 partial_variables 参数
from langchain_core.prompts import PromptTemplate

template = PromptTemplate(
    template="请给我一个关于{product}的评价,包含{aspect1}和{aspect2}",
    input_variables=["product", "aspect1", "aspect2"],
    partial_variables={"aspect1": "价格", "aspect2": "性能"},  # 指定变量aspect1、aspect2的默认值
)

prompt = template.format(product="手机")  # 使用模版生成提示词, aspect1和aspect2的会使用之前的默认值
print(prompt) # 请给我一个关于手机的评价,包含系统和拍照
  • 方式2:使用 partial 方法指定默认值
from langchain_core.prompts import PromptTemplate

# 使用from_template方法创建模版
template = PromptTemplate.from_template("请给我一个关于{product}的评价,包含{aspect1}和{aspect2}")
# 预先定义部分变量的默认值
partial_template = template.partial(product="手机")
# 使用模版生成提示词
prompt = partial_template.format(aspect1="续航", aspect2="拍照")
print(prompt)  # 请给我一个关于手机的评价,包含续航和拍照

4.2.3 调用方式

除了 format 方法,也可以使用 invoke 方法调用。

invoke 方法返回 PromptValue 对象,可以使用 to_string 方法将其转换为字符串。

举例:invoke 方法调用

from langchain_core.prompts import PromptTemplate

# 使用from_template方法创建模版
template = PromptTemplate.from_template("请给我一个关于{product}的评价,包含{aspect1}和{aspect2}")
# 预先定义部分变量的默认值
partial_template = template.partial(product="手机")
# 使用模版生成提示词
prompt = partial_template.invoke({"aspect1": "续航", "aspect2":"拍照"})
print(prompt)  # 请给我一个关于手机的评价,包含续航和拍照

print(prompt, type(prompt))
# text='请给我一个关于手机的评价,包含续航和拍照' <class 'langchain_core.prompt_values.StringPromptValue'>

prompt_str = prompt.to_string()
print(prompt_str, type(prompt_str))
# 请给我一个关于手机的评价,包含续航和拍照 <class 'str'>

4.3 ChatPromptTemplate

ChatPromptTemplate是创建聊天消息列表的提示模板。相较于普通 PromptTemplate更适合处理多角色、多轮次的对话场景。支持 System/Human/AI 等不同角色的消息模板。

4.3.1 实例化

ChatPromptTemplate 可以通过构造方法或 from_messages 方法来实例化提示词模板。

实例化时需要传入 messages 参数,messages 参数支持如下格式:

  • tuple 构成的列表,格式为[(role, content)]
  • dict 构成的列表,格式为[{“role”:... , “content”:...}]
  • Message 类构成的列表

举例:

from langchain_core.prompts import ChatPromptTemplate

template = ChatPromptTemplate(
    [
        ("system", "你是一个AI智能体开发工程师,你叫{name},你正在开发一个智能助手,请使用中文回答问题。"),
        ("human", "你能帮我做什么呢?"),
        ("ai", "我能开发{thing}。"),
        ("human", "我需要开发一个{user_input},请给我一些建议。"),
    ]
)
# 使用format_messages方法生成提示
prompt = template.format_messages(name="深瞳", thing="Agent", user_input="查询农业信息的智能助手")

输出:

[
SystemMessage(content='你是一个AI智能体开发工程师,你叫深瞳,你正在开发一个智能助手,请使用中文回答问题。', additional_kwargs={}, response_metadata={}), 
HumanMessage(content='你能帮我做什么呢?', additional_kwargs={}, response_metadata={}), 
AIMessage(content='我能开发Agent。', additional_kwargs={}, response_metadata={}), HumanMessage(content='我需要开发一个查询农业信息的智能助手,请给我一些建议。', additional_kwargs={}, response_metadata={})
]

4.3.2 调用方式

推荐使用 from_messages 方法或 invoke 方法调用。

举例:invoke 方法调用

from langchain_core.prompts import ChatPromptTemplate

template = ChatPromptTemplate(
    [
        ("system", "你是一个AI智能体开发工程师,你叫{name},你正在开发一个智能助手,请使用中文回答问题。"),
        ("human", "你能帮我做什么呢?"),
        ("ai", "我能开发{thing}。"),
        ("human", "我需要开发一个{user_input},请给我一些建议。"),
    ]
)
# 使用invoke方法生成提示
prompt = template.invoke({"name":"深瞳", "thing":"Agent", "user_input":"查询农业信息的智能助手"})

print(prompt)

4.3.3 消息占位符

当希望在格式化过程中插入消息列表时,比如 Agent 暂存中间步骤,需要使用 MessagesPlaceholder,负责在特定位置添加消息列表。

举例:

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

# 使用from_messages方法创建模版
template = ChatPromptTemplate.from_messages(
    [
        ("system", "你是一个AI智能体开发工程师,你正在开发一个智能助手,请使用中文回答问题。"),
        ("placeholder", "{conversation}")
        # 等同于 MessagesPlaceholder(variable_name="conversation", optional=True),
    ]
)

# 使用format_messages方法生成提示
prompt = template.format_messages(conversation=[
    ("human", "你能帮我做什么呢?"),
    ("ai", "我能开发Agent智能体"),
    ("human", "我需要开发一个关于智能客服的智能体,请给我一些建议。"),
])
print(prompt)

[
SystemMessage(content='你是一个AI智能体开发工程师,你正在开发一个智能助手,请使用中文回答问题。', additional_kwargs={}, response_metadata={}), 
HumanMessage(content='你能帮我做什么呢?', additional_kwargs={}, response_metadata={}), 
AIMessage(content='我能开发Agent智能体', additional_kwargs={}, response_metadata={}), 
HumanMessage(content='我需要开发一个关于智能客服的智能体,请给我一些建议。', additional_kwargs={}, response_metadata={})
]

4.3.4 多模态提示词

可以使用提示模板来格式化多模态输入,比如将图片链接作为输入。

举例:

准备一张图片:

1

 代码如下:

from langchain_core.prompts import ChatPromptTemplate
from langchain_ollama import ChatOllama


# 初始化模型
llm = ChatOllama(
    model="qwen2.5vl:7b",
    base_url="http://127.0.0.1:11434"
)

# 创建模版
template = ChatPromptTemplate([
    {"role": "system", "content": "用中文简单描述图片内容"},
    {"role": "user", "content": [{"image_url": "{image_url}"}]}
])
# template = ChatPromptTemplate([
#     ("system", "用中文简单描述图片内容"),
#     ("user", [{"type": "image_url", "image_url": {"url": "{image_url}"}}])
# ])

# 使用format_messages方法生成提示,准备一张本地图片
prompt = template.format_messages(image_url="1.png")

# 调用模型
response = llm.invoke(prompt)
print(response.content)

输出:

这张图片中,一位女性靠在窗户边,穿着一件白色的针织开衫,头发是棕色的,披散在肩上。背景是蓝色的墙壁和白色的百叶窗,阳光透过窗户洒在她的身上,营造出一种温暖的氛围。

4.4 外部加载Prompt

可以将 prompt 保存为 JSON 或者 YAML 等格式的文件,通过读取指定路径的格式化文件,获取相应的 prompt。这样方便对 prompt 进行管理和维护。

4.4.1 json格式提示词

prompts目录下创建json文件:prompt.json

{
  "_type": "prompt",
  "input_variables": ["name", "what"],
  "template": "请{name}讲一个{what}的故事"
}

代码:

from langchain_core.prompts import load_prompt

template = load_prompt("prompt.json", encoding="utf-8")
print(template.format(name="李丽", what="恐怖的"))

输出:

请李丽讲一个恐怖的的故事

4.4.2 yaml格式提示词

prompts目录下创建yaml文件:prompt.yaml

_type: "prompt"
input_variables: ["name", "what"]
template: "请{name}讲一个{what}的故事"

代码:

from langchain_core.prompts import load_prompt

template = load_prompt("prompt.yaml", encoding="utf-8")
print(template.format(name="李丽", what="恐怖的"))

五、Output Parsers

5.1 输出解析器介绍

在应用开发中,大模型的输出可能是下一步逻辑处理的关键输入。因此,在这种情况下,规范化输出是必须要做的任务,以确保应用能够顺利进行后续的逻辑处理。

语言模型返回的内容通常都是文本字符串,而实际 AI 应用开发过程中有时希望模型可以返回更直观、更格式化的内容,LangChain 提供了输出解析器(Output Parser)将模型输出解析为结构化数据。

有多种类型的输出解析器,常用的有 StrOutputParser(字符串解析器)与 JsonOutputParser(JSON解析器)

5.2 StrOutputParser

StrOutputParser 是一个简单的解析器,从结果中提取 content 字段。

举例:将模型输出结果解析为字符串

from langchain_core.output_parsers import StrOutputParser
from langchain_ollama import ChatOllama

ollama_llm = ChatOllama(
    model="qwen3:8b",
    base_url="http://192.168.130.154:11434",  # 自定义地址
)
messages = [
    {"role": "system", "content": "你好,请介绍一下你自己"},
    {"role": "user", "content": "你好"},
]

# 调用invoke方法
resp = ollama_llm.invoke(messages)
print(resp)
"""
content='你好!我是Qwen,通义千问的超大规模语言模型。我能够帮助你解答问题、提供信息、进行创作,或者只是闲聊。有什么我可以帮你的吗?😊' 
additional_kwargs={} 
response_metadata={'model': 'qwen3:8b', 'created_at': '2025-12-09T02:34:39.1717765Z', 'done': True, 'done_reason': 'stop', 'total_duration': 11788952800, 'load_duration': 100126900, 'prompt_eval_count': 20, 'prompt_eval_duration': 186150600, 'eval_count': 174, 'eval_duration': 11479165500, 'model_name': 'qwen3:8b', 'model_provider': 'ollama'} id='lc_run--019b00f5-ccb5-7193-9603-7eb1bf5508b8-0' usage_metadata={'input_tokens': 20, 'output_tokens': 174, 'total_tokens': 194}
"""

# 调用StrOutputParser()将结果转为字符串
resp_str = StrOutputParser().invoke(resp)
print(resp_str)  # 你好!我是Qwen,通义千问的超大规模语言模型。我能够帮助你解答问题、提供信息、进行创作,或者只是闲聊。有什么我可以帮你的吗?😊

5.3 JsonOutputParser

JSON 解析器用于将大模型的自由文本输出转换为结构化JSON数据的工具。特别适用于需要严格结构化输出的场景,比如 API 调用、数据存储或下游任务处理。

JsonOutputParser 能够结合 Pydantic 模型进行数据验证,自动验证字段类型和内容(如字符串、数字、嵌套对象等)

使用 get_format_instructions() 获取JSON解析的格式化指令:

invoke调用

from typing import List

from langchain_core.output_parsers import JsonOutputParser
from langchain_ollama import ChatOllama
from pydantic import BaseModel, Field
from langchain_core.prompts import ChatPromptTemplate

# 定义数据模型
class PersonInfo(BaseModel):
    name: str = Field(description="用户姓名")
    age: int = Field(description="用户年龄")
    hobbies: List[str] = Field(description="用户爱好列表")


# 创建解析器
parser = JsonOutputParser(pydantic_object=PersonInfo)
# 获取格式化命令
format_instructions = parser.get_format_instructions()
print(format_instructions) # The output should be formatted as a JSON instance that conforms to the JSON schema below. 。。。

ollama_llm = ChatOllama(
    model="qwen3:8b",
    base_url="http://192.168.130.154:11434",  # 自定义地址
)
messages = [
    {"role": "system", "content": "请严格按照一下格式回答用户的问题:{format_instructions}"},
    {"role": "user", "content": "你好,请介绍一下蔡徐坤,他今年30岁,喜欢唱、跳rap篮球"},
]

# 创建模版, 并传入格式化命令,parser.get_format_instructions()表示使用解析器生成的格式化命令 
prompt_template = ChatPromptTemplate.from_messages(messages).partial(format_instructions=parser.get_format_instructions())

# 调用invoke方法,传入格式化后的提示
res_json = ollama_llm.invoke(prompt_template.format_messages())
print(res_json.content)  # {"name": "蔡徐坤", "age": 30, "hobbies": ["唱", "跳", "rap", "篮球"]}

链式调用

from typing import List

from langchain_core.output_parsers import JsonOutputParser
from langchain_ollama import ChatOllama
from pydantic import BaseModel, Field
from langchain_core.prompts import ChatPromptTemplate

# 定义数据模型
class PersonInfo(BaseModel):
    name: str = Field(description="用户姓名")
    age: int = Field(description="用户年龄")
    hobbies: List[str] = Field(description="用户爱好列表")


# 创建解析器
parser = JsonOutputParser(pydantic_object=PersonInfo)
# 获取格式化命令
format_instructions = parser.get_format_instructions()
print(format_instructions) # The output should be formatted as a JSON instance that conforms to the JSON schema below. 。。。

ollama_llm = ChatOllama(
    model="qwen3:8b",
    base_url="http://192.168.130.154:11434",  # 自定义地址
)
messages = [
    {"role": "system", "content": "请严格按照一下格式回答用户的问题:{format_instructions}"},
    {"role": "user", "content": "你好,请介绍一下蔡徐坤,他今年30岁,喜欢唱、跳rap篮球"},
]

# 创建模版, 并传入格式化命令
prompt_template = ChatPromptTemplate.from_messages(messages)

# 链式调用(Chain),分别是:提示词模版|模型|解析器
chain = prompt_template | ollama_llm | parser
res_josn = chain.invoke({"format_instructions": parser.get_format_instructions()})
print(res_josn) # {'name': '蔡徐坤', 'age': 30, 'hobbies': ['唱', '跳', 'rap', '篮球']}

六、Structured Outputs

可以要求模型按照给定的模式格式提供其响应,这有助于确保输出可以被轻松解析并在后续处理中使用。LangChain 支持多种模式类型和强制结构化输出的方法。

6.1 TypedDict

TypedDict 提供了一个使用 Python 内置类型的简单方案,但是没有验证功能。

from typing import TypedDict, Annotated, List
from langchain_ollama import ChatOllama


# 数据模型准备
class Animal(TypedDict):
    name: Annotated[str, "动物名称"]
    emoji: Annotated[str, "动物表情"]


class AnimalList(TypedDict):
    animals: Annotated[List[Animal], "动物和标签列表"]


ollama_llm = ChatOllama(
    model="qwen3:8b",
    base_url="http://192.168.130.154:11434",  # 自定义地址
)

messages = [
    {"role": "user", "content": "生成任意三种动物以及他们的emoji表情"},
]

# 指定模型处理后输出结构
llm_with_structured_output = ollama_llm.with_structured_output(AnimalList)
# 调用invoke方法
resp = llm_with_structured_output.invoke(messages)
print(resp) #{'animals': [{'name': '狗', 'emoji': '🐶'}, {'name': '猫', 'emoji': '🐱'}, {'name': '狮子', 'emoji': '🦁'}]}

6.2 Pydantic

pydantic 是一个第三方库,不是 Python 标准库的一部分。它最初于 2015 年发布,用于数据验证和设置管理。

主要特性
✅ 数据验证和设置管理
✅ 类型提示支持
✅ JSON 序列化/反序列化
✅ 与现代 Python 特性兼容

from typing import List
from langchain_ollama import ChatOllama
from pydantic.v1 import Field, BaseModel


# 数据模型准备
class Animal(BaseModel):
    name: str = Field(description="动物")
    emoji: str = Field(description="动物表情")


class AnimalList(BaseModel):
    animals: List[Animal] = Field(description="动物和标签列表")


ollama_llm = ChatOllama(
    model="qwen3:8b",
    base_url="http://192.168.130.154:11434",  # 自定义地址
)

messages = [
    {"role": "user", "content": "生成任意三种动物以及他们的emoji表情"},
]

# 指定模型处理后输出结构
llm_with_structured_output = ollama_llm.with_structured_output(AnimalList)
# 调用invoke方法
resp = llm_with_structured_output.invoke(messages)
print(resp)  # animals=[Animal(name='狗', emoji='🐶'), Animal(name='猫', emoji='🐱'), Animal(name='兔子', emoji='🐰')]

6.3 JSON Schema

若需最大程度的控制或互操作性,可以提供一个原始的 JSON Schema。详情可参考 https://platform.openai.com/docs/guides/structured-outputs/json-schema#supported-schemas

可以将原始响应与解析后的表示一起返回,可在调用 with_structured_output 时设置 include_raw=True 来实现。

 

import os
from langchain.chat_models import init_chat_model

llm = init_chat_model(
    model="openai/gpt-oss-20b:free",
    model_provider="openai",
    base_url="https://openrouter.ai/api/v1",
    api_key=os.getenv("OPENROUTER_API_KEY"),
)

schema = {
    "name": "animal_list",
    "schema": {
        "type": "array",
        "items": {
            "type": "object",
            "properties": {
                "animal": {"type": "string", "description": "动物名称"},
                "emoji": {"type": "string", "description": "动物的emoji表情"},
            },
            "required": ["animal", "emoji"],
        },
    },
}

messages = [{"role": "user", "content": "任意生成三种动物,以及他们的 emoji 表情"}]

llm_with_structured_output = llm.with_structured_output(
    schema, method="json_schema", include_raw=True
)
resp = llm_with_structured_output.invoke(messages)
print(resp)
print(resp["raw"])
print(resp["parsed"])
# [{'animal': '企鹅', 'emoji': '🐧'}, {'animal': '大象', 'emoji': '🐘'}, {'animal': '袋鼠', 'emoji': '🦘'}]

注意,如果使用ollama部署的模型,with_structured_output(schema, method="json_mode", include_raw=True)中一定是method="json_mode"

from langchain_ollama import ChatOllama


schema = {
    "name": "animal_list",
    "schema": {
        "type": "array",
        "items": {
            "type": "object",
            "properties": {
                "animal": {"type": "string", "description": "动物名称"},
                "emoji": {"type": "string", "description": "动物的emoji表情"},
            },
            "required": ["animal", "emoji"],
        },
    },
}

ollama_llm = ChatOllama(
    model="qwen3:8b",  # llama3.1:8b
    base_url="http://127.0.0.1:11434"
)

messages = [
    {"role": "user", "content": "生成任意三种动物以及他们的emoji表情"},
]

# 指定模型处理后输出结构
llm_with_structured_output = ollama_llm.with_structured_output(schema, method="json_mode", include_raw=True)

# 调用invoke方法
resp = llm_with_structured_output.invoke(messages)
print(resp["raw"].content)

 

 

 

 

 

 

 

 

posted @ 2025-12-08 18:17  酒剑仙*  阅读(6)  评论(0)    收藏  举报