返回笔记首页

LangChain、LangGraph等前端AI库深度解析

主题配置

一、先搞清楚一个核心问题

你真的需要这些库吗?

答案:80%的情况下,你不需要

typescript
// 很多时候,你只需要这样:
const response = await fetch('/api/chat', {
    method: 'POST',
    body: JSON.stringify({ message: userInput }),
})

// 就够了!
什么时候需要这些库?
  • 你要做复杂的多步骤工作流(不是简单问答)
  • 你需要整合多个数据源和工具
  • 你在做RAG(检索增强生成)
  • 你要实现Agent(让AI自己决定调用什么工具)

二、LangChain.js - 最流行但也最复杂

它到底是干什么的?

官方说法: "用于开发由语言模型驱动的应用程序的框架"

  • 用统一的接口调用不同的大模型(OpenAI、Claude、本地模型等)
  • 把复杂任务拆成多个步骤(Chain)
  • 管理Prompt模板
  • 连接各种数据源(数据库、API、文档等)
  • 实现RAG(检索增强生成)

核心概念详解

1. LLM(大语言模型)- 最基础的封装

typescript
import { ChatOpenAI } from '@langchain/openai'

// 不用LangChain的写法
const response = await fetch('https://api.openai.com/v1/chat/completions', {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
        'Content-Type': 'application/json',
    },
    body: JSON.stringify({
        model: 'gpt-4',
        messages: [{ role: 'user', content: 'Hello' }],
    }),
})

// 用LangChain的写法
const llm = new ChatOpenAI({
    modelName: 'gpt-4',
    temperature: 0.7,
})

const response = await llm.invoke('Hello')

好处: 切换模型只需要改一行代码

typescript
// 换成Claude
const llm = new ChatAnthropic({ model: 'claude-3-opus' })

// 换成本地模型
const llm = new ChatOllama({ model: 'llama2' })

// 其他代码不用改!

2. Prompt Templates - 模板管理

typescript
import { ChatPromptTemplate } from '@langchain/core/prompts'

// 不用LangChain:手动拼接字符串
const prompt = `你是一个${role}。
用户背景:${userContext}
请回答:${question}`

// 用LangChain:模板化管理
const promptTemplate = ChatPromptTemplate.fromMessages([
    ['system', '你是一个{role}。'],
    ['human', '用户背景:{userContext}'],
    ['human', '请回答:{question}'],
])

const prompt = await promptTemplate.format({
    role: '专业的代码审查专家',
    userContext: '3年前端经验',
    question: '这段代码有什么问题?',
})
好处
  • Prompt可以复用
  • 变量清晰,不容易拼错
  • 方便测试和迭代

3. Chains - 多步骤流程

这是LangChain的核心价值
typescript
import { RunnableSequence } from '@langchain/core/runnables'

// 场景:用户上传简历,要做3件事:
// 1. 提取关键信息
// 2. 生成职位匹配度分析
// 3. 给出改进建议

// 不用LangChain:手动串联
async function analyzeResume(resume: string) {
    // 第1步
    const extracted = await openai.chat.completions.create({
        messages: [
            {
                role: 'user',
                content: `提取简历关键信息:${resume}`,
            },
        ],
    })

    // 第2步
    const analysis = await openai.chat.completions.create({
        messages: [
            {
                role: 'user',
                content: `分析匹配度:${extracted.choices[0].message.content}`,
            },
        ],
    })

    // 第3步
    const suggestions = await openai.chat.completions.create({
        messages: [
            {
                role: 'user',
                content: `给出建议:${analysis.choices[0].message.content}`,
            },
        ],
    })

    return suggestions
}

// 用LangChain:声明式定义流程
const extractChain = promptTemplate1.pipe(llm)
const analysisChain = promptTemplate2.pipe(llm)
const suggestionChain = promptTemplate3.pipe(llm)

const fullChain = RunnableSequence.from([
    extractChain,
    analysisChain,
    suggestionChain,
])

// 一行调用
const result = await fullChain.invoke({ resume })
好处
  • 代码更清晰,流程一目了然
  • 容易修改某一步
  • 可以并行执行某些步骤
  • 内置错误处理和重试

4. Document Loaders - 文档加载

typescript
import { PDFLoader } from 'langchain/document_loaders/fs/pdf'
import { CSVLoader } from 'langchain/document_loaders/fs/csv'
import { WebBaseLoader } from 'langchain/document_loaders/web/cheerio'

// 加载PDF
const loader = new PDFLoader('resume.pdf')
const docs = await loader.load()

// 加载网页
const webLoader = new WebBaseLoader('https://example.com')
const webDocs = await webLoader.load()

// docs是统一的格式,都有pageContent和metadata
docs.forEach((doc) => {
    console.log(doc.pageContent) // 文本内容
    console.log(doc.metadata) // 元数据(来源、页码等)
})

好处: 不用自己写各种文件解析代码

5. Vector Stores - 向量数据库集成

typescript
import { OpenAIEmbeddings } from '@langchain/openai'
import { MemoryVectorStore } from 'langchain/vectorstores/memory'

// 1. 把文档切片
const docs = await loader.load()
const splitter = new RecursiveCharacterTextSplitter({
    chunkSize: 1000,
    chunkOverlap: 200,
})
const splits = await splitter.splitDocuments(docs)

// 2. 向量化并存储
const embeddings = new OpenAIEmbeddings()
const vectorStore = await MemoryVectorStore.fromDocuments(splits, embeddings)

// 3. 搜索相关文档
const results = await vectorStore.similaritySearch(
    '用户的问题',
    3 // 返回最相关的3个片段
)

// 4. 把结果塞进Prompt
const context = results.map((r) => r.pageContent).join('\n\n')
const answer = await llm.invoke(`
  参考以下内容回答问题:
  ${context}

  问题:${question}
`)
这就是RAG(检索增强生成)的完整流程

6. Agents - 让AI自己决定做什么

这是最高级的功能
typescript
import { AgentExecutor, createOpenAIFunctionsAgent } from 'langchain/agents'
import { Calculator } from 'langchain/tools/calculator'
import { WebBrowser } from 'langchain/tools/webbrowser'

// 定义AI可以使用的工具
const tools = [
    new Calculator(),
    new WebBrowser(),
    // 自定义工具
    {
        name: 'get_weather',
        description: '获取指定城市的天气',
        func: async (city: string) => {
            const weather = await fetch(`https://api.weather.com/${city}`)
            return weather.json()
        },
    },
]

// 创建Agent
const agent = await createOpenAIFunctionsAgent({
    llm,
    tools,
    prompt: '你是一个助手,可以使用工具帮助用户。',
})

const executor = new AgentExecutor({
    agent,
    tools,
})

// 用户问:"北京明天天气怎么样,如果下雨的概率超过50%,帮我计算一下打车费用(15公里)"
const result = await executor.invoke({
    input: '北京明天天气怎么样,如果下雨的概率超过50%,帮我计算一下打车费用(15公里)',
})

// Agent会自动:
// 1. 先调用get_weather工具获取天气
// 2. 判断降雨概率
// 3. 如果>50%,调用Calculator计算15*单价
// 4. 返回完整答案

好处: 你不需要写if-else判断逻辑,AI自己决定调用什么工具


三、LangGraph - 更复杂的工作流

为什么有了LangChain还要LangGraph?

LangChain的Chain是线性的

plain
步骤1 → 步骤2 → 步骤3 → 结束
但实际业务可能是这样的
plain
开始 → 判断 → 如果A → 步骤1 → 步骤2
             ↓
             如果B → 步骤3 → 返回判断 → 继续
LangGraph用图的方式定义工作流
typescript
import { StateGraph } from '@langchain/langgraph'

// 定义状态
interface State {
    userInput: string
    category?: string
    needMoreInfo?: boolean
    response?: string
}

// 定义节点(每个节点是一个处理函数)
const graph = new StateGraph<State>({
    channels: {
        userInput: null,
        category: null,
        needMoreInfo: null,
        response: null,
    },
})

// 节点1:分类用户意图
graph.addNode('categorize', async (state) => {
    const category = await llm.invoke(`分类用户意图:${state.userInput}`)
    return { category }
})

// 节点2:检查是否需要更多信息
graph.addNode('check', async (state) => {
    const needMore = state.category === '复杂问题'
    return { needMoreInfo: needMore }
})

// 节点3:简单回答
graph.addNode('simple_answer', async (state) => {
    const response = await llm.invoke(`回答:${state.userInput}`)
    return { response }
})

// 节点4:复杂处理
graph.addNode('complex_process', async (state) => {
    // 多步处理...
    return { response: '复杂答案' }
})

// 定义边(节点之间的连接)
graph.addEdge('categorize', 'check')
graph.addConditionalEdges(
    'check',
    (state) => (state.needMoreInfo ? 'complex' : 'simple'),
    {
        simple: 'simple_answer',
        complex: 'complex_process',
    }
)

// 设置入口和出口
graph.setEntryPoint('categorize')
graph.addEdge('simple_answer', '__end__')
graph.addEdge('complex_process', '__end__')

// 编译并运行
const app = graph.compile()
const result = await app.invoke({ userInput: '用户问题' })
实际业务场景
typescript
// 场景:智能客服
// 流程:
// 1. 理解用户问题
// 2. 查询知识库
// 3. 如果找到答案 → 直接回复
//    如果没找到 → 转人工
//    如果答案不确定 → 追问用户

const customerServiceGraph = new StateGraph({...});

// 节点:理解问题
graph.addNode("understand", async (state) => {
  const intent = await llm.invoke(`理解意图:${state.message}`);
  return { intent };
});

// 节点:搜索知识库
graph.addNode("search_kb", async (state) => {
  const results = await vectorStore.search(state.intent);
  return {
    kbResults: results,
    confidence: results[0]?.score || 0,
  };
});

// 节点:直接回答
graph.addNode("direct_answer", async (state) => {
  const answer = await llm.invoke(`基于${state.kbResults}回答`);
  return { response: answer };
});

// 节点:追问用户
graph.addNode("ask_more", async (state) => {
  return { response: "您能详细说明一下吗?" };
});

// 节点:转人工
graph.addNode("to_human", async (state) => {
  await notifyHumanAgent(state);
  return { response: "正在为您转接人工客服..." };
});

// 条件分支
graph.addConditionalEdges(
  "search_kb",
  (state) => {
    if (state.confidence > 0.8) return "direct";
    if (state.confidence > 0.5) return "ask";
    return "human";
  },
  {
    direct: "direct_answer",
    ask: "ask_more",
    human: "to_human",
  }
);
好处
  • 可以处理复杂的分支逻辑
  • 可以循环(比如多轮对话)
  • 可以并行执行某些节点
  • 状态管理清晰

四、其他前端AI库对比

1. Vercel AI SDK - 最推荐给前端的

typescript
import { useChat } from 'ai/react';

function Chat() {
  const { messages, input, handleInputChange, handleSubmit } = useChat();

  return (
    <div>
      {messages.map(m => <div key={m.id}>{m.content}</div>)}
      <form onSubmit={handleSubmit}>
        <input value={input} onChange={handleInputChange} />
      </form>
    </div>
  );
}

对比LangChain

  • ✅ 专为前端设计,React Hooks超好用
  • ✅ 自动处理流式响应
  • ✅ 内置状态管理
  • ✅ 代码量少,学习曲线平缓
  • ❌ 功能相对简单,不支持复杂工作流

适合场景: 80%的前端AI应用(聊天、文本生成、简单的RAG)

2. LlamaIndex.js - 专注RAG

typescript
import { VectorStoreIndex, SimpleDirectoryReader } from 'llamaindex'

// 加载文档并建立索引
const documents = await new SimpleDirectoryReader().loadData('./docs')
const index = await VectorStoreIndex.fromDocuments(documents)

// 查询
const queryEngine = index.asQueryEngine()
const response = await queryEngine.query('用户问题')

对比LangChain

  • ✅ RAG功能更强大
  • ✅ 性能优化更好
  • ✅ 文档质量高
  • ❌ 只能做RAG,不像LangChain那么全能

适合场景: 专门做知识库问答、文档搜索的项目

3. ai.js (AI.JSX) - 用JSX写AI应用

typescript
import { ChatCompletion, UserMessage } from 'ai-jsx/core/completion';

function MyAI() {
  return (
    <ChatCompletion>
      <UserMessage>写一首诗</UserMessage>
    </ChatCompletion>
  );
}

特点

  • 用JSX语法写AI逻辑
  • 概念很新颖,但社区较小

4. LangChain.js vs Python版本

特性 Python版 JS版
功能完整度 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
文档质量 ⭐⭐⭐⭐⭐ ⭐⭐⭐
社区规模 中等
更新速度 稍慢
前端集成

结论: 如果你是纯前端项目,用JS版;如果需要复杂功能,考虑用Python做后端


五、前端大模型应用的技术选择建议

场景1:简单的聊天应用

推荐:Vercel AI SDK

typescript
// 前端
import { useChat } from 'ai/react'

// 后端API (Next.js)
import { OpenAIStream, StreamingTextResponse } from 'ai'

export async function POST(req: Request) {
    const { messages } = await req.json()
    const response = await openai.chat.completions.create({
        model: 'gpt-4',
        messages,
        stream: true,
    })
    const stream = OpenAIStream(response)
    return new StreamingTextResponse(stream)
}

不需要: LangChain、LangGraph

场景2:文档问答(RAG)

推荐:LangChain.js 或 LlamaIndex.js

typescript
// 用LangChain实现RAG
import { RetrievalQAChain } from 'langchain/chains'
import { OpenAIEmbeddings } from '@langchain/openai'
import { MemoryVectorStore } from 'langchain/vectorstores/memory'

// 1. 加载并向量化文档
const docs = await loader.load()
const vectorStore = await MemoryVectorStore.fromDocuments(
    docs,
    new OpenAIEmbeddings()
)

// 2. 创建QA链
const chain = RetrievalQAChain.fromLLM(llm, vectorStore.asRetriever())

// 3. 查询
const answer = await chain.call({ query: '用户问题' })
为什么需要
  • 文档加载器省事
  • 向量存储封装好
  • RAG流程标准化

场景3:多步骤工作流(比如:简历分析)

推荐:LangChain Chains

typescript
import { RunnableSequence } from '@langchain/core/runnables'

const chain = RunnableSequence.from([
    extractionChain, // 提取信息
    analysisChain, // 分析匹配度
    suggestionChain, // 生成建议
])

const result = await chain.invoke({ resume })
为什么需要
  • 多步骤逻辑清晰
  • 错误处理方便
  • 容易测试和调试

场景4:复杂的决策流程(比如:智能客服)

推荐:LangGraph

typescript
// 需要条件分支、循环、状态管理
const graph = new StateGraph({...});
graph.addNode("understand", ...);
graph.addNode("search", ...);
graph.addConditionalEdges(...);
为什么需要
  • 有复杂的if-else逻辑
  • 需要多轮对话
  • 要根据AI的回答决定下一步

场景5:Agent(让AI使用工具)

推荐:LangChain Agents

typescript
const agent = await createOpenAIFunctionsAgent({
    llm,
    tools: [weatherTool, calculatorTool, databaseTool],
})

// AI会自己决定调用哪个工具
const result = await executor.invoke({ input: userMessage })
为什么需要
  • 需要AI自主决策
  • 工具调用逻辑复杂
  • 不确定用户会问什么

六、实战建议

1. 什么时候不需要这些库?

大多数情况下,你只需要

typescript
// 前端
async function chat(message: string) {
    const response = await fetch('/api/chat', {
        method: 'POST',
        body: JSON.stringify({ message }),
    })

    const reader = response.body.getReader()
    // 处理流式响应...
}

// 后端 (Next.js API)
export async function POST(req: Request) {
    const { message } = await req.json()

    const stream = await openai.chat.completions.create({
        model: 'gpt-4',
        messages: [{ role: 'user', content: message }],
        stream: true,
    })

    return new Response(stream)
}
这样写的好处
  • 代码简单,容易理解
  • 依赖少,打包体积小
  • 灵活,想怎么改就怎么改

2. 什么时候考虑用LangChain?

出现这些需求时

  • ✅ 需要同时支持多个大模型(OpenAI、Claude、本地模型)
  • ✅ 要做RAG,需要管理向量数据库
  • ✅ 有多步骤的复杂流程
  • ✅ 需要让AI使用工具(Agent)
  • ✅ 团队其他人用Python版LangChain,前端要对齐

3. 学习顺序建议

第1周:不用任何库

plain
直接调API → 理解基本原理 → 自己实现流式响应
第2周:用Vercel AI SDK
plain
体验框架的便利性 → 对比手写代码 → 理解抽象层
第3周:用LangChain做简单RAG
plain
加载文档 → 向量化 → 检索 → 回答
第4周:用LangChain做复杂流程
plain
多步Chain → 条件分支 → Agent

不推荐: 一上来就学LangChain,容易被复杂的概念搞晕

4. 避坑指南

坑1:过度工程化

typescript
// ❌ 不要为了用库而用库
import { LLMChain } from 'langchain/chains'

const chain = new LLMChain({ llm, prompt })
const result = await chain.call({ input: message })

// ✅ 这种简单场景直接调用就行
const result = await llm.invoke(message)

坑2:不了解原理就用库

typescript
// 你至少要知道这个Chain内部做了什么
const chain = RetrievalQAChain.fromLLM(llm, retriever)

// 实际上是:
// 1. 用retriever搜索相关文档
// 2. 把文档塞进Prompt
// 3. 调用LLM生成答案
// 如果不知道这个流程,出bug很难调试

坑3:版本不兼容

bash
# LangChain更新很快,文档和实际版本可能不一致
# 固定版本号,不要用^或~
npm install langchain@0.1.30 --save-exact

坑4:打包体积大

typescript
// ❌ 导入整个库
import { OpenAI } from 'langchain'

// ✅ 按需导入
import { ChatOpenAI } from '@langchain/openai'

七、完整实战案例对比

案例:企业知识库问答系统

方案A:不用任何库(纯手写)

typescript
// 优点:完全可控,轻量
// 缺点:要自己实现所有功能

// 1. 文档处理
async function processDocument(file: File) {
    const text = await extractText(file) // 自己写PDF解析
    const chunks = splitText(text, 1000) // 自己写分块逻辑

    // 2. 向量化
    const embeddings = await Promise.all(
        chunks.map((chunk) => openai.embeddings.create({ input: chunk }))
    )

    // 3. 存储(自己管理数组)
    documents.push(
        ...chunks.map((chunk, i) => ({
            text: chunk,
            embedding: embeddings[i].data[0].embedding,
        }))
    )
}

// 4. 查询
async function query(question: string) {
    // 向量化问题
    const qEmbed = await openai.embeddings.create({ input: question })

    // 计算相似度(自己写余弦相似度)
    const scores = documents.map((doc) =>
        cosineSimilarity(qEmbed.data[0].embedding, doc.embedding)
    )

    // 取top-k
    const topDocs = documents
        .map((doc, i) => ({ doc, score: scores[i] }))
        .sort((a, b) => b.score - a.score)
        .slice(0, 3)

    // 生成答案
    const context = topDocs.map((d) => d.doc.text).join('\n')
    const answer = await openai.chat.completions.create({
        messages: [
            {
                role: 'user',
                content: `参考:${context}\n\n问题:${question}`,
            },
        ],
    })

    return answer
}

方案B:用Vercel AI SDK(前端)+ LangChain(后端)
typescript
// 后端 (Next.js API)
import { RetrievalQAChain } from "langchain/chains";
import { OpenAIEmbeddings } from "@langchain/openai";
import { PDFLoader } from "langchain/document_loaders/fs/pdf";
import { MemoryVectorStore } from "langchain/vectorstores/memory";
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";

// 初始化(启动时运行一次)
const loader = new PDFLoader("docs/company-handbook.pdf");
const docs = await loader.load();

const splitter = new RecursiveCharacterTextSplitter({
  chunkSize: 1000,
  chunkOverlap: 200,
});
const splits = await splitter.splitDocuments(docs);

const vectorStore = await MemoryVectorStore.fromDocuments(
  splits,
  new OpenAIEmbeddings()
);

const chain = RetrievalQAChain.fromLLM(
  new ChatOpenAI({ modelName: "gpt-4" }),
  vectorStore.asRetriever(3), // top-3
  { returnSourceDocuments: true } // 返回来源
);

// API路由
export async function POST(req: Request) {
  const { question } = await req.json();

  const result = await chain.call({ query: question });

  return Response.json({
    answer: result.text,
    sources: result.sourceDocuments.map(doc => ({
      content: doc.pageContent,
      metadata: doc.metadata,
    })),
  });
}

// 前端
import { useState } from 'react';

function KnowledgeBase() {
  const [question, setQuestion] = useState('');
  const [answer, setAnswer] = useState('');
  const [sources, setSources] = useState([]);

  async function ask() {
    const response = await fetch('/api/knowledge-base', {
      method: 'POST',
      body: JSON.stringify({ question }),
    });

    const data = await response.json();
    setAnswer(data.answer);
    setSources(data.sources);
  }

  return (
    <div>
      <input value={question} onChange={e => setQuestion(e.target.value)} />
      <button onClick={ask}>提问</button>

      <div>{answer}</div>

      <div>
        <h4>参考来源:</h4>
        {sources.map((src, i) => (
          <div key={i}>
            <p>{src.content}</p>
            <small>来源:{src.metadata.source}</small>
          </div>
        ))}
      </div>
    </div>
  );
}
代码量

八、总结:选择建议

如果你是前端新手做AI应用

推荐路线

  1. 第1个项目: 不用任何库,直接调OpenAI API
    • 目的:理解流式响应、Prompt工程
  2. 第2个项目: 用Vercel AI SDK
    • 目的:体验框架的便利性
  3. 第3个项目: 简单的RAG需求,试试LangChain
    • 目的:理解RAG工作流
  4. 之后: 根据需求选择

快速决策表

你的需求 推荐方案 理由
简单聊天 Vercel AI SDK 最简单,开箱即用
多模型切换 LangChain 统一接口
文档问答 LangChain/LlamaIndex 省去RAG实现
复杂工作流 LangChain Chains 多步骤管理
条件分支 LangGraph 图状态机
Agent LangChain Agents 工具调用封装好
只做前端 手写 + Vercel SDK 不依赖Python生态
全栈项目 LangChain (后端) + Vercel SDK (前端) 最佳组合

最后建议

不要追求"完美的技术选型"

  • 先快速做出来一个能用的
  • 遇到问题再重构
  • 技术服务业务,不要为了用新库而用新库
记住:80%的AI应用不需要LangChain

用最简单的方式,解决你的实际问题,就是最好的选择。