name: LangChain Patterns description: 构建由LLM驱动的应用程序的LangChain - 链、代理、RAG、内存和生产模式,用于AI驱动的应用程序。
LangChain Patterns
概览
LangChain 是一个用于构建由LLM驱动的应用程序的框架。它帮助管理提示链、内存、检索、代理和工具使用的复杂性,使得构建AI应用程序更加快速。
为什么这很重要
- 抽象: 为各种LLM提供商提供统一的接口
- 可组合性: 轻松地将组件链式组合在一起
- RAG就绪: 内置的检索和向量存储集成
- 生产: LangSmith用于监控和调试
核心概念
1. 基本设置
// lib/langchain.ts
import { ChatOpenAI } from '@langchain/openai';
import { ChatAnthropic } from '@langchain/anthropic';
import { StringOutputParser } from '@langchain/core/output_parsers';
import { ChatPromptTemplate } from '@langchain/core/prompts';
// 初始化模型
export const openai = new ChatOpenAI({
modelName: 'gpt-4-turbo-preview',
temperature: 0,
openAIApiKey: process.env.OPENAI_API_KEY,
});
export const claude = new ChatAnthropic({
modelName: 'claude-3-opus-20240229',
temperature: 0,
anthropicApiKey: process.env.ANTHROPIC_API_KEY,
});
// 简单链
const prompt = ChatPromptTemplate.fromMessages([
['system', '你是一个帮助助手,将{input_language}翻译成{output_language}。'],
['human', '{text}'],
]);
const chain = prompt.pipe(openai).pipe(new StringOutputParser());
// 使用方法
const result = await chain.invoke({
input_language: 'English',
output_language: 'Thai',
text: 'Hello, how are you?',
});
2. 结构化输出
import { z } from 'zod';
import { StructuredOutputParser } from 'langchain/output_parsers';
import { ChatPromptTemplate } from '@langchain/core/prompts';
// 定义模式
const productSchema = z.object({
name: z.string().describe('产品名称'),
description: z.string().describe('产品描述'),
price: z.number().describe('美元价格'),
category: z.enum(['electronics', 'clothing', 'food', 'other']),
tags: z.array(z.string()).describe('产品标签'),
});
const parser = StructuredOutputParser.fromZodSchema(productSchema);
const prompt = ChatPromptTemplate.fromMessages([
['system', `从文本中提取产品信息。
{format_instructions}`],
['human', '{text}'],
]);
const chain = prompt.pipe(openai).pipe(parser);
const result = await chain.invoke({
text: 'New iPhone 15 Pro, amazing camera, $999, perfect for photography enthusiasts',
format_instructions: parser.getFormatInstructions(),
});
// result: { name: 'iPhone 15 Pro', description: '...', price: 999, category: 'electronics', tags: ['photography', 'camera'] }
3. RAG (检索增强生成)
import { OpenAIEmbeddings } from '@langchain/openai';
import { SupabaseVectorStore } from '@langchain/community/vectorstores/supabase';
import { createClient } from '@supabase/supabase-js';
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
import { createRetrievalChain } from 'langchain/chains/retrieval';
import { createStuffDocumentsChain } from 'langchain/chains/combine_documents';
// 设置向量存储
const supabase = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_SERVICE_KEY!
);
const embeddings = new OpenAIEmbeddings({
openAIApiKey: process.env.OPENAI_API_KEY,
});
const vectorStore = new SupabaseVectorStore(embeddings, {
client: supabase,
tableName: 'documents',
queryName: 'match_documents',
});
// 索引文档
async function indexDocuments(documents: string[]) {
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 1000,
chunkOverlap: 200,
});
const docs = await splitter.createDocuments(documents);
await vectorStore.addDocuments(docs);
}
// 创建RAG链
const retriever = vectorStore.asRetriever({
k: 5,
searchType: 'similarity',
});
const qaPrompt = ChatPromptTemplate.fromMessages([
['system', `基于以下上下文回答问题:
{context}
如果你不知道答案,请说“我没有足够的信息来回答这个问题。”`],
['human', '{input}'],
]);
const documentChain = await createStuffDocumentsChain({
llm: openai,
prompt: qaPrompt,
});
const ragChain = await createRetrievalChain({
combineDocsChain: documentChain,
retriever,
});
// 使用方法
const response = await ragChain.invoke({
input: '我们的退款政策是什么?',
});
console.log(response.answer);
console.log(response.context); // 源文档
4. 对话内存
import { BufferMemory, ConversationSummaryMemory } from 'langchain/memory';
import { ConversationChain } from 'langchain/chains';
import { UpstashRedisChatMessageHistory } from '@langchain/community/stores/message/upstash_redis';
// 简单的缓冲内存
const bufferMemory = new BufferMemory({
returnMessages: true,
memoryKey: 'history',
});
// Redis支持的内存(生产用)
const redisMemory = new BufferMemory({
chatHistory: new UpstashRedisChatMessageHistory({
sessionId: `user-${userId}-session-${sessionId}`,
config: {
url: process.env.UPSTASH_REDIS_URL!,
token: process.env.UPSTASH_REDIS_TOKEN!,
},
}),
returnMessages: true,
memoryKey: 'history',
});
// 摘要内存(长对话用)
const summaryMemory = new ConversationSummaryMemory({
llm: openai,
returnMessages: true,
});
// 带内存的对话链
const conversationChain = new ConversationChain({
llm: openai,
memory: redisMemory,
verbose: true,
});
// 多轮对话
await conversationChain.call({ input: '我的名字叫John' });
await conversationChain.call({ input: '我的名字是什么?' }); // 记住“John”
5. 带有工具的代理
import { ChatOpenAI } from '@langchain/openai';
import { createOpenAIFunctionsAgent, AgentExecutor } from 'langchain/agents';
import { DynamicTool, DynamicStructuredTool } from '@langchain/core/tools';
import { pull } from 'langchain/hub';
import { z } from 'zod';
// 定义工具
const searchTool = new DynamicTool({
name: 'search',
description: '搜索网络以获取当前信息',
func: async (query: string) => {
// 实现搜索逻辑
const results = await searchWeb(query);
return JSON.stringify(results);
},
});
const calculatorTool = new DynamicStructuredTool({
name: 'calculator',
description: '执行数学计算',
schema: z.object({
expression: z.string().describe('要评估的数学表达式'),
}),
func: async ({ expression }) => {
try {
const result = eval(expression); // 在生产中使用安全的数学解析器
return `结果: ${result}`;
} catch (error) {
return `错误: 无效的表达式`;
}
},
});
const databaseTool = new DynamicStructuredTool({
name: 'query_database',
description: '查询产品数据库',
schema: z.object({
query: z.string().describe('产品搜索查询'),
category: z.string().optional().describe('按类别过滤'),
maxPrice: z.number().optional().describe('最高价格过滤'),
}),
func: async ({ query, category, maxPrice }) => {
const products = await prisma.product.findMany({
where: {
name: { contains: query, mode: 'insensitive' },
...(category && { category }),
...(maxPrice && { price: { lte: maxPrice } }),
},
take: 5,
});
return JSON.stringify(products);
},
});
// 创建代理
const tools = [searchTool, calculatorTool, databaseTool];
const prompt = await pull<ChatPromptTemplate>('hwchase17/openai-functions-agent');
const agent = await createOpenAIFunctionsAgent({
llm: openai,
tools,
prompt,
});
const agentExecutor = new AgentExecutor({
agent,
tools,
verbose: true,
maxIterations: 5,
});
// 使用方法
const result = await agentExecutor.invoke({
input: '在1000美元以下找到一台笔记本电脑并计算加上10%的税的价格',
});
6. 流式传输
import { ChatOpenAI } from '@langchain/openai';
import { StringOutputParser } from '@langchain/core/output_parsers';
const model = new ChatOpenAI({
modelName: 'gpt-4-turbo-preview',
streaming: true,
});
// 带回调的流
const stream = await model.stream('写一个关于机器人的故事');
for await (const chunk of stream) {
process.stdout.write(chunk.content);
}
// 在Next.js API路由中的流
// app/api/chat/route.ts
import { StreamingTextResponse, LangChainStream } from 'ai';
export async function POST(req: Request) {
const { messages } = await req.json();
const { stream, handlers } = LangChainStream();
const chain = prompt.pipe(model).pipe(new StringOutputParser());
chain.invoke(
{ messages },
{ callbacks: [handlers] }
);
return new StreamingTextResponse(stream);
}
7. 文档加载器
import { PDFLoader } from 'langchain/document_loaders/fs/pdf';
import { CSVLoader } from 'langchain/document_loaders/fs/csv';
import { WebBaseLoader } from 'langchain/document_loaders/web/web_base';
import { NotionAPILoader } from 'langchain/document_loaders/web/notionapi';
import { GithubRepoLoader } from 'langchain/document_loaders/web/github';
// 加载PDF
const pdfLoader = new PDFLoader('path/to/document.pdf', {
splitPages: true,
});
const pdfDocs = await pdfLoader.load();
// 加载CSV
const csvLoader = new CSVLoader('path/to/data.csv', {
column: 'text', // 用作页面内容的列
});
const csvDocs = await csvLoader.load();
// 从网络加载
const webLoader = new WebBaseLoader('https://example.com/article');
const webDocs = await webLoader.load();
// 从Notion加载
const notionLoader = new NotionAPILoader({
clientOptions: {
auth: process.env.NOTION_API_KEY,
},
id: 'page-or-database-id',
type: 'page',
});
const notionDocs = await notionLoader.load();
// 从GitHub仓库加载
const githubLoader = new GithubRepoLoader(
'https://github.com/username/repo',
{
branch: 'main',
recursive: true,
unknown: 'warn',
accessToken: process.env.GITHUB_TOKEN,
}
);
const repoDocs = await githubLoader.load();
8. LangSmith集成(生产监控)
// 启用跟踪
process.env.LANGCHAIN_TRACING_V2 = 'true';
process.env.LANGCHAIN_API_KEY = 'your-langsmith-api-key';
process.env.LANGCHAIN_PROJECT = 'my-project';
// 或者以编程方式配置
import { Client } from 'langsmith';
import { LangChainTracer } from 'langchain/callbacks';
const client = new Client({
apiKey: process.env.LANGSMITH_API_KEY,
});
const tracer = new LangChainTracer({
projectName: 'my-project',
client,
});
// 与任何链一起使用
const result = await chain.invoke(
{ input: 'Hello' },
{ callbacks: [tracer] }
);
// 评估运行
import { evaluate } from 'langsmith/evaluation';
await evaluate(
(input) => chain.invoke(input),
{
data: 'my-dataset-name',
evaluators: [
// 自定义评估器
async ({ run, example }) => {
const score = calculateScore(run.outputs, example.outputs);
return { key: 'accuracy', score };
},
],
}
);
快速开始
-
安装包:
npm install langchain @langchain/openai @langchain/community -
设置环境变量:
OPENAI_API_KEY=sk-... LANGCHAIN_TRACING_V2=true LANGCHAIN_API_KEY=ls-... -
创建一个简单的链:
import { ChatOpenAI } from '@langchain/openai'; import { ChatPromptTemplate } from '@langchain/core/prompts'; const chain = ChatPromptTemplate .fromTemplate('告诉我一个关于{topic}的笑话') .pipe(new ChatOpenAI()) .pipe(new StringOutputParser()); const joke = await chain.invoke({ topic: '编程' });
生产检查清单
- [ ] 启用LangSmith跟踪
- [ ] 配置错误处理和重试
- [ ] 实施速率限制
- [ ] 缓存层嵌入
- [ ] 监控令牌使用情况
- [ ] 配置备用模型
- [ ] 输入验证
- [ ] 输出验证/防护栏
反模式
- 长响应无流式传输: 总是流式传输以获得更好的用户体验
- 忽略令牌限制: 监控并处理上下文长度
- 无错误处理: LLM调用可能会失败 - 优雅地处理
- 硬编码提示: 使用提示模板和版本控制
集成点
- 向量存储: Pinecone, Supabase, Chroma, Weaviate
- LLMs: OpenAI, Anthropic, Google, Cohere, 本地模型
- 内存: Redis, PostgreSQL, 内存中
- 工具: 自定义APIs, 数据库, 搜索引擎