模型(Models)详解:LLM与大语言模型、ChatModel与文本补全模型、模型参数调优、多模型切换与API密钥管理
模型这一块,说白了就是整个 LangChain 的发动机。你链写得再花哨,提示词写得再漂亮,最后都得交给模型去干活。我刚开始接触 LangChain 时,以为模型就是调个 API 完事,后来才发现里面的门道还真不少。
LLM 模型与大语言模型:到底有什么区别?
先把这个概念理清楚。很多人把 LLM 和大语言模型混着叫,其实在 LangChain 里,它们有明确的层级关系。
| 概念 | 说明 | LangChain 中的对应 |
|---|---|---|
| 大语言模型(LLM) | 泛指所有大规模预训练语言模型,如 GPT-4、Claude、文心一言 | 底层模型本身 |
| LLM 类 | LangChain 对文本补全模型的抽象封装 | from langchain.llms import OpenAI |
| ChatModel 类 | LangChain 对对话模型的抽象封装 | from langchain.chat_models import ChatOpenAI |
我个人习惯把 LLM 类叫做「老派模型」,ChatModel 叫做「新派模型」。为什么这么分?因为 LLM 类接收的是纯文本字符串,返回的也是纯文本。而 ChatModel 接收的是消息列表(HumanMessage、AIMessage、SystemMessage),返回的也是结构化的消息对象。
核心区别一句话:LLM 类做文本补全,ChatModel 做对话补全。现在主流应用基本都用 ChatModel,但有些遗留系统或特定场景仍然需要 LLM 类。
ChatModel 与文本补全模型:选哪个?
我在项目中遇到过这样一个场景:做一个客服问答系统,一开始用了 LLM 类,每次都要手动拼接对话历史,代码又臭又长。后来换成 ChatModel,几行代码就搞定了。
来看看它们的具体差异:
| 特性 | LLM(文本补全) | ChatModel(对话模型) |
|---|---|---|
| 输入格式 | 纯字符串 | 消息列表(HumanMessage, SystemMessage 等) |
| 输出格式 | 纯字符串 | AIMessage 对象(包含 content 和额外信息) |
| 适用场景 | 文本生成、摘要、翻译 | 多轮对话、角色扮演、指令跟随 |
| 典型模型 | text-davinci-003(已退役) | gpt-3.5-turbo, gpt-4 |
代码示例对比一下:
# LLM 类 —— 文本补全
from langchain.llms import OpenAI
llm = OpenAI(model="text-davinci-003")
result = llm("给我写一首关于春天的诗")
print(result) # 直接输出诗的内容
# ChatModel 类 —— 对话模型
from langchain.chat_models import ChatOpenAI
from langchain.schema import HumanMessage, SystemMessage
chat = ChatOpenAI(model="gpt-3.5-turbo")
messages = [
SystemMessage(content="你是一个诗人"),
HumanMessage(content="给我写一首关于春天的诗")
]
result = chat(messages)
print(result.content) # 通过 .content 获取文本
我的建议:新项目直接用 ChatModel。除非你是在做纯文本处理任务(比如批量生成文章),否则 ChatModel 的灵活性远高于 LLM 类。
模型参数调优:temperature、max_tokens 等
调参数这件事,我踩过不少坑。记得有一次做代码生成,temperature 设成了 0.9,结果模型每次生成的代码都不一样,调试起来简直崩溃。后来把 temperature 降到 0.1,问题就解决了。
核心参数一览:
| 参数 | 作用 | 取值范围 | 我的经验值 |
|---|---|---|---|
| temperature | 控制输出的随机性。越高越有创意,越低越确定 | 0.0 ~ 2.0 | 代码/事实类用 0.1,创意写作用 0.7 |
| max_tokens | 限制输出最大 token 数 | 取决于模型(通常 1~4096) | 先设 512,不够再加 |
| top_p | 核采样,只考虑概率累积到 p 的 token | 0.0 ~ 1.0 | 一般不动,调 temperature 就够了 |
| frequency_penalty | 惩罚重复出现的 token | -2.0 ~ 2.0 | 需要去重时设 0.5 |
| presence_penalty | 鼓励谈论新话题 | -2.0 ~ 2.0 | 对话多样性不够时设 0.3 |
实际调参代码:
from langchain.chat_models import ChatOpenAI
chat = ChatOpenAI(
model="gpt-4",
temperature=0.3, # 低温度,保证稳定性
max_tokens=1024, # 限制输出长度
frequency_penalty=0.5, # 减少重复
presence_penalty=0.2 # 稍微鼓励新内容
)
response = chat([
HumanMessage(content="用 Python 写一个快速排序算法")
])
print(response.content)
避坑指南:我曾经把 max_tokens 设得太小(比如 50),结果模型输出被截断,返回的内容不完整。另一个坑是 temperature 和 top_p 同时调,官方建议只调其中一个,否则效果会互相干扰。
多模型切换与 API 密钥管理
实际项目中,你不可能只用一家模型。我做过一个系统,白天用 GPT-4 处理复杂问题,晚上用 GPT-3.5-turbo 处理简单请求,既保证质量又省钱。多模型切换的核心就是「工厂模式」。
先看一个简单的多模型切换实现:
from langchain.chat_models import ChatOpenAI, ChatAnthropic
from langchain.llms import OpenAI
class ModelFactory:
def __init__(self):
self._models = {}
def register_model(self, name, model_instance):
self._models[name] = model_instance
def get_model(self, name):
if name not in self._models:
raise ValueError(f"模型 {name} 未注册")
return self._models[name]
# 使用示例
factory = ModelFactory()
factory.register_model("gpt4", ChatOpenAI(model="gpt-4", temperature=0.1))
factory.register_model("gpt35", ChatOpenAI(model="gpt-3.5-turbo", temperature=0.3))
factory.register_model("claude", ChatAnthropic(model="claude-2"))
# 根据需求切换
current_model = factory.get_model("gpt4")
result = current_model([HumanMessage(content="解释量子计算")])
API 密钥管理这块,我见过最糟糕的做法是把密钥硬编码在代码里,然后不小心提交到 GitHub。嗯,那画面太美我不敢看。
正确的做法:
- 环境变量:用
os.getenv()读取,配合.env文件 - 密钥轮换:多个密钥轮流使用,避免单个密钥超限
- 权限最小化:只给模型调用权限,不给管理权限
一个带密钥轮换的封装:
import os
import itertools
from langchain.chat_models import ChatOpenAI
class KeyRotatingChat:
def __init__(self, keys: list, **kwargs):
self._keys = itertools.cycle(keys)
self._kwargs = kwargs
self._current_key = None
def _get_chat(self):
self._current_key = next(self._keys)
return ChatOpenAI(
openai_api_key=self._current_key,
**self._kwargs
)
def __call__(self, messages):
chat = self._get_chat()
try:
return chat(messages)
except Exception as e:
print(f"密钥 {self._current_key[:8]}... 失败: {e}")
# 换下一个密钥重试
chat = self._get_chat()
return chat(messages)
# 使用
keys = [
os.getenv("OPENAI_API_KEY_1"),
os.getenv("OPENAI_API_KEY_2"),
os.getenv("OPENAI_API_KEY_3")
]
chat = KeyRotatingChat(keys, model="gpt-4", temperature=0.1)
小技巧:我习惯把密钥放在 .env 文件中,然后用 python-dotenv 加载。这样不同环境(开发、测试、生产)可以有不同的密钥配置,互不干扰。
本章知识体系
下面这张图把模型相关的核心概念串起来了,你可以对照着梳理自己的理解: