深入浅出 LangChain —— 第三章:模型抽象层

1 阅读15分钟

📖 本章学习目标

  • ✅ 理解模型抽象层的设计理念和核心价值
  • ✅ 掌握两种模型配置方式及其适用场景
  • ✅ 接入主流 LLM Provider(OpenAI/Anthropic/Google/Ollama)
  • ✅ 理解消息格式和多轮对话的工作原理
  • ✅ 实现流式输出提升用户体验
  • ✅ 使用结构化输出获取类型安全的数据
  • ✅ 根据业务场景选择合适的模型

一、为什么需要模型抽象层

在实际开发中,你可能会遇到这样的困境:

场景: 你的团队正在用 OpenAI 的 GPT-4o 开发一个 AI 助手。突然有一天:

  • OpenAI 涨价了 50%
  • 或者某个功能在 Claude 上表现更好
  • 或者公司要求数据不能出境,必须用本地模型

如果没有抽象层,你需要重写大量代码来切换模型。这就是 LangChain.js 模型抽象层要解决的问题。

1、传统方式的痛点

如果直接使用各家的原生 SDK,代码会是这样:

// ❌ 直接绑定 OpenAI SDK
import OpenAI from "openai";
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const response = await openai.chat.completions.create({
  model: "gpt-4o",
  messages: [{ role: "user", content: "你好" }]
});

想切换到 Anthropic?需要完全重写:

// ❌ 切换到 Anthropic,API 完全不同
import Anthropic from "@anthropic-ai/sdk";
const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
const response = await anthropic.messages.create({
  model: "claude-3-opus",
  messages: [{ role: "user", content: "你好" }]
});

问题所在:

  • API 格式不同(参数名、返回结构都不一样)
  • 认证方式不同
  • 错误处理逻辑不同
  • 流式输出的实现方式不同

2、LangChain.js 的解决方案

LangChain.js 提供统一的接口层,无论底层用什么模型,调用方式完全一致:

flowchart TB
    App["你的应用代码<br/>createAgent({ model: '...' })"]

    subgraph LangChain["LangChain.js 抽象层"]
        direction LR
        Interface["统一接口<br/>ChatModel"]
    end

    subgraph Providers["各 Provider SDK"]
        direction LR
        OAI["@langchain/openai<br/>GPT-4o, o1..."]
        ANT["@langchain/anthropic<br/>Claude Opus/Sonnet..."]
        GGL["@langchain/google-genai<br/>Gemini 2.0..."]
        OLL["@langchain/ollama<br/>本地模型"]
    end

    App --> Interface
    Interface --> OAI
    Interface --> ANT
    Interface --> GGL
    Interface --> OLL

    style App fill:#e8f4fd,stroke:#1890ff
    style Interface fill:#f6ffed,stroke:#52c41a,stroke-width:3px

核心优势:

优势说明实际价值
快速切换在不同模型间做对比测试,只需改一个字符串节省数小时的重构时间
避免供应商锁定即使某个 Provider 涨价或出故障,迁移成本极低降低商业风险
统一的流式接口不用为每个 Provider 写不同的解析逻辑代码复用率提升 80%+
统一的错误处理所有 Provider 的错误格式标准化简化异常处理逻辑

二、两种模型配置方式

LangChain.js 支持两种方式配置模型,分别适用于不同场景。

1、字符串标识符(推荐用于大多数场景)

在 v1.x 中,最简洁的方式是直接传 "provider:model-name" 格式的字符串:

(1)OpenAI 系列

import { createAgent } from "langchain";

// GPT-4o:综合能力最强,适合复杂任务
const agent1 = createAgent({ model: "openai:gpt-4o" });

// GPT-4o-mini:性价比高,适合简单任务
const agent2 = createAgent({ model: "openai:gpt-4o-mini" });

// o1-mini:专门优化的推理模型,适合数学和逻辑
const agent3 = createAgent({ model: "openai:o1-mini" });

(2)Anthropic 系列

// Claude Opus:最强推理能力,适合复杂分析
const agent4 = createAgent({ model: "anthropic:claude-opus-4-5" });

// Claude Sonnet:平衡性能和成本
const agent5 = createAgent({ model: "anthropic:claude-sonnet-4-6" });

// Claude Haiku:速度最快,成本最低
const agent6 = createAgent({ model: "anthropic:claude-haiku-3-5" });

(3)Google 系列

// Gemini 2.0 Flash:免费额度高,响应速度快
const agent7 = createAgent({ model: "google:gemini-2.0-flash" });

(4)本地模型(Ollama)

// Llama 3.2:Meta 开源模型,可在本地运行
const agent8 = createAgent({ model: "ollama:llama3.2" });

这种方式的优势:

  • ✅ 代码简洁,一行搞定
  • ✅ 自动处理认证和配置
  • ✅ 适合快速原型开发和大多数生产场景

2、显式实例化(需要精细配置时)

当你需要配置温度、超时、最大 Token 等参数时,直接实例化 ChatModel 类:

import { ChatOpenAI } from "@langchain/openai";
import { createAgent } from "langchain";

// 精细配置 OpenAI 模型
const model = new ChatOpenAI({
  model: "gpt-4o",       // 模型名称
  temperature: 0.1,      // 输出随机性(0: 最确定,1: 最随机)
  maxTokens: 2000,       // 限制输出长度
  timeout: 30000,        // 超时时间(毫秒)
  maxRetries: 3,         // 失败自动重试次数
});

const agent = createAgent({ model, tools: [] });

常用配置参数详解:

参数说明建议值影响
temperature控制输出随机性工具调用:00.2
创意写作:0.7
1.0
值越高,输出越有创造性但也越不可控
maxTokens最大输出 Token 数根据任务设置
一般 1000~4000
限制输出长度,控制成本
timeout单次调用超时时间30000~60000 ms防止网络问题导致长时间挂起
maxRetries失败自动重试次数2~3 次处理网络抖动和临时故障
topP核采样参数0.8~0.95与 temperature 配合控制多样性

💡 何时使用显式实例化?

  • 需要精确控制温度(如结构化输出时必须用低温度)
  • 需要自定义超时和重试策略
  • 需要使用 Provider 特有的高级参数
  • 需要在运行时动态调整配置

三、主流 Provider 接入详解

1、OpenAI(GPT 系列)

OpenAI 是最成熟的 LLM Provider,工具调用能力最强。

(1)安装

pnpm add @langchain/openai

(2)基础用法

import { ChatOpenAI } from "@langchain/openai";

// 创建模型实例
const model = new ChatOpenAI({
  model: "gpt-4o",
  temperature: 0,  // 确定性输出
});

// 直接调用模型(不经过 Agent)
const response = await model.invoke([
  { role: "system", content: "你是一个有帮助的助手。" },
  { role: "user", content: "用一句话解释什么是递归。" },
]);

console.log(response.content);
// 输出:递归是函数调用自身来解决更小规模同类问题的编程技巧。

代码解读:

  • 第 4-7 行:创建模型实例,指定模型和温度
  • 第 11-12 行:构建消息列表(系统消息 + 用户消息)
  • 第 10 行:调用模型并等待响应
  • 第 15 行:输出响应内容

(3)OpenAI 特有参数

const model = new ChatOpenAI({
  model: "gpt-4o",
  
  // 强制开启 JSON 输出模式
  // 注意:这只是保证输出是合法 JSON,不保证字段结构
  responseFormat: { type: "json_object" },
  
  // 指定使用的 API Base URL
  // 适用于代理服务器或兼容 OpenAI 接口的服务
  configuration: {
    baseURL: "https://your-proxy.com/v1",
  },
});

⚠️ 注意responseFormat: { type: "json_object" } 只能保证输出是合法的 JSON,但不能保证字段名称和类型符合预期。如果需要严格的结构化输出,应该使用后面介绍的 withStructuredOutput() 方法。

2、Anthropic(Claude 系列)

Claude 系列在长文本处理和复杂推理方面表现优秀。

(1)安装

pnpm add @langchain/anthropic

(2)基础用法

import { ChatAnthropic } from "@langchain/anthropic";

const model = new ChatAnthropic({
  model: "claude-opus-4-5",  // Claude 最强模型
  temperature: 0,
  maxTokens: 4096,           // Claude 需要显式指定 maxTokens
});

const response = await model.invoke([
  { role: "user", content: "用一句话解释什么是递归。" },
]);

console.log(response.content);

Claude 的特点:

特点说明适用场景
超长上下文Opus/Sonnet 支持 200K tokens处理整本书、大型代码库
优秀的代码理解对代码结构和逻辑把握准确代码审查、重构建议
复杂的推理能力在多步推理任务中表现出色数据分析、逻辑推导
自然的对话风格回答更加流畅和人性化客服、教育应用

💡 重要提示:Anthropic 的 API 要求必须设置 maxTokens 参数,否则会报错。这与 OpenAI 不同(OpenAI 有默认值)。

3、Google(Gemini 系列)

Gemini 的优势是免费额度高,多模态能力强。

(1)安装

pnpm add @langchain/google-genai

(2)基础用法

import { ChatGoogleGenerativeAI } from "@langchain/google-genai";

const model = new ChatGoogleGenerativeAI({
  model: "gemini-2.0-flash",
  temperature: 0,
});

const response = await model.invoke([
  { role: "user", content: "介绍一下你自己。" },
]);

console.log(response.content);

Gemini 的优势:

  • 每月有大量免费额度(适合个人开发者和小团队)
  • 原生支持图像、音频等多模态输入
  • 响应速度快

4、Ollama(本地模型)

Ollama 允许你在本地运行开源模型,数据不离开你的机器。

(1)安装 Ollama

# macOS/Linux
curl -fsSL https://ollama.ai/install.sh | sh

# Windows
# 从 https://ollama.ai/download 下载安装包

(2)下载并运行模型

# 下载 Llama 3.2 模型(约 2GB)
ollama pull llama3.2

# 测试运行
ollama run llama3.2

# 查看已安装的模型
ollama list

(3)安装 LangChain 集成

pnpm add @langchain/ollama

(4)使用本地模型

import { ChatOllama } from "@langchain/ollama";

const model = new ChatOllama({
  model: "llama3.2",
  baseUrl: "http://localhost:11434",  // Ollama 默认地址
  temperature: 0,
});

const response = await model.invoke([
  { role: "user", content: "你好!" },
]);

console.log(response.content);

💡 什么时候用本地模型?

优势:

  • ✅ 数据隐私:敏感数据不会发送到外部 API
  • ✅ 零成本:本地运行完全免费
  • ✅ 离线可用:不需要网络连接

劣势:

  • ❌ 硬件要求高:需要较好的 GPU(至少 8GB 显存)
  • ❌ 推理速度慢:比 API 慢 5-10 倍
  • ❌ 质量稍逊:开源模型在复杂推理上通常不如 GPT-4o 或 Claude

典型场景:

  • 企业内部文档问答(数据敏感)
  • 开发阶段高频测试(控制成本)
  • 边缘设备部署(无网络环境)

四、消息格式:理解多角色对话

LangChain.js 的消息系统与 OpenAI 的 Chat Completion 格式对齐。理解消息类型对于写好 Prompt 至关重要。

1、四种消息角色

flowchart LR
    S["system<br/>系统消息<br/>设定角色和规则"] --> H
    H["human<br/>用户消息<br/>用户的输入"] --> A
    A["tool<br/>工具返回的信息"]-->H2
    H2["assistant<br/>助手消息<br/>模型的历史回复"]
    H-->H2

    style S fill:#e8f4fd,stroke:#1890ff
    style H fill:#f6ffed,stroke:#52c41a
    style A fill:#fff7e6,stroke:#fa8c16
    style H2 fill:#f6ffed,stroke:#52c41a
消息类型角色用途示例
SystemMessagesystem设定模型的角色、行为规范、背景知识"你是一个专业的 Python 程序员"
HumanMessagehuman/user用户的输入"如何排序一个列表?"
AIMessageassistant模型的历史回复(多轮对话中用到)"你可以使用 sorted() 函数..."
ToolMessagetool工具调用的返回结果(Agent 内部使用)"{result: [1,2,3]}"

2、构建多轮对话

import { ChatOpenAI } from "@langchain/openai";
import { 
  HumanMessage, 
  AIMessage, 
  SystemMessage 
} from "@langchain/core/messages";

const model = new ChatOpenAI({ model: "gpt-4o" });

// 构建多轮对话历史
const messages = [
  // 第一步:系统消息(设定角色)
  new SystemMessage("你是一个专业的 TypeScript 开发助手。"),
  
  // 第二步:第一轮对话
  new HumanMessage("什么是泛型?"),
  new AIMessage("泛型是 TypeScript 中允许类型作为参数的机制,可以编写适用于多种类型的通用代码。"),
  
  // 第三步:当前问题(基于上下文的追问)
  new HumanMessage("能给一个实际的例子吗?"),
];

const response = await model.invoke(messages);
console.log(response.content);
// 输出会基于上下文,给出泛型的具体代码示例

执行流程:

  1. 系统消息告诉模型它的角色和专业领域
  2. 第一轮对话建立了基础知识
  3. 第二轮问题基于第一轮的上下文,模型会给出更深入的回答

💡 实际应用提示

在实际 Agent 开发中,你不需要手动管理这个消息列表——LangChain.js 会自动维护对话历史。但理解消息格式,对于调试 Prompt 和理解 Agent 的工作原理很有帮助。

3、使用对象简写(推荐)

除了使用消息类,你也可以直接用对象表示(更简洁):

const messages = [
  { role: "system", content: "你是一个助手。" },
  { role: "user", content: "你好!" },
  { role: "assistant", content: "你好!有什么可以帮助你的?" },
  { role: "user", content: "谢谢!" },
];

const response = await model.invoke(messages);

两种方式的区别:

  • 消息类(HumanMessage 等):提供更多元数据和方法,适合复杂场景
  • 对象简写:代码更简洁,适合大多数场景

五、流式输出:提升用户体验

LLM 生成文本是逐 Token 的过程(类似打字机逐个字符输出),等全部生成完才返回会有明显的等待感。

举例: 如果模型要生成 500 个 token,可能需要 5-10 秒。如果等全部生成完再显示,用户会觉得"卡住了"。

流式输出(Streaming) 可以让用户实时看到生成的内容,大幅改善体验。

1、基础流式调用

import { ChatOpenAI } from "@langchain/openai";

const model = new ChatOpenAI({ model: "gpt-4o" });

// 使用 stream() 方法替代 invoke()
const stream = await model.stream([
  { role: "user", content: "用 100 字介绍一下 TypeScript。" },
]);

// 逐块处理输出
process.stdout.write("回答:");
for await (const chunk of stream) {
  // chunk.content 是当前块的文本内容
  process.stdout.write(chunk.content as string);
}
process.stdout.write("\n");

代码解读:

  • 第 5 行:使用 stream() 方法,返回一个异步迭代器
  • 第 10-13 行:使用 for await...of 循环遍历流式数据
  • 第 14 行:每收到一块数据就立即输出(实现打字机效果)

效果:打字机效果

回答:TypeScript是JavaScript的超集,添加了静态类型系统。它能在编译时发现类型错误,提高代码可维护性。TypeScript支持接口、泛型、装饰器等高级特性,被广泛应用于大型前端项目。→

2、在 Agent 中获取流式输出

import { createAgent, tool } from "langchain";
import { z } from "zod";

const agent = createAgent({
  model: "openai:gpt-4o",
  tools: [],
});

// streamMode: "messages" 可以获取逐 Token 的流式输出
const stream = await agent.stream(
  { messages: [{ role: "user", content: "写一首关于编程的短诗。" }] },
  { streamMode: "messages" }
);

for await (const [message, _metadata] of stream) {
  // 只处理 AI 的文本输出块
  if (message.role === "assistant" && typeof message.content === "string") {
    process.stdout.write(message.content);
  }
}

streamMode 的三种模式:

模式返回格式适用场景
"messages"[message, metadata] 元组对话界面,逐 Token 显示
"values"完整状态对象需要了解 Agent 的中间思考过程
"updates"增量变化部分只关心状态变化的场景

⚠️ 注意:不同 streamMode 的返回格式不同,需要根据你的需求选择合适的模式。

3、Web 应用中的流式输出

在真实的 Web 应用中,你会通过 Server-Sent Events (SSE)WebSocket 将流式数据推送到前端:

// Express.js 示例
app.get("/chat", async (req, res) => {
  res.setHeader("Content-Type", "text/event-stream");
  res.setHeader("Cache-Control", "no-cache");
  
  const stream = await agent.stream({
    messages: [{ role: "user", content: req.query.q }]
  }, { streamMode: "messages" });
  
  for await (const [message] of stream) {
    if (message.role === "assistant") {
      res.write(`data: ${JSON.stringify({ content: message.content })}\n\n`);
    }
  }
  
  res.end();
});

六、结构化输出:让模型返回可靠的数据

LLM 默认返回自由格式的文本,但在实际应用中,我们往往需要模型输出结构化的数据。

典型场景:

  • 从文章中提取人名、地点、时间等实体
  • 将用户描述解析为 JSON 格式的配置
  • 对文本进行分类并返回固定类别
  • 生成符合特定 Schema 的代码

1、为什么需要结构化输出?

问题场景: 假设你要从新闻中提取信息:

// ❌ 不使用结构化输出
const response = await model.invoke([
  { role: "user", content: "提取这篇文章的标题、摘要和标签" }
]);

// 返回的是自由文本,难以解析
console.log(response.content);
// "标题:AI技术突破\n摘要:最近AI领域...\n标签:AI, 技术"

// 你需要写复杂的正则表达式来解析
const title = response.content.match(/标题:(.*)/)?.[1];

解决方案: 使用 withStructuredOutput() + Zod Schema

// ✅ 使用结构化输出
import { ChatOpenAI } from "@langchain/openai";
import { z } from "zod";

// 定义期望的输出结构
const ArticleSchema = z.object({
  title: z.string().describe("文章标题"),
  summary: z.string().describe("文章摘要,不超过 100 字"),
  tags: z.array(z.string()).describe("文章标签列表,3-5 个"),
  difficulty: z.enum(["入门", "中级", "高级"]).describe("文章难度"),
});

const model = new ChatOpenAI({ model: "gpt-4o" });

// 绑定结构化输出 Schema
const structuredModel = model.withStructuredOutput(ArticleSchema);

const result = await structuredModel.invoke([
  {
    role: "user",
    content: `分析这篇文章并提取信息:
    "本文介绍了如何使用 React Hooks 优化组件性能,包括 useMemo、useCallback 和 memo 的使用场景与最佳实践,适合有 React 基础的开发者。"`,
  },
]);

// result 是完全类型安全的!TypeScript 知道它的结构
console.log(result.title);      // string
console.log(result.summary);    // string
console.log(result.tags);       // string[]
console.log(result.difficulty); // "入门" | "中级" | "高级"

优势对比:

方式返回值类型类型安全可靠性易用性
自由文本string❌ 需要手动解析
JSON 模式any⚠️ 部分⚠️ 字段可能缺失⚠️
结构化输出定义的 Schema✅ 完全✅ 字段保证存在

2、提取多个实体

import { ChatOpenAI } from "@langchain/openai";
import { z } from "zod";

// 定义联系人 Schema
const ContactSchema = z.object({
  contacts: z.array(
    z.object({
      name: z.string().describe("姓名"),
      email: z.string().email().optional().describe("邮箱,如果有的话"),
      phone: z.string().optional().describe("电话号码,如果有的话"),
      company: z.string().optional().describe("公司名称,如果有的话"),
    })
  ).describe("提取出的联系人列表"),
});

const model = new ChatOpenAI({ model: "gpt-4o" });
const extractor = model.withStructuredOutput(ContactSchema);

const text = `
  本次会议参与者:
  张三(zhangsan@example.com,18800001111,来自 ABC 科技)
  李四(lisi@company.com,来自 XYZ 集团)
  王五(13900002222)
`;

const result = await extractor.invoke([
  { role: "user", content: `从以下文本中提取联系人信息:\n${text}` }
]);

console.log(result.contacts);
// [
//   { name: "张三", email: "zhangsan@example.com", phone: "18800001111", company: "ABC 科技" },
//   { name: "李四", email: "lisi@company.com", company: "XYZ 集团" },
//   { name: "王五", phone: "13900002222" }
// ]

关键点:

  • 使用 .optional() 标记可选字段
  • 使用 .email() 等验证器确保格式正确
  • describe() 中详细说明每个字段的含义

3、strict 模式(v1.2.0 新增)

v1.2.0 中新增了 strict 参数,强制模型严格按照 Schema 输出:

const structuredModel = model.withStructuredOutput(ArticleSchema, {
  strict: true,  // 严格模式
});

strict 模式的作用:

  • ✅ 减少字段遗漏的概率
  • ✅ 减少格式错误的概率
  • ✅ 提高输出的可靠性
  • ⚠️ 可能会略微增加延迟

💡 最佳实践:在生产环境中,始终开启 strict: true,除非你有特殊理由。

4、结构化输出 vs JSON 模式

一些 Provider(如 OpenAI)支持 responseFormat: { type: "json_object" } 的 JSON 模式,但这只能保证输出是合法的 JSON,不能保证字段名称和类型符合预期。

对比:

// ❌ JSON 模式:只保证是合法 JSON
const model1 = new ChatOpenAI({
  model: "gpt-4o",
  responseFormat: { type: "json_object" }
});
// 返回可能是:{"title": "..."} 或 {"Title": "..."} 或 {"name": "..."}
// 字段名不确定!

// ✅ 结构化输出:保证字段名和类型都符合 Schema
const model2 = new ChatOpenAI({ model: "gpt-4o" });
const structured = model2.withStructuredOutput(ArticleSchema);
// 返回一定是:{ title: string, summary: string, tags: string[], ... }
// 字段名和类型都确定!

结论: 在需要精确数据的场景,始终优先使用 withStructuredOutput()


七、动态模型选择(高级用法)

在某些场景下,你可能希望根据任务复杂度动态切换模型——简单任务用便宜的小模型,复杂任务用性能更强的大模型,以此平衡效果和成本。

实现思路

LangChain.js 通过中间件实现这一点:

import { ChatOpenAI } from "@langchain/openai";
import { createAgent, createMiddleware } from "langchain";

// 定义两个不同档次的模型
const basicModel = new ChatOpenAI({ 
  model: "gpt-4o-mini"     // 低成本
});
const advancedModel = new ChatOpenAI({ 
  model: "gpt-4o"          // 高质量
});

// 创建动态模型选择中间件
const dynamicModelMiddleware = createMiddleware({
  name: "DynamicModelSelection",
  
  // 拦截模型调用请求
  wrapModelCall: (request, handler) => {
    const messageCount = request.messages.length;
    
    // 超过 10 条消息时,认为任务较复杂,切换到高级模型
    const selectedModel = messageCount > 10 ? advancedModel : basicModel;
    
    console.log(`选择模型: ${selectedModel.model} (消息数: ${messageCount})`);
    
    // 用选中的模型处理请求
    return handler({ ...request, model: selectedModel });
  },
});

// 创建 Agent 并注册中间件
const agent = createAgent({
  model: basicModel,  // 默认模型
  tools: [],
  middleware: [dynamicModelMiddleware],
});

工作原理:

  1. 中间件拦截每次模型调用请求
  2. 根据请求内容(如消息数量)决定使用哪个模型
  3. 用选中的模型处理请求

💡 实际应用场景:

  • 客服系统:简单问题用 mini 模型,复杂投诉用完整版
  • 代码助手:语法查询用 mini,架构设计用完整版
  • 成本控制:90% 的请求用便宜模型,只有 10% 用昂贵模型

注意: 中间件系统会在第七章详细讲解,这里只是展示一个直觉性的例子。


八、模型选型建议

在实际项目中,如何选择合适的模型?以下是一份参考矩阵:

场景化推荐

场景推荐模型理由成本参考
工具调用密集的 Agentopenai:gpt-4o工具调用准确率高,多工具并行能力强$$$
长文档分析(>50K tokens)anthropic:claude-opus-4-5超长上下文窗口,长文阅读理解优秀$$$$
高频低延迟场景openai:gpt-4o-mini
anthropic:claude-haiku-3-5
响应快(<1s),成本低$
复杂推理 / 数学openai:o1-mini
anthropic:claude-opus-4-5
推理能力专门优化$$$-$$$$
代码生成anthropic:claude-sonnet-4-6
openai:gpt-4o
代码质量高,理解上下文能力强$$-$$$
本地部署 / 数据敏感ollama:llama3.2
ollama:qwen2.5
数据不出本地,免费使用免费
开发调试阶段任何 *-mini / *-flash 版本响应快、成本低,适合高频测试$

成本对比(每 1M tokens)

模型输入价格输出价格相对成本
GPT-4o$2.50$10.00基准
GPT-4o-mini$0.15$0.606%
Claude Opus$15.00$75.00600%
Claude Sonnet$3.00$15.00120%
Claude Haiku$0.25$1.2510%
Gemini 2.0 Flash$0.10$0.404%

💡 实践建议:

  1. 开发阶段:优先用便宜的 mini 模型,功能跑通后再切换到高质量模型做效果对比
  2. A/B 测试:同时用两个模型处理相同任务,对比质量和成本
  3. 监控成本:接入 LangSmith,实时监控 Token 消耗和费用
  4. 缓存策略:对重复问题使用缓存,避免重复调用 LLM

九、本章小结

模型抽象层是 LangChain.js 的基础设施之一。这一章我们学习了:

📝 核心知识点回顾

  1. 统一接口的价值:无论哪家 Provider,调用方式一致,避免供应商锁定
  2. 两种配置方式
    • 字符串标识符:简洁,适合大多数场景
    • 显式实例化:灵活,适合需要精细控制的场景
  3. 四大 Provider:OpenAI、Anthropic、Google、Ollama 的特点和接入方式
  4. 消息格式system / human / assistant / tool 四种消息类型的作用
  5. 流式输出stream() 方法和三种 streamMode 的选择
  6. 结构化输出withStructuredOutput() + Zod Schema实现类型安全的结构化返回
  7. 模型选型:根据场景选择合适模型的参考框架

🎯 动手练习

尝试完成以下练习,巩固所学知识:

练习 1:切换模型对比 创建一个简单的问答 Agent,分别用 gpt-4ogpt-4o-mini 回答同一个复杂问题,对比响应时间和质量差异。

练习 2:结构化输出实战 设计一个 Schema,从电影评论中提取:评分(1-5)、情感倾向(正面/负面/中性)、提到的演员名单。

练习 3:流式输出 Web 应用 创建一个简单的 Express.js 应用,实现 SSE 流式输出,在前端页面上实现打字机效果。

练习 4:本地模型测试 安装 Ollama 和 Llama 3.2,对比本地模型和 GPT-4o 在代码生成任务上的表现。

📚 延伸阅读


下一章:《第四章 —— 提示词工程(Prompt Engineering)》(待撰写)