Langchainjs - 聊天模型

2 阅读12分钟

02-Chat Models & Basic Interactions 章节总结

章节概述

本章教授如何与 AI 模型进行自然对话的艺术。学习如何维护多轮对话的上下文、实时流式响应以提升用户体验、优雅地处理错误和重试逻辑,以及控制 AI 创造力的关键参数(如 temperature)和理解 token 使用以优化成本。

学习目标

完成本章后,你应该能够:

  • ✅ 与 AI 进行多轮对话
  • ✅ 流式传输响应以获得更好的用户体验
  • ✅ 优雅地处理错误
  • ✅ 使用参数控制模型行为
  • ✅ 理解 token 使用

主要要点

1. 知识渊博的朋友类比

想象你正在和一位知识渊博的朋友喝咖啡。

当你们交谈时:

  • 💬 你们有来回对话(不仅仅是一个问题)
  • 🧠 他们记得你之前说的话(对话上下文)
  • 🗣️ 他们边思考边说话(流式响应)
  • 😊 根据你的偏好调整语气(模型参数)
  • ⚠️ 有时他们需要澄清(错误处理)

聊天模型也是如此!

与简单的一次性问题不同,聊天模型擅长:

  • 多轮对话
  • 维护上下文
  • 实时流式响应
  • 适应行为

2. 多轮对话

对话历史如何工作

聊天模型实际上并不"记住"之前的消息。相反,你在每次发送新消息时都会发送整个对话历史。

就像这样:每次发送消息时,你都在向 AI 展示到目前为止的完整对话线程。

LangChain 中的消息类型

LangChain 提供三种核心消息类型用于构建对话:

类型用途示例
SystemMessage设置 AI 行为和个性new SystemMessage("You are a helpful coding tutor")
HumanMessage用户输入和问题new HumanMessage("What is TypeScript?")
AIMessage带有元数据的 AI 响应model.invoke() 返回,包含 contentusage_metadataid
创建消息

本课程使用消息类来创建消息。这种方法明确且对初学者友好:

import { SystemMessage, HumanMessage, AIMessage } from "langchain";

const messages = [
  new SystemMessage("You are a helpful assistant"),
  new HumanMessage("Hello!")
];

为什么使用消息类?

  • 清晰明确 - 容易理解每条消息代表什么
  • 类型安全 - TypeScript 在运行前捕获错误
  • 更好的自动完成 - 编辑器帮助你更快编写代码
  • 一致的模式 - 整个课程使用相同的方法
Code 示例 1: 01-multi-turn.ts

功能:演示如何维护对话上下文

关键代码

// 构建对话数组
const messages: BaseMessage[] = [
  new SystemMessage("You are a helpful coding tutor who gives clear, concise explanations."),
  new HumanMessage("What is TypeScript?"),
];

// 获取 AI 响应并添加到历史记录
const response1 = await model.invoke(messages);
messages.push(new AIMessage(String(response1.content)));

// 继续对话 - AI 记住上下文
messages.push(new HumanMessage("Can you show me a simple example?"));
const response2 = await model.invoke(messages);

工作原理

  1. Messages 数组保存整个对话 - 创建一个数组存储所有消息(系统、人类和 AI)
  2. 每个响应都添加到历史记录中 - 获取响应后,将其推送到 messages 数组
  3. AI 可以引用之前的消息 - 当问"Can you show me a simple example?"时,AI 知道我们在谈论第一次交流中的 TypeScript
  4. 每次都发送完整历史记录 - 每次调用 invoke() 时,发送完整的对话历史

为什么这很重要:AI 实际上不"记住"任何东西。它只知道你发送的 messages 数组中的内容。这就是为什么维护对话历史对于多轮对话至关重要。

3. 流式响应

当你问一个复杂问题时,等待整个响应可能会感觉很慢。流式传输在生成响应时逐字发送。

就像看着朋友边思考边说话,而不是等待他们完成整个想法。

Code 示例 2: 02-streaming.ts

功能:比较流式和非流式响应,演示流式响应的即时反馈效果

关键代码

// 流式传输响应而不是一次性等待
const stream = await model.stream("Explain how the internet works in 3 paragraphs.");

// 在每个块到达时循环处理
for await (const chunk of stream) {
  process.stdout.write(chunk.content);  // 立即显示而不换行
}

代码结构

  • nonStreamingExample(): 传统的非流式方式,等待完整响应
  • streamingExample(): 流式方式,逐块显示响应
  • 比较两者的时间和用户体验

工作原理

  1. 调用 model.stream() 而不是 model.invoke()
  2. 返回一个异步可迭代对象,在生成时产生块
  3. 使用 for await...of 循环遍历每个块
  4. 每个块包含响应的一部分(通常是几个词)
  5. 使用 process.stdout.write() 显示文本而不换行,创建流式效果

流式传输的好处

  • 更好的用户体验(即时反馈)
  • 感觉更响应 - 用户立即看到进度
  • 用户可以在 AI 生成其余部分时开始阅读
  • 即使总时间相同,感知性能也会提高

何时使用

  • ✅ 长响应(文章、解释、代码)
  • ✅ 面向用户的聊天机器人和交互式应用程序
  • ✅ 当你想向用户显示进度时
  • ❌ 当你需要先获得完整响应时(解析、验证、后处理)

4. 模型参数

你可以通过调整参数来控制 AI 的响应方式。这些参数可能因提供商/模型而异,因此请始终查看文档。

Temperature (0.0 - 2.0)

Temperature 控制随机性和创造力:

  • 0.0 = 确定性:相同问题 → 相同答案
    • 用于:代码生成、事实性答案
  • 1.0 = 平衡(默认):一致性和多样性的混合
    • 用于:一般对话
  • 2.0 = 创造性:某些模型支持高达 2.0 以获得更随机和创造性的响应,但通常不太可预测
    • 用于:创意写作、头脑风暴

提供商和模型差异

  • GitHub Models (OpenAI):大多数模型支持 0.0 到 2.0
  • Microsoft Foundry:根据模型通常限制 temperature 为 0.0-1.0
  • 某些模型(如 gpt-5-mini):可能只支持默认 temperature 值 (1) 并拒绝其他值
Max Tokens

什么是 token? Token 是 AI 模型处理的基本文本单位。将它们视为单词片段 - 大约 1 token ≈ 4 个字符或 ¾ 个单词。例如,"Hello world!" 大约是 3 个 token。

限制响应长度:

  • 控制响应可以有多长
  • 设置 maxTokens: 100 将响应限制到大约 75 个单词
  • 通过限制输出长度防止成本失控
Code 示例 3: 03-parameters.ts

功能:演示 temperature 和 maxTokens 参数的效果

关键代码

const temperatures = [0, 1, 2];

for (const temp of temperatures) {
  // 创建具有特定 temperature 的模型
  const model = new ChatOpenAI({
    model: process.env.AI_MODEL,
    temperature: temp,  // 控制随机性/创造力
    // ... 其他配置
  });

  // 两次尝试相同的提示以查看变化
  const response = await model.invoke(prompt);
}

代码结构

  • temperatureComparison(): 比较不同 temperature 值(0, 1, 2)的效果
  • maxTokensExample(): 比较不同 token 限制(50, 150, 500)的效果
  • 包含错误处理以跳过不支持的参数值

工作原理

  1. Temperature 参数

    • 使用三个不同的 temperature 设置(0, 1, 2)使用相同的提示
    • 代码将每个模型调用包装在 try-catch 中以处理不支持的 temperature 值
    • 如果模型不支持特定的 temperature,显示警告并继续到下一个值
    • Temperature 0 产生最可预测的响应(当支持时)
    • Temperature 1(默认)平衡一致性和创造力
    • Temperature 2 产生更不寻常和创造性的变化
  2. Max Tokens 参数

    • 脚本测试三个不同的 token 限制:50, 150, 500
    • 每个限制控制 AI 可以在其响应中使用多少 token(单词片段)
    • 较低的限制(50)通常导致不完整、截断的响应
    • 中等限制(150)提供部分解释
    • 较高的限制(500)允许完整、详细的响应
    • 字符数显示 token 和实际文本长度之间的关系

成本优化策略

  1. 使用 maxTokens 限制响应长度
  2. 修剪对话历史 - 只保留最近的消息以减少输入 token

5. 错误处理与内置重试

API 调用可能因速率限制、网络问题或临时服务问题而失败。LangChain 提供内置的重试逻辑和指数退避。

常见错误
  • 429 Too Many Requests:超过速率限制(免费层最常见)
  • 401 Unauthorized:无效的 API 密钥
  • 500 Server Error:临时提供商问题
  • Network timeout:连接问题
Code 示例 5: 05-error-handling.ts

功能:演示如何使用 LangChain 的内置重试逻辑处理错误

关键代码

const model = new ChatOpenAI({
  model: process.env.AI_MODEL,
  // ... 其他配置
});

// 添加自动重试逻辑和指数退避
const modelWithRetry = model.withRetry({
  stopAfterAttempt: 3,  // 将重试最多 3 次
});

// 像普通模型一样使用它 - 重试自动发生
const response = await modelWithRetry.invoke("What is LangChain.js?");

代码结构

  • robustCall(): 使用 withRetry() 的健壮调用函数
  • errorExamples(): 演示不同的错误场景
    • 无效 API 密钥示例
    • 模拟瞬态失败的重试演示
    • 正常 withRetry() 使用
    • 错误类型分类
  • showBestPractices(): 错误处理最佳实践

工作原理

  1. withRetry() 用重试逻辑包装模型
  2. 如果请求失败(429, 500, 超时),它自动重试
  3. 指数退避增加重试之间的等待时间
  4. 达到最大尝试次数后,它抛出错误供你处理

内置重试的好处

  • 自动指数退避:每次重试之间等待更长时间(1s, 2s, 4s...)
  • 适用于所有 LangChain 组件:与代理、RAG 和链兼容
  • 优雅地处理 429 错误:自动重试速率限制错误
  • 更少的代码:无需手动重试循环

错误处理最佳实践

  1. ✅ 始终将 API 调用包装在 try-catch 中
  2. ✅ 使用内置重试逻辑 withRetry()
  3. ✅ 处理特定错误类型(429, 401, 超时)
  4. ✅ 记录错误以进行调试
  5. ✅ 向用户提供有用的错误消息
  6. ✅ 有回退行为(缓存响应或默认响应)
  7. ✅ 在生产环境中监控错误率

已知限制withRetry() 目前在流式传输(.stream())方面存在问题。重试逻辑与 .invoke() 正确工作,但可能无法与 .stream() 一起执行。对于需要重试逻辑的关键操作,使用 .invoke() 而不是 .stream()

6. Token 跟踪和成本

Token 驱动 AI 模型,它们直接影响成本和性能。

Code 示例 6: 06-token-tracking.ts

功能:演示如何跟踪 token 使用以进行成本估算和监控

关键代码

// 发出请求
const response = await model.invoke("Explain what TypeScript is in 2 sentences.");

// 从响应元数据中提取 token 使用情况
const usage = response.usage_metadata;

console.log(`  Prompt tokens:     ${usage.input_tokens}`);   // 你的输入
console.log(`  Completion tokens: ${usage.output_tokens}`);  // AI 的响应
console.log(`  Total tokens:      ${usage.total_tokens}`);   // 总成本基础

工作原理

  1. 发出 API 调用:向模型发送提示
  2. 提取元数据:获取 response.usage_metadata
  3. 计算成本:将 token 乘以提供商费率
  4. 跟踪支出:监控每个请求的成本

关键见解

  • Prompt tokens:你的输入(问题 + 对话历史)
  • Completion tokens:AI 的输出(响应)
  • Total tokens:两者的总和(你支付的费用)

为什么跟踪 token?

  • 💰 成本监控:了解你的 API 支出
  • 性能:更多 token = 更慢的响应
  • 📊 优化:识别昂贵的查询
  • 🎯 预算编制:预测生产环境的成本

7. 提供商无关的初始化(附录)

Code 示例 4: 04-init-chat-model.ts

功能:演示 initChatModel() 用于提供商无关的初始化(本课程的替代方案)

关键代码

import { initChatModel } from "langchain/chat_models/universal";

// 在不同提供商类型之间切换(概念性)
const openaiModel = await initChatModel("gpt-5-mini", {
  modelProvider: "openai",
  apiKey: process.env.OPENAI_API_KEY,
});

const anthropicModel = await initChatModel("claude-3-5-sonnet-20241022", {
  modelProvider: "anthropic",
  apiKey: process.env.ANTHROPIC_API_KEY,
});

// 本课程推荐(GitHub Models/Azure)
const model = new ChatOpenAI({
  model: process.env.AI_MODEL,
  configuration: { baseURL: process.env.AI_ENDPOINT },
  apiKey: process.env.AI_API_KEY
});

何时使用 initChatModel()

  • 🔄 多种提供商类型:在 OpenAI、Anthropic、Google 等之间切换
  • 🏗️ 框架构建:创建支持许多提供商的库
  • 🎯 提供商无关代码:编写一次,适用于任何标准提供商

何时使用 ChatOpenAI(本课程)

  • GitHub Models:自定义端点需要特定配置
  • Azure OpenAI:非标准 API 路径与 ChatOpenAI 配合得更好
  • 学习:更明确且更容易理解
  • 单一提供商:当你主要使用一个提供商时

比较

功能ChatOpenAI(推荐)initChatModel()
自定义端点✅ 优秀⚠️ 有限
类型安全✅ 优秀✅ 良好
学习曲线✅ 更容易🔄 中等
用例单一提供商或自定义端点多种标准提供商

对于本课程:坚持使用 ChatOpenAI。它更明确,并且最适用于 GitHub Models 和 Azure OpenAI。

关键要点

  • 多轮对话:每次调用都发送完整的消息历史
  • 流式传输:在生成时显示响应以获得更好的 UX
  • Temperature:控制随机性(0 = 一致,2 = 创造性)
  • 错误处理:始终使用 try-catch 并实现重试
  • Token 跟踪:监控使用情况并估算成本以进行预算编制
  • 成本优化:选择合适的模型,限制响应,缓存结果
  • Token:影响成本和限制(1 token ≈ 4 个字符)
  • 上下文窗口:模型只能处理有限的对话历史

实际应用

  • 聊天机器人和虚拟助手:使用模型、内存和系统消息来维护有用的对话
  • 内容生成工具:使用提示和模板创建一致、高质量的内容
  • 代码助手:使用工具和代理搜索文档、运行测试并建议改进
  • 客户支持系统:使用消息类型设置语气,使用内存在对话之间维护上下文

Code 示例总结

本章节包含 6 个代码示例:

  1. 01-multi-turn.ts - 多轮对话,演示如何维护对话历史
  2. 02-streaming.ts - 流式响应,比较流式和非流式方式
  3. 03-parameters.ts - 模型参数,演示 temperature 和 maxTokens 的效果
  4. 04-init-chat-model.ts - 提供商无关初始化(附录,本课程推荐使用 ChatOpenAI)
  5. 05-error-handling.ts - 错误处理,演示内置重试逻辑和错误分类
  6. 06-token-tracking.ts - Token 跟踪,演示如何监控 token 使用和成本

下一步

你已经学会了如何与 AI 聊天模型交互——从基本调用到处理带有消息历史的对话。现在你可以与 AI 进行来回对话!

下一步:学习如何使用提示控制这些对话并获得可靠的、结构化的输出,让你的代码可以依赖!