1 框架介绍
1.1 LangChain.js概述
01.框架简介
a.核心特性
a.功能说明
LangChain.js是LangChain的JavaScript/TypeScript实现,为前端和Node.js提供LLM应用开发能力。支持全栈开发,从浏览器到服务端无缝集成。提供完整的类型定义,开发体验优秀。LangChain.js是JS生态构建AI应用的首选框架。
b.代码示例
---
// 1. LangChain.js核心特性
// 完整的TypeScript支持
import { ChatOpenAI } from "@langchain/openai";
import { PromptTemplate } from "@langchain/core/prompts";
// 类型安全的链式调用
const model = new ChatOpenAI({
modelName: "gpt-3.5-turbo",
temperature: 0.7
});
const prompt = PromptTemplate.fromTemplate(
"回答以下问题:{question}"
);
// LCEL语法支持
const chain = prompt.pipe(model);
// 2. 多环境支持
// Node.js环境
// npm install @langchain/openai @langchain/core
import { ChatOpenAI } from "@langchain/openai";
const llm = new ChatOpenAI({
openAIApiKey: process.env.OPENAI_API_KEY
});
const result = await llm.invoke("你好");
console.log(result.content);
// 浏览器环境
// <script type="module">
// import { ChatOpenAI } from "https://esm.sh/@langchain/openai";
// </script>
// 3. 模块化架构
// @langchain/core - 核心抽象
import { BaseLanguageModel } from "@langchain/core/language_models/base";
import { Runnable } from "@langchain/core/runnables";
// @langchain/openai - OpenAI集成
import { ChatOpenAI, OpenAIEmbeddings } from "@langchain/openai";
// @langchain/community - 社区集成
import { HNSWLib } from "@langchain/community/vectorstores/hnswlib";
// @langchain/textsplitters - 文本分割
import { RecursiveCharacterTextSplitter } from "@langchain/textsplitters";
// 4. 异步流式处理
const streamingModel = new ChatOpenAI({
streaming: true,
callbacks: [{
handleLLMNewToken(token: string) {
process.stdout.write(token);
}
}]
});
await streamingModel.invoke("讲个故事");
// 5. 与Python版本的对比
// Python版本
// from langchain.chat_models import ChatOpenAI
// llm = ChatOpenAI()
// result = llm.invoke("Hello")
// JavaScript版本
import { ChatOpenAI } from "@langchain/openai";
const llm_js = new ChatOpenAI();
const result_js = await llm_js.invoke("Hello");
// API几乎一致,但使用async/await
// 6. 包管理
// package.json
/*
{
"dependencies": {
"@langchain/core": "^0.1.0",
"@langchain/openai": "^0.0.19",
"@langchain/community": "^0.0.20",
"langchain": "^0.1.0"
},
"devDependencies": {
"@types/node": "^20.0.0",
"typescript": "^5.0.0"
}
}
*/
// 7. 快速示例
import { ChatOpenAI } from "@langchain/openai";
import { PromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";
async function quickExample() {
// 创建模型
const model = new ChatOpenAI({
modelName: "gpt-3.5-turbo",
temperature: 0
});
// 创建提示
const prompt = PromptTemplate.fromTemplate(
"将以下文本翻译为{language}:{text}"
);
// 创建输出解析器
const parser = new StringOutputParser();
// 构建链
const chain = prompt.pipe(model).pipe(parser);
// 执行
const result = await chain.invoke({
language: "英语",
text: "你好,世界"
});
console.log(result); // "Hello, World"
return result;
}
// 8. 错误处理
import { ChatOpenAI } from "@langchain/openai";
async function withErrorHandling() {
try {
const model = new ChatOpenAI({
openAIApiKey: process.env.OPENAI_API_KEY,
timeout: 10000,
maxRetries: 3
});
const result = await model.invoke("测试");
return result;
} catch (error) {
if (error.message.includes("API key")) {
console.error("API密钥错误");
} else if (error.message.includes("timeout")) {
console.error("请求超时");
} else {
console.error("未知错误:", error);
}
throw error;
}
}
// 9. 配置管理
// .env文件
// OPENAI_API_KEY=sk-...
// OPENAI_MODEL=gpt-4
// TEMPERATURE=0.7
import * as dotenv from "dotenv";
dotenv.config();
const model = new ChatOpenAI({
openAIApiKey: process.env.OPENAI_API_KEY,
modelName: process.env.OPENAI_MODEL || "gpt-3.5-turbo",
temperature: parseFloat(process.env.TEMPERATURE || "0.7")
});
// 10. 日志调试
import { ChatOpenAI } from "@langchain/openai";
const model = new ChatOpenAI({
verbose: true, // 启用详细日志
callbacks: [{
handleLLMStart(llm, prompts) {
console.log("LLM开始:", prompts);
},
handleLLMEnd(output) {
console.log("LLM结束:", output);
},
handleLLMError(error) {
console.error("LLM错误:", error);
}
}]
});
await model.invoke("测试日志");
console.log("✓ LangChain.js核心特性示例完成");
---
b.版本兼容
a.功能说明
LangChain.js支持Node.js 18+和现代浏览器。提供ESM和CommonJS两种模块格式。与LangChain Python版本保持API一致性。定期更新同步新特性。版本兼容性确保平滑升级。
b.代码示例
---
// 1. Node.js版本要求
// package.json
{
"engines": {
"node": ">=18.0.0"
}
}
// 检查Node.js版本
import * as process from 'process';
const nodeVersion = process.version;
const major = parseInt(nodeVersion.split('.')[0].substring(1));
if (major < 18) {
console.error("需要Node.js 18或更高版本");
process.exit(1);
}
// 2. 模块格式
// ESM (推荐)
import { ChatOpenAI } from "@langchain/openai";
const model = new ChatOpenAI();
// CommonJS
const { ChatOpenAI } = require("@langchain/openai");
const model_cjs = new ChatOpenAI();
// 3. TypeScript配置
// tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "node",
"lib": ["ES2020"],
"esModuleInterop": true,
"skipLibCheck": true,
"strict": true,
"resolveJsonModule": true,
"isolatedModules": true,
"allowSyntheticDefaultImports": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
// 4. 浏览器兼容性
// 现代浏览器(Chrome 90+, Firefox 88+, Safari 14+)
// 使用ES2020特性
// 对于旧浏览器,使用polyfill
// npm install core-js
import "core-js/stable";
import "regenerator-runtime/runtime";
// 5. 版本迁移
// 从0.0.x迁移到0.1.x
// 旧版本(0.0.x)
// import { OpenAI } from "langchain/llms/openai";
// 新版本(0.1.x)
import { ChatOpenAI } from "@langchain/openai";
// 旧版本
// import { PromptTemplate } from "langchain/prompts";
// 新版本
import { PromptTemplate } from "@langchain/core/prompts";
// 6. API版本检查
import { ChatOpenAI } from "@langchain/openai";
async function checkAPIVersion() {
const model = new ChatOpenAI({
modelName: "gpt-3.5-turbo", // 或 "gpt-4"
});
try {
const result = await model.invoke("test");
console.log("API版本兼容");
return true;
} catch (error) {
if (error.message.includes("model_not_found")) {
console.error("模型不存在或API版本不兼容");
}
return false;
}
}
// 7. 依赖版本管理
// package.json - 锁定主版本
{
"dependencies": {
"@langchain/core": "~0.1.0", // 只更新补丁版本
"@langchain/openai": "^0.0.19", // 允许次版本更新
"langchain": "0.1.0" // 精确版本
}
}
// 检查过时的依赖
// npm outdated
// 8. 运行时环境检测
function detectEnvironment() {
// Node.js环境
if (typeof process !== 'undefined' && process.versions?.node) {
return {
runtime: 'node',
version: process.version
};
}
// 浏览器环境
if (typeof window !== 'undefined') {
return {
runtime: 'browser',
userAgent: navigator.userAgent
};
}
// Deno环境
if (typeof Deno !== 'undefined') {
return {
runtime: 'deno',
version: Deno.version.deno
};
}
return { runtime: 'unknown' };
}
const env = detectEnvironment();
console.log("运行环境:", env);
// 9. 功能检测
function checkFeatureSupport() {
const features = {
streaming: true,
embeddings: true,
vectorStores: typeof window === 'undefined', // Node.js only
agents: true,
memory: true
};
return features;
}
// 10. 版本信息
import packageJson from './package.json' assert { type: 'json' };
console.log("LangChain版本:", packageJson.dependencies["langchain"]);
console.log("Node版本:", process.version);
console.log("TypeScript版本:", packageJson.devDependencies["typescript"]);
// 或从npm获取
import { execSync } from 'child_process';
const langchainVersion = execSync('npm list langchain --depth=0')
.toString()
.match(/langchain@([\d.]+)/)?.[1];
console.log("安装的LangChain版本:", langchainVersion);
console.log("✓ 版本兼容性检查完成");
---
02.生态系统
a.包结构
a.功能说明
LangChain.js采用monorepo架构,核心功能和集成分离。@langchain/core提供基础抽象,各集成包独立维护。按需安装减少bundle大小。清晰的包结构便于维护和扩展。
b.代码示例
---
// 1. 核心包
// @langchain/core - 核心抽象和接口
import {
BaseLanguageModel,
BaseChatModel
} from "@langchain/core/language_models/base";
import {
BasePromptTemplate,
PromptTemplate,
ChatPromptTemplate
} from "@langchain/core/prompts";
import {
Runnable,
RunnableSequence,
RunnableParallel
} from "@langchain/core/runnables";
import {
BaseOutputParser,
StringOutputParser,
StructuredOutputParser
} from "@langchain/core/output_parsers";
// 2. LLM集成包
// @langchain/openai - OpenAI集成
import {
ChatOpenAI,
OpenAIEmbeddings
} from "@langchain/openai";
// @langchain/anthropic - Anthropic Claude集成
// import { ChatAnthropic } from "@langchain/anthropic";
// @langchain/google-genai - Google Gemini集成
// import { ChatGoogleGenerativeAI } from "@langchain/google-genai";
// @langchain/cohere - Cohere集成
// import { CohereEmbeddings } from "@langchain/cohere";
// 3. 社区集成包
// @langchain/community - 社区维护的集成
import {
HNSWLib, // 向量存储
Chroma, // 向量存储
Pinecone // 向量存储
} from "@langchain/community/vectorstores";
import {
Calculator, // 工具
SerpAPI // 工具
} from "@langchain/community/tools";
// 4. 文本处理包
// @langchain/textsplitters - 文本分割
import {
RecursiveCharacterTextSplitter,
TokenTextSplitter,
MarkdownTextSplitter
} from "@langchain/textsplitters";
// 5. 文档加载器
// langchain/document_loaders(在core或community中)
import { TextLoader } from "langchain/document_loaders/fs/text";
import { PDFLoader } from "langchain/document_loaders/fs/pdf";
import { CSVLoader } from "langchain/document_loaders/fs/csv";
// 6. 按需安装示例
// 最小安装(仅OpenAI)
// npm install @langchain/core @langchain/openai
// 添加向量存储
// npm install @langchain/community hnswlib-node
// 添加文本分割
// npm install @langchain/textsplitters
// 完整安装
// npm install langchain @langchain/openai @langchain/community
// 7. 包大小对比
// @langchain/core: ~50KB (gzipped)
// @langchain/openai: ~20KB
// @langchain/community: ~200KB (按需导入可减小)
// langchain (完整): ~500KB
// 按需导入优化
// ✓ 推荐
import { ChatOpenAI } from "@langchain/openai";
// ✗ 不推荐(导入整个包)
// import * as langchain from "langchain";
// 8. 模块依赖关系
/*
@langchain/core (核心)
↑
├── @langchain/openai
├── @langchain/anthropic
└── @langchain/google-genai
@langchain/community (社区)
↑
└── 依赖core和各LLM包
langchain (主包)
↑
└── 重新导出core、openai、community等
*/
// 9. 包导入策略
// 策略1:使用主包(简单但体积大)
import { ChatOpenAI } from "langchain/chat_models/openai";
import { PromptTemplate } from "langchain/prompts";
// 策略2:使用独立包(推荐,体积小)
import { ChatOpenAI } from "@langchain/openai";
import { PromptTemplate } from "@langchain/core/prompts";
// 策略3:混合使用
import { ChatOpenAI } from "@langchain/openai"; // 独立包
import { loadSummarizationChain } from "langchain/chains"; // 主包的高级功能
// 10. 包版本锁定
// package-lock.json 或 yarn.lock
// 确保团队使用相同版本
// package.json
{
"dependencies": {
"@langchain/core": "0.1.52",
"@langchain/openai": "0.0.28",
"@langchain/community": "0.0.45"
},
"resolutions": {
"@langchain/core": "0.1.52"
}
}
console.log("✓ LangChain.js包结构示例完成");
---
1.2 TypeScript支持
01.类型定义
a.完整类型
a.功能说明
LangChain.js提供完整的TypeScript类型定义,所有API都有类型注解。支持泛型和类型推导,开发时获得智能提示。类型安全减少运行时错误。TypeScript是LangChain.js的一等公民。
b.代码示例
---
// 1. 基础类型导入
import { ChatOpenAI } from "@langchain/openai";
import type {
BaseLanguageModelInput,
BaseLanguageModelCallOptions
} from "@langchain/core/language_models/base";
// 类型推导
const model = new ChatOpenAI({
modelName: "gpt-3.5-turbo", // 字符串类型
temperature: 0.7, // 数字类型
maxTokens: 1000 // 数字类型,可选
});
// 2. 消息类型
import type {
BaseMessage,
HumanMessage,
AIMessage,
SystemMessage
} from "@langchain/core/messages";
import {
HumanMessage as HumanMsg,
AIMessage as AIMsg
} from "@langchain/core/messages";
const messages: BaseMessage[] = [
new HumanMsg("你好"),
new AIMsg("你好!有什么可以帮助你的?")
];
// 3. Chain类型
import type { Runnable } from "@langchain/core/runnables";
import { ChatOpenAI } from "@langchain/openai";
import { PromptTemplate } from "@langchain/core/prompts";
// 类型推导的Chain
const prompt = PromptTemplate.fromTemplate("翻译:{text}");
const model = new ChatOpenAI();
const chain: Runnable = prompt.pipe(model);
// 指定输入输出类型
interface TranslateInput {
text: string;
targetLang: string;
}
type TranslateOutput = string;
const typedChain: Runnable<TranslateInput, TranslateOutput> =
prompt.pipe(model);
console.log("✓ TypeScript类型定义示例完成");
---
1.3 浏览器兼容性
01.浏览器支持
a.现代浏览器
a.功能说明
LangChain.js支持所有现代浏览器(Chrome、Firefox、Safari、Edge)。使用ES2020特性,提供原生浏览器体验。可以直接在前端调用LLM API。浏览器支持使LangChain.js成为全栈AI框架。
b.代码示例
---
// 1. CDN导入(开发测试)
<!DOCTYPE html>
<html>
<head>
<title>LangChain.js浏览器示例</title>
</head>
<body>
<h1>LangChain.js在浏览器中运行</h1>
<button id="testBtn">测试LLM</button>
<div id="output"></div>
<script type="module">
// 从CDN导入
import { ChatOpenAI } from "https://esm.sh/@langchain/openai";
document.getElementById('testBtn').addEventListener('click', async () => {
const model = new ChatOpenAI({
openAIApiKey: "YOUR_API_KEY",
streaming: true
});
const output = document.getElementById('output');
output.textContent = "思考中...";
try {
const result = await model.invoke("你好");
output.textContent = result.content;
} catch (error) {
output.textContent = "错误:" + error.message;
}
});
</script>
</body>
</html>
console.log("✓ 浏览器兼容性示例完成");
---
2 快速开始
2.1 环境配置
01.安装依赖
a.Node.js安装
a.功能说明
LangChain.js需要Node.js 18+环境。使用npm或yarn管理依赖包。配置TypeScript编译环境。环境配置是开发的第一步。
b.代码示例
---
// 1. 检查Node.js版本
// 命令行执行
// node --version
// 输出:v18.0.0 或更高
// 检查npm版本
// npm --version
// 2. 初始化项目
// 创建项目目录
// mkdir langchain-js-demo
// cd langchain-js-demo
// 初始化package.json
// npm init -y
// 生成的package.json
{
"name": "langchain-js-demo",
"version": "1.0.0",
"description": "LangChain.js示例项目",
"main": "dist/index.js",
"type": "module",
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"dev": "tsx watch src/index.ts",
"test": "jest"
},
"keywords": ["langchain", "ai", "llm"],
"author": "",
"license": "MIT"
}
// 3. 安装LangChain.js
// 核心包
// npm install @langchain/core @langchain/openai
// 或使用yarn
// yarn add @langchain/core @langchain/openai
// package.json依赖
{
"dependencies": {
"@langchain/core": "^0.1.52",
"@langchain/openai": "^0.0.28",
"dotenv": "^16.0.3"
},
"devDependencies": {
"@types/node": "^20.0.0",
"typescript": "^5.3.0",
"tsx": "^4.7.0"
}
}
// 4. 安装TypeScript
// npm install -D typescript @types/node
// 初始化tsconfig.json
// npx tsc --init
// 5. 配置tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020"],
"moduleResolution": "node",
"rootDir": "./src",
"outDir": "./dist",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"resolveJsonModule": true,
"declaration": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
// 6. 创建目录结构
/*
langchain-js-demo/
├── src/
│ ├── index.ts
│ ├── chains/
│ ├── agents/
│ └── utils/
├── dist/
├── tests/
├── .env
├── .gitignore
├── package.json
└── tsconfig.json
*/
// 7. 配置.env
// .env文件
OPENAI_API_KEY=sk-...
OPENAI_MODEL=gpt-3.5-turbo
TEMPERATURE=0.7
// 8. 配置.gitignore
// .gitignore
node_modules/
dist/
.env
.env.local
*.log
.DS_Store
// 9. 安装开发工具
// ESLint
// npm install -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
// Prettier
// npm install -D prettier
// .eslintrc.json
{
"parser": "@typescript-eslint/parser",
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"rules": {
"@typescript-eslint/no-explicit-any": "warn"
}
}
// 10. 验证安装
// src/index.ts
import { ChatOpenAI } from "@langchain/openai";
import * as dotenv from "dotenv";
dotenv.config();
async function main() {
console.log("Node版本:", process.version);
console.log("环境配置完成");
// 测试LangChain.js
const model = new ChatOpenAI({
openAIApiKey: process.env.OPENAI_API_KEY
});
console.log("✓ LangChain.js安装成功");
}
main();
// 编译并运行
// npm run build
// npm start
console.log("✓ 环境配置完成");
---
b.IDE配置
a.功能说明
配置VSCode等IDE以获得最佳开发体验。安装必要的扩展插件。配置调试环境。IDE配置提升开发效率。
b.代码示例
---
// 1. VSCode推荐扩展
// .vscode/extensions.json
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"ms-vscode.vscode-typescript-next",
"usernamehw.errorlens"
]
}
// 2. VSCode设置
// .vscode/settings.json
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.organizeImports": true
},
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"files.exclude": {
"**/node_modules": true,
"**/dist": true
}
}
// 3. 调试配置
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Debug TypeScript",
"program": "${workspaceFolder}/src/index.ts",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
"sourceMaps": true,
"console": "integratedTerminal",
"skipFiles": ["<node_internals>/**"]
},
{
"type": "node",
"request": "launch",
"name": "Debug Current File",
"program": "${file}",
"runtimeArgs": ["--loader", "tsx"],
"console": "integratedTerminal",
"skipFiles": ["<node_internals>/**"]
}
]
}
// 4. 任务配置
// .vscode/tasks.json
{
"version": "2.0.0",
"tasks": [
{
"type": "typescript",
"tsconfig": "tsconfig.json",
"option": "watch",
"problemMatcher": ["$tsc-watch"],
"group": "build",
"label": "tsc: watch"
},
{
"type": "npm",
"script": "build",
"group": {
"kind": "build",
"isDefault": true
},
"label": "npm: build"
}
]
}
// 5. Prettier配置
// .prettierrc
{
"semi": true,
"trailingComma": "es5",
"singleQuote": false,
"printWidth": 80,
"tabWidth": 2,
"arrowParens": "always"
}
// 6. 代码片段
// .vscode/typescript.code-snippets
{
"LangChain Chain": {
"prefix": "lc-chain",
"body": [
"import { ChatOpenAI } from \"@langchain/openai\";",
"import { PromptTemplate } from \"@langchain/core/prompts\";",
"",
"const model = new ChatOpenAI();",
"const prompt = PromptTemplate.fromTemplate(\"${1:template}\");",
"const chain = prompt.pipe(model);",
"",
"const result = await chain.invoke({ ${2:input} });",
"$0"
],
"description": "创建LangChain链"
}
}
// 7. 快捷键配置
// .vscode/keybindings.json
[
{
"key": "ctrl+shift+t",
"command": "workbench.action.tasks.runTask",
"args": "npm: build"
}
]
// 8. Git集成
// .gitignore
node_modules/
dist/
.env
.env.local
*.log
.DS_Store
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
// 9. 终端配置
// package.json添加脚本
{
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsc",
"start": "node dist/index.js",
"lint": "eslint src/**/*.ts",
"lint:fix": "eslint src/**/*.ts --fix",
"format": "prettier --write \"src/**/*.ts\"",
"test": "jest",
"test:watch": "jest --watch"
}
}
// 使用
// npm run dev # 开发模式(热重载)
// npm run build # 构建
// npm run lint # 检查代码
// npm run format # 格式化代码
// 10. 项目模板
// 创建项目模板脚本
// create-langchain-project.sh
#!/bin/bash
PROJECT_NAME=$1
if [ -z "$PROJECT_NAME" ]; then
echo "用法: ./create-langchain-project.sh <项目名>"
exit 1
fi
mkdir -p "$PROJECT_NAME"/{src,tests}
cd "$PROJECT_NAME"
# 初始化npm
npm init -y
# 安装依赖
npm install @langchain/core @langchain/openai dotenv
npm install -D typescript @types/node tsx prettier eslint
# 创建配置文件
# tsconfig.json, .env等...
echo "✓ 项目$PROJECT_NAME创建完成"
console.log("✓ IDE配置完成");
---
02.快速验证
a.测试连接
a.功能说明
验证环境配置是否正确,测试LLM连接。检查API密钥和网络。确认依赖安装成功。快速验证是开发的质量保证。
b.代码示例
---
// 1. 基础连接测试
// src/test-connection.ts
import { ChatOpenAI } from "@langchain/openai";
import * as dotenv from "dotenv";
dotenv.config();
async function testConnection() {
console.log("🔍 测试LangChain.js连接...");
try {
// 检查环境变量
if (!process.env.OPENAI_API_KEY) {
throw new Error("❌ 缺少OPENAI_API_KEY环境变量");
}
console.log("✓ 环境变量已配置");
// 创建模型
const model = new ChatOpenAI({
openAIApiKey: process.env.OPENAI_API_KEY,
modelName: "gpt-3.5-turbo",
temperature: 0
});
console.log("✓ 模型初始化成功");
// 测试调用
console.log("🤖 发送测试消息...");
const result = await model.invoke("说'测试成功'");
console.log("✓ LLM响应:", result.content);
console.log("✓ 连接测试通过!");
return true;
} catch (error) {
console.error("❌ 连接测试失败:", error.message);
return false;
}
}
testConnection();
// 运行测试
// npx tsx src/test-connection.ts
// 2. 详细诊断测试
import { ChatOpenAI } from "@langchain/openai";
async function diagnosticTest() {
const diagnostics = {
nodeVersion: process.version,
environment: process.env.NODE_ENV || "development",
apiKeyPresent: !!process.env.OPENAI_API_KEY,
apiKeyLength: process.env.OPENAI_API_KEY?.length || 0,
tests: {
initialization: false,
apiCall: false,
streaming: false
}
};
console.log("📊 系统诊断:");
console.log("Node版本:", diagnostics.nodeVersion);
console.log("环境:", diagnostics.environment);
console.log("API Key:", diagnostics.apiKeyPresent ? "✓ 已配置" : "✗ 未配置");
// 测试1:初始化
try {
const model = new ChatOpenAI();
diagnostics.tests.initialization = true;
console.log("✓ 模型初始化测试通过");
} catch (error) {
console.error("✗ 模型初始化失败:", error.message);
}
// 测试2:API调用
try {
const model = new ChatOpenAI();
await model.invoke("test");
diagnostics.tests.apiCall = true;
console.log("✓ API调用测试通过");
} catch (error) {
console.error("✗ API调用失败:", error.message);
}
// 测试3:流式输出
try {
const model = new ChatOpenAI({ streaming: true });
const stream = await model.stream("test");
for await (const chunk of stream) {
// 处理chunk
}
diagnostics.tests.streaming = true;
console.log("✓ 流式输出测试通过");
} catch (error) {
console.error("✗ 流式输出失败:", error.message);
}
// 输出诊断报告
const passed = Object.values(diagnostics.tests).filter(v => v).length;
const total = Object.keys(diagnostics.tests).length;
console.log(`\n📋 测试结果: ${passed}/${total} 通过`);
if (passed === total) {
console.log("🎉 所有测试通过!环境配置正确");
} else {
console.log("⚠️ 部分测试失败,请检查配置");
}
return diagnostics;
}
// 3. 性能测试
async function performanceTest() {
const model = new ChatOpenAI();
console.log("⏱️ 性能测试...");
// 单次调用测试
const start = Date.now();
await model.invoke("测试");
const singleCallTime = Date.now() - start;
console.log(`单次调用: ${singleCallTime}ms`);
// 批量调用测试
const batchStart = Date.now();
await Promise.all([
model.invoke("test1"),
model.invoke("test2"),
model.invoke("test3")
]);
const batchTime = Date.now() - batchStart;
console.log(`并发3次调用: ${batchTime}ms`);
console.log(`平均每次: ${Math.round(batchTime / 3)}ms`);
}
// 4. 错误处理测试
async function errorHandlingTest() {
console.log("🔧 错误处理测试...");
// 测试无效API Key
try {
const model = new ChatOpenAI({
openAIApiKey: "invalid-key"
});
await model.invoke("test");
} catch (error) {
console.log("✓ API Key验证正常:", error.message.includes("API key"));
}
// 测试超时
try {
const model = new ChatOpenAI({
timeout: 1 // 1ms超时
});
await model.invoke("test");
} catch (error) {
console.log("✓ 超时处理正常:", error.message.includes("timeout"));
}
}
// 5. 网络连接测试
import https from "https";
function testNetworkConnectivity(): Promise<boolean> {
return new Promise((resolve) => {
const options = {
hostname: "api.openai.com",
port: 443,
path: "/",
method: "HEAD"
};
const req = https.request(options, (res) => {
console.log("✓ 网络连接正常");
resolve(true);
});
req.on("error", (error) => {
console.error("✗ 网络连接失败:", error.message);
resolve(false);
});
req.end();
});
}
// 6. 依赖版本检查
import packageJson from "../package.json" assert { type: "json" };
function checkDependencies() {
console.log("📦 依赖版本:");
const deps = packageJson.dependencies;
console.log("@langchain/core:", deps["@langchain/core"]);
console.log("@langchain/openai:", deps["@langchain/openai"]);
// 检查是否有更新
// npm outdated
}
// 7. 完整测试套件
async function runAllTests() {
console.log("=== LangChain.js 环境测试套件 ===\n");
// 1. 网络测试
console.log("1️⃣ 网络连接测试");
await testNetworkConnectivity();
console.log();
// 2. 依赖检查
console.log("2️⃣ 依赖版本检查");
checkDependencies();
console.log();
// 3. 基础连接
console.log("3️⃣ LLM连接测试");
await testConnection();
console.log();
// 4. 诊断测试
console.log("4️⃣ 系统诊断");
await diagnosticTest();
console.log();
// 5. 性能测试
console.log("5️⃣ 性能测试");
await performanceTest();
console.log();
// 6. 错误处理
console.log("6️⃣ 错误处理测试");
await errorHandlingTest();
console.log();
console.log("=== 测试完成 ===");
}
// 8. 自动修复脚本
async function autoFix() {
console.log("🔧 自动修复配置问题...");
// 检查.env文件
const fs = await import("fs/promises");
try {
await fs.access(".env");
console.log("✓ .env文件存在");
} catch {
console.log("⚠️ 创建.env文件...");
await fs.writeFile(".env", "OPENAI_API_KEY=\n");
console.log("✓ .env文件已创建,请填入API Key");
}
// 检查node_modules
try {
await fs.access("node_modules");
console.log("✓ 依赖已安装");
} catch {
console.log("⚠️ 安装依赖...");
const { execSync } = await import("child_process");
execSync("npm install", { stdio: "inherit" });
console.log("✓ 依赖安装完成");
}
}
// 9. 健康检查
async function healthCheck() {
const health = {
status: "healthy",
checks: []
};
// API连接
try {
const model = new ChatOpenAI();
await model.invoke("ping");
health.checks.push({ name: "API", status: "pass" });
} catch {
health.checks.push({ name: "API", status: "fail" });
health.status = "unhealthy";
}
// 环境变量
if (process.env.OPENAI_API_KEY) {
health.checks.push({ name: "环境变量", status: "pass" });
} else {
health.checks.push({ name: "环境变量", status: "fail" });
health.status = "unhealthy";
}
return health;
}
// 10. 启动脚本
// package.json
{
"scripts": {
"test:connection": "tsx src/test-connection.ts",
"test:full": "tsx src/diagnostic-test.ts",
"fix": "tsx src/auto-fix.ts",
"health": "tsx src/health-check.ts"
}
}
// 使用
// npm run test:connection # 快速测试
// npm run test:full # 完整测试
// npm run fix # 自动修复
// npm run health # 健康检查
console.log("✓ 测试验证完成");
---
2.2 基础示例
01.Hello World
a.简单调用
a.功能说明
创建第一个LangChain.js程序,调用LLM生成响应。理解基本的调用流程。验证环境配置正确。Hello World是学习的第一步。
b.代码示例
---
// 1. 最简单的示例
// src/hello-world.ts
import { ChatOpenAI } from "@langchain/openai";
import * as dotenv from "dotenv";
dotenv.config();
async function helloWorld() {
// 创建模型
const model = new ChatOpenAI({
openAIApiKey: process.env.OPENAI_API_KEY,
modelName: "gpt-3.5-turbo"
});
// 调用模型
const response = await model.invoke("你好,世界!");
// 输出结果
console.log(response.content);
}
helloWorld();
// 运行
// npx tsx src/hello-world.ts
// 2. 带错误处理的版本
async function safeHelloWorld() {
try {
const model = new ChatOpenAI({
openAIApiKey: process.env.OPENAI_API_KEY
});
const response = await model.invoke("你好!");
console.log("AI回复:", response.content);
} catch (error) {
console.error("错误:", error.message);
}
}
// 3. 使用消息格式
import { HumanMessage } from "@langchain/core/messages";
async function messageExample() {
const model = new ChatOpenAI();
// 使用消息对象
const message = new HumanMessage("介绍一下自己");
const response = await model.invoke([message]);
console.log(response.content);
}
// 4. 多轮对话
import { HumanMessage, AIMessage } from "@langchain/core/messages";
async function conversation() {
const model = new ChatOpenAI();
const messages = [
new HumanMessage("你好"),
new AIMessage("你好!我是AI助手"),
new HumanMessage("天气怎么样?")
];
const response = await model.invoke(messages);
console.log(response.content);
}
// 5. 设置温度
async function withTemperature() {
// 温度0:更确定性
const deterministicModel = new ChatOpenAI({
temperature: 0
});
// 温度0.9:更随机
const creativeModel = new ChatOpenAI({
temperature: 0.9
});
const prompt = "讲个笑话";
console.log("确定性模型:");
console.log((await deterministicModel.invoke(prompt)).content);
console.log("\n创造性模型:");
console.log((await creativeModel.invoke(prompt)).content);
}
// 6. 限制token数
async function withMaxTokens() {
const model = new ChatOpenAI({
maxTokens: 50 // 限制50个token
});
const response = await model.invoke("写一篇长文章");
console.log("Token限制响应:", response.content);
console.log("长度:", response.content.length);
}
// 7. 流式输出
async function streamingExample() {
const model = new ChatOpenAI({
streaming: true
});
console.log("AI: ");
const stream = await model.stream("讲个故事");
for await (const chunk of stream) {
process.stdout.write(chunk.content);
}
console.log("\n");
}
// 8. 批量调用
async function batchExample() {
const model = new ChatOpenAI();
const inputs = [
"1+1=?",
"2+2=?",
"3+3=?"
];
const results = await model.batch(inputs);
results.forEach((result, i) => {
console.log(`${inputs[i]} => ${result.content}`);
});
}
// 9. 带回调的调用
async function withCallbacks() {
const model = new ChatOpenAI({
callbacks: [{
handleLLMStart(llm, prompts) {
console.log("开始调用LLM...");
},
handleLLMEnd(output) {
console.log("LLM调用完成");
console.log("Token使用:", output.llmOutput?.tokenUsage);
},
handleLLMError(error) {
console.error("LLM错误:", error);
}
}]
});
await model.invoke("测试");
}
// 10. 完整示例
import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage, SystemMessage } from "@langchain/core/messages";
import * as dotenv from "dotenv";
dotenv.config();
async function fullExample() {
console.log("=== LangChain.js Hello World ===\n");
// 1. 创建模型
const model = new ChatOpenAI({
openAIApiKey: process.env.OPENAI_API_KEY,
modelName: "gpt-3.5-turbo",
temperature: 0.7,
maxTokens: 100
});
// 2. 准备消息
const messages = [
new SystemMessage("你是一个友好的AI助手"),
new HumanMessage("你好!请简单介绍一下自己")
];
// 3. 调用模型
console.log("🤖 AI思考中...\n");
const response = await model.invoke(messages);
// 4. 输出结果
console.log("AI:", response.content);
console.log("\n=== 完成 ===");
}
fullExample();
console.log("✓ Hello World示例完成");
---
b.使用提示模板
a.功能说明
使用PromptTemplate创建可复用的提示模板。支持变量插值和模板组合。提升提示工程效率。提示模板是构建应用的基础。
b.代码示例
---
// 1. 基础提示模板
import { PromptTemplate } from "@langchain/core/prompts";
import { ChatOpenAI } from "@langchain/openai";
async function basicTemplate() {
// 创建模板
const template = PromptTemplate.fromTemplate(
"将以下文本翻译为{language}:{text}"
);
// 格式化提示
const prompt = await template.format({
language: "英语",
text: "你好,世界"
});
console.log("格式化后的提示:", prompt);
// 输出:将以下文本翻译为英语:你好,世界
// 使用模型
const model = new ChatOpenAI();
const response = await model.invoke(prompt);
console.log("翻译结果:", response.content);
}
// 2. 多行模板
const multilineTemplate = PromptTemplate.fromTemplate(`
你是一个专业的{role}。
任务:{task}
要求:
1. {requirement1}
2. {requirement2}
请开始:
`.trim());
const prompt = await multilineTemplate.format({
role: "翻译专家",
task: "将文本翻译为英语",
requirement1: "保持原意",
requirement2: "语言流畅"
});
// 3. 聊天提示模板
import { ChatPromptTemplate } from "@langchain/core/prompts";
async function chatTemplate() {
const template = ChatPromptTemplate.fromMessages([
["system", "你是一个{expertise}专家"],
["human", "请回答:{question}"]
]);
const prompt = await template.formatMessages({
expertise: "编程",
question: "什么是TypeScript?"
});
const model = new ChatOpenAI();
const response = await model.invoke(prompt);
console.log(response.content);
}
// 4. 带示例的提示模板
import { FewShotPromptTemplate } from "@langchain/core/prompts";
const examples = [
{ input: "happy", output: "sad" },
{ input: "tall", output: "short" },
{ input: "hot", output: "cold" }
];
const examplePrompt = PromptTemplate.fromTemplate(
"Input: {input}\nOutput: {output}"
);
const fewShotTemplate = new FewShotPromptTemplate({
examples,
examplePrompt,
prefix: "给出以下单词的反义词:",
suffix: "Input: {word}\nOutput:",
inputVariables: ["word"]
});
const prompt = await fewShotTemplate.format({ word: "big" });
console.log(prompt);
// 5. 模板与模型链接
async function templateWithChain() {
const template = PromptTemplate.fromTemplate(
"用{style}风格写一个关于{topic}的段落"
);
const model = new ChatOpenAI();
// 创建链
const chain = template.pipe(model);
// 执行链
const result = await chain.invoke({
style: "诗意",
topic: "春天"
});
console.log(result.content);
}
// 6. 部分变量
async function partialVariables() {
const template = PromptTemplate.fromTemplate(
"今天是{date},{question}"
);
// 部分填充
const partialTemplate = await template.partial({
date: new Date().toLocaleDateString("zh-CN")
});
// 只需提供question
const prompt = await partialTemplate.format({
question: "天气怎么样?"
});
console.log(prompt);
}
// 7. 动态模板选择
async function dynamicTemplate(taskType: "translate" | "summarize") {
const templates = {
translate: PromptTemplate.fromTemplate(
"翻译为{language}:{text}"
),
summarize: PromptTemplate.fromTemplate(
"总结以下内容:{text}"
)
};
const template = templates[taskType];
const model = new ChatOpenAI();
const chain = template.pipe(model);
return chain;
}
// 使用
const translateChain = await dynamicTemplate("translate");
const result = await translateChain.invoke({
language: "英语",
text: "你好"
});
// 8. 模板组合
async function templateComposition() {
const analysisTemplate = PromptTemplate.fromTemplate(
"分析以下{type}:{content}"
);
const summaryTemplate = PromptTemplate.fromTemplate(
"基于分析结果,生成摘要:{analysis}"
);
const model = new ChatOpenAI();
// 第一步:分析
const analysis = await analysisTemplate
.pipe(model)
.invoke({
type: "文章",
content: "..."
});
// 第二步:摘要
const summary = await summaryTemplate
.pipe(model)
.invoke({
analysis: analysis.content
});
return summary.content;
}
// 9. 条件模板
async function conditionalTemplate(isDetailed: boolean) {
const template = isDetailed
? PromptTemplate.fromTemplate(
"详细解释{topic},包括示例和背景"
)
: PromptTemplate.fromTemplate(
"简短解释{topic}"
);
const model = new ChatOpenAI();
const chain = template.pipe(model);
return await chain.invoke({ topic: "AI" });
}
// 10. 模板最佳实践
// 创建可复用的模板工厂
class TemplateFactory {
static translate(language: string) {
return PromptTemplate.fromTemplate(
`将以下文本翻译为${language}:{text}`
);
}
static summarize(maxWords: number) {
return PromptTemplate.fromTemplate(
`用不超过${maxWords}字总结:{content}`
);
}
static qa(context: string) {
return PromptTemplate.fromTemplate(
`根据以下上下文回答问题:\n${context}\n\n问题:{question}`
);
}
}
// 使用
const translateTemplate = TemplateFactory.translate("英语");
const summaryTemplate = TemplateFactory.summarize(100);
console.log("✓ 提示模板示例完成");
---
02.链式调用
a.创建链
a.功能说明
使用pipe操作符连接多个组件,构建处理链。链式调用简化代码结构。支持复杂的数据流转换。链是LangChain的核心概念。
b.代码示例
---
// 1. 基础链
import { PromptTemplate } from "@langchain/core/prompts";
import { ChatOpenAI } from "@langchain/openai";
import { StringOutputParser } from "@langchain/core/output_parsers";
async function basicChain() {
// 组件
const prompt = PromptTemplate.fromTemplate("回答:{question}");
const model = new ChatOpenAI();
const parser = new StringOutputParser();
// 创建链
const chain = prompt.pipe(model).pipe(parser);
// 执行链
const result = await chain.invoke({
question: "什么是LangChain?"
});
console.log(result); // string类型
}
// 2. 多步骤链
async function multiStepChain() {
// 步骤1:生成标题
const titlePrompt = PromptTemplate.fromTemplate(
"为关于{topic}的文章生成标题"
);
// 步骤2:生成内容
const contentPrompt = PromptTemplate.fromTemplate(
"标题:{title}\n\n写一段文章内容"
);
const model = new ChatOpenAI();
const parser = new StringOutputParser();
// 构建链
const titleChain = titlePrompt.pipe(model).pipe(parser);
const contentChain = contentPrompt.pipe(model).pipe(parser);
// 执行
const title = await titleChain.invoke({ topic: "AI" });
const content = await contentChain.invoke({ title });
console.log("标题:", title);
console.log("内容:", content);
}
// 3. 使用RunnableSequence
import { RunnableSequence } from "@langchain/core/runnables";
const chain = RunnableSequence.from([
prompt,
model,
parser
]);
// 4. 并行链
import { RunnableParallel } from "@langchain/core/runnables";
async function parallelChain() {
const model = new ChatOpenAI();
// 并行执行多个任务
const parallelChain = RunnableParallel.from({
title: PromptTemplate.fromTemplate(
"生成标题:{topic}"
).pipe(model),
summary: PromptTemplate.fromTemplate(
"生成摘要:{topic}"
).pipe(model),
keywords: PromptTemplate.fromTemplate(
"提取关键词:{topic}"
).pipe(model)
});
const result = await parallelChain.invoke({
topic: "人工智能"
});
console.log("标题:", result.title.content);
console.log("摘要:", result.summary.content);
console.log("关键词:", result.keywords.content);
}
// 5. 条件链
import { RunnableBranch } from "@langchain/core/runnables";
const branch = RunnableBranch.from([
[
(input) => input.language === "zh",
PromptTemplate.fromTemplate("用中文回答:{question}")
],
[
(input) => input.language === "en",
PromptTemplate.fromTemplate("Answer in English: {question}")
],
PromptTemplate.fromTemplate("回答:{question}") // 默认
]).pipe(model);
// 6. 数据转换链
async function transformChain() {
const chain = RunnableSequence.from([
// 输入转换
{
transform: (input: { text: string }) => ({
question: `分析:${input.text}`
})
},
// LLM处理
prompt.pipe(model),
// 输出转换
{
transform: (output) => ({
result: output.content,
length: output.content.length,
timestamp: new Date().toISOString()
})
}
]);
const result = await chain.invoke({ text: "测试" });
console.log(result);
}
// 7. 带错误处理的链
async function chainWithErrorHandling() {
const chain = prompt
.pipe(model)
.pipe(parser)
.withFallbacks({
fallbacks: [
async () => "默认响应"
]
});
try {
const result = await chain.invoke({ question: "test" });
return result;
} catch (error) {
console.error("链执行失败:", error);
}
}
// 8. 流式链
async function streamingChain() {
const chain = prompt.pipe(model).pipe(parser);
const stream = await chain.stream({
question: "讲个故事"
});
for await (const chunk of stream) {
process.stdout.write(chunk);
}
}
// 9. 批量链
async function batchChain() {
const chain = prompt.pipe(model).pipe(parser);
const inputs = [
{ question: "问题1" },
{ question: "问题2" },
{ question: "问题3" }
];
const results = await chain.batch(inputs);
results.forEach((result, i) => {
console.log(`${i + 1}. ${result}`);
});
}
// 10. 链的组合和复用
// 创建可复用的链组件
const llmChain = (prompt: PromptTemplate) => {
return prompt
.pipe(new ChatOpenAI())
.pipe(new StringOutputParser());
};
// 组合使用
const translateChain = llmChain(
PromptTemplate.fromTemplate("翻译:{text}")
);
const summarizeChain = llmChain(
PromptTemplate.fromTemplate("总结:{text}")
);
// 链式处理
const fullChain = RunnableSequence.from([
{ text: (input) => input.original },
translateChain,
{ text: (translated) => translated },
summarizeChain
]);
console.log("✓ 链式调用示例完成");
---
2.3 模块导入
01.导入方式
a.ESM导入
a.功能说明
使用ES模块语法导入LangChain.js组件。支持命名导入和默认导入。Tree-shaking优化bundle大小。ESM是现代JavaScript的标准。
b.代码示例
---
// 1. 基础导入
// 导入模型
import { ChatOpenAI } from "@langchain/openai";
import { OpenAIEmbeddings } from "@langchain/openai";
// 导入提示模板
import { PromptTemplate, ChatPromptTemplate } from "@langchain/core/prompts";
// 导入输出解析器
import { StringOutputParser } from "@langchain/core/output_parsers";
// 2. 类型导入
// 导入类型定义
import type { BaseMessage } from "@langchain/core/messages";
import type { Runnable } from "@langchain/core/runnables";
// 混合导入
import { ChatOpenAI, type ChatOpenAICallOptions } from "@langchain/openai";
// 3. 命名空间导入
// 导入整个模块
import * as prompts from "@langchain/core/prompts";
const template = prompts.PromptTemplate.fromTemplate("...");
// 4. 动态导入
// 按需加载
async function loadModel() {
const { ChatOpenAI } = await import("@langchain/openai");
return new ChatOpenAI();
}
// 条件导入
async function getModel(provider: string) {
if (provider === "openai") {
const { ChatOpenAI } = await import("@langchain/openai");
return new ChatOpenAI();
} else if (provider === "anthropic") {
const { ChatAnthropic } = await import("@langchain/anthropic");
return new ChatAnthropic();
}
}
// 5. 重命名导入
import {
ChatOpenAI as OpenAI,
OpenAIEmbeddings as Embeddings
} from "@langchain/openai";
const model = new OpenAI();
const embeddings = new Embeddings();
// 6. 消息类导入
import {
HumanMessage,
AIMessage,
SystemMessage,
FunctionMessage
} from "@langchain/core/messages";
const messages = [
new SystemMessage("你是AI助手"),
new HumanMessage("你好"),
new AIMessage("你好!")
];
// 7. Runnable导入
import {
Runnable,
RunnableSequence,
RunnableParallel,
RunnableBranch,
RunnablePassthrough
} from "@langchain/core/runnables";
// 8. 工具导入
import { Tool } from "@langchain/core/tools";
import { Calculator } from "@langchain/community/tools/calculator";
// 9. 向量存储导入
import { MemoryVectorStore } from "langchain/vectorstores/memory";
import { HNSWLib } from "@langchain/community/vectorstores/hnswlib";
// 10. 文档加载器导入
import { TextLoader } from "langchain/document_loaders/fs/text";
import { PDFLoader } from "langchain/document_loaders/fs/pdf";
console.log("✓ ESM导入示例完成");
---
b.CommonJS导入
a.功能说明
支持CommonJS模块格式,兼容旧项目。使用require语法导入。适用于Node.js环境。CommonJS确保向后兼容。
b.代码示例
---
// 1. 基础require
const { ChatOpenAI } = require("@langchain/openai");
const { PromptTemplate } = require("@langchain/core/prompts");
// 2. 解构导入
const {
ChatOpenAI,
OpenAIEmbeddings
} = require("@langchain/openai");
// 3. 整体导入
const langchain = require("@langchain/openai");
const model = new langchain.ChatOpenAI();
// 4. 动态require
function loadModel(provider) {
if (provider === "openai") {
const { ChatOpenAI } = require("@langchain/openai");
return new ChatOpenAI();
}
}
// 5. 条件导入
let model;
if (process.env.USE_OPENAI) {
const { ChatOpenAI } = require("@langchain/openai");
model = new ChatOpenAI();
} else {
const { ChatAnthropic } = require("@langchain/anthropic");
model = new ChatAnthropic();
}
// 6. 混合使用
// package.json
{
"type": "commonjs", // 使用CommonJS
"main": "dist/index.js"
}
// 转换为ESM
// npm install --save-dev @babel/core @babel/preset-env
// 7. TypeScript中的CommonJS
// tsconfig.json
{
"compilerOptions": {
"module": "CommonJS",
"esModuleInterop": true
}
}
// 使用
import { ChatOpenAI } from "@langchain/openai";
// 编译后变为:
// const { ChatOpenAI } = require("@langchain/openai");
// 8. 向ESM迁移
// 步骤1:更新package.json
{
"type": "module"
}
// 步骤2:修改导入
// 从
const { ChatOpenAI } = require("@langchain/openai");
// 改为
import { ChatOpenAI } from "@langchain/openai";
// 步骤3:更新文件扩展名
// 确保导入时包含.js扩展名
import { helper } from "./utils.js";
console.log("✓ CommonJS导入示例完成");
---
02.模块组织
a.项目结构
a.功能说明
合理组织项目文件结构,提升代码可维护性。分离关注点,模块化设计。清晰的目录结构便于团队协作。项目结构是工程化的基础。
b.代码示例
---
// 1. 推荐的项目结构
/*
langchain-project/
├── src/
│ ├── index.ts # 入口文件
│ ├── config/ # 配置
│ │ ├── env.ts
│ │ └── models.ts
│ ├── chains/ # 链
│ │ ├── translate.ts
│ │ └── summarize.ts
│ ├── agents/ # Agent
│ │ └── chatAgent.ts
│ ├── tools/ # 工具
│ │ ├── calculator.ts
│ │ └── search.ts
│ ├── prompts/ # 提示模板
│ │ └── templates.ts
│ ├── utils/ # 工具函数
│ │ └── helpers.ts
│ └── types/ # 类型定义
│ └── index.ts
├── tests/ # 测试
│ └── chains.test.ts
├── dist/ # 编译输出
├── .env # 环境变量
├── .gitignore
├── package.json
├── tsconfig.json
└── README.md
*/
// 2. 配置模块
// src/config/env.ts
import * as dotenv from "dotenv";
dotenv.config();
export const config = {
openai: {
apiKey: process.env.OPENAI_API_KEY!,
model: process.env.OPENAI_MODEL || "gpt-3.5-turbo"
},
app: {
port: parseInt(process.env.PORT || "3000"),
env: process.env.NODE_ENV || "development"
}
};
// src/config/models.ts
import { ChatOpenAI } from "@langchain/openai";
import { config } from "./env";
export const createModel = () => {
return new ChatOpenAI({
openAIApiKey: config.openai.apiKey,
modelName: config.openai.model
});
};
// 3. 链模块
// src/chains/translate.ts
import { PromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";
import { createModel } from "../config/models";
export function createTranslateChain(targetLanguage: string) {
const prompt = PromptTemplate.fromTemplate(
`翻译为${targetLanguage}:{text}`
);
const model = createModel();
const parser = new StringOutputParser();
return prompt.pipe(model).pipe(parser);
}
// 使用
import { createTranslateChain } from "./chains/translate";
const chain = createTranslateChain("英语");
// 4. 工具模块
// src/tools/calculator.ts
import { Tool } from "@langchain/core/tools";
export class CalculatorTool extends Tool {
name = "calculator";
description = "计算数学表达式";
async _call(input: string): Promise<string> {
try {
const result = eval(input);
return String(result);
} catch (error) {
return "计算错误";
}
}
}
// 5. 提示模板模块
// src/prompts/templates.ts
import { PromptTemplate } from "@langchain/core/prompts";
export const templates = {
translate: (language: string) =>
PromptTemplate.fromTemplate(
`翻译为${language}:{text}`
),
summarize: (maxWords: number) =>
PromptTemplate.fromTemplate(
`用不超过${maxWords}字总结:{content}`
),
qa: PromptTemplate.fromTemplate(
"根据上下文回答:\n{context}\n\n问题:{question}"
)
};
// 6. 工具函数模块
// src/utils/helpers.ts
export function formatResponse(response: string): string {
return response.trim();
}
export function validateInput(input: string): boolean {
return input.length > 0 && input.length < 10000;
}
export async function retry<T>(
fn: () => Promise<T>,
maxRetries: number = 3
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (i === maxRetries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
throw new Error("Max retries exceeded");
}
// 7. 类型定义模块
// src/types/index.ts
export interface ChatMessage {
role: "user" | "assistant" | "system";
content: string;
timestamp?: Date;
}
export interface ChainConfig {
model: string;
temperature: number;
maxTokens?: number;
}
export type SupportedLanguage = "zh" | "en" | "ja" | "ko";
// 8. 入口文件
// src/index.ts
import { createTranslateChain } from "./chains/translate";
import { templates } from "./prompts/templates";
import { config } from "./config/env";
import { formatResponse } from "./utils/helpers";
async function main() {
console.log("启动应用...");
console.log("环境:", config.app.env);
// 使用翻译链
const translateChain = createTranslateChain("英语");
const result = await translateChain.invoke({
text: "你好,世界"
});
console.log("翻译结果:", formatResponse(result));
}
main();
// 9. 导出API
// src/index.ts
// 导出所有公共API
export { createTranslateChain } from "./chains/translate";
export { CalculatorTool } from "./tools/calculator";
export { templates } from "./prompts/templates";
export * from "./types";
// 用户导入
import { createTranslateChain, templates, type ChatMessage } from "your-package";
// 10. 包管理
// package.json
{
"name": "my-langchain-app",
"version": "1.0.0",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.cjs",
"types": "./dist/index.d.ts"
},
"./chains": {
"import": "./dist/chains/index.js",
"types": "./dist/chains/index.d.ts"
},
"./tools": {
"import": "./dist/tools/index.js",
"types": "./dist/tools/index.d.ts"
}
},
"files": [
"dist"
]
}
// 使用子路径导入
import { createTranslateChain } from "my-langchain-app/chains";
import { CalculatorTool } from "my-langchain-app/tools";
console.log("✓ 模块组织示例完成");
---
3 核心组件
3.1 Models模型
01.语言模型
a.ChatModels
a.功能说明
ChatModels专门用于对话场景,接受消息列表作为输入。支持系统消息、用户消息、AI消息。返回结构化的消息对象。ChatModels是构建聊天应用的核心。
b.代码示例
---
// 1. 基础ChatModel
import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage, SystemMessage } from "@langchain/core/messages";
async function basicChatModel() {
const model = new ChatOpenAI({
modelName: "gpt-3.5-turbo",
temperature: 0.7
});
const messages = [
new SystemMessage("你是一个友好的AI助手"),
new HumanMessage("你好!")
];
const response = await model.invoke(messages);
console.log("AI:", response.content);
// response是AIMessage类型
}
// 2. 多模型支持
// OpenAI
import { ChatOpenAI } from "@langchain/openai";
const openai = new ChatOpenAI({ modelName: "gpt-4" });
// Anthropic Claude
import { ChatAnthropic } from "@langchain/anthropic";
const claude = new ChatAnthropic({ modelName: "claude-3-sonnet" });
// Google Gemini
import { ChatGoogleGenerativeAI } from "@langchain/google-genai";
const gemini = new ChatGoogleGenerativeAI({ modelName: "gemini-pro" });
// 3. 模型配置
const model = new ChatOpenAI({
modelName: "gpt-3.5-turbo",
temperature: 0.7,
maxTokens: 1000,
topP: 1,
frequencyPenalty: 0,
presencePenalty: 0,
n: 1, // 生成1个响应
streaming: false,
verbose: false
});
// 4. 函数调用
const modelWithFunctions = new ChatOpenAI({
modelName: "gpt-3.5-turbo",
}).bind({
functions: [
{
name: "get_weather",
description: "获取天气信息",
parameters: {
type: "object",
properties: {
location: {
type: "string",
description: "城市名称"
}
},
required: ["location"]
}
}
]
});
const response = await modelWithFunctions.invoke([
new HumanMessage("北京天气怎么样?")
]);
// 5. 流式ChatModel
async function streamingChat() {
const model = new ChatOpenAI({
streaming: true,
callbacks: [{
handleLLMNewToken(token: string) {
process.stdout.write(token);
}
}]
});
await model.invoke([
new HumanMessage("讲个故事")
]);
}
// 6. 批量调用
async function batchChatModel() {
const model = new ChatOpenAI();
const batchMessages = [
[new HumanMessage("1+1=?")],
[new HumanMessage("2+2=?")],
[new HumanMessage("3+3=?")]
];
const results = await model.batch(batchMessages);
results.forEach((result, i) => {
console.log(`${i + 1}. ${result.content}`);
});
}
// 7. 缓存
import { ChatOpenAI } from "@langchain/openai";
const model = new ChatOpenAI({
cache: true // 启用缓存
});
// 第一次调用
const result1 = await model.invoke("什么是AI?");
// 第二次调用(相同输入,使用缓存)
const result2 = await model.invoke("什么是AI?");
// 8. 超时和重试
const model = new ChatOpenAI({
timeout: 30000, // 30秒超时
maxRetries: 3 // 最多重试3次
});
// 9. 自定义ChatModel
import { BaseChatModel } from "@langchain/core/language_models/chat_models";
import type { BaseMessage } from "@langchain/core/messages";
import { AIMessage } from "@langchain/core/messages";
import type { ChatResult } from "@langchain/core/outputs";
class CustomChatModel extends BaseChatModel {
_llmType(): string {
return "custom";
}
async _generate(
messages: BaseMessage[],
options?: this["ParsedCallOptions"]
): Promise<ChatResult> {
// 自定义生成逻辑
const lastMessage = messages[messages.length - 1];
return {
generations: [{
text: `处理: ${lastMessage.content}`,
message: new AIMessage(`处理: ${lastMessage.content}`)
}]
};
}
}
const customModel = new CustomChatModel({});
// 10. 本地模型(Ollama)
import { ChatOllama } from "@langchain/community/chat_models/ollama";
const localModel = new ChatOllama({
baseUrl: "http://localhost:11434",
model: "qwen:7b"
});
const response = await localModel.invoke([
new HumanMessage("你好")
]);
console.log("✓ ChatModels示例完成");
---
b.Embeddings
a.功能说明
Embeddings将文本转换为向量表示,用于语义搜索和相似度计算。支持多种embedding模型。可以批量处理文本。Embeddings是向量检索的基础。
b.代码示例
---
// 1. 基础Embeddings
import { OpenAIEmbeddings } from "@langchain/openai";
async function basicEmbeddings() {
const embeddings = new OpenAIEmbeddings({
openAIApiKey: process.env.OPENAI_API_KEY,
modelName: "text-embedding-ada-002"
});
// 嵌入单个文本
const vector = await embeddings.embedQuery("你好,世界");
console.log("向量维度:", vector.length);
console.log("前5个值:", vector.slice(0, 5));
}
// 2. 批量嵌入
async function batchEmbeddings() {
const embeddings = new OpenAIEmbeddings();
const texts = [
"人工智能",
"机器学习",
"深度学习",
"神经网络"
];
const vectors = await embeddings.embedDocuments(texts);
console.log(`生成${vectors.length}个向量`);
console.log(`每个向量维度:${vectors[0].length}`);
}
// 3. 相似度计算
function cosineSimilarity(vec1: number[], vec2: number[]): number {
const dotProduct = vec1.reduce((sum, a, i) => sum + a * vec2[i], 0);
const mag1 = Math.sqrt(vec1.reduce((sum, a) => sum + a * a, 0));
const mag2 = Math.sqrt(vec2.reduce((sum, a) => sum + a * a, 0));
return dotProduct / (mag1 * mag2);
}
async function similarityExample() {
const embeddings = new OpenAIEmbeddings();
const vec1 = await embeddings.embedQuery("猫");
const vec2 = await embeddings.embedQuery("狗");
const vec3 = await embeddings.embedQuery("汽车");
const sim12 = cosineSimilarity(vec1, vec2);
const sim13 = cosineSimilarity(vec1, vec3);
console.log("猫和狗的相似度:", sim12); // 较高
console.log("猫和汽车的相似度:", sim13); // 较低
}
// 4. 其他Embeddings模型
// Cohere
import { CohereEmbeddings } from "@langchain/cohere";
const cohereEmbed = new CohereEmbeddings({
apiKey: process.env.COHERE_API_KEY
});
// HuggingFace
import { HuggingFaceInferenceEmbeddings } from "@langchain/community/embeddings/hf";
const hfEmbed = new HuggingFaceInferenceEmbeddings({
apiKey: process.env.HUGGINGFACEHUB_API_KEY
});
// 5. 自定义Embeddings
import { Embeddings } from "@langchain/core/embeddings";
class CustomEmbeddings extends Embeddings {
async embedDocuments(texts: string[]): Promise<number[][]> {
// 自定义嵌入逻辑
return texts.map(text => {
// 简单示例:使用字符串长度作为特征
return [text.length, text.split(" ").length];
});
}
async embedQuery(text: string): Promise<number[]> {
return [text.length, text.split(" ").length];
}
}
// 6. 缓存Embeddings
import { OpenAIEmbeddings } from "@langchain/openai";
const cache = new Map<string, number[]>();
async function cachedEmbedQuery(text: string): Promise<number[]> {
if (cache.has(text)) {
console.log("使用缓存");
return cache.get(text)!;
}
const embeddings = new OpenAIEmbeddings();
const vector = await embeddings.embedQuery(text);
cache.set(text, vector);
return vector;
}
// 7. Embeddings性能测试
async function embeddingsPerformance() {
const embeddings = new OpenAIEmbeddings();
const texts = Array.from({ length: 100 }, (_, i) => `文本${i}`);
// 单个嵌入
const start1 = Date.now();
for (const text of texts) {
await embeddings.embedQuery(text);
}
const time1 = Date.now() - start1;
// 批量嵌入
const start2 = Date.now();
await embeddings.embedDocuments(texts);
const time2 = Date.now() - start2;
console.log(`单个嵌入耗时: ${time1}ms`);
console.log(`批量嵌入耗时: ${time2}ms`);
console.log(`提升: ${((time1 - time2) / time1 * 100).toFixed(1)}%`);
}
console.log("✓ Embeddings示例完成");
---
3.2 Prompts提示
01.提示模板
a.基础模板
a.功能说明
PromptTemplate将文本模板与变量结合,生成提示。支持f-string风格的变量插值。可复用的模板提升开发效率。提示模板是提示工程的工具。
b.代码示例
---
// 1. 基础PromptTemplate
import { PromptTemplate } from "@langchain/core/prompts";
const template = PromptTemplate.fromTemplate(
"告诉我关于{topic}的{count}个事实"
);
const prompt = await template.format({
topic: "AI",
count: "3"
});
console.log(prompt);
// 输出:告诉我关于AI的3个事实
// 2. 多变量模板
const complexTemplate = PromptTemplate.fromTemplate(
`
角色:{role}
任务:{task}
输入:{input}
输出格式:{format}
`
);
// 3. ChatPromptTemplate
import { ChatPromptTemplate } from "@langchain/core/prompts";
const chatTemplate = ChatPromptTemplate.fromMessages([
["system", "你是一个{expertise}专家"],
["human", "{question}"]
]);
const messages = await chatTemplate.formatMessages({
expertise: "编程",
question: "什么是TypeScript?"
});
// 4. 消息占位符
import {
ChatPromptTemplate,
MessagesPlaceholder
} from "@langchain/core/prompts";
const template = ChatPromptTemplate.fromMessages([
["system", "你是AI助手"],
new MessagesPlaceholder("history"),
["human", "{input}"]
]);
const messages = await template.formatMessages({
history: [
new HumanMessage("你好"),
new AIMessage("你好!")
],
input: "继续聊天"
});
// 5. 部分变量
const template = PromptTemplate.fromTemplate(
"日期:{date}\n主题:{topic}\n内容:{content}"
);
// 预填充日期
const partialTemplate = await template.partial({
date: new Date().toLocaleDateString("zh-CN")
});
// 只需提供topic和content
const prompt = await partialTemplate.format({
topic: "AI",
content: "介绍"
});
// 6. 少样本提示
import { FewShotPromptTemplate } from "@langchain/core/prompts";
const examples = [
{ word: "happy", opposite: "sad" },
{ word: "tall", opposite: "short" }
];
const exampleTemplate = PromptTemplate.fromTemplate(
"Word: {word}\nOpposite: {opposite}"
);
const fewShotPrompt = new FewShotPromptTemplate({
examples,
examplePrompt: exampleTemplate,
prefix: "给出反义词:",
suffix: "Word: {word}\nOpposite:",
inputVariables: ["word"]
});
// 7. 模板组合
const systemTemplate = PromptTemplate.fromTemplate(
"你是{role}"
);
const userTemplate = PromptTemplate.fromTemplate(
"问题:{question}"
);
const combined = ChatPromptTemplate.fromMessages([
["system", await systemTemplate.format({ role: "助手" })],
["human", await userTemplate.format({ question: "测试" })]
]);
// 8. 模板验证
const template = PromptTemplate.fromTemplate(
"分析{text}的{aspect}"
);
// 检查输入变量
console.log("输入变量:", template.inputVariables);
// ["text", "aspect"]
// 验证输入
try {
await template.format({ text: "内容" });
// 缺少aspect,会报错
} catch (error) {
console.error("缺少必需变量");
}
// 9. 链式使用
import { ChatOpenAI } from "@langchain/openai";
import { StringOutputParser } from "@langchain/core/output_parsers";
const template = PromptTemplate.fromTemplate("总结:{text}");
const model = new ChatOpenAI();
const parser = new StringOutputParser();
const chain = template.pipe(model).pipe(parser);
const result = await chain.invoke({ text: "长文本..." });
// 10. 动态模板
function createDynamicTemplate(type: "formal" | "casual") {
const templates = {
formal: "尊敬的用户,{content}",
casual: "嘿,{content}"
};
return PromptTemplate.fromTemplate(templates[type]);
}
const formalTemplate = createDynamicTemplate("formal");
const casualTemplate = createDynamicTemplate("casual");
console.log("✓ 提示模板示例完成");
---
3.3 Chains链
01.LCEL语法
a.pipe操作符
a.功能说明
使用pipe操作符连接组件,构建数据处理管道。简洁的语法表达复杂的处理流程。支持类型推导和链式调用。LCEL是LangChain的声明式API。
b.代码示例
---
// 1. 基础pipe
import { PromptTemplate } from "@langchain/core/prompts";
import { ChatOpenAI } from "@langchain/openai";
import { StringOutputParser } from "@langchain/core/output_parsers";
const chain = PromptTemplate.fromTemplate("翻译:{text}")
.pipe(new ChatOpenAI())
.pipe(new StringOutputParser());
const result = await chain.invoke({ text: "你好" });
// 2. 多步骤pipe
const multiStep = PromptTemplate.fromTemplate("分析:{text}")
.pipe(new ChatOpenAI({ temperature: 0 }))
.pipe(new StringOutputParser())
.pipe({
transform: (text: string) => ({
analysis: text,
wordCount: text.length
})
});
// 3. 并行pipe
import { RunnableParallel } from "@langchain/core/runnables";
const parallel = RunnableParallel.from({
sentiment: PromptTemplate.fromTemplate("情感:{text}")
.pipe(model),
summary: PromptTemplate.fromTemplate("总结:{text}")
.pipe(model),
keywords: PromptTemplate.fromTemplate("关键词:{text}")
.pipe(model)
});
const result = await parallel.invoke({ text: "文章内容" });
// { sentiment: ..., summary: ..., keywords: ... }
// 4. 条件pipe
import { RunnableBranch } from "@langchain/core/runnables";
const conditional = RunnableBranch.from([
[
(input) => input.type === "short",
shortChain
],
[
(input) => input.type === "long",
longChain
],
defaultChain
]);
// 5. 数据转换pipe
const transformChain = RunnableSequence.from([
// 输入转换
{ toUpper: (input: { text: string }) => input.text.toUpperCase() },
// LLM处理
PromptTemplate.fromTemplate("处理:{toUpper}").pipe(model),
// 输出转换
{ final: (output) => output.content.toLowerCase() }
]);
console.log("✓ LCEL语法示例完成");
---
3.4 Memory记忆
01.对话记忆
a.BufferMemory
a.功能说明
BufferMemory存储完整的对话历史。保持上下文连贯性。支持限制历史长度。Buffer Memory是最简单的记忆类型。
b.代码示例
---
// 1. 基础BufferMemory
import { BufferMemory } from "langchain/memory";
import { ChatOpenAI } from "@langchain/openai";
import { ConversationChain } from "langchain/chains";
async function basicMemory() {
// 创建记忆
const memory = new BufferMemory({
returnMessages: true,
memoryKey: "history"
});
// 创建对话链
const model = new ChatOpenAI();
const chain = new ConversationChain({
llm: model,
memory
});
// 第一轮
const response1 = await chain.call({
input: "我叫张三"
});
console.log("AI:", response1.response);
// 第二轮(记住名字)
const response2 = await chain.call({
input: "我叫什么名字?"
});
console.log("AI:", response2.response);
}
// 2. 手动管理记忆
import { BufferMemory } from "langchain/memory";
const memory = new BufferMemory();
// 保存对话
await memory.saveContext(
{ input: "你好" },
{ output: "你好!我是AI助手" }
);
// 加载记忆
const history = await memory.loadMemoryVariables({});
console.log("历史:", history);
// 3. 限制记忆长度
import { BufferWindowMemory } from "langchain/memory";
const windowMemory = new BufferWindowMemory({
k: 5, // 保留最近5轮对话
returnMessages: true
});
// 4. ConversationSummaryMemory
import { ConversationSummaryMemory } from "langchain/memory";
const summaryMemory = new ConversationSummaryMemory({
llm: new ChatOpenAI(),
returnMessages: true
});
// 对话被自动总结,节省token
// 5. 持久化记忆
import { BufferMemory } from "langchain/memory";
import * as fs from "fs/promises";
class PersistentMemory extends BufferMemory {
private filename: string;
constructor(filename: string) {
super({ returnMessages: true });
this.filename = filename;
}
async loadFromFile() {
try {
const data = await fs.readFile(this.filename, "utf-8");
const history = JSON.parse(data);
for (const msg of history) {
await this.saveContext(
{ input: msg.input },
{ output: msg.output }
);
}
} catch (error) {
// 文件不存在,忽略
}
}
async saveToFile() {
const history = await this.loadMemoryVariables({});
await fs.writeFile(
this.filename,
JSON.stringify(history, null, 2)
);
}
}
const persistentMemory = new PersistentMemory("chat_history.json");
await persistentMemory.loadFromFile();
// 6. 多用户记忆
class MultiUserMemory {
private memories = new Map<string, BufferMemory>();
getMemory(userId: string): BufferMemory {
if (!this.memories.has(userId)) {
this.memories.set(userId, new BufferMemory({
returnMessages: true
}));
}
return this.memories.get(userId)!;
}
async clearMemory(userId: string) {
this.memories.delete(userId);
}
}
const multiMemory = new MultiUserMemory();
// 用户1的记忆
const user1Memory = multiMemory.getMemory("user1");
await user1Memory.saveContext(
{ input: "我是用户1" },
{ output: "你好,用户1" }
);
// 用户2的记忆
const user2Memory = multiMemory.getMemory("user2");
await user2Memory.saveContext(
{ input: "我是用户2" },
{ output: "你好,用户2" }
);
console.log("✓ 对话记忆示例完成");
---
4 向量存储
4.1 Embeddings
01.嵌入模型
a.OpenAI Embeddings
a.功能说明
OpenAI Embeddings将文本转换为高维向量表示,捕获语义信息。支持text-embedding-ada-002等模型。向量可用于语义搜索、聚类、推荐等任务。Embeddings是向量检索的基础技术。
b.代码示例
---
// 1. 基础文本嵌入
import { OpenAIEmbeddings } from "@langchain/openai";
async function basicEmbedding() {
const embeddings = new OpenAIEmbeddings({
openAIApiKey: process.env.OPENAI_API_KEY,
modelName: "text-embedding-ada-002"
});
// 嵌入单个文本
const vector = await embeddings.embedQuery("你好,世界");
console.log("向量维度:", vector.length); // 1536
console.log("前5个值:", vector.slice(0, 5));
// [0.0023, -0.0089, 0.0156, -0.0034, 0.0078]
}
// 2. 批量嵌入
async function batchEmbedding() {
const embeddings = new OpenAIEmbeddings();
const texts = [
"人工智能是计算机科学的一个分支",
"机器学习是AI的核心技术",
"深度学习基于神经网络",
"自然语言处理处理文本数据"
];
// 批量转换为向量
const vectors = await embeddings.embedDocuments(texts);
console.log(`生成了${vectors.length}个向量`);
console.log(`每个向量维度: ${vectors[0].length}`);
}
// 3. 相似度计算
function cosineSimilarity(vec1: number[], vec2: number[]): number {
const dotProduct = vec1.reduce((sum, a, i) => sum + a * vec2[i], 0);
const mag1 = Math.sqrt(vec1.reduce((sum, a) => sum + a * a, 0));
const mag2 = Math.sqrt(vec2.reduce((sum, a) => sum + a * a, 0));
return dotProduct / (mag1 * mag2);
}
async function similarityExample() {
const embeddings = new OpenAIEmbeddings();
const vec1 = await embeddings.embedQuery("猫");
const vec2 = await embeddings.embedQuery("狗");
const vec3 = await embeddings.embedQuery("汽车");
const sim12 = cosineSimilarity(vec1, vec2);
const sim13 = cosineSimilarity(vec1, vec3);
console.log("猫和狗的相似度:", sim12.toFixed(3)); // ~0.85
console.log("猫和汽车的相似度:", sim13.toFixed(3)); // ~0.45
}
// 4. 配置参数
const customEmbeddings = new OpenAIEmbeddings({
openAIApiKey: process.env.OPENAI_API_KEY,
modelName: "text-embedding-ada-002",
stripNewLines: true, // 移除换行符
batchSize: 512, // 批处理大小
timeout: 10000, // 超时设置
maxRetries: 3 // 重试次数
});
// 5. 多语言支持
async function multilingualEmbedding() {
const embeddings = new OpenAIEmbeddings();
const texts = [
"Hello, world",
"你好,世界",
"こんにちは、世界",
"안녕하세요, 세계"
];
const vectors = await embeddings.embedDocuments(texts);
// 计算跨语言相似度
const simEnZh = cosineSimilarity(vectors[0], vectors[1]);
console.log("英文-中文相似度:", simEnZh.toFixed(3)); // 很高
}
// 6. 其他嵌入模型
// Cohere Embeddings
import { CohereEmbeddings } from "@langchain/cohere";
const cohereEmbed = new CohereEmbeddings({
apiKey: process.env.COHERE_API_KEY,
model: "embed-english-v3.0"
});
// HuggingFace Embeddings
import { HuggingFaceInferenceEmbeddings } from "@langchain/community/embeddings/hf";
const hfEmbed = new HuggingFaceInferenceEmbeddings({
apiKey: process.env.HUGGINGFACEHUB_API_KEY,
model: "sentence-transformers/all-MiniLM-L6-v2"
});
// 7. 缓存嵌入结果
class CachedEmbeddings {
private cache = new Map<string, number[]>();
constructor(private embeddings: OpenAIEmbeddings) {}
async embedQuery(text: string): Promise<number[]> {
if (this.cache.has(text)) {
console.log("使用缓存");
return this.cache.get(text)!;
}
const vector = await this.embeddings.embedQuery(text);
this.cache.set(text, vector);
return vector;
}
clearCache(): void {
this.cache.clear();
}
}
const cachedEmbed = new CachedEmbeddings(new OpenAIEmbeddings());
// 8. 性能测试
async function performanceTest() {
const embeddings = new OpenAIEmbeddings();
const texts = Array.from({ length: 100 }, (_, i) => `文本${i}`);
// 单个嵌入
console.time("单个嵌入");
for (const text of texts.slice(0, 10)) {
await embeddings.embedQuery(text);
}
console.timeEnd("单个嵌入");
// 批量嵌入
console.time("批量嵌入");
await embeddings.embedDocuments(texts.slice(0, 10));
console.timeEnd("批量嵌入");
// 批量嵌入快得多
}
// 9. 维度归一化
function normalize(vector: number[]): number[] {
const magnitude = Math.sqrt(
vector.reduce((sum, val) => sum + val * val, 0)
);
return vector.map(val => val / magnitude);
}
async function normalizedEmbedding() {
const embeddings = new OpenAIEmbeddings();
const vector = await embeddings.embedQuery("测试");
const normalized = normalize(vector);
// 归一化后的向量模长为1
const magnitude = Math.sqrt(
normalized.reduce((sum, val) => sum + val * val, 0)
);
console.log("归一化后模长:", magnitude.toFixed(3)); // 1.000
}
// 10. 向量聚类
async function clusterVectors() {
const embeddings = new OpenAIEmbeddings();
const texts = [
"猫", "狗", "兔子", // 动物
"苹果", "香蕉", "橙子", // 水果
"汽车", "飞机", "火车" // 交通工具
];
const vectors = await embeddings.embedDocuments(texts);
// 简单的k-means聚类(需要额外库)
// 或计算相似度矩阵
console.log("可以基于向量进行聚类分析");
}
console.log("✓ Embeddings示例完成");
---
b.自定义Embeddings
a.功能说明
创建自定义嵌入模型扩展框架能力。可集成本地模型或私有API。继承Embeddings基类实现接口方法。自定义Embeddings满足特殊需求。
b.代码示例
---
// 1. 基础自定义Embeddings
import { Embeddings } from "@langchain/core/embeddings";
class SimpleEmbeddings extends Embeddings {
async embedDocuments(texts: string[]): Promise<number[][]> {
// 简单示例:基于字符特征生成向量
return texts.map(text => this.textToVector(text));
}
async embedQuery(text: string): Promise<number[]> {
return this.textToVector(text);
}
private textToVector(text: string): number[] {
// 简化示例:使用文本特征
const features = [
text.length, // 长度
(text.match(/[a-z]/gi) || []).length, // 字母数
(text.match(/\d/g) || []).length, // 数字数
(text.match(/[\u4e00-\u9fa5]/g) || []).length // 中文数
];
// 归一化
const max = Math.max(...features, 1);
return features.map(f => f / max);
}
}
// 使用
const simpleEmbed = new SimpleEmbeddings();
const vector = await simpleEmbed.embedQuery("Hello 世界 123");
console.log("向量:", vector);
// 2. 本地模型Embeddings
import * as fs from "fs";
class LocalModelEmbeddings extends Embeddings {
private model: any;
constructor(modelPath: string) {
super();
// 加载本地模型(需要相应的库)
// this.model = loadModel(modelPath);
}
async embedDocuments(texts: string[]): Promise<number[][]> {
// 使用本地模型批量推理
// return this.model.encode(texts);
return texts.map(() => Array(768).fill(0).map(() => Math.random()));
}
async embedQuery(text: string): Promise<number[]> {
// return this.model.encode([text])[0];
return Array(768).fill(0).map(() => Math.random());
}
}
// 3. API包装Embeddings
class CustomAPIEmbeddings extends Embeddings {
constructor(
private apiUrl: string,
private apiKey: string
) {
super();
}
async embedDocuments(texts: string[]): Promise<number[][]> {
const response = await fetch(this.apiUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${this.apiKey}`
},
body: JSON.stringify({ texts })
});
const data = await response.json();
return data.embeddings;
}
async embedQuery(text: string): Promise<number[]> {
const results = await this.embedDocuments([text]);
return results[0];
}
}
// 4. 缓存装饰器
class CachedEmbeddingsWrapper extends Embeddings {
private cache = new Map<string, number[]>();
constructor(private baseEmbeddings: Embeddings) {
super();
}
async embedQuery(text: string): Promise<number[]> {
if (this.cache.has(text)) {
return this.cache.get(text)!;
}
const vector = await this.baseEmbeddings.embedQuery(text);
this.cache.set(text, vector);
return vector;
}
async embedDocuments(texts: string[]): Promise<number[][]> {
const results: number[][] = [];
const toEmbed: string[] = [];
const indices: number[] = [];
texts.forEach((text, i) => {
if (this.cache.has(text)) {
results[i] = this.cache.get(text)!;
} else {
toEmbed.push(text);
indices.push(i);
}
});
if (toEmbed.length > 0) {
const newVectors = await this.baseEmbeddings.embedDocuments(toEmbed);
indices.forEach((idx, i) => {
results[idx] = newVectors[i];
this.cache.set(toEmbed[i], newVectors[i]);
});
}
return results;
}
}
// 使用
const cachedEmbed = new CachedEmbeddingsWrapper(
new OpenAIEmbeddings()
);
console.log("✓ 自定义Embeddings示例完成");
---
4.2 Vector Stores
01.向量存储类型
a.Memory Vector Store
a.功能说明
Memory Vector Store在内存中存储向量,适合开发和测试。支持快速的相似度搜索。无需外部依赖,即开即用。内存向量存储是快速原型开发的选择。
b.代码示例
---
// 1. 基础Memory Vector Store
import { MemoryVectorStore } from "langchain/vectorstores/memory";
import { OpenAIEmbeddings } from "@langchain/openai";
import { Document } from "@langchain/core/documents";
async function basicMemoryStore() {
// 创建文档
const docs = [
new Document({ pageContent: "LangChain是AI应用开发框架" }),
new Document({ pageContent: "TypeScript是类型安全的JavaScript" }),
new Document({ pageContent: "React是前端UI库" })
];
// 创建向量存储
const vectorStore = await MemoryVectorStore.fromDocuments(
docs,
new OpenAIEmbeddings()
);
// 相似度搜索
const results = await vectorStore.similaritySearch("前端开发", 2);
results.forEach((doc, i) => {
console.log(`${i + 1}. ${doc.pageContent}`);
});
}
// 2. 带元数据的文档
async function storeWithMetadata() {
const docs = [
new Document({
pageContent: "Python是编程语言",
metadata: { category: "编程", level: "入门" }
}),
new Document({
pageContent: "机器学习是AI的核心",
metadata: { category: "AI", level: "进阶" }
})
];
const vectorStore = await MemoryVectorStore.fromDocuments(
docs,
new OpenAIEmbeddings()
);
// 搜索并访问元数据
const results = await vectorStore.similaritySearch("AI技术", 1);
console.log("分类:", results[0].metadata.category);
}
// 3. 元数据过滤
async function filterByMetadata() {
const vectorStore = await MemoryVectorStore.fromDocuments(docs, embeddings);
// 只搜索特定分类
const results = await vectorStore.similaritySearch(
"编程",
2,
(doc) => doc.metadata.category === "编程"
);
}
// 4. 带分数的搜索
async function searchWithScore() {
const vectorStore = await MemoryVectorStore.fromDocuments(docs, embeddings);
const resultsWithScores = await vectorStore.similaritySearchWithScore(
"AI技术",
3
);
resultsWithScores.forEach(([doc, score]) => {
console.log(`${doc.pageContent} (相似度: ${score.toFixed(3)})`);
});
}
// 5. 动态添加和删除
async function dynamicOperations() {
const vectorStore = await MemoryVectorStore.fromDocuments(
[],
new OpenAIEmbeddings()
);
// 添加文档
await vectorStore.addDocuments([
new Document({ pageContent: "新文档1" }),
new Document({ pageContent: "新文档2" })
]);
// 获取添加的ID
const ids = await vectorStore.addDocuments([
new Document({ pageContent: "临时文档" })
]);
// 删除文档
await vectorStore.delete({ ids });
}
// 6. 批量搜索
async function batchSearch() {
const vectorStore = await MemoryVectorStore.fromDocuments(docs, embeddings);
const queries = ["前端", "后端", "AI"];
const results = await Promise.all(
queries.map(q => vectorStore.similaritySearch(q, 1))
);
results.forEach((docs, i) => {
console.log(`${queries[i]}: ${docs[0]?.pageContent || "无结果"}`);
});
}
// 7. MMR搜索(最大边际相关性)
async function mmrSearch() {
const vectorStore = await MemoryVectorStore.fromDocuments(docs, embeddings);
// MMR平衡相关性和多样性
const results = await vectorStore.maxMarginalRelevanceSearch(
"编程语言",
{
k: 5, // 返回5个结果
fetchK: 20 // 从20个候选中选择
}
);
}
console.log("✓ Memory Vector Store示例完成");
---
b.HNSWLib
a.功能说明
HNSWLib是高性能的近似最近邻搜索库。支持持久化存储到磁盘。HNSW算法提供快速的向量检索。HNSWLib适合生产环境使用。
b.代码示例
---
// 1. 基础HNSWLib使用
import { HNSWLib } from "@langchain/community/vectorstores/hnswlib";
import { OpenAIEmbeddings } from "@langchain/openai";
async function basicHNSW() {
const docs = [
new Document({ pageContent: "内容1" }),
new Document({ pageContent: "内容2" })
];
// 创建HNSWLib存储
const vectorStore = await HNSWLib.fromDocuments(
docs,
new OpenAIEmbeddings()
);
// 搜索
const results = await vectorStore.similaritySearch("查询", 2);
console.log(results);
}
// 2. 持久化存储
async function persistHNSW() {
const vectorStore = await HNSWLib.fromDocuments(
docs,
new OpenAIEmbeddings()
);
// 保存到磁盘
await vectorStore.save("./hnswlib_store");
console.log("✓ 向量存储已保存");
}
// 3. 加载已保存的存储
async function loadHNSW() {
// 从磁盘加载
const vectorStore = await HNSWLib.load(
"./hnswlib_store",
new OpenAIEmbeddings()
);
// 使用加载的存储
const results = await vectorStore.similaritySearch("查询", 2);
console.log(results);
}
// 4. 增量添加文档
async function incrementalAdd() {
const vectorStore = await HNSWLib.load(
"./hnswlib_store",
new OpenAIEmbeddings()
);
// 添加新文档
await vectorStore.addDocuments([
new Document({ pageContent: "新内容" })
]);
// 重新保存
await vectorStore.save("./hnswlib_store");
}
// 5. 性能配置
async function configuredHNSW() {
const vectorStore = await HNSWLib.fromDocuments(
docs,
new OpenAIEmbeddings(),
{
space: "cosine", // 距离度量:cosine, l2, ip
numDimensions: 1536 // 向量维度
}
);
}
console.log("✓ HNSWLib示例完成");
---
02.浏览器存储
a.IndexedDB Vector Store
a.功能说明
在浏览器中使用IndexedDB存储向量。支持离线使用和本地缓存。持久化数据到浏览器存储。IndexedDB Vector Store适合PWA应用。
b.代码示例
---
// 1. 浏览器IndexedDB存储
// 需要安装:npm install idb
import { openDB } from "idb";
import type { DBSchema } from "idb";
interface VectorDB extends DBSchema {
vectors: {
key: string;
value: {
id: string;
content: string;
vector: number[];
metadata: Record<string, any>;
};
};
}
class IndexedDBVectorStore {
private db;
constructor() {
this.db = openDB<VectorDB>("vector-store", 1, {
upgrade(db) {
db.createObjectStore("vectors", { keyPath: "id" });
}
});
}
async addDocuments(docs: Array<{
content: string;
vector: number[];
metadata?: Record<string, any>;
}>) {
const db = await this.db;
const tx = db.transaction("vectors", "readwrite");
for (const doc of docs) {
await tx.store.add({
id: crypto.randomUUID(),
content: doc.content,
vector: doc.vector,
metadata: doc.metadata || {}
});
}
await tx.done;
}
async similaritySearch(
queryVector: number[],
k: number = 5
) {
const db = await this.db;
const all = await db.getAll("vectors");
// 计算相似度
const withScores = all.map(item => ({
...item,
score: this.cosineSimilarity(queryVector, item.vector)
}));
// 排序并返回top k
return withScores
.sort((a, b) => b.score - a.score)
.slice(0, k);
}
private cosineSimilarity(a: number[], b: number[]): number {
const dotProduct = a.reduce((sum, val, i) => sum + val * b[i], 0);
const magA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0));
const magB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0));
return dotProduct / (magA * magB);
}
}
// 2. 使用IndexedDB存储
async function useIndexedDBStore() {
const store = new IndexedDBVectorStore();
const embeddings = new OpenAIEmbeddings();
// 嵌入文档并存储
const texts = ["文档1", "文档2", "文档3"];
const vectors = await embeddings.embedDocuments(texts);
await store.addDocuments(
texts.map((text, i) => ({
content: text,
vector: vectors[i]
}))
);
// 搜索
const queryVector = await embeddings.embedQuery("查询");
const results = await store.similaritySearch(queryVector, 2);
console.log("搜索结果:", results);
}
// 3. 清理存储
async function clearIndexedDB() {
const db = await openDB<VectorDB>("vector-store", 1);
await db.clear("vectors");
console.log("✓ IndexedDB已清理");
}
console.log("✓ IndexedDB Vector Store示例完成");
---
4.3 Retrievers
01.基础检索器
a.Vector Store Retriever
a.功能说明
Vector Store Retriever从向量存储中检索相关文档。支持多种检索策略和配置选项。可设置返回数量和过滤条件。检索器是RAG系统的核心组件。
b.代码示例
---
// 1. 基础检索器创建
import { MemoryVectorStore } from "langchain/vectorstores/memory";
import { OpenAIEmbeddings } from "@langchain/openai";
import { Document } from "@langchain/core/documents";
async function basicRetriever() {
// 创建向量存储
const vectorStore = await MemoryVectorStore.fromDocuments(
[
new Document({ pageContent: "LangChain是AI框架" }),
new Document({ pageContent: "TypeScript是编程语言" }),
new Document({ pageContent: "React是前端库" })
],
new OpenAIEmbeddings()
);
// 创建检索器
const retriever = vectorStore.asRetriever({
k: 2 // 返回top 2文档
});
// 检索
const docs = await retriever.getRelevantDocuments("前端开发");
docs.forEach((doc, i) => {
console.log(`${i + 1}. ${doc.pageContent}`);
});
}
// 2. 配置检索策略
async function configuredRetriever() {
const vectorStore = await MemoryVectorStore.fromDocuments(
docs,
new OpenAIEmbeddings()
);
// 相似度搜索(默认)
const similarityRetriever = vectorStore.asRetriever({
searchType: "similarity",
k: 5
});
// MMR搜索(平衡相关性和多样性)
const mmrRetriever = vectorStore.asRetriever({
searchType: "mmr",
k: 5,
fetchK: 20 // 从20个候选中选择
});
// 相似度阈值
const thresholdRetriever = vectorStore.asRetriever({
searchType: "similarity_score_threshold",
k: 10,
scoreThreshold: 0.8 // 只返回相似度>0.8的
});
}
// 3. 元数据过滤
async function filterRetriever() {
const docsWithMetadata = [
new Document({
pageContent: "Python教程",
metadata: { language: "python", level: "beginner" }
}),
new Document({
pageContent: "JavaScript高级",
metadata: { language: "javascript", level: "advanced" }
})
];
const vectorStore = await MemoryVectorStore.fromDocuments(
docsWithMetadata,
new OpenAIEmbeddings()
);
const retriever = vectorStore.asRetriever({
k: 10,
filter: (doc) => doc.metadata.language === "python"
});
const results = await retriever.getRelevantDocuments("编程教程");
// 只返回Python相关文档
}
// 4. 自定义检索器
import { BaseRetriever } from "@langchain/core/retrievers";
import type { Document } from "@langchain/core/documents";
class CustomRetriever extends BaseRetriever {
lc_namespace = ["custom", "retrievers"];
constructor(private vectorStore: MemoryVectorStore) {
super();
}
async _getRelevantDocuments(query: string): Promise<Document[]> {
// 自定义检索逻辑
const results = await this.vectorStore.similaritySearch(query, 5);
// 后处理:去重、排序、过滤等
return results.filter(doc => doc.pageContent.length > 10);
}
}
// 5. 批量检索
async function batchRetrieval() {
const retriever = vectorStore.asRetriever({ k: 3 });
const queries = [
"什么是AI?",
"如何学习编程?",
"前端框架有哪些?"
];
const results = await Promise.all(
queries.map(q => retriever.getRelevantDocuments(q))
);
results.forEach((docs, i) => {
console.log(`查询${i + 1}:`, docs.map(d => d.pageContent));
});
}
console.log("✓ Vector Store Retriever示例完成");
---
b.Multi Query Retriever
a.功能说明
Multi Query Retriever自动生成多个查询变体提升召回率。使用LLM生成不同表述的查询。合并多个查询的结果去重。适合处理模糊或复杂的查询。
b.代码示例
---
// 1. 基础Multi Query Retriever
import { MultiQueryRetriever } from "langchain/retrievers/multi_query";
import { ChatOpenAI } from "@langchain/openai";
async function multiQueryRetriever() {
const vectorStore = await MemoryVectorStore.fromDocuments(
docs,
new OpenAIEmbeddings()
);
// 创建Multi Query Retriever
const retriever = MultiQueryRetriever.fromLLM({
llm: new ChatOpenAI({ temperature: 0 }),
retriever: vectorStore.asRetriever(),
verbose: true // 显示生成的查询
});
// 单个查询会被扩展为多个
const results = await retriever.getRelevantDocuments(
"AI应用开发"
);
// 内部可能生成:
// - "AI应用开发"
// - "人工智能应用程序开发"
// - "开发AI驱动的应用"
// 然后合并所有结果
console.log("检索到文档:", results.length);
}
// 2. 自定义查询生成
import { PromptTemplate } from "@langchain/core/prompts";
async function customQueryGeneration() {
const queryPrompt = PromptTemplate.fromTemplate(`
生成3个不同的查询来搜索以下问题:
原始查询:{question}
要求:
1. 使用不同的表述方式
2. 包含同义词
3. 一行一个查询
`);
// 使用自定义提示
const retriever = MultiQueryRetriever.fromLLM({
llm: new ChatOpenAI(),
retriever: vectorStore.asRetriever(),
queryPrompt
});
}
console.log("✓ Multi Query Retriever示例完成");
---
02.高级检索器
a.Contextual Compression
a.功能说明
Contextual Compression压缩检索到的文档保留关键信息。使用LLM提取与查询最相关的内容。减少token使用提升响应速度。适合处理长文档。
b.代码示例
---
// 1. 基础压缩检索器
import { ContextualCompressionRetriever } from "langchain/retrievers/contextual_compression";
import { LLMChainExtractor } from "langchain/retrievers/document_compressors/chain_extract";
import { ChatOpenAI } from "@langchain/openai";
async function compressionRetriever() {
// 基础检索器
const baseRetriever = vectorStore.asRetriever({ k: 5 });
// 压缩器
const compressor = LLMChainExtractor.fromLLM(
new ChatOpenAI({ temperature: 0 })
);
// 压缩检索器
const compressionRetriever = new ContextualCompressionRetriever({
baseCompressor: compressor,
baseRetriever
});
// 检索并压缩
const query = "什么是LangChain?";
const compressedDocs = await compressionRetriever.getRelevantDocuments(query);
// 返回的文档只包含与查询相关的部分
compressedDocs.forEach((doc, i) => {
console.log(`${i + 1}. ${doc.pageContent}`);
});
}
// 2. 嵌入过滤器
import { EmbeddingsFilter } from "langchain/retrievers/document_compressors/embeddings_filter";
async function embeddingFilter() {
const filter = new EmbeddingsFilter({
embeddings: new OpenAIEmbeddings(),
similarityThreshold: 0.8 // 相似度阈值
});
const compressionRetriever = new ContextualCompressionRetriever({
baseCompressor: filter,
baseRetriever: vectorStore.asRetriever({ k: 10 })
});
// 返回相似度>0.8的文档
const results = await compressionRetriever.getRelevantDocuments("查询");
}
// 3. 组合多个压缩器
import { DocumentCompressorPipeline } from "langchain/retrievers/document_compressors";
async function compressorPipeline() {
// 创建压缩管道
const compressor = new DocumentCompressorPipeline({
transformers: [
new EmbeddingsFilter({
embeddings: new OpenAIEmbeddings(),
similarityThreshold: 0.7
}),
LLMChainExtractor.fromLLM(new ChatOpenAI())
]
});
const retriever = new ContextualCompressionRetriever({
baseCompressor: compressor,
baseRetriever: vectorStore.asRetriever()
});
// 先过滤,再提取
const results = await retriever.getRelevantDocuments("查询");
}
console.log("✓ Contextual Compression示例完成");
---
b.Parent Document Retriever
a.功能说明
Parent Document Retriever检索小块但返回完整的父文档。提升检索精度同时保留完整上下文。分离存储和检索的粒度。适合需要完整上下文的场景。
b.代码示例
---
// 1. 基础Parent Document Retriever
import { ParentDocumentRetriever } from "langchain/retrievers/parent_document";
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
import { InMemoryStore } from "langchain/storage/in_memory";
async function parentDocRetriever() {
// 原始文档
const docs = [
new Document({
pageContent: "这是一个很长的文档...\n包含多个段落...\n每个段落讨论不同主题...",
metadata: { source: "doc1.txt" }
})
];
// 子文档分割器(用于检索)
const childSplitter = new RecursiveCharacterTextSplitter({
chunkSize: 100,
chunkOverlap: 20
});
// 父文档分割器(可选)
const parentSplitter = new RecursiveCharacterTextSplitter({
chunkSize: 500,
chunkOverlap: 50
});
// 文档存储(存储父文档)
const docstore = new InMemoryStore();
// 创建检索器
const retriever = new ParentDocumentRetriever({
vectorstore: vectorStore,
docstore,
childSplitter,
parentSplitter // 可选
});
// 添加文档
await retriever.addDocuments(docs);
// 检索:搜索小块,返回父文档
const results = await retriever.getRelevantDocuments("主题");
// 返回完整的父文档而不是小块
console.log("父文档:", results[0].pageContent);
}
// 2. 只检索父文档(不分割)
async function wholeDocRetriever() {
const retriever = new ParentDocumentRetriever({
vectorstore: vectorStore,
docstore: new InMemoryStore(),
childSplitter,
// 不设置parentSplitter,返回完整原始文档
});
await retriever.addDocuments(docs);
// 返回完整的原始文档
const results = await retriever.getRelevantDocuments("查询");
}
console.log("✓ Parent Document Retriever示例完成");
---
5 Agent系统
5.1 Agent类型
01.ReAct Agent
a.Zero-Shot React
a.功能说明
Zero-Shot React Agent采用"推理-行动"模式解决问题。无需示例即可理解任务并选择工具。通过思考-行动-观察循环完成复杂任务。ReAct是最常用的Agent类型。
b.代码示例
---
// 1. 基础Zero-Shot React Agent
import { initializeAgentExecutorWithOptions } from "langchain/agents";
import { ChatOpenAI } from "@langchain/openai";
import { Calculator } from "@langchain/community/tools/calculator";
import { DynamicTool } from "@langchain/core/tools";
async function zeroShotReactAgent() {
// 定义工具
const searchTool = new DynamicTool({
name: "search",
description: "搜索最新信息",
func: async (query: string) => {
// 模拟搜索
return `${query}的搜索结果...`;
}
});
const tools = [
new Calculator(),
searchTool
];
// 创建Agent
const executor = await initializeAgentExecutorWithOptions(
tools,
new ChatOpenAI({ temperature: 0 }),
{
agentType: "zero-shot-react-description",
verbose: true
}
);
// 执行任务
const result = await executor.call({
input: "2024年奥运会在哪举办?那个城市的人口是多少?"
});
console.log("结果:", result.output);
// Agent思考过程:
// Thought: 我需要搜索2024奥运会举办城市
// Action: search
// Action Input: "2024奥运会举办城市"
// Observation: 巴黎
// Thought: 现在我需要搜索巴黎人口
// Action: search
// Action Input: "巴黎人口"
// Observation: 约210万
// Thought: 我现在知道最终答案了
// Final Answer: 2024年奥运会在巴黎举办,巴黎人口约210万
}
// 2. 配置React Agent
async function configuredReactAgent() {
const executor = await initializeAgentExecutorWithOptions(
tools,
new ChatOpenAI({ temperature: 0 }),
{
agentType: "zero-shot-react-description",
maxIterations: 5, // 最多5步
earlyStoppingMethod: "generate", // 停止策略
verbose: true,
returnIntermediateSteps: true // 返回中间步骤
}
);
const result = await executor.call({
input: "任务"
});
// 查看中间步骤
console.log("中间步骤:", result.intermediateSteps);
}
// 3. React Agent with Memory
import { BufferMemory } from "langchain/memory";
async function reactAgentWithMemory() {
const memory = new BufferMemory({
returnMessages: true,
memoryKey: "chat_history"
});
const executor = await initializeAgentExecutorWithOptions(
tools,
new ChatOpenAI(),
{
agentType: "chat-conversational-react-description",
memory,
verbose: true
}
);
// 第一轮
await executor.call({ input: "我叫张三" });
// 第二轮(记住名字)
const result = await executor.call({ input: "我叫什么?" });
console.log(result.output); // "你叫张三"
}
console.log("✓ Zero-Shot React Agent示例完成");
---
b.Structured Chat Agent
a.功能说明
Structured Chat Agent支持结构化输入的工具。使用JSON Schema定义工具参数。适合复杂参数的工具调用。结构化Chat提供更好的类型安全。
b.代码示例
---
// 1. Structured Chat Agent
import { StructuredTool } from "@langchain/core/tools";
import { z } from "zod";
class WeatherTool extends StructuredTool {
name = "get_weather";
description = "获取指定城市的天气信息";
schema = z.object({
city: z.string().describe("城市名称"),
unit: z.enum(["celsius", "fahrenheit"]).describe("温度单位")
});
async _call(input: { city: string; unit: string }): Promise<string> {
// 模拟API调用
return `${input.city}今天晴天,25°${input.unit === "celsius" ? "C" : "F"}`;
}
}
async function structuredChatAgent() {
const tools = [new WeatherTool()];
const executor = await initializeAgentExecutorWithOptions(
tools,
new ChatOpenAI(),
{
agentType: "structured-chat-zero-shot-react-description",
verbose: true
}
);
const result = await executor.call({
input: "北京的天气怎么样?用摄氏度"
});
// Agent会正确调用:
// get_weather({ city: "北京", unit: "celsius" })
}
console.log("✓ Structured Chat Agent示例完成");
---
02.OpenAI Functions Agent
a.Function Calling
a.功能说明
OpenAI Functions Agent使用OpenAI的函数调用API。更可靠的工具选择和参数提取。原生支持结构化输出。需要GPT-3.5-turbo或GPT-4。
b.代码示例
---
// 1. 基础OpenAI Functions Agent
async function openAIFunctionsAgent() {
const tools = [
new DynamicTool({
name: "calculator",
description: "计算数学表达式",
func: async (input: string) => {
return String(eval(input));
}
})
];
const executor = await initializeAgentExecutorWithOptions(
tools,
new ChatOpenAI({ modelName: "gpt-4" }),
{
agentType: "openai-functions",
verbose: true
}
);
const result = await executor.call({
input: "100乘以25是多少?"
});
console.log("结果:", result.output);
}
// 2. OpenAI Tools Agent
async function openAIToolsAgent() {
// 使用最新的tools API
const executor = await initializeAgentExecutorWithOptions(
tools,
new ChatOpenAI({ modelName: "gpt-4-turbo" }),
{
agentType: "openai-tools",
verbose: true
}
);
const result = await executor.call({
input: "任务"
});
}
// 3. 并行函数调用
async function parallelFunctionCalls() {
const tools = [
new DynamicTool({
name: "get_weather",
description: "获取天气",
func: async (city: string) => `${city}:晴天`
}),
new DynamicTool({
name: "get_time",
description: "获取时间",
func: async (city: string) => `${city}:14:00`
})
];
const executor = await initializeAgentExecutorWithOptions(
tools,
new ChatOpenAI({ modelName: "gpt-4" }),
{
agentType: "openai-functions"
}
);
// Agent可能并行调用多个函数
const result = await executor.call({
input: "北京的天气和时间分别是什么?"
});
}
console.log("✓ OpenAI Functions Agent示例完成");
---
03.Plan and Execute Agent
a.规划执行模式
a.功能说明
Plan and Execute Agent先规划任务步骤再逐步执行。适合复杂的多步骤任务。分离规划和执行提升可靠性。适用于需要长期规划的场景。
b.代码示例
---
// 1. Plan and Execute Agent
import { PlanAndExecuteAgentExecutor } from "langchain/experimental/plan_and_execute";
import { ChatOpenAI } from "@langchain/openai";
async function planAndExecuteAgent() {
const tools = [
new Calculator(),
searchTool
];
const model = new ChatOpenAI({
modelName: "gpt-4",
temperature: 0
});
const executor = PlanAndExecuteAgentExecutor.fromLLMAndTools({
llm: model,
tools,
verbose: true
});
const result = await executor.call({
input: "找出世界上人口最多的国家,计算其人口的平方根"
});
// Agent首先规划:
// 1. 搜索世界人口最多的国家
// 2. 获取该国人口数量
// 3. 计算平方根
// 然后逐步执行每个步骤
console.log("结果:", result.output);
}
console.log("✓ Plan and Execute Agent示例完成");
---
01.工具定义
a.自定义工具
a.功能说明
创建自定义工具扩展Agent能力。定义工具名称、描述和执行逻辑。支持同步和异步工具。工具是Agent与外部系统的桥梁。
b.代码示例
---
// 1. 基础自定义工具
import { Tool } from "@langchain/core/tools";
class CurrentTimeTool extends Tool {
name = "current_time";
description = "获取当前时间";
async _call(input: string): Promise<string> {
return new Date().toLocaleString("zh-CN");
}
}
const timeTool = new CurrentTimeTool();
const result = await timeTool.call("");
console.log("当前时间:", result);
// 2. 带参数的工具
import { StructuredTool } from "@langchain/core/tools";
import { z } from "zod";
class CalculateTool extends StructuredTool {
name = "calculate";
description = "执行数学计算";
schema = z.object({
operation: z.enum(["add", "subtract", "multiply", "divide"])
.describe("运算类型"),
a: z.number().describe("第一个数"),
b: z.number().describe("第二个数")
});
async _call(input: {
operation: string;
a: number;
b: number;
}): Promise<string> {
const { operation, a, b } = input;
let result: number;
switch (operation) {
case "add":
result = a + b;
break;
case "subtract":
result = a - b;
break;
case "multiply":
result = a * b;
break;
case "divide":
result = a / b;
break;
default:
throw new Error("未知运算");
}
return String(result);
}
}
// 3. 异步工具
class AsyncSearchTool extends Tool {
name = "search";
description = "搜索信息";
async _call(query: string): Promise<string> {
// 模拟API调用
await new Promise(resolve => setTimeout(resolve, 1000));
return `搜索结果:${query}`;
}
}
// 4. 工具with错误处理
class RobustTool extends Tool {
name = "robust_tool";
description = "带错误处理的工具";
async _call(input: string): Promise<string> {
try {
// 执行操作
const result = await this.performOperation(input);
return result;
} catch (error) {
return `错误: ${error.message}`;
}
}
private async performOperation(input: string): Promise<string> {
// 实际操作
return `处理: ${input}`;
}
}
// 5. 数据库工具
import { StructuredTool } from "@langchain/core/tools";
import { z } from "zod";
class DatabaseQueryTool extends StructuredTool {
name = "query_database";
description = "查询数据库";
schema = z.object({
table: z.string().describe("表名"),
condition: z.string().describe("查询条件")
});
async _call(input: {
table: string;
condition: string;
}): Promise<string> {
// 模拟数据库查询
const sql = `SELECT * FROM ${input.table} WHERE ${input.condition}`;
// 执行查询(实际应使用数据库驱动)
const results = [
{ id: 1, name: "张三" },
{ id: 2, name: "李四" }
];
return JSON.stringify(results, null, 2);
}
}
// 6. HTTP请求工具
class HttpTool extends StructuredTool {
name = "http_request";
description = "发送HTTP请求";
schema = z.object({
url: z.string().describe("URL"),
method: z.enum(["GET", "POST"]).describe("请求方法")
});
async _call(input: {
url: string;
method: string;
}): Promise<string> {
const response = await fetch(input.url, {
method: input.method
});
return await response.text();
}
}
// 7. 文件操作工具
class FileReadTool extends StructuredTool {
name = "read_file";
description = "读取文件内容";
schema = z.object({
path: z.string().describe("文件路径")
});
async _call(input: { path: string }): Promise<string> {
const fs = await import("fs/promises");
try {
const content = await fs.readFile(input.path, "utf-8");
return content;
} catch (error) {
return `无法读取文件: ${error.message}`;
}
}
}
// 8. 动态工具
import { DynamicTool } from "@langchain/core/tools";
function createDynamicTool(name: string, description: string) {
return new DynamicTool({
name,
description,
func: async (input: string) => {
// 动态逻辑
return `${name}处理: ${input}`;
}
});
}
const tool1 = createDynamicTool("tool1", "工具1");
const tool2 = createDynamicTool("tool2", "工具2");
console.log("✓ 工具定义示例完成");
---
5.3 自定义Agent
01.创建自定义Agent
a.基于Prompt的Agent
a.功能说明
通过自定义Prompt创建特定行为的Agent。控制Agent的推理风格和输出格式。使用模板库或自定义模板。Prompt是Agent行为的核心。
b.代码示例
---
// 1. 自定义Prompt Agent
import { createReactAgent } from "langchain/agents";
import { AgentExecutor } from "langchain/agents";
import { pull } from "langchain/hub";
import { ChatOpenAI } from "@langchain/openai";
async function customPromptAgent() {
// 从Hub拉取提示模板
const prompt = await pull("hwchase17/react");
// 或创建自定义提示
const customPrompt = PromptTemplate.fromTemplate(`
你是一个专业的助手。使用以下工具回答问题。
工具:
{tools}
工具名称:{tool_names}
使用以下格式:
Question: 输入问题
Thought: 思考如何解决
Action: 选择工具
Action Input: 工具输入
Observation: 工具输出
... (重复Thought/Action/Observation多次)
Thought: 我知道最终答案了
Final Answer: 最终答案
开始!
Question: {input}
Thought: {agent_scratchpad}
`);
const tools = [...];
const agent = await createReactAgent({
llm: new ChatOpenAI(),
tools,
prompt: customPrompt
});
const executor = new AgentExecutor({
agent,
tools,
verbose: true
});
const result = await executor.invoke({
input: "问题"
});
}
// 2. 中文提示Agent
const chinesePrompt = PromptTemplate.fromTemplate(`
你是一个AI助手,使用以下工具帮助用户。
可用工具:
{tools}
格式要求:
问题:用户问题
思考:分析问题并选择工具
行动:工具名称
行动输入:工具的输入参数
观察:工具的返回结果
... (可以重复多次)
思考:我已经知道答案了
最终答案:给用户的回答
问题:{input}
{agent_scratchpad}
`);
// 3. 特定领域Agent
const domainPrompt = PromptTemplate.fromTemplate(`
你是一个医疗领域的专家AI助手。
你的任务是准确回答医疗相关问题。
可用工具:
{tools}
重要提示:
1. 提供准确的医疗信息
2. 必要时建议咨询专业医生
3. 不提供诊断或处方建议
问题:{input}
{agent_scratchpad}
`);
console.log("✓ 基于Prompt的Agent示例完成");
---
b.自定义Agent类
a.功能说明
继承BaseAgent类创建完全自定义的Agent。实现自定义的决策逻辑和工具选择。完全控制Agent行为。适合特殊需求场景。
b.代码示例
---
// 1. 自定义Agent类
import { Agent, AgentStep, AgentFinish, AgentAction } from "langchain/agents";
import type { ChainValues } from "@langchain/core/utils/types";
import type { BaseLanguageModel } from "@langchain/core/language_models/base";
import type { Tool } from "@langchain/core/tools";
class CustomAgent extends Agent {
lc_namespace = ["custom", "agents"];
constructor(
private llm: BaseLanguageModel,
private tools: Tool[]
) {
super();
}
_agentType(): string {
return "custom-agent";
}
async plan(
steps: AgentStep[],
inputs: ChainValues,
callbackManager?: any
): Promise<AgentAction | AgentFinish> {
// 自定义决策逻辑
const input = inputs.input as string;
// 简单规则:如果是计算问题,使用计算器
if (input.includes("计算") || /\d+/.test(input)) {
return {
tool: "calculator",
toolInput: input,
log: "选择计算器工具"
};
}
// 否则使用LLM决定
const llmResponse = await this.llm.invoke(`
选择合适的工具:${input}
可用工具:${this.tools.map(t => t.name).join(", ")}
`);
// 解析LLM响应并返回Action或Finish
return {
returnValues: { output: llmResponse.content },
log: "完成"
};
}
}
// 使用自定义Agent
async function useCustomAgent() {
const agent = new CustomAgent(
new ChatOpenAI(),
[new Calculator()]
);
const executor = new AgentExecutor({
agent,
tools: agent.tools
});
const result = await executor.invoke({
input: "计算100+200"
});
}
// 2. 状态机Agent
class StateMachineAgent extends Agent {
private state: "initial" | "searching" | "calculating" | "done" = "initial";
async plan(steps: AgentStep[], inputs: ChainValues): Promise<AgentAction | AgentFinish> {
const input = inputs.input as string;
switch (this.state) {
case "initial":
if (input.includes("搜索")) {
this.state = "searching";
return {
tool: "search",
toolInput: input,
log: "进入搜索状态"
};
} else if (input.includes("计算")) {
this.state = "calculating";
return {
tool: "calculator",
toolInput: input,
log: "进入计算状态"
};
}
break;
case "searching":
case "calculating":
this.state = "done";
return {
returnValues: {
output: steps[steps.length - 1].observation
},
log: "任务完成"
};
case "done":
return {
returnValues: { output: "已完成" },
log: "结束"
};
}
return {
returnValues: { output: "无法处理" },
log: "未知状态"
};
}
_agentType(): string {
return "state-machine";
}
}
// 3. 优先级Agent
class PriorityAgent extends Agent {
private toolPriority = {
"critical_tool": 10,
"important_tool": 5,
"normal_tool": 1
};
async plan(steps: AgentStep[], inputs: ChainValues): Promise<AgentAction | AgentFinish> {
// 根据优先级选择工具
const availableTools = this.tools
.map(tool => ({
tool,
priority: this.toolPriority[tool.name] || 0
}))
.sort((a, b) => b.priority - a.priority);
// 选择优先级最高的工具
const selectedTool = availableTools[0].tool;
return {
tool: selectedTool.name,
toolInput: inputs.input,
log: `选择优先级最高的工具: ${selectedTool.name}`
};
}
_agentType(): string {
return "priority-agent";
}
}
console.log("✓ 自定义Agent类示例完成");
---
02.Agent Callbacks
a.执行回调
a.功能说明
通过Callbacks监控和控制Agent执行过程。捕获每个步骤的事件。实现日志、监控、调试功能。Callbacks提供Agent的可观测性。
b.代码示例
---
// 1. 基础Callbacks
import type { AgentAction, AgentFinish } from "langchain/agents";
const customCallbacks = {
handleAgentAction(action: AgentAction) {
console.log("🤖 Agent行动:");
console.log(" 工具:", action.tool);
console.log(" 输入:", action.toolInput);
},
handleAgentEnd(finish: AgentFinish) {
console.log("✅ Agent完成:");
console.log(" 输出:", finish.returnValues.output);
},
handleToolStart(tool: { name: string }, input: string) {
console.log(`🔧 工具开始: ${tool.name}`);
console.log(` 输入: ${input}`);
},
handleToolEnd(output: string) {
console.log("✓ 工具完成");
console.log(` 输出: ${output}`);
},
handleToolError(error: Error) {
console.error("❌ 工具错误:", error.message);
},
handleLLMStart(llm: any, prompts: string[]) {
console.log("💭 LLM开始思考...");
},
handleLLMEnd(output: any) {
console.log("✓ LLM思考完成");
}
};
// 使用Callbacks
async function agentWithCallbacks() {
const executor = await initializeAgentExecutorWithOptions(
tools,
new ChatOpenAI(),
{
agentType: "zero-shot-react-description",
callbacks: [customCallbacks]
}
);
await executor.call({ input: "测试任务" });
}
// 2. 性能监控Callbacks
class PerformanceCallbacks {
private startTimes = new Map<string, number>();
handleAgentAction(action: AgentAction) {
this.startTimes.set("agent", Date.now());
}
handleAgentEnd(finish: AgentFinish) {
const duration = Date.now() - this.startTimes.get("agent")!;
console.log(`Agent耗时: ${duration}ms`);
}
handleToolStart(tool: { name: string }) {
this.startTimes.set(tool.name, Date.now());
}
handleToolEnd(output: string, toolName?: string) {
if (toolName) {
const duration = Date.now() - this.startTimes.get(toolName)!;
console.log(`工具${toolName}耗时: ${duration}ms`);
}
}
}
// 3. 日志记录Callbacks
class LoggingCallbacks {
private logs: Array<{
timestamp: Date;
type: string;
data: any;
}> = [];
handleAgentAction(action: AgentAction) {
this.logs.push({
timestamp: new Date(),
type: "agent_action",
data: action
});
}
handleToolStart(tool: { name: string }, input: string) {
this.logs.push({
timestamp: new Date(),
type: "tool_start",
data: { tool: tool.name, input }
});
}
handleToolEnd(output: string) {
this.logs.push({
timestamp: new Date(),
type: "tool_end",
data: { output }
});
}
exportLogs(): string {
return JSON.stringify(this.logs, null, 2);
}
}
// 4. 错误恢复Callbacks
class ErrorRecoveryCallbacks {
private retryCount = 0;
private maxRetries = 3;
async handleToolError(error: Error, tool?: { name: string }) {
console.error(`工具${tool?.name}错误:`, error.message);
if (this.retryCount < this.maxRetries) {
this.retryCount++;
console.log(`尝试重试 (${this.retryCount}/${this.maxRetries})`);
// 可以在这里实现重试逻辑
return true; // 继续执行
}
return false; // 停止执行
}
}
console.log("✓ Agent Callbacks示例完成");
---
6 前端集成
6.1 React集成
01.React组件
a.聊天界面
a.功能说明
在React中集成LangChain.js聊天功能。构建交互式UI组件。处理流式输出和状态管理。React集成提供用户友好的界面。
b.代码示例
---
// 1. 基础聊天组件
// ChatComponent.tsx
import React, { useState } from "react";
import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage, AIMessage } from "@langchain/core/messages";
interface Message {
role: "user" | "ai";
content: string;
}
export function ChatComponent() {
const [messages, setMessages] = useState<Message[]>([]);
const [input, setInput] = useState("");
const [loading, setLoading] = useState(false);
const handleSend = async () => {
if (!input.trim()) return;
// 添加用户消息
const userMessage: Message = {
role: "user",
content: input
};
setMessages([...messages, userMessage]);
setInput("");
setLoading(true);
try {
// 调用LLM
const model = new ChatOpenAI({
openAIApiKey: import.meta.env.VITE_OPENAI_API_KEY
});
const response = await model.invoke([
...messages.map(m =>
m.role === "user"
? new HumanMessage(m.content)
: new AIMessage(m.content)
),
new HumanMessage(input)
]);
// 添加AI消息
const aiMessage: Message = {
role: "ai",
content: response.content as string
};
setMessages(prev => [...prev, aiMessage]);
} catch (error) {
console.error("Error:", error);
} finally {
setLoading(false);
}
};
return (
<div className="chat-container">
<div className="messages">
{messages.map((msg, i) => (
<div key={i} className={`message ${msg.role}`}>
<strong>{msg.role === "user" ? "你" : "AI"}:</strong>
<p>{msg.content}</p>
</div>
))}
{loading && <div className="loading">AI思考中...</div>}
</div>
<div className="input-area">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={(e) => e.key === "Enter" && handleSend()}
placeholder="输入消息..."
/>
<button onClick={handleSend} disabled={loading}>
发送
</button>
</div>
</div>
);
}
// 2. 流式聊天组件
import { useCallback, useRef } from "react";
export function StreamingChat() {
const [messages, setMessages] = useState<Message[]>([]);
const [currentResponse, setCurrentResponse] = useState("");
const [isStreaming, setIsStreaming] = useState(false);
const handleStreamingSend = useCallback(async (input: string) => {
setIsStreaming(true);
setCurrentResponse("");
const model = new ChatOpenAI({
streaming: true,
callbacks: [{
handleLLMNewToken(token: string) {
setCurrentResponse(prev => prev + token);
},
handleLLMEnd() {
setIsStreaming(false);
}
}]
});
await model.invoke([new HumanMessage(input)]);
// 流式完成后,添加到消息列表
setMessages(prev => [...prev, {
role: "ai",
content: currentResponse
}]);
setCurrentResponse("");
}, [currentResponse]);
return (
<div>
{/* 消息列表 */}
{messages.map((msg, i) => (
<div key={i}>{msg.content}</div>
))}
{/* 当前流式输出 */}
{isStreaming && (
<div className="streaming">
{currentResponse}
<span className="cursor">|</span>
</div>
)}
</div>
);
}
// 3. 使用Hook的聊天
// useLangChain.ts
import { useState, useCallback } from "react";
import { ChatOpenAI } from "@langchain/openai";
export function useLangChain() {
const [model] = useState(() => new ChatOpenAI());
const [loading, setLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const invoke = useCallback(async (input: string) => {
setLoading(true);
setError(null);
try {
const response = await model.invoke(input);
return response.content as string;
} catch (err) {
setError(err as Error);
throw err;
} finally {
setLoading(false);
}
}, [model]);
return { invoke, loading, error };
}
// 使用Hook
export function ChatWithHook() {
const { invoke, loading, error } = useLangChain();
const [response, setResponse] = useState("");
const handleSend = async (input: string) => {
const result = await invoke(input);
setResponse(result);
};
return (
<div>
{loading && <p>加载中...</p>}
{error && <p>错误: {error.message}</p>}
{response && <p>响应: {response}</p>}
</div>
);
}
// 4. RAG组件
import { MemoryVectorStore } from "langchain/vectorstores/memory";
import { OpenAIEmbeddings } from "@langchain/openai";
import { Document } from "@langchain/core/documents";
export function RAGChat() {
const [vectorStore, setVectorStore] = useState<MemoryVectorStore | null>(null);
useEffect(() => {
// 初始化向量存储
const initVectorStore = async () => {
const docs = [
new Document({ pageContent: "公司成立于2020年" }),
new Document({ pageContent: "总部位于北京" })
];
const store = await MemoryVectorStore.fromDocuments(
docs,
new OpenAIEmbeddings()
);
setVectorStore(store);
};
initVectorStore();
}, []);
const handleQuery = async (query: string) => {
if (!vectorStore) return;
// 检索相关文档
const docs = await vectorStore.similaritySearch(query, 2);
const context = docs.map(d => d.pageContent).join("\n");
// 生成回答
const model = new ChatOpenAI();
const response = await model.invoke([
new SystemMessage(`基于以下信息回答:\n${context}`),
new HumanMessage(query)
]);
return response.content;
};
return <div>{/* UI */}</div>;
}
console.log("✓ React聊天组件示例完成");
---
6.2 Vue集成
01.Vue3组件
a.Composition API
a.功能说明
在Vue3中使用Composition API集成LangChain.js。利用响应式系统管理状态。创建可复用的Composables。Composition API提供灵活的组合方式。
b.代码示例
---
// 1. 基础Vue3聊天组件
// ChatComponent.vue
<template>
<div class="chat-container">
<div class="messages">
<div
v-for="(msg, index) in messages"
:key="index"
:class="['message', msg.role]"
>
<strong>{{ msg.role === 'user' ? '你' : 'AI' }}:</strong>
<p>{{ msg.content }}</p>
</div>
<div v-if="loading" class="loading">
AI思考中...
</div>
</div>
<div class="input-area">
<input
v-model="input"
@keypress.enter="handleSend"
placeholder="输入消息..."
:disabled="loading"
/>
<button @click="handleSend" :disabled="loading">
发送
</button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { ChatOpenAI } from '@langchain/openai';
import { HumanMessage, AIMessage } from '@langchain/core/messages';
interface Message {
role: 'user' | 'ai';
content: string;
}
const messages = ref<Message[]>([]);
const input = ref('');
const loading = ref(false);
const model = new ChatOpenAI({
openAIApiKey: import.meta.env.VITE_OPENAI_API_KEY
});
const handleSend = async () => {
if (!input.value.trim() || loading.value) return;
// 添加用户消息
messages.value.push({
role: 'user',
content: input.value
});
const userInput = input.value;
input.value = '';
loading.value = true;
try {
// 构建消息历史
const messageHistory = messages.value.map(m =>
m.role === 'user'
? new HumanMessage(m.content)
: new AIMessage(m.content)
);
// 调用LLM
const response = await model.invoke(messageHistory);
// 添加AI消息
messages.value.push({
role: 'ai',
content: response.content as string
});
} catch (error) {
console.error('Error:', error);
} finally {
loading.value = false;
}
};
</script>
<style scoped>
.chat-container {
display: flex;
flex-direction: column;
height: 100vh;
padding: 20px;
}
.messages {
flex: 1;
overflow-y: auto;
margin-bottom: 20px;
}
.message {
margin-bottom: 15px;
padding: 10px;
border-radius: 8px;
}
.message.user {
background: #e3f2fd;
margin-left: 20%;
}
.message.ai {
background: #f5f5f5;
margin-right: 20%;
}
.input-area {
display: flex;
gap: 10px;
}
.input-area input {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
.input-area button {
padding: 10px 20px;
background: #1976d2;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.input-area button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.loading {
text-align: center;
color: #666;
font-style: italic;
}
</style>
// 2. Composable Hook
// composables/useLangChain.ts
import { ref, type Ref } from 'vue';
import { ChatOpenAI } from '@langchain/openai';
export interface UseLangChainOptions {
apiKey?: string;
model?: string;
temperature?: number;
}
export function useLangChain(options: UseLangChainOptions = {}) {
const loading = ref(false);
const error = ref<Error | null>(null);
const model = new ChatOpenAI({
openAIApiKey: options.apiKey || import.meta.env.VITE_OPENAI_API_KEY,
modelName: options.model || 'gpt-3.5-turbo',
temperature: options.temperature ?? 0.7
});
const invoke = async (input: string): Promise<string> => {
loading.value = true;
error.value = null;
try {
const response = await model.invoke(input);
return response.content as string;
} catch (err) {
error.value = err as Error;
throw err;
} finally {
loading.value = false;
}
};
return {
invoke,
loading,
error
};
}
// 使用Composable
// Component.vue
<script setup lang="ts">
import { ref } from 'vue';
import { useLangChain } from '@/composables/useLangChain';
const { invoke, loading, error } = useLangChain();
const response = ref('');
const handleQuery = async (query: string) => {
response.value = await invoke(query);
};
</script>
// 3. 流式输出组件
// StreamingChat.vue
<script setup lang="ts">
import { ref } from 'vue';
import { ChatOpenAI } from '@langchain/openai';
const currentResponse = ref('');
const isStreaming = ref(false);
const model = new ChatOpenAI({
streaming: true,
callbacks: [{
handleLLMNewToken(token: string) {
currentResponse.value += token;
},
handleLLMEnd() {
isStreaming.value = false;
}
}]
});
const handleStream = async (input: string) => {
isStreaming.value = true;
currentResponse.value = '';
await model.invoke(input);
};
</script>
<template>
<div>
<div class="streaming-response">
{{ currentResponse }}
<span v-if="isStreaming" class="cursor">|</span>
</div>
</div>
</template>
// 4. RAG组件
// RAGChat.vue
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { MemoryVectorStore } from 'langchain/vectorstores/memory';
import { OpenAIEmbeddings } from '@langchain/openai';
import { Document } from '@langchain/core/documents';
import { ChatOpenAI } from '@langchain/openai';
import { SystemMessage, HumanMessage } from '@langchain/core/messages';
const vectorStore = ref<MemoryVectorStore | null>(null);
const loading = ref(true);
onMounted(async () => {
// 初始化向量存储
const docs = [
new Document({ pageContent: '公司成立于2020年' }),
new Document({ pageContent: '总部位于北京' }),
new Document({ pageContent: '员工超过1000人' })
];
vectorStore.value = await MemoryVectorStore.fromDocuments(
docs,
new OpenAIEmbeddings()
);
loading.value = false;
});
const handleQuery = async (query: string) => {
if (!vectorStore.value) return;
// 检索相关文档
const docs = await vectorStore.value.similaritySearch(query, 2);
const context = docs.map(d => d.pageContent).join('\n');
// 生成回答
const model = new ChatOpenAI();
const response = await model.invoke([
new SystemMessage(`基于以下信息回答:\n${context}`),
new HumanMessage(query)
]);
return response.content;
};
</script>
console.log("✓ Vue3组件示例完成");
---
b.状态管理
a.功能说明
使用Pinia管理全局LangChain状态。集中管理模型实例和配置。支持模块化的状态组织。Pinia是Vue3推荐的状态管理方案。
b.代码示例
---
// 1. Pinia Store
// stores/langchain.ts
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
import { ChatOpenAI } from '@langchain/openai';
import { MemoryVectorStore } from 'langchain/vectorstores/memory';
export const useLangChainStore = defineStore('langchain', () => {
// State
const model = ref<ChatOpenAI | null>(null);
const vectorStore = ref<MemoryVectorStore | null>(null);
const isInitialized = ref(false);
const config = ref({
temperature: 0.7,
maxTokens: 1000
});
// Getters
const hasModel = computed(() => model.value !== null);
// Actions
const initialize = async () => {
model.value = new ChatOpenAI({
openAIApiKey: import.meta.env.VITE_OPENAI_API_KEY,
temperature: config.value.temperature,
maxTokens: config.value.maxTokens
});
// 初始化向量存储
vectorStore.value = await MemoryVectorStore.fromDocuments(
[],
new OpenAIEmbeddings()
);
isInitialized.value = true;
};
const updateConfig = (newConfig: Partial<typeof config.value>) => {
config.value = { ...config.value, ...newConfig };
// 重新创建模型
if (model.value) {
model.value = new ChatOpenAI({
openAIApiKey: import.meta.env.VITE_OPENAI_API_KEY,
temperature: config.value.temperature,
maxTokens: config.value.maxTokens
});
}
};
const invoke = async (input: string): Promise<string> => {
if (!model.value) {
throw new Error('模型未初始化');
}
const response = await model.value.invoke(input);
return response.content as string;
};
return {
model,
vectorStore,
isInitialized,
config,
hasModel,
initialize,
updateConfig,
invoke
};
});
// 2. 在组件中使用Store
// Component.vue
<script setup lang="ts">
import { onMounted } from 'vue';
import { useLangChainStore } from '@/stores/langchain';
const langchainStore = useLangChainStore();
onMounted(async () => {
if (!langchainStore.isInitialized) {
await langchainStore.initialize();
}
});
const handleQuery = async (query: string) => {
const response = await langchainStore.invoke(query);
console.log(response);
};
</script>
// 3. 配置管理Store
// stores/config.ts
import { defineStore } from 'pinia';
import { ref } from 'vue';
export const useConfigStore = defineStore('config', () => {
const apiKeys = ref({
openai: import.meta.env.VITE_OPENAI_API_KEY || ''
});
const modelSettings = ref({
temperature: 0.7,
maxTokens: 1000,
streaming: false
});
const setApiKey = (service: string, key: string) => {
apiKeys.value[service] = key;
// 持久化到localStorage
localStorage.setItem(`${service}_api_key`, key);
};
const getApiKey = (service: string): string => {
return apiKeys.value[service] ||
localStorage.getItem(`${service}_api_key`) ||
'';
};
return {
apiKeys,
modelSettings,
setApiKey,
getApiKey
};
});
console.log("✓ 状态管理示例完成");
---
6.3 Next.js集成
01.服务端渲染
a.Server Components
a.功能说明
在Next.js 13+中使用Server Components集成LangChain.js。在服务端直接调用LLM无需暴露API密钥。利用服务端渲染提升性能。Server Components是Next.js的核心特性。
b.代码示例
---
// 1. 基础Server Component
// app/chat/page.tsx
import { ChatOpenAI } from '@langchain/openai';
import { PromptTemplate } from '@langchain/core/prompts';
// Server Component(默认)
export default async function ChatPage() {
const model = new ChatOpenAI({
openAIApiKey: process.env.OPENAI_API_KEY
});
const prompt = PromptTemplate.fromTemplate(
'用一句话介绍{topic}'
);
const chain = prompt.pipe(model);
// 直接在服务端调用
const response = await chain.invoke({
topic: 'Next.js'
});
return (
<div>
<h1>AI回答</h1>
<p>{response.content as string}</p>
</div>
);
}
// 2. 动态Server Component
// app/answer/[question]/page.tsx
interface Props {
params: {
question: string;
};
}
export default async function AnswerPage({ params }: Props) {
const model = new ChatOpenAI({
openAIApiKey: process.env.OPENAI_API_KEY
});
const question = decodeURIComponent(params.question);
const response = await model.invoke(question);
return (
<div>
<h2>问题:{question}</h2>
<p>回答:{response.content as string}</p>
</div>
);
}
// 3. 流式Server Component
// app/stream/page.tsx
import { Suspense } from 'react';
async function StreamedContent() {
const model = new ChatOpenAI({
openAIApiKey: process.env.OPENAI_API_KEY
});
const response = await model.invoke('讲个故事');
return <div>{response.content as string}</div>;
}
export default function StreamPage() {
return (
<div>
<h1>AI内容</h1>
<Suspense fallback={<div>加载中...</div>}>
<StreamedContent />
</Suspense>
</div>
);
}
// 4. RAG Server Component
// app/rag/page.tsx
import { MemoryVectorStore } from 'langchain/vectorstores/memory';
import { OpenAIEmbeddings } from '@langchain/openai';
import { Document } from '@langchain/core/documents';
import { ChatOpenAI } from '@langchain/openai';
async function initializeRAG() {
const docs = [
new Document({ pageContent: '公司信息...' }),
new Document({ pageContent: '产品信息...' })
];
const vectorStore = await MemoryVectorStore.fromDocuments(
docs,
new OpenAIEmbeddings({
openAIApiKey: process.env.OPENAI_API_KEY
})
);
return vectorStore;
}
export default async function RAGPage({
searchParams
}: {
searchParams: { q?: string };
}) {
const query = searchParams.q || '';
if (!query) {
return <div>请输入查询</div>;
}
const vectorStore = await initializeRAG();
const docs = await vectorStore.similaritySearch(query, 2);
const model = new ChatOpenAI({
openAIApiKey: process.env.OPENAI_API_KEY
});
const context = docs.map(d => d.pageContent).join('\n');
const response = await model.invoke(`
基于以下信息回答:${context}
问题:${query}
`);
return (
<div>
<h2>查询:{query}</h2>
<p>{response.content as string}</p>
</div>
);
}
console.log("✓ Server Components示例完成");
---
b.API Routes
a.功能说明
创建API路由处理LangChain请求。支持服务端逻辑和客户端调用。实现RESTful或RPC风格的API。API Routes是Next.js的标准后端方案。
b.代码示例
---
// 1. 基础API Route
// app/api/chat/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { ChatOpenAI } from '@langchain/openai';
export async function POST(request: NextRequest) {
try {
const { message } = await request.json();
const model = new ChatOpenAI({
openAIApiKey: process.env.OPENAI_API_KEY
});
const response = await model.invoke(message);
return NextResponse.json({
response: response.content
});
} catch (error) {
return NextResponse.json(
{ error: '处理失败' },
{ status: 500 }
);
}
}
// 客户端调用
// components/ChatClient.tsx
'use client';
import { useState } from 'react';
export default function ChatClient() {
const [message, setMessage] = useState('');
const [response, setResponse] = useState('');
const [loading, setLoading] = useState(false);
const handleSend = async () => {
setLoading(true);
try {
const res = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message })
});
const data = await res.json();
setResponse(data.response);
} catch (error) {
console.error(error);
} finally {
setLoading(false);
}
};
return (
<div>
<input
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="输入消息"
/>
<button onClick={handleSend} disabled={loading}>
发送
</button>
{response && <p>{response}</p>}
</div>
);
}
// 2. 流式API Route
// app/api/stream/route.ts
import { ChatOpenAI } from '@langchain/openai';
export async function POST(request: NextRequest) {
const { message } = await request.json();
const model = new ChatOpenAI({
openAIApiKey: process.env.OPENAI_API_KEY,
streaming: true
});
// 创建流式响应
const encoder = new TextEncoder();
const stream = new ReadableStream({
async start(controller) {
await model.invoke(message, {
callbacks: [{
handleLLMNewToken(token: string) {
controller.enqueue(
encoder.encode(`data: ${JSON.stringify({ token })}\n\n`)
);
},
handleLLMEnd() {
controller.enqueue(
encoder.encode('data: [DONE]\n\n')
);
controller.close();
}
}]
});
}
});
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
}
});
}
// 客户端消费流式响应
'use client';
async function consumeStream(message: string) {
const response = await fetch('/api/stream', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message })
});
const reader = response.body?.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader!.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') break;
const { token } = JSON.parse(data);
console.log(token);
}
}
}
}
// 3. RAG API Route
// app/api/rag/route.ts
import { MemoryVectorStore } from 'langchain/vectorstores/memory';
import { OpenAIEmbeddings } from '@langchain/openai';
// 在模块级别初始化(单例)
let vectorStore: MemoryVectorStore | null = null;
async function getVectorStore() {
if (!vectorStore) {
vectorStore = await MemoryVectorStore.fromDocuments(
docs,
new OpenAIEmbeddings({
openAIApiKey: process.env.OPENAI_API_KEY
})
);
}
return vectorStore;
}
export async function POST(request: NextRequest) {
const { query } = await request.json();
const store = await getVectorStore();
const docs = await store.similaritySearch(query, 3);
const model = new ChatOpenAI({
openAIApiKey: process.env.OPENAI_API_KEY
});
const context = docs.map(d => d.pageContent).join('\n');
const response = await model.invoke(`
基于:${context}
回答:${query}
`);
return NextResponse.json({
answer: response.content,
sources: docs.map(d => d.metadata)
});
}
console.log("✓ API Routes示例完成");
---
02.性能优化
a.缓存策略
a.功能说明
使用Next.js缓存机制优化LangChain性能。实现请求级和应用级缓存。减少重复的LLM调用。缓存显著降低响应时间和成本。
b.代码示例
---
// 1. Next.js fetch缓存
// app/cached/page.tsx
async function getCachedResponse(query: string) {
// Next.js自动缓存fetch请求
const response = await fetch('https://api.example.com/data', {
next: { revalidate: 3600 } // 1小时后重新验证
});
return response.json();
}
// 2. React Cache
import { cache } from 'react';
import { ChatOpenAI } from '@langchain/openai';
// 使用React cache API
const getCachedAnswer = cache(async (question: string) => {
const model = new ChatOpenAI({
openAIApiKey: process.env.OPENAI_API_KEY
});
const response = await model.invoke(question);
return response.content as string;
});
export default async function CachedPage() {
// 相同问题只会调用一次
const answer1 = await getCachedAnswer('什么是AI?');
const answer2 = await getCachedAnswer('什么是AI?'); // 使用缓存
return <div>{answer1}</div>;
}
// 3. 自定义缓存层
// lib/cache.ts
interface CacheEntry {
value: any;
timestamp: number;
ttl: number;
}
class LLMCache {
private cache = new Map<string, CacheEntry>();
get(key: string): any | null {
const entry = this.cache.get(key);
if (!entry) return null;
// 检查是否过期
if (Date.now() - entry.timestamp > entry.ttl) {
this.cache.delete(key);
return null;
}
return entry.value;
}
set(key: string, value: any, ttl: number = 3600000) {
this.cache.set(key, {
value,
timestamp: Date.now(),
ttl
});
}
}
export const llmCache = new LLMCache();
// 使用缓存
// app/api/cached-chat/route.ts
import { llmCache } from '@/lib/cache';
export async function POST(request: NextRequest) {
const { message } = await request.json();
// 检查缓存
const cached = llmCache.get(message);
if (cached) {
return NextResponse.json({ response: cached, cached: true });
}
// 调用LLM
const model = new ChatOpenAI({
openAIApiKey: process.env.OPENAI_API_KEY
});
const response = await model.invoke(message);
const result = response.content as string;
// 存入缓存
llmCache.set(message, result);
return NextResponse.json({ response: result, cached: false });
}
// 4. Redis缓存
// lib/redis.ts
import { Redis } from '@upstash/redis';
const redis = new Redis({
url: process.env.UPSTASH_REDIS_REST_URL!,
token: process.env.UPSTASH_REDIS_REST_TOKEN!
});
export async function getCachedLLMResponse(
query: string
): Promise<string | null> {
return await redis.get(`llm:${query}`);
}
export async function setCachedLLMResponse(
query: string,
response: string,
ttl: number = 3600
) {
await redis.setex(`llm:${query}`, ttl, response);
}
console.log("✓ 缓存策略示例完成");
---
7 流式处理
7.1 流式输出
01.基础流式
a.Stream API
a.功能说明
使用Stream API实现流式输出。逐token显示LLM响应。提升用户体验和交互性。流式输出是现代聊天应用的标配。
b.代码示例
---
// 1. 基础流式调用
import { ChatOpenAI } from "@langchain/openai";
async function basicStreaming() {
const model = new ChatOpenAI({
streaming: true
});
const stream = await model.stream("讲个故事");
for await (const chunk of stream) {
process.stdout.write(chunk.content);
}
}
// 2. 流式with回调
async function streamingWithCallbacks() {
const model = new ChatOpenAI({
streaming: true,
callbacks: [{
handleLLMStart() {
console.log("开始生成...\n");
},
handleLLMNewToken(token: string) {
process.stdout.write(token);
},
handleLLMEnd() {
console.log("\n\n生成完成");
}
}]
});
await model.invoke("你好");
}
// 3. 链式流式
import { PromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";
async function chainStreaming() {
const prompt = PromptTemplate.fromTemplate("回答:{question}");
const model = new ChatOpenAI({ streaming: true });
const parser = new StringOutputParser();
const chain = prompt.pipe(model).pipe(parser);
const stream = await chain.stream({
question: "什么是AI?"
});
for await (const chunk of stream) {
process.stdout.write(chunk);
}
}
// 4. React中的流式
import { useState } from "react";
export function StreamingChat() {
const [response, setResponse] = useState("");
const [isStreaming, setIsStreaming] = useState(false);
const handleStream = async (input: string) => {
setIsStreaming(true);
setResponse("");
const model = new ChatOpenAI({
streaming: true,
callbacks: [{
handleLLMNewToken(token: string) {
setResponse(prev => prev + token);
},
handleLLMEnd() {
setIsStreaming(false);
}
}]
});
await model.invoke(input);
};
return (
<div>
<div className="response">
{response}
{isStreaming && <span className="cursor">|</span>}
</div>
<button onClick={() => handleStream("你好")}>
发送
</button>
</div>
);
}
// 5. SSE流式
// 服务端(Node.js + Express)
import express from "express";
import { ChatOpenAI } from "@langchain/openai";
const app = express();
app.get("/stream", async (req, res) => {
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache");
res.setHeader("Connection", "keep-alive");
const model = new ChatOpenAI({
streaming: true,
callbacks: [{
handleLLMNewToken(token: string) {
res.write(`data: ${JSON.stringify({ token })}\n\n`);
},
handleLLMEnd() {
res.write("data: [DONE]\n\n");
res.end();
}
}]
});
await model.invoke(req.query.input as string);
});
// 客户端
function streamFromSSE(input: string) {
const eventSource = new EventSource(`/stream?input=${input}`);
eventSource.onmessage = (event) => {
if (event.data === "[DONE]") {
eventSource.close();
return;
}
const { token } = JSON.parse(event.data);
console.log(token);
};
}
// 6. WebSocket流式
// 服务端
import { WebSocketServer } from "ws";
const wss = new WebSocketServer({ port: 8080 });
wss.on("connection", (ws) => {
ws.on("message", async (data) => {
const { input } = JSON.parse(data.toString());
const model = new ChatOpenAI({
streaming: true,
callbacks: [{
handleLLMNewToken(token: string) {
ws.send(JSON.stringify({ type: "token", data: token }));
},
handleLLMEnd() {
ws.send(JSON.stringify({ type: "done" }));
}
}]
});
await model.invoke(input);
});
});
// 客户端
const ws = new WebSocket("ws://localhost:8080");
ws.onmessage = (event) => {
const { type, data } = JSON.parse(event.data);
if (type === "token") {
console.log(data);
} else if (type === "done") {
console.log("完成");
}
};
ws.send(JSON.stringify({ input: "你好" }));
// 7. 流式控制
async function controllableStream() {
const model = new ChatOpenAI({ streaming: true });
const stream = await model.stream("讲个长故事");
let shouldStop = false;
// 设置10秒后停止
setTimeout(() => {
shouldStop = true;
}, 10000);
for await (const chunk of stream) {
if (shouldStop) {
break;
}
process.stdout.write(chunk.content);
}
}
console.log("✓ 流式输出示例完成");
---
7.2 SSE实现
01.Server-Sent Events
a.服务端实现
a.功能说明
使用SSE(Server-Sent Events)实现服务端到客户端的单向流式传输。基于HTTP协议,配置简单。支持自动重连和事件ID。SSE适合LLM流式输出场景。
b.代码示例
---
// 1. 基础SSE服务端(Express)
import express from 'express';
import { ChatOpenAI } from '@langchain/openai';
const app = express();
app.get('/sse/chat', async (req, res) => {
// 设置SSE响应头
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.setHeader('Access-Control-Allow-Origin', '*');
const query = req.query.message as string;
const model = new ChatOpenAI({
openAIApiKey: process.env.OPENAI_API_KEY,
streaming: true,
callbacks: [{
handleLLMNewToken(token: string) {
// 发送SSE事件
res.write(`data: ${JSON.stringify({ token })}\n\n`);
},
handleLLMEnd() {
res.write('data: [DONE]\n\n');
res.end();
},
handleLLMError(error: Error) {
res.write(`data: ${JSON.stringify({ error: error.message })}\n\n`);
res.end();
}
}]
});
// 开始流式生成
await model.invoke(query);
});
app.listen(3000, () => {
console.log('SSE服务器运行在 http://localhost:3000');
});
// 2. Next.js API Route SSE
// app/api/sse-chat/route.ts
import { ChatOpenAI } from '@langchain/openai';
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const message = searchParams.get('message') || '';
const encoder = new TextEncoder();
const stream = new ReadableStream({
async start(controller) {
const model = new ChatOpenAI({
openAIApiKey: process.env.OPENAI_API_KEY,
streaming: true,
callbacks: [{
handleLLMNewToken(token: string) {
const data = `data: ${JSON.stringify({ token })}\n\n`;
controller.enqueue(encoder.encode(data));
},
handleLLMEnd() {
controller.enqueue(encoder.encode('data: [DONE]\n\n'));
controller.close();
}
}]
});
await model.invoke(message);
}
});
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
}
});
}
// 3. 带事件ID的SSE
app.get('/sse/chat-with-id', async (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
let eventId = 0;
const model = new ChatOpenAI({
streaming: true,
callbacks: [{
handleLLMNewToken(token: string) {
// 带事件ID
res.write(`id: ${eventId++}\n`);
res.write(`data: ${JSON.stringify({ token })}\n\n`);
},
handleLLMEnd() {
res.write(`id: ${eventId++}\n`);
res.write('data: [DONE]\n\n');
res.end();
}
}]
});
await model.invoke(req.query.message as string);
});
// 4. 自定义事件类型
app.get('/sse/typed-events', async (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
const model = new ChatOpenAI({
streaming: true,
callbacks: [{
handleLLMStart() {
res.write('event: start\n');
res.write('data: {"status":"started"}\n\n');
},
handleLLMNewToken(token: string) {
res.write('event: token\n');
res.write(`data: ${JSON.stringify({ token })}\n\n`);
},
handleLLMEnd() {
res.write('event: end\n');
res.write('data: {"status":"completed"}\n\n');
res.end();
}
}]
});
await model.invoke(req.query.message as string);
});
// 5. 心跳保持连接
app.get('/sse/with-heartbeat', async (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
// 每30秒发送心跳
const heartbeat = setInterval(() => {
res.write(': heartbeat\n\n');
}, 30000);
// 清理函数
req.on('close', () => {
clearInterval(heartbeat);
});
const model = new ChatOpenAI({
streaming: true,
callbacks: [{
handleLLMNewToken(token: string) {
res.write(`data: ${JSON.stringify({ token })}\n\n`);
},
handleLLMEnd() {
clearInterval(heartbeat);
res.write('data: [DONE]\n\n');
res.end();
}
}]
});
await model.invoke(req.query.message as string);
});
console.log("✓ SSE服务端示例完成");
---
b.客户端消费
a.功能说明
在浏览器中使用EventSource API消费SSE流。监听不同类型的事件。处理连接错误和重连。EventSource提供简单的SSE客户端实现。
b.代码示例
---
// 1. 基础EventSource客户端
function consumeSSE(message: string) {
const eventSource = new EventSource(
`/sse/chat?message=${encodeURIComponent(message)}`
);
eventSource.onmessage = (event) => {
if (event.data === '[DONE]') {
eventSource.close();
console.log('流式传输完成');
return;
}
const { token } = JSON.parse(event.data);
console.log(token);
// 或更新UI
// setResponse(prev => prev + token);
};
eventSource.onerror = (error) => {
console.error('SSE错误:', error);
eventSource.close();
};
}
// 2. React Hook for SSE
import { useState, useEffect, useCallback } from 'react';
interface UseSSEOptions {
onToken?: (token: string) => void;
onComplete?: () => void;
onError?: (error: Event) => void;
}
export function useSSE() {
const [isConnected, setIsConnected] = useState(false);
const [error, setError] = useState<Error | null>(null);
const connect = useCallback((
url: string,
options: UseSSEOptions = {}
) => {
const eventSource = new EventSource(url);
setIsConnected(true);
setError(null);
eventSource.onmessage = (event) => {
if (event.data === '[DONE]') {
eventSource.close();
setIsConnected(false);
options.onComplete?.();
return;
}
try {
const { token } = JSON.parse(event.data);
options.onToken?.(token);
} catch (err) {
console.error('解析错误:', err);
}
};
eventSource.onerror = (error) => {
console.error('SSE错误:', error);
setError(new Error('连接失败'));
setIsConnected(false);
eventSource.close();
options.onError?.(error);
};
return () => {
eventSource.close();
setIsConnected(false);
};
}, []);
return { connect, isConnected, error };
}
// 使用Hook
function ChatComponent() {
const [response, setResponse] = useState('');
const { connect, isConnected } = useSSE();
const handleSend = (message: string) => {
setResponse('');
connect(`/sse/chat?message=${encodeURIComponent(message)}`, {
onToken: (token) => {
setResponse(prev => prev + token);
},
onComplete: () => {
console.log('完成');
}
});
};
return (
<div>
<button onClick={() => handleSend('你好')}>
发送
</button>
<div>{response}</div>
{isConnected && <span>正在接收...</span>}
</div>
);
}
// 3. 监听自定义事件
function listenTypedEvents(message: string) {
const eventSource = new EventSource(
`/sse/typed-events?message=${encodeURIComponent(message)}`
);
// 监听start事件
eventSource.addEventListener('start', (event) => {
console.log('开始:', event.data);
});
// 监听token事件
eventSource.addEventListener('token', (event) => {
const { token } = JSON.parse(event.data);
console.log('Token:', token);
});
// 监听end事件
eventSource.addEventListener('end', (event) => {
console.log('完成:', event.data);
eventSource.close();
});
eventSource.onerror = (error) => {
console.error('错误:', error);
eventSource.close();
};
}
// 4. 自动重连
function autoReconnectSSE(url: string) {
let eventSource: EventSource | null = null;
let reconnectTimer: NodeJS.Timeout | null = null;
let reconnectAttempts = 0;
const maxReconnectAttempts = 5;
function connect() {
eventSource = new EventSource(url);
eventSource.onmessage = (event) => {
// 重置重连计数
reconnectAttempts = 0;
// 处理消息
console.log(event.data);
};
eventSource.onerror = (error) => {
console.error('连接错误:', error);
eventSource?.close();
// 尝试重连
if (reconnectAttempts < maxReconnectAttempts) {
reconnectAttempts++;
const delay = Math.min(1000 * Math.pow(2, reconnectAttempts), 30000);
console.log(`${delay}ms后重连 (尝试${reconnectAttempts})`);
reconnectTimer = setTimeout(() => {
connect();
}, delay);
} else {
console.error('达到最大重连次数');
}
};
}
connect();
// 返回清理函数
return () => {
if (reconnectTimer) {
clearTimeout(reconnectTimer);
}
eventSource?.close();
};
}
// 5. Vue3 Composable
// composables/useSSE.ts
import { ref, onUnmounted } from 'vue';
export function useSSE() {
const isConnected = ref(false);
const error = ref<Error | null>(null);
let eventSource: EventSource | null = null;
const connect = (url: string, callbacks: {
onMessage?: (data: any) => void;
onError?: (error: Event) => void;
}) => {
eventSource = new EventSource(url);
isConnected.value = true;
eventSource.onmessage = (event) => {
if (event.data === '[DONE]') {
disconnect();
return;
}
callbacks.onMessage?.(JSON.parse(event.data));
};
eventSource.onerror = (err) => {
error.value = new Error('连接失败');
isConnected.value = false;
callbacks.onError?.(err);
disconnect();
};
};
const disconnect = () => {
eventSource?.close();
eventSource = null;
isConnected.value = false;
};
onUnmounted(() => {
disconnect();
});
return {
connect,
disconnect,
isConnected,
error
};
}
console.log("✓ SSE客户端示例完成");
---
7.3 WebSocket
01.WebSocket服务端
a.基础服务器
a.功能说明
使用WebSocket实现双向实时通信。支持服务端主动推送和客户端实时响应。适合需要持久连接的场景。WebSocket是实时应用的标准方案。
b.代码示例
---
// 1. 基础WebSocket服务器(ws库)
import { WebSocketServer } from 'ws';
import { ChatOpenAI } from '@langchain/openai';
const wss = new WebSocketServer({ port: 8080 });
wss.on('connection', (ws) => {
console.log('客户端已连接');
ws.on('message', async (data) => {
try {
const { message } = JSON.parse(data.toString());
const model = new ChatOpenAI({
openAIApiKey: process.env.OPENAI_API_KEY,
streaming: true,
callbacks: [{
handleLLMNewToken(token: string) {
ws.send(JSON.stringify({
type: 'token',
data: token
}));
},
handleLLMEnd() {
ws.send(JSON.stringify({
type: 'done'
}));
},
handleLLMError(error: Error) {
ws.send(JSON.stringify({
type: 'error',
data: error.message
}));
}
}]
});
await model.invoke(message);
} catch (error) {
ws.send(JSON.stringify({
type: 'error',
data: error.message
}));
}
});
ws.on('close', () => {
console.log('客户端已断开');
});
ws.on('error', (error) => {
console.error('WebSocket错误:', error);
});
});
console.log('WebSocket服务器运行在 ws://localhost:8080');
// 2. Express + ws集成
import express from 'express';
import { WebSocketServer } from 'ws';
import http from 'http';
const app = express();
const server = http.createServer(app);
const wss = new WebSocketServer({ server });
wss.on('connection', (ws) => {
ws.on('message', async (data) => {
// 处理消息
});
});
server.listen(3000, () => {
console.log('服务器运行在 http://localhost:3000');
});
// 3. 广播消息
function broadcast(wss: WebSocketServer, data: any) {
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify(data));
}
});
}
wss.on('connection', (ws) => {
ws.on('message', async (data) => {
const { message } = JSON.parse(data.toString());
// 广播给所有客户端
broadcast(wss, {
type: 'message',
data: message,
timestamp: new Date().toISOString()
});
});
});
// 4. 房间/频道管理
const rooms = new Map<string, Set<WebSocket>>();
wss.on('connection', (ws) => {
let currentRoom: string | null = null;
ws.on('message', (data) => {
const msg = JSON.parse(data.toString());
switch (msg.type) {
case 'join':
// 加入房间
currentRoom = msg.room;
if (!rooms.has(currentRoom)) {
rooms.set(currentRoom, new Set());
}
rooms.get(currentRoom)!.add(ws);
break;
case 'leave':
// 离开房间
if (currentRoom) {
rooms.get(currentRoom)?.delete(ws);
}
break;
case 'message':
// 向房间发送消息
if (currentRoom) {
const roomClients = rooms.get(currentRoom);
roomClients?.forEach((client) => {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify({
type: 'message',
data: msg.data
}));
}
});
}
break;
}
});
ws.on('close', () => {
// 清理
if (currentRoom) {
rooms.get(currentRoom)?.delete(ws);
}
});
});
// 5. 心跳保持
function heartbeat(ws: WebSocket) {
ws.isAlive = true;
}
wss.on('connection', (ws) => {
ws.isAlive = true;
ws.on('pong', () => {
ws.isAlive = true;
});
ws.on('message', async (data) => {
// 处理消息
});
});
// 定时检查
const interval = setInterval(() => {
wss.clients.forEach((ws) => {
if (!ws.isAlive) {
return ws.terminate();
}
ws.isAlive = false;
ws.ping();
});
}, 30000);
wss.on('close', () => {
clearInterval(interval);
});
console.log("✓ WebSocket服务端示例完成");
---
b.Next.js WebSocket
a.功能说明
在Next.js中集成WebSocket服务。使用API Routes处理WebSocket连接。支持服务端渲染和WebSocket共存。Next.js需要自定义服务器支持WebSocket。
b.代码示例
---
// 1. Next.js自定义服务器
// server.ts
import { createServer } from 'http';
import { parse } from 'url';
import next from 'next';
import { WebSocketServer } from 'ws';
import { ChatOpenAI } from '@langchain/openai';
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
app.prepare().then(() => {
const server = createServer((req, res) => {
const parsedUrl = parse(req.url!, true);
handle(req, res, parsedUrl);
});
// WebSocket服务器
const wss = new WebSocketServer({ server });
wss.on('connection', (ws) => {
console.log('WebSocket客户端连接');
ws.on('message', async (data) => {
const { message } = JSON.parse(data.toString());
const model = new ChatOpenAI({
openAIApiKey: process.env.OPENAI_API_KEY,
streaming: true,
callbacks: [{
handleLLMNewToken(token: string) {
ws.send(JSON.stringify({ type: 'token', data: token }));
},
handleLLMEnd() {
ws.send(JSON.stringify({ type: 'done' }));
}
}]
});
await model.invoke(message);
});
});
server.listen(3000, () => {
console.log('> Ready on http://localhost:3000');
});
});
// package.json
{
"scripts": {
"dev": "ts-node server.ts",
"build": "next build",
"start": "NODE_ENV=production ts-node server.ts"
}
}
console.log("✓ Next.js WebSocket示例完成");
---
02.WebSocket客户端
a.浏览器WebSocket
a.功能说明
使用浏览器原生WebSocket API连接服务器。发送和接收实时消息。处理连接状态和错误。WebSocket API是W3C标准。
b.代码示例
---
// 1. 基础WebSocket客户端
function connectWebSocket(url: string) {
const ws = new WebSocket(url);
ws.onopen = () => {
console.log('WebSocket已连接');
// 发送消息
ws.send(JSON.stringify({
message: '你好'
}));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
switch (data.type) {
case 'token':
console.log('Token:', data.data);
break;
case 'done':
console.log('完成');
break;
case 'error':
console.error('错误:', data.data);
break;
}
};
ws.onerror = (error) => {
console.error('WebSocket错误:', error);
};
ws.onclose = (event) => {
console.log('WebSocket已关闭:', event.code, event.reason);
};
return ws;
}
// 使用
const ws = connectWebSocket('ws://localhost:8080');
// 2. React Hook
import { useState, useEffect, useRef, useCallback } from 'react';
interface UseWebSocketOptions {
onMessage?: (data: any) => void;
onOpen?: () => void;
onClose?: () => void;
onError?: (error: Event) => void;
reconnect?: boolean;
reconnectInterval?: number;
}
export function useWebSocket(url: string, options: UseWebSocketOptions = {}) {
const [isConnected, setIsConnected] = useState(false);
const [lastMessage, setLastMessage] = useState<any>(null);
const wsRef = useRef<WebSocket | null>(null);
const reconnectTimerRef = useRef<NodeJS.Timeout | null>(null);
const connect = useCallback(() => {
const ws = new WebSocket(url);
ws.onopen = () => {
setIsConnected(true);
options.onOpen?.();
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
setLastMessage(data);
options.onMessage?.(data);
};
ws.onerror = (error) => {
options.onError?.(error);
};
ws.onclose = () => {
setIsConnected(false);
options.onClose?.();
// 自动重连
if (options.reconnect) {
reconnectTimerRef.current = setTimeout(() => {
connect();
}, options.reconnectInterval || 3000);
}
};
wsRef.current = ws;
}, [url, options]);
const send = useCallback((data: any) => {
if (wsRef.current?.readyState === WebSocket.OPEN) {
wsRef.current.send(JSON.stringify(data));
}
}, []);
const disconnect = useCallback(() => {
if (reconnectTimerRef.current) {
clearTimeout(reconnectTimerRef.current);
}
wsRef.current?.close();
}, []);
useEffect(() => {
connect();
return () => {
disconnect();
};
}, [connect, disconnect]);
return {
send,
disconnect,
isConnected,
lastMessage
};
}
// 使用Hook
function ChatComponent() {
const [messages, setMessages] = useState<string[]>([]);
const [currentResponse, setCurrentResponse] = useState('');
const { send, isConnected } = useWebSocket('ws://localhost:8080', {
onMessage: (data) => {
if (data.type === 'token') {
setCurrentResponse(prev => prev + data.data);
} else if (data.type === 'done') {
setMessages(prev => [...prev, currentResponse]);
setCurrentResponse('');
}
},
reconnect: true
});
const handleSend = (message: string) => {
send({ message });
};
return (
<div>
<div>状态: {isConnected ? '已连接' : '未连接'}</div>
<div>{currentResponse}</div>
<button onClick={() => handleSend('你好')}>
发送
</button>
</div>
);
}
// 3. Vue3 Composable
// composables/useWebSocket.ts
import { ref, onUnmounted } from 'vue';
export function useWebSocket(url: string) {
const isConnected = ref(false);
const lastMessage = ref<any>(null);
let ws: WebSocket | null = null;
const connect = () => {
ws = new WebSocket(url);
ws.onopen = () => {
isConnected.value = true;
console.log('WebSocket已连接');
};
ws.onmessage = (event) => {
lastMessage.value = JSON.parse(event.data);
};
ws.onclose = () => {
isConnected.value = false;
console.log('WebSocket已关闭');
};
ws.onerror = (error) => {
console.error('WebSocket错误:', error);
};
};
const send = (data: any) => {
if (ws?.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify(data));
}
};
const disconnect = () => {
ws?.close();
};
// 自动连接
connect();
// 组件卸载时断开
onUnmounted(() => {
disconnect();
});
return {
isConnected,
lastMessage,
send,
disconnect
};
}
// 4. 重连策略
class WebSocketClient {
private ws: WebSocket | null = null;
private reconnectAttempts = 0;
private maxReconnectAttempts = 5;
private reconnectDelay = 1000;
constructor(
private url: string,
private handlers: {
onMessage?: (data: any) => void;
onOpen?: () => void;
onClose?: () => void;
onError?: (error: Event) => void;
}
) {
this.connect();
}
private connect() {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
this.reconnectAttempts = 0;
this.handlers.onOpen?.();
};
this.ws.onmessage = (event) => {
const data = JSON.parse(event.data);
this.handlers.onMessage?.(data);
};
this.ws.onerror = (error) => {
this.handlers.onError?.(error);
};
this.ws.onclose = () => {
this.handlers.onClose?.();
this.attemptReconnect();
};
}
private attemptReconnect() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts);
console.log(`${delay}ms后重连 (尝试${this.reconnectAttempts})`);
setTimeout(() => {
this.connect();
}, delay);
} else {
console.error('达到最大重连次数');
}
}
send(data: any) {
if (this.ws?.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(data));
}
}
close() {
this.maxReconnectAttempts = 0; // 禁止重连
this.ws?.close();
}
}
// 使用
const wsClient = new WebSocketClient('ws://localhost:8080', {
onMessage: (data) => console.log(data),
onOpen: () => console.log('已连接'),
onClose: () => console.log('已断开')
});
console.log("✓ WebSocket客户端示例完成");
---
8 实战案例
8.1 聊天机器人
01.基础聊天机器人
a.对话管理
a.功能说明
构建具有对话记忆和上下文理解的聊天机器人。管理多轮对话历史。支持个性化响应。对话管理是聊天机器人的核心能力。
b.代码示例
---
// 1. 基础聊天机器人
import { ChatOpenAI } from '@langchain/openai';
import { BufferMemory } from 'langchain/memory';
import { ConversationChain } from 'langchain/chains';
class BasicChatBot {
private chain: ConversationChain;
constructor() {
const model = new ChatOpenAI({
openAIApiKey: process.env.OPENAI_API_KEY,
temperature: 0.7
});
const memory = new BufferMemory({
returnMessages: true,
memoryKey: 'history'
});
this.chain = new ConversationChain({
llm: model,
memory
});
}
async chat(message: string): Promise<string> {
const response = await this.chain.call({
input: message
});
return response.response;
}
}
// 使用
const bot = new BasicChatBot();
// 第一轮
const response1 = await bot.chat('我叫张三');
console.log('Bot:', response1);
// 第二轮(记住名字)
const response2 = await bot.chat('我叫什么名字?');
console.log('Bot:', response2); // "你叫张三"
// 2. 带系统提示的聊天机器人
import { ChatPromptTemplate, MessagesPlaceholder } from '@langchain/core/prompts';
async function createCustomBot(systemPrompt: string) {
const prompt = ChatPromptTemplate.fromMessages([
['system', systemPrompt],
new MessagesPlaceholder('history'),
['human', '{input}']
]);
const model = new ChatOpenAI();
const memory = new BufferMemory({
returnMessages: true,
memoryKey: 'history'
});
const chain = new ConversationChain({
llm: model,
memory,
prompt
});
return chain;
}
// 创建不同性格的机器人
const friendlyBot = await createCustomBot(
'你是一个友好、幽默的AI助手,总是用积极的语气回答问题。'
);
const professionalBot = await createCustomBot(
'你是一个专业、严谨的AI助手,提供准确、详细的信息。'
);
// 3. 多用户聊天机器人
class MultiUserChatBot {
private sessions = new Map<string, ConversationChain>();
private createSession(): ConversationChain {
const model = new ChatOpenAI();
const memory = new BufferMemory({
returnMessages: true
});
return new ConversationChain({
llm: model,
memory
});
}
async chat(userId: string, message: string): Promise<string> {
if (!this.sessions.has(userId)) {
this.sessions.set(userId, this.createSession());
}
const chain = this.sessions.get(userId)!;
const response = await chain.call({ input: message });
return response.response;
}
clearSession(userId: string): void {
this.sessions.delete(userId);
}
getAllSessions(): string[] {
return Array.from(this.sessions.keys());
}
}
// 使用
const multiBot = new MultiUserChatBot();
// 用户1
await multiBot.chat('user1', '我喜欢编程');
// 用户2
await multiBot.chat('user2', '我喜欢音乐');
// 用户1(记住爱好)
await multiBot.chat('user1', '我喜欢什么?'); // "你喜欢编程"
// 4. 流式聊天机器人
class StreamingChatBot {
private model: ChatOpenAI;
private memory: BufferMemory;
constructor() {
this.model = new ChatOpenAI({
streaming: true
});
this.memory = new BufferMemory({
returnMessages: true
});
}
async chat(
message: string,
onToken: (token: string) => void
): Promise<void> {
// 获取历史
const history = await this.memory.loadMemoryVariables({});
// 流式调用
this.model.callbacks = [{
handleLLMNewToken(token: string) {
onToken(token);
}
}];
const response = await this.model.invoke([
...history.history,
{ role: 'human', content: message }
]);
// 保存到记忆
await this.memory.saveContext(
{ input: message },
{ output: response.content as string }
);
}
}
// 使用
const streamBot = new StreamingChatBot();
await streamBot.chat('你好', (token) => {
process.stdout.write(token);
});
// 5. 带工具的聊天机器人
import { initializeAgentExecutorWithOptions } from 'langchain/agents';
import { Calculator } from '@langchain/community/tools/calculator';
import { DynamicTool } from '@langchain/core/tools';
class ToolEnabledChatBot {
private executor: any;
async initialize() {
const searchTool = new DynamicTool({
name: 'search',
description: '搜索信息',
func: async (query: string) => {
// 模拟搜索
return `关于"${query}"的搜索结果...`;
}
});
const tools = [
new Calculator(),
searchTool
];
this.executor = await initializeAgentExecutorWithOptions(
tools,
new ChatOpenAI({ temperature: 0 }),
{
agentType: 'chat-conversational-react-description',
memory: new BufferMemory({
returnMessages: true,
memoryKey: 'chat_history'
})
}
);
}
async chat(message: string): Promise<string> {
const result = await this.executor.call({
input: message
});
return result.output;
}
}
// 使用
const toolBot = new ToolEnabledChatBot();
await toolBot.initialize();
await toolBot.chat('100乘以25是多少?'); // 使用计算器
await toolBot.chat('搜索最新的AI新闻'); // 使用搜索
// 6. 上下文窗口管理
import { BufferWindowMemory } from 'langchain/memory';
class WindowedChatBot {
private chain: ConversationChain;
constructor(windowSize: number = 5) {
const model = new ChatOpenAI();
// 只保留最近N轮对话
const memory = new BufferWindowMemory({
k: windowSize,
returnMessages: true
});
this.chain = new ConversationChain({
llm: model,
memory
});
}
async chat(message: string): Promise<string> {
const response = await this.chain.call({
input: message
});
return response.response;
}
}
// 7. 个性化聊天机器人
interface UserProfile {
name: string;
age?: number;
interests?: string[];
preferences?: Record<string, any>;
}
class PersonalizedChatBot {
private profiles = new Map<string, UserProfile>();
setProfile(userId: string, profile: UserProfile) {
this.profiles.set(userId, profile);
}
async chat(userId: string, message: string): Promise<string> {
const profile = this.profiles.get(userId);
let systemPrompt = '你是一个友好的AI助手。';
if (profile) {
systemPrompt += `\n用户信息:`;
systemPrompt += `\n- 名字:${profile.name}`;
if (profile.age) {
systemPrompt += `\n- 年龄:${profile.age}`;
}
if (profile.interests) {
systemPrompt += `\n- 兴趣:${profile.interests.join('、')}`;
}
}
const model = new ChatOpenAI();
const response = await model.invoke([
{ role: 'system', content: systemPrompt },
{ role: 'user', content: message }
]);
return response.content as string;
}
}
// 使用
const personalBot = new PersonalizedChatBot();
personalBot.setProfile('user1', {
name: '张三',
age: 25,
interests: ['编程', '阅读']
});
await personalBot.chat('user1', '推荐一本书');
// Bot会考虑用户的兴趣推荐编程或其他书籍
console.log("✓ 基础聊天机器人示例完成");
---
b.意图识别
a.功能说明
识别用户消息的意图并路由到相应处理逻辑。支持多意图分类。提升响应的准确性和相关性。意图识别是智能对话的关键。
b.代码示例
---
// 1. 基础意图识别
import { ChatOpenAI } from '@langchain/openai';
import { StructuredOutputParser } from 'langchain/output_parsers';
import { z } from 'zod';
class IntentClassifier {
private model: ChatOpenAI;
private parser: StructuredOutputParser<any>;
constructor() {
this.model = new ChatOpenAI({ temperature: 0 });
this.parser = StructuredOutputParser.fromZodSchema(
z.object({
intent: z.enum([
'greeting', // 问候
'question', // 提问
'command', // 命令
'feedback', // 反馈
'chitchat', // 闲聊
'other' // 其他
]).describe('用户意图'),
confidence: z.number().min(0).max(1).describe('置信度'),
entities: z.array(z.string()).describe('实体')
})
);
}
async classify(message: string) {
const prompt = `
分析用户消息的意图:
消息:"${message}"
${this.parser.getFormatInstructions()}
`;
const response = await this.model.invoke(prompt);
const result = await this.parser.parse(response.content as string);
return result;
}
}
// 使用
const classifier = new IntentClassifier();
const intent1 = await classifier.classify('你好');
console.log(intent1); // { intent: 'greeting', ... }
const intent2 = await classifier.classify('今天天气怎么样?');
console.log(intent2); // { intent: 'question', ... }
// 2. 意图路由聊天机器人
class IntentRoutedChatBot {
private classifier: IntentClassifier;
private handlers: Map<string, (message: string) => Promise<string>>;
constructor() {
this.classifier = new IntentClassifier();
this.handlers = new Map();
// 注册处理器
this.registerHandler('greeting', this.handleGreeting.bind(this));
this.registerHandler('question', this.handleQuestion.bind(this));
this.registerHandler('command', this.handleCommand.bind(this));
this.registerHandler('chitchat', this.handleChitchat.bind(this));
}
registerHandler(
intent: string,
handler: (message: string) => Promise<string>
) {
this.handlers.set(intent, handler);
}
async chat(message: string): Promise<string> {
// 识别意图
const { intent, confidence } = await this.classifier.classify(message);
console.log(`识别意图: ${intent} (置信度: ${confidence})`);
// 路由到对应处理器
const handler = this.handlers.get(intent);
if (handler && confidence > 0.7) {
return await handler(message);
} else {
return await this.handleDefault(message);
}
}
private async handleGreeting(message: string): Promise<string> {
return '你好!很高兴见到你。';
}
private async handleQuestion(message: string): Promise<string> {
// 使用LLM回答问题
const model = new ChatOpenAI();
const response = await model.invoke(message);
return response.content as string;
}
private async handleCommand(message: string): Promise<string> {
return '正在执行命令...';
}
private async handleChitchat(message: string): Promise<string> {
const model = new ChatOpenAI({ temperature: 0.9 });
const response = await model.invoke(message);
return response.content as string;
}
private async handleDefault(message: string): Promise<string> {
return '抱歉,我没理解你的意思。能否换个方式说?';
}
}
// 使用
const routedBot = new IntentRoutedChatBot();
await routedBot.chat('你好'); // 问候处理
await routedBot.chat('什么是AI?'); // 问题处理
await routedBot.chat('今天天气不错'); // 闲聊处理
console.log("✓ 意图识别示例完成");
---
8.2 文档问答
01.PDF问答系统
a.文档加载
a.功能说明
构建PDF文档问答系统。自动提取和索引文档内容。基于文档回答用户问题。文档问答助力知识管理。
b.代码示例
---
// 1. PDF加载和分割
import { PDFLoader } from "langchain/document_loaders/fs/pdf";
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
async function loadAndSplitPDF(filePath: string) {
// 加载PDF
const loader = new PDFLoader(filePath);
const docs = await loader.load();
console.log(`加载了${docs.length}页`);
// 分割文本
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 1000,
chunkOverlap: 200
});
const splitDocs = await splitter.splitDocuments(docs);
console.log(`分割成${splitDocs.length}块`);
return splitDocs;
}
// 2. 向量化和索引
import { MemoryVectorStore } from "langchain/vectorstores/memory";
import { OpenAIEmbeddings } from "@langchain/openai";
async function indexDocuments(docs: Document[]) {
const vectorStore = await MemoryVectorStore.fromDocuments(
docs,
new OpenAIEmbeddings()
);
return vectorStore;
}
// 3. 问答链
import { RetrievalQAChain } from "langchain/chains";
import { ChatOpenAI } from "@langchain/openai";
async function createQAChain(vectorStore: MemoryVectorStore) {
const chain = RetrievalQAChain.fromLLM(
new ChatOpenAI({ modelName: "gpt-3.5-turbo" }),
vectorStore.asRetriever({
k: 4 // 检索4个相关块
})
);
return chain;
}
// 4. 完整PDF问答系统
class PDFQASystem {
private vectorStore: MemoryVectorStore | null = null;
private qaChain: RetrievalQAChain | null = null;
async loadDocument(filePath: string) {
console.log("📄 加载文档...");
// 1. 加载PDF
const docs = await loadAndSplitPDF(filePath);
// 2. 向量化
console.log("🔄 向量化中...");
this.vectorStore = await indexDocuments(docs);
// 3. 创建QA链
this.qaChain = await createQAChain(this.vectorStore);
console.log("✓ 文档加载完成");
}
async ask(question: string) {
if (!this.qaChain) {
throw new Error("请先加载文档");
}
console.log(`\n❓ 问题: ${question}`);
const result = await this.qaChain.call({
query: question
});
console.log(`💡 答案: ${result.text}`);
return result.text;
}
async askWithSources(question: string) {
if (!this.vectorStore) {
throw new Error("请先加载文档");
}
// 检索相关文档
const relevantDocs = await this.vectorStore.similaritySearch(
question,
4
);
// 生成答案
const context = relevantDocs
.map((doc, i) => `[${i + 1}] ${doc.pageContent}`)
.join("\n\n");
const model = new ChatOpenAI();
const response = await model.invoke([
{
role: "system",
content: "根据以下文档内容回答问题,并引用来源"
},
{
role: "user",
content: `文档内容:\n${context}\n\n问题: ${question}`
}
]);
return {
answer: response.content,
sources: relevantDocs.map(doc => ({
content: doc.pageContent,
metadata: doc.metadata
}))
};
}
}
// 使用
const qaSystem = new PDFQASystem();
await qaSystem.loadDocument("./document.pdf");
const answer1 = await qaSystem.ask("文档的主要内容是什么?");
const answer2 = await qaSystem.ask("关于XX的描述有哪些?");
// 5. 多文档问答
class MultiDocQA {
private vectorStores = new Map<string, MemoryVectorStore>();
async addDocument(docId: string, filePath: string) {
const docs = await loadAndSplitPDF(filePath);
const vectorStore = await indexDocuments(docs);
this.vectorStores.set(docId, vectorStore);
}
async askAcrossDocuments(question: string) {
// 从所有文档检索
const allResults = await Promise.all(
Array.from(this.vectorStores.values()).map(store =>
store.similaritySearch(question, 2)
)
);
const allDocs = allResults.flat();
// 生成答案
const model = new ChatOpenAI();
const context = allDocs.map(d => d.pageContent).join("\n");
const response = await model.invoke([
{
role: "system",
content: "基于多个文档回答问题"
},
{
role: "user",
content: `上下文:\n${context}\n\n问题:${question}`
}
]);
return response.content;
}
}
// 6. 浏览器中的PDF问答
// 使用Web API
async function browserPDFQA(file: File) {
// 读取文件
const arrayBuffer = await file.arrayBuffer();
const blob = new Blob([arrayBuffer], { type: "application/pdf" });
// 使用pdfjs-dist解析(需要安装)
// import * as pdfjsLib from "pdfjs-dist";
// const pdf = await pdfjsLib.getDocument(arrayBuffer).promise;
// const numPages = pdf.numPages;
// 提取文本...
// 向量化...
// 构建QA系统...
}
console.log("✓ 文档问答示例完成");
---
8.3 智能搜索
01.语义搜索
a.向量搜索引擎
a.功能说明
基于向量相似度实现语义搜索。理解查询意图而非关键词匹配。返回语义相关的结果。语义搜索提升搜索准确性和用户体验。
b.代码示例
---
// 1. 基础语义搜索引擎
import { MemoryVectorStore } from 'langchain/vectorstores/memory';
import { OpenAIEmbeddings } from '@langchain/openai';
import { Document } from '@langchain/core/documents';
class SemanticSearchEngine {
private vectorStore: MemoryVectorStore | null = null;
async initialize(documents: string[]) {
const docs = documents.map(content =>
new Document({ pageContent: content })
);
this.vectorStore = await MemoryVectorStore.fromDocuments(
docs,
new OpenAIEmbeddings({
openAIApiKey: process.env.OPENAI_API_KEY
})
);
console.log(`索引了${documents.length}个文档`);
}
async search(query: string, topK: number = 5) {
if (!this.vectorStore) {
throw new Error('搜索引擎未初始化');
}
const results = await this.vectorStore.similaritySearchWithScore(
query,
topK
);
return results.map(([doc, score]) => ({
content: doc.pageContent,
score: score,
metadata: doc.metadata
}));
}
}
// 使用
const searchEngine = new SemanticSearchEngine();
await searchEngine.initialize([
'Python是一种编程语言',
'JavaScript用于Web开发',
'React是前端框架',
'Node.js是JavaScript运行时',
'TypeScript是JavaScript的超集'
]);
// 语义搜索
const results = await searchEngine.search('前端技术', 3);
results.forEach((result, i) => {
console.log(`${i + 1}. ${result.content} (相似度: ${result.score.toFixed(3)})`);
});
// 2. 带元数据的搜索
class MetadataSearchEngine {
private vectorStore: MemoryVectorStore | null = null;
async initialize(items: Array<{
content: string;
metadata: Record<string, any>;
}>) {
const docs = items.map(item =>
new Document({
pageContent: item.content,
metadata: item.metadata
})
);
this.vectorStore = await MemoryVectorStore.fromDocuments(
docs,
new OpenAIEmbeddings()
);
}
async search(
query: string,
filters?: Record<string, any>,
topK: number = 5
) {
if (!this.vectorStore) {
throw new Error('未初始化');
}
const results = await this.vectorStore.similaritySearch(
query,
topK,
filters ? (doc) => {
// 应用过滤器
return Object.entries(filters).every(([key, value]) =>
doc.metadata[key] === value
);
} : undefined
);
return results;
}
}
// 使用
const metaSearchEngine = new MetadataSearchEngine();
await metaSearchEngine.initialize([
{
content: 'Python教程',
metadata: { category: 'programming', language: 'python', level: 'beginner' }
},
{
content: 'JavaScript高级',
metadata: { category: 'programming', language: 'javascript', level: 'advanced' }
},
{
content: '机器学习入门',
metadata: { category: 'ai', level: 'beginner' }
}
]);
// 搜索:编程类的初级内容
const results = await metaSearchEngine.search(
'学习编程',
{ category: 'programming', level: 'beginner' }
);
// 3. 混合搜索(关键词+语义)
class HybridSearchEngine {
private vectorStore: MemoryVectorStore;
private documents: Document[];
constructor(docs: Document[]) {
this.documents = docs;
}
async initialize() {
this.vectorStore = await MemoryVectorStore.fromDocuments(
this.documents,
new OpenAIEmbeddings()
);
}
// 关键词搜索
private keywordSearch(query: string, topK: number): Document[] {
const queryLower = query.toLowerCase();
const keywords = queryLower.split(/\s+/);
const scored = this.documents.map(doc => {
const contentLower = doc.pageContent.toLowerCase();
// 计算关键词匹配分数
const score = keywords.reduce((sum, keyword) => {
const count = (contentLower.match(new RegExp(keyword, 'g')) || []).length;
return sum + count;
}, 0);
return { doc, score };
});
return scored
.filter(item => item.score > 0)
.sort((a, b) => b.score - a.score)
.slice(0, topK)
.map(item => item.doc);
}
async search(query: string, topK: number = 5) {
// 语义搜索
const semanticResults = await this.vectorStore.similaritySearch(
query,
topK
);
// 关键词搜索
const keywordResults = this.keywordSearch(query, topK);
// 合并去重
const resultMap = new Map<string, Document>();
semanticResults.forEach(doc => {
resultMap.set(doc.pageContent, doc);
});
keywordResults.forEach(doc => {
resultMap.set(doc.pageContent, doc);
});
return Array.from(resultMap.values()).slice(0, topK);
}
}
// 4. 增量索引
class IncrementalSearchEngine {
private vectorStore: MemoryVectorStore;
async initialize() {
this.vectorStore = await MemoryVectorStore.fromDocuments(
[],
new OpenAIEmbeddings()
);
}
async addDocument(content: string, metadata: Record<string, any> = {}) {
await this.vectorStore.addDocuments([
new Document({ pageContent: content, metadata })
]);
console.log('文档已添加到索引');
}
async addDocuments(items: Array<{
content: string;
metadata?: Record<string, any>;
}>) {
const docs = items.map(item =>
new Document({
pageContent: item.content,
metadata: item.metadata || {}
})
);
await this.vectorStore.addDocuments(docs);
console.log(`${items.length}个文档已添加到索引`);
}
async search(query: string, topK: number = 5) {
return await this.vectorStore.similaritySearch(query, topK);
}
}
// 使用
const incrementalEngine = new IncrementalSearchEngine();
await incrementalEngine.initialize();
// 逐步添加文档
await incrementalEngine.addDocument('新文档1');
await incrementalEngine.addDocument('新文档2');
// 搜索
const results = await incrementalEngine.search('查询');
// 5. 多语言搜索
class MultilingualSearchEngine {
private vectorStore: MemoryVectorStore;
async initialize(documents: Array<{
content: string;
language: string;
}>) {
const docs = documents.map(item =>
new Document({
pageContent: item.content,
metadata: { language: item.language }
})
);
// OpenAI embeddings支持多语言
this.vectorStore = await MemoryVectorStore.fromDocuments(
docs,
new OpenAIEmbeddings()
);
}
async search(
query: string,
language?: string,
topK: number = 5
) {
const results = await this.vectorStore.similaritySearch(
query,
topK,
language ? (doc) => doc.metadata.language === language : undefined
);
return results;
}
}
// 使用
const multilingualEngine = new MultilingualSearchEngine();
await multilingualEngine.initialize([
{ content: 'Hello, world', language: 'en' },
{ content: '你好,世界', language: 'zh' },
{ content: 'こんにちは、世界', language: 'ja' }
]);
// 跨语言搜索(自动找到相关内容)
const results = await multilingualEngine.search('greeting');
// 或限定语言
const zhResults = await multilingualEngine.search('问候', 'zh');
console.log("✓ 语义搜索引擎示例完成");
---
b.重排序优化
a.功能说明
对搜索结果进行重排序提升相关性。使用LLM评估结果质量。结合多个因素综合排序。重排序优化搜索体验。
b.代码示例
---
// 1. LLM重排序
import { ChatOpenAI } from '@langchain/openai';
class RerankedSearchEngine {
private vectorStore: MemoryVectorStore;
private model: ChatOpenAI;
constructor() {
this.model = new ChatOpenAI({ temperature: 0 });
}
async initialize(docs: Document[]) {
this.vectorStore = await MemoryVectorStore.fromDocuments(
docs,
new OpenAIEmbeddings()
);
}
async search(query: string, topK: number = 5) {
// 先检索更多候选
const candidates = await this.vectorStore.similaritySearch(
query,
topK * 2
);
// 使用LLM重排序
const reranked = await this.rerank(query, candidates);
return reranked.slice(0, topK);
}
private async rerank(
query: string,
documents: Document[]
): Promise<Document[]> {
const prompt = `
查询:${query}
以下是搜索结果,请根据与查询的相关性从高到低排序(只返回索引号):
${documents.map((doc, i) => `${i}. ${doc.pageContent}`).join('\n')}
返回格式:0,2,1,3...(用逗号分隔的索引)
`;
const response = await this.model.invoke(prompt);
const indices = (response.content as string)
.split(',')
.map(s => parseInt(s.trim()));
return indices.map(i => documents[i]).filter(Boolean);
}
}
// 2. 多因素排序
class MultiFactorSearchEngine {
private vectorStore: MemoryVectorStore;
async initialize(docs: Array<{
content: string;
popularity: number; // 热度
freshness: number; // 新鲜度
quality: number; // 质量
}>) {
const documents = docs.map(item =>
new Document({
pageContent: item.content,
metadata: {
popularity: item.popularity,
freshness: item.freshness,
quality: item.quality
}
})
);
this.vectorStore = await MemoryVectorStore.fromDocuments(
documents,
new OpenAIEmbeddings()
);
}
async search(query: string, topK: number = 5) {
// 语义搜索
const results = await this.vectorStore.similaritySearchWithScore(
query,
topK * 2
);
// 综合排序
const scored = results.map(([doc, similarity]) => {
const { popularity, freshness, quality } = doc.metadata;
// 加权计算最终分数
const finalScore =
similarity * 0.4 +
popularity * 0.3 +
freshness * 0.2 +
quality * 0.1;
return { doc, score: finalScore };
});
// 按最终分数排序
scored.sort((a, b) => b.score - a.score);
return scored.slice(0, topK).map(item => item.doc);
}
}
console.log("✓ 重排序优化示例完成");
---
9 最佳实践
9.1 性能优化
01.优化策略
a.缓存机制
a.功能说明
实施多层缓存策略减少API调用。使用请求去重和批量处理。监控性能指标持续优化。性能优化降低成本提升体验。
b.代码示例
---
// 1. 多层缓存系统
class MultiLevelCache {
private l1Cache = new Map<string, any>(); // 内存缓存
private l2Cache: any; // localStorage
private maxL1Size = 100;
constructor() {
if (typeof localStorage !== "undefined") {
this.l2Cache = localStorage;
}
}
get(key: string): any | null {
// L1缓存
if (this.l1Cache.has(key)) {
console.log("L1 缓存命中");
return this.l1Cache.get(key);
}
// L2缓存
if (this.l2Cache) {
const value = this.l2Cache.getItem(key);
if (value) {
console.log("L2 缓存命中");
const parsed = JSON.parse(value);
// 提升到L1
this.l1Cache.set(key, parsed);
return parsed;
}
}
return null;
}
set(key: string, value: any, ttl?: number): void {
// 存入L1
if (this.l1Cache.size >= this.maxL1Size) {
// LRU淘汰
const firstKey = this.l1Cache.keys().next().value;
this.l1Cache.delete(firstKey);
}
this.l1Cache.set(key, value);
// 存入L2
if (this.l2Cache) {
const item = {
value,
timestamp: Date.now(),
ttl
};
this.l2Cache.setItem(key, JSON.stringify(item));
}
}
clear(): void {
this.l1Cache.clear();
if (this.l2Cache) {
this.l2Cache.clear();
}
}
}
// 2. 请求去重
class RequestDeduplicator {
private pending = new Map<string, Promise<any>>();
async deduplicate<T>(
key: string,
fn: () => Promise<T>
): Promise<T> {
// 如果请求正在进行,返回同一个Promise
if (this.pending.has(key)) {
console.log("请求去重:", key);
return this.pending.get(key)!;
}
// 创建新请求
const promise = fn().finally(() => {
this.pending.delete(key);
});
this.pending.set(key, promise);
return promise;
}
}
// 使用
const dedup = new RequestDeduplicator();
// 同时发起相同请求
const [result1, result2, result3] = await Promise.all([
dedup.deduplicate("query1", () => model.invoke("test")),
dedup.deduplicate("query1", () => model.invoke("test")),
dedup.deduplicate("query1", () => model.invoke("test"))
]);
// 只会实际调用1次API
// 3. 批量处理优化
class BatchProcessor {
private queue: Array<{
input: string;
resolve: (value: any) => void;
reject: (error: any) => void;
}> = [];
private batchSize = 10;
private batchDelay = 100; // ms
private timer: any = null;
constructor(private model: ChatOpenAI) {}
async process(input: string): Promise<any> {
return new Promise((resolve, reject) => {
this.queue.push({ input, resolve, reject });
// 设置延迟批处理
if (!this.timer) {
this.timer = setTimeout(() => {
this.flush();
}, this.batchDelay);
}
// 达到批大小立即处理
if (this.queue.length >= this.batchSize) {
clearTimeout(this.timer);
this.timer = null;
this.flush();
}
});
}
private async flush() {
if (this.queue.length === 0) return;
const batch = this.queue.splice(0, this.batchSize);
const inputs = batch.map(item => item.input);
try {
const results = await this.model.batch(inputs);
batch.forEach((item, i) => {
item.resolve(results[i]);
});
} catch (error) {
batch.forEach(item => {
item.reject(error);
});
}
}
}
// 使用
const batchProcessor = new BatchProcessor(new ChatOpenAI());
// 多个请求自动批处理
const results = await Promise.all([
batchProcessor.process("query1"),
batchProcessor.process("query2"),
batchProcessor.process("query3")
]);
// 4. 性能监控
class PerformanceMonitor {
private metrics: Array<{
operation: string;
duration: number;
timestamp: Date;
}> = [];
async measure<T>(
operation: string,
fn: () => Promise<T>
): Promise<T> {
const start = Date.now();
try {
const result = await fn();
const duration = Date.now() - start;
this.metrics.push({
operation,
duration,
timestamp: new Date()
});
console.log(`${operation}: ${duration}ms`);
return result;
} catch (error) {
throw error;
}
}
getStats() {
const byOperation = new Map<string, number[]>();
this.metrics.forEach(metric => {
if (!byOperation.has(metric.operation)) {
byOperation.set(metric.operation, []);
}
byOperation.get(metric.operation)!.push(metric.duration);
});
const stats: any = {};
byOperation.forEach((durations, operation) => {
const avg = durations.reduce((a, b) => a + b) / durations.length;
const min = Math.min(...durations);
const max = Math.max(...durations);
stats[operation] = {
count: durations.length,
avg: Math.round(avg),
min,
max
};
});
return stats;
}
}
const monitor = new PerformanceMonitor();
// 监控操作
await monitor.measure("llm_invoke", async () => {
return await model.invoke("test");
});
// 查看统计
console.log(monitor.getStats());
// 5. 连接池
class ModelPool {
private pool: ChatOpenAI[] = [];
private maxSize = 5;
private inUse = new Set<ChatOpenAI>();
constructor() {
// 预创建连接
for (let i = 0; i < this.maxSize; i++) {
this.pool.push(new ChatOpenAI());
}
}
async acquire(): Promise<ChatOpenAI> {
// 从池中获取
if (this.pool.length > 0) {
const model = this.pool.pop()!;
this.inUse.add(model);
return model;
}
// 等待释放
await new Promise(resolve => setTimeout(resolve, 100));
return this.acquire();
}
release(model: ChatOpenAI): void {
this.inUse.delete(model);
this.pool.push(model);
}
async use<T>(fn: (model: ChatOpenAI) => Promise<T>): Promise<T> {
const model = await this.acquire();
try {
return await fn(model);
} finally {
this.release(model);
}
}
}
const pool = new ModelPool();
// 使用连接池
const result = await pool.use(async (model) => {
return await model.invoke("test");
});
console.log("✓ 性能优化示例完成");
---
9.2 错误处理
01.异常捕获
a.Try-Catch模式
a.功能说明
使用try-catch捕获和处理LangChain错误。区分不同类型的错误。提供友好的错误信息。完善的错误处理提升应用稳定性。
b.代码示例
---
// 1. 基础错误处理
import { ChatOpenAI } from '@langchain/openai';
async function basicErrorHandling() {
const model = new ChatOpenAI({
openAIApiKey: process.env.OPENAI_API_KEY
});
try {
const result = await model.invoke('测试');
console.log('成功:', result.content);
} catch (error) {
console.error('错误:', error.message);
// 检查错误类型
if (error.message.includes('API key')) {
console.error('❌ API密钥无效或缺失');
} else if (error.message.includes('timeout')) {
console.error('❌ 请求超时');
} else if (error.message.includes('rate limit')) {
console.error('❌ 超出速率限制');
} else {
console.error('❌ 未知错误');
}
}
}
// 2. 错误分类处理
enum ErrorType {
API_KEY_ERROR = 'API_KEY_ERROR',
TIMEOUT_ERROR = 'TIMEOUT_ERROR',
RATE_LIMIT_ERROR = 'RATE_LIMIT_ERROR',
NETWORK_ERROR = 'NETWORK_ERROR',
VALIDATION_ERROR = 'VALIDATION_ERROR',
UNKNOWN_ERROR = 'UNKNOWN_ERROR'
}
function classifyError(error: Error): ErrorType {
const message = error.message.toLowerCase();
if (message.includes('api key') || message.includes('unauthorized')) {
return ErrorType.API_KEY_ERROR;
} else if (message.includes('timeout')) {
return ErrorType.TIMEOUT_ERROR;
} else if (message.includes('rate limit') || message.includes('quota')) {
return ErrorType.RATE_LIMIT_ERROR;
} else if (message.includes('network') || message.includes('ECONNREFUSED')) {
return ErrorType.NETWORK_ERROR;
} else if (message.includes('validation')) {
return ErrorType.VALIDATION_ERROR;
}
return ErrorType.UNKNOWN_ERROR;
}
async function handleClassifiedError() {
const model = new ChatOpenAI();
try {
await model.invoke('测试');
} catch (error) {
const errorType = classifyError(error as Error);
switch (errorType) {
case ErrorType.API_KEY_ERROR:
console.error('请检查API密钥配置');
// 可能的恢复操作:提示用户重新输入密钥
break;
case ErrorType.TIMEOUT_ERROR:
console.error('请求超时,请稍后重试');
// 可能的恢复操作:自动重试
break;
case ErrorType.RATE_LIMIT_ERROR:
console.error('请求过于频繁,请稍后再试');
// 可能的恢复操作:等待后重试
break;
case ErrorType.NETWORK_ERROR:
console.error('网络连接失败');
// 可能的恢复操作:检查网络连接
break;
default:
console.error('发生未知错误');
break;
}
}
}
// 3. 自定义错误类
class LangChainError extends Error {
constructor(
message: string,
public errorType: ErrorType,
public originalError?: Error
) {
super(message);
this.name = 'LangChainError';
}
}
async function throwCustomError() {
try {
const model = new ChatOpenAI();
await model.invoke('测试');
} catch (error) {
const errorType = classifyError(error as Error);
throw new LangChainError(
`LangChain操作失败: ${error.message}`,
errorType,
error as Error
);
}
}
// 使用
try {
await throwCustomError();
} catch (error) {
if (error instanceof LangChainError) {
console.error('错误类型:', error.errorType);
console.error('错误信息:', error.message);
console.error('原始错误:', error.originalError);
}
}
// 4. 错误边界(React)
// ErrorBoundary.tsx
import React, { Component, ReactNode } from 'react';
interface Props {
children: ReactNode;
fallback?: ReactNode;
}
interface State {
hasError: boolean;
error: Error | null;
}
export class LangChainErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error('LangChain错误:', error);
console.error('错误信息:', errorInfo);
// 可以发送到错误监控服务
// logErrorToService(error, errorInfo);
}
render() {
if (this.state.hasError) {
return this.props.fallback || (
<div>
<h2>出错了</h2>
<p>{this.state.error?.message}</p>
<button onClick={() => this.setState({ hasError: false, error: null })}>
重试
</button>
</div>
);
}
return this.props.children;
}
}
// 使用
<LangChainErrorBoundary>
<ChatComponent />
</LangChainErrorBoundary>
// 5. 错误日志记录
class ErrorLogger {
private logs: Array<{
timestamp: Date;
error: Error;
context: any;
}> = [];
log(error: Error, context: any = {}) {
this.logs.push({
timestamp: new Date(),
error,
context
});
// 输出到控制台
console.error('[错误]', {
message: error.message,
stack: error.stack,
context
});
// 可以发送到远程日志服务
// this.sendToRemote(error, context);
}
getLogs() {
return this.logs;
}
getRecentErrors(count: number = 10) {
return this.logs.slice(-count);
}
clear() {
this.logs = [];
}
}
const errorLogger = new ErrorLogger();
// 使用
try {
await model.invoke('测试');
} catch (error) {
errorLogger.log(error as Error, {
operation: 'invoke',
input: '测试'
});
}
console.log("✓ 异常捕获示例完成");
---
b.重试机制
a.功能说明
自动重试失败的请求提升成功率。使用指数退避策略避免过载。配置最大重试次数。重试机制增强系统鲁棒性。
b.代码示例
---
// 1. 基础重试函数
async function retry<T>(
fn: () => Promise<T>,
maxRetries: number = 3,
delay: number = 1000
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
console.log(`第${i + 1}次尝试失败`);
if (i === maxRetries - 1) {
throw error;
}
// 等待后重试
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw new Error('Max retries exceeded');
}
// 使用
const result = await retry(async () => {
const model = new ChatOpenAI();
return await model.invoke('测试');
}, 3, 1000);
// 2. 指数退避重试
async function retryWithExponentialBackoff<T>(
fn: () => Promise<T>,
maxRetries: number = 5,
initialDelay: number = 1000,
maxDelay: number = 30000
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (i === maxRetries - 1) {
throw error;
}
// 指数退避:1s, 2s, 4s, 8s, 16s...
const delay = Math.min(
initialDelay * Math.pow(2, i),
maxDelay
);
console.log(`等待${delay}ms后重试(第${i + 1}次失败)`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw new Error('Max retries exceeded');
}
// 使用
const result = await retryWithExponentialBackoff(async () => {
const model = new ChatOpenAI();
return await model.invoke('测试');
});
// 3. 条件重试
async function retryWithCondition<T>(
fn: () => Promise<T>,
shouldRetry: (error: Error) => boolean,
maxRetries: number = 3
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
// 检查是否应该重试
if (!shouldRetry(error as Error)) {
throw error;
}
if (i === maxRetries - 1) {
throw error;
}
console.log(`可重试的错误,第${i + 1}次尝试失败`);
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
throw new Error('Max retries exceeded');
}
// 使用
const result = await retryWithCondition(
async () => {
const model = new ChatOpenAI();
return await model.invoke('测试');
},
(error) => {
// 只重试网络错误和超时,不重试API密钥错误
return error.message.includes('timeout') ||
error.message.includes('network');
}
);
// 4. 带回调的重试
interface RetryOptions<T> {
maxRetries: number;
delay: number;
onRetry?: (attempt: number, error: Error) => void;
onSuccess?: (result: T) => void;
onFailure?: (error: Error) => void;
}
async function retryWithCallbacks<T>(
fn: () => Promise<T>,
options: RetryOptions<T>
): Promise<T> {
for (let i = 0; i < options.maxRetries; i++) {
try {
const result = await fn();
options.onSuccess?.(result);
return result;
} catch (error) {
options.onRetry?.(i + 1, error as Error);
if (i === options.maxRetries - 1) {
options.onFailure?.(error as Error);
throw error;
}
await new Promise(resolve => setTimeout(resolve, options.delay));
}
}
throw new Error('Max retries exceeded');
}
// 使用
const result = await retryWithCallbacks(
async () => {
const model = new ChatOpenAI();
return await model.invoke('测试');
},
{
maxRetries: 3,
delay: 1000,
onRetry: (attempt, error) => {
console.log(`重试第${attempt}次:`, error.message);
},
onSuccess: (result) => {
console.log('成功:', result);
},
onFailure: (error) => {
console.error('最终失败:', error.message);
}
}
);
// 5. 重试装饰器
function withRetry(maxRetries: number = 3, delay: number = 1000) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const originalMethod = descriptor.value;
descriptor.value = async function (...args: any[]) {
for (let i = 0; i < maxRetries; i++) {
try {
return await originalMethod.apply(this, args);
} catch (error) {
if (i === maxRetries - 1) {
throw error;
}
await new Promise(resolve => setTimeout(resolve, delay));
}
}
};
return descriptor;
};
}
// 使用
class LLMService {
@withRetry(3, 1000)
async generateText(prompt: string): Promise<string> {
const model = new ChatOpenAI();
const response = await model.invoke(prompt);
return response.content as string;
}
}
// 6. 智能重试(分析错误决定策略)
async function smartRetry<T>(
fn: () => Promise<T>,
maxRetries: number = 5
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
const errorType = classifyError(error as Error);
// 根据错误类型决定是否重试
if (errorType === ErrorType.API_KEY_ERROR) {
// API密钥错误不重试
throw error;
}
if (errorType === ErrorType.RATE_LIMIT_ERROR) {
// 速率限制错误等待更长时间
const delay = 5000 * Math.pow(2, i);
console.log(`速率限制,等待${delay}ms`);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
if (i === maxRetries - 1) {
throw error;
}
// 默认策略
const delay = 1000 * Math.pow(2, i);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw new Error('Max retries exceeded');
}
console.log("✓ 重试机制示例完成");
---
9.3 生产部署
01.部署策略
a.容器化部署
a.功能说明
使用Docker容器化LangChain.js应用。配置环境变量和依赖管理。实现CI/CD自动化部署。容器化简化部署和扩展。
b.代码示例
---
// 1. Dockerfile
// Dockerfile
FROM node:18-alpine
WORKDIR /app
# 复制package文件
COPY package*.json ./
# 安装依赖
RUN npm ci --only=production
# 复制源代码
COPY . .
# 构建TypeScript
RUN npm run build
# 暴露端口
EXPOSE 3000
# 启动命令
CMD ["node", "dist/index.js"]
// 2. docker-compose.yml
version: '3.8'
services:
langchain-app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- OPENAI_API_KEY=${OPENAI_API_KEY}
volumes:
- ./data:/app/data
restart: unless-stopped
# Redis缓存
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis-data:/data
# 向量数据库(可选)
qdrant:
image: qdrant/qdrant
ports:
- "6333:6333"
volumes:
- qdrant-data:/qdrant/storage
volumes:
redis-data:
qdrant-data:
// 3. 环境配置
// .env.production
NODE_ENV=production
# API Keys
OPENAI_API_KEY=sk-...
# 应用配置
PORT=3000
LOG_LEVEL=info
# Redis
REDIS_URL=redis://redis:6379
# 向量数据库
QDRANT_URL=http://qdrant:6333
// 4. 健康检查
// src/health.ts
import express from "express";
import { ChatOpenAI } from "@langchain/openai";
const app = express();
let isHealthy = true;
// 健康检查端点
app.get("/health", (req, res) => {
if (isHealthy) {
res.status(200).json({
status: "healthy",
timestamp: new Date().toISOString()
});
} else {
res.status(503).json({
status: "unhealthy"
});
}
});
// 就绪检查
app.get("/ready", async (req, res) => {
try {
// 检查LLM连接
const model = new ChatOpenAI();
await model.invoke("ping");
res.status(200).json({
status: "ready"
});
} catch (error) {
res.status(503).json({
status: "not ready",
error: error.message
});
}
});
app.listen(3000);
// 5. Kubernetes部署
// k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: langchain-app
spec:
replicas: 3
selector:
matchLabels:
app: langchain-app
template:
metadata:
labels:
app: langchain-app
spec:
containers:
- name: langchain-app
image: your-registry/langchain-app:latest
ports:
- containerPort: 3000
env:
- name: OPENAI_API_KEY
valueFrom:
secretKeyRef:
name: langchain-secrets
key: openai-api-key
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 10
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: langchain-service
spec:
selector:
app: langchain-app
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: LoadBalancer
// 6. CI/CD配置
// .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [ main ]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build
run: npm run build
- name: Build Docker image
run: docker build -t langchain-app:latest .
- name: Push to registry
run: |
docker tag langchain-app:latest registry.example.com/langchain-app:latest
docker push registry.example.com/langchain-app:latest
- name: Deploy to Kubernetes
run: |
kubectl apply -f k8s/
// 7. 监控配置
// prometheus.yml
scrape_configs:
- job_name: 'langchain-app'
static_configs:
- targets: ['langchain-service:3000']
metrics_path: '/metrics'
// 8. 日志配置
// src/logger.ts
import winston from "winston";
export const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
// 使用
logger.info("应用启动", { port: 3000 });
logger.error("错误发生", { error: error.message });
// 9. 优雅关闭
// src/graceful-shutdown.ts
import { Server } from "http";
export function setupGracefulShutdown(server: Server) {
const signals = ["SIGTERM", "SIGINT"];
signals.forEach(signal => {
process.on(signal, () => {
console.log(`收到${signal}信号,开始优雅关闭...`);
server.close(() => {
console.log("HTTP服务器关闭");
// 关闭数据库连接等
// db.close();
process.exit(0);
});
// 强制关闭超时
setTimeout(() => {
console.error("强制关闭");
process.exit(1);
}, 10000);
});
});
}
// 10. 部署检查清单
/*
部署前检查:
✓ 环境变量配置正确
✓ API密钥已设置
✓ 依赖版本锁定
✓ 测试全部通过
✓ 构建成功
✓ 健康检查配置
✓ 日志配置
✓ 监控配置
✓ 备份策略
✓ 回滚计划
*/
console.log("✓ 生产部署示例完成");
---