前端从0开始的LangChain学习(一)

LangChain是什么,干什么的

LangChain是AI Agent 开发的一个框架。可以理解为:Lang + Chain = 语言模型 + 链式调用。它支持几乎所有的大模型(deepseek、chatGpt等),并且提供了统一的调用方式,让你轻松切换不同的模型

所以可以通过 LangChain 这个框架去开发AI Agent,方式是通过链式调用,把大模型(LLM)能力连接到实际应用中

为什么需要LangChain

传统的AI对话开发:核心在于后端业务逻辑、数据库设计、API 接口等技术栈。

而AI Agent开发:核心变成了如何与大模型对话、如何优化提示词、如何管理对话流程

而Langchain就可以做到

对于前端来说难吗

LangChain 原生支持 TypeScript!这意味着前端开发者可以用自己熟悉的 JavaScript/TypeScript 来构建 AI 应用。

LangChain的6个核心

Models模型接入

首先安装一下环境

js 复制代码
pnpm i @langchain/core @langchain/openai dotenv langchain zod

然后根目录创建.env文件

.env 复制代码
DEEPSEEK_API_KEY="你的apikey,这里我用的deepSeek"

LangChain提供了统一的大模型接口

js 复制代码
import { HumanMessage } from "@langchain/core/messages";
import { ChatOpenAI } from "@langchain/openai";
import dotenv from "dotenv";

dotenv.config();

// DeepSeek 配置(兼容 OpenAI 接口)
const deepseekModel = new ChatOpenAI({ 
  model: "deepseek-chat", 
  temperature: 0.7,
  apiKey: process.env.DEEPSEEK_API_KEY,
  configuration: {
    baseURL: 'https://api.deepseek.com/v1'
  }
});

// 也可以使用其他模型
// const openaiModel = new ChatOpenAI({ model: "gpt-4", apiKey: process.env.OPENAI_API_KEY });
// const claudeModel = new ChatAnthropic({ model: "claude-3-opus-20240229", apiKey: process.env.ANTHROPIC_API_KEY });

// 统一调用方式
const messages = [new HumanMessage("你好,今天成都天气怎么样")];
const response = await deepseekModel.invoke(messages);
console.log(response.content);

Prompts提示词管理

基础模板

PromptTemplate.fromTemplate

js 复制代码
/**
 * 演示langchain创建prompt提示词
 */
import { PromptTemplate } from "@langchain/core/prompts";
import { ChatOpenAI } from "@langchain/openai";
import dotenv from "dotenv";

dotenv.config();

// 创建提示词模板
const promptModel = PromptTemplate.fromTemplate(`
  你是一个{role},
  请用不超过{limit}个字符回答以下问题:
  {question}  
`);
// 根据模板,填充提示词
const createPrompt = await promptModel.format({
  role: '专业翻译',
  limit: 10,
  question: '你好请翻译,我想知道你是男是女。',
});
// 初始化deepseek模型
const model = new ChatOpenAI({
  model: 'deepseek-reasoner',
  temperature: 0.7,
  apiKey: process.env.DEEPSEEK_API_KEY,
  configuration: {
    baseURL: process.env.DEEPSEEK_API_URL,
  }
});
// 调用模型
const response = await model.invoke(createPrompt);
console.log(response.content);

多消息模板

js 复制代码
/**
 * 演示langchain创建多消息prompt提示词
 */
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { ChatOpenAI } from "@langchain/openai";
import dotenv from "dotenv";

dotenv.config();

// 创建提示词模板
const chatPrompt = ChatPromptTemplate.fromMessages([
  { role: 'system', content: '你是一个专业的{role},请用{language}回答。' },
  { role: 'human', content: '{input}' },
]);
// 根据模板,填充提示词
const createPrompt = await chatPrompt.formatMessages({
  role: 'AI应用开发专家',
  language: '中文',
  input: '如何使用DeepSeekAPI',
});
// 初始化deepseek模型
const model = new ChatOpenAI({
  model: 'deepseek-reasoner',
  temperature: 0.7,
  apiKey: process.env.DEEPSEEK_API_KEY,
  configuration: {
    baseURL: process.env.DEEPSEEK_API_URL,
  },
});
// 调用模型
const response = await model.invoke(createPrompt);
console.log(response.content);

占位符模板(记忆)

js 复制代码
const promptWithMemory = ChatPromptTemplate.fromMessages([
  ["system", "你是一个友好的 AI 助手,由深度求索公司开发"],
  new MessagesPlaceholder("chat_history"),  // 记忆会动态插入到这里
  ["human", "{input}"]
]);

Chains:流程编排

LangChain 表达式语言 (LCEL, LangChain Expression Language) 是最推荐的方式(类似于promise),它用 | 管道符将组件串联起来:

js 复制代码
import { StringOutputParser } from "@langchain/core/output_parsers";
import { PromptTemplate } from "@langchain/core/prompts";
import { ChatOpenAI } from "@langchain/openai";
import dotenv from 'dotenv';
dotenv.config();

// 配置 DeepSeek 模型
const model = new ChatOpenAI({ 
  model: "deepseek-chat", 
  temperature: 0.7,
  apiKey: process.env.DEEPSEEK_API_KEY,
  configuration: {
    baseURL: process.env.DEEPSEEK_API_URL,
  }
});

const prompt = PromptTemplate.fromTemplate("给我讲一个关于{topic}的笑话,要简短有趣");

// 使用 LCEL 语法构建链
const chain = prompt.pipe(model).pipe(new StringOutputParser());

// 执行链
const result = await chain.invoke({ topic: "程序员" });
console.log(result);

// 我们也可以串联更复杂的操作
const complexChain = prompt
  .pipe(model)
  .pipe(new StringOutputParser())
  .pipe((text) => `🤖 AI 说:${text}`); // 自定义处理

const upperResult = await complexChain.invoke({ topic: "AI" });
console.log(upperResult);

通过 pipe 方法串联。

Tools工具

工具是 Agent 的"手脚",可以拓展 Agent 的功能。LangChain 提供了多种定义工具的方式:

一个 LangChain 工具包含三个核心要素:

  • name:工具名称,AI通过它选择工具
  • description:工具描述,AI判断何时使用
  • func:实际执行的函数

字符串输入

js 复制代码
import { DynamicTool } from "@langchain/core/tools";

const simpleTool = new DynamicTool({
  name: "get_time",
  description: "获取当前时间",
  func: async (input) => {
    if (input) {
        return '不告诉你'
    }
    return new Date().toLocaleString();
  }
});

// 使用
const result = await simpleTool.func("当前时间");
console.log("当前时间:", result); //'不告诉你'

验证参数类型(推荐)

js 复制代码
import { DynamicStructuredTool } from "langchain";
import z from "zod";

const weatherTool = new DynamicStructuredTool({
    name: 'weather',
    description: '查询天气信息,输入城市名称',
    schema: z.object({
        city: z.string().describe('城市名称'),
        unit: z.enum(['celsius', 'fahrenheit']).optional().describe('温度单位')
    }),
    func: async ({ city, unit = 'celsius' }) => {
        // 模拟天气数据
        const weatherData = {
            "北京": { temp: 22, condition: "晴" },
            "上海": { temp: 18, condition: "雨" },
            "武汉": { temp: 25, condition: "阴" }
        };

        const data = weatherData[city];
        if (!data) {
            return `未找到城市 "${city}" 的天气信息`;
        }

        const temp = unit === "celsius" ? `${data.temp}°C` : `${data.temp * 9 / 5 + 32}°F`;
        return `${city}今天${data.condition},温度${temp}`;
    }
})

const res = await weatherTool.func({ city: '北京' });
console.log(res);
// 北京今天晴,温度22°C

其中 zod 库可以帮我们验证参数类型,也能通过 describe 为模型提供参数说明。工具的 description 直接决定 AI 调用工具的准确率

zod schema 详解

基础类型
ts 复制代码
const basicSchema = z.object({
  name: z.string(),           // 字符串
  age: z.number(),            // 数字
  isActive: z.boolean(),      // 布尔值
  tags: z.array(z.string())   // 数组
});
带约束的类型
ts 复制代码
const constrainedSchema = z.object({
  name: z.string().min(1).max(100),           // 长度限制
  age: z.number().min(0).max(150),            // 数值范围
  email: z.string().email(),                  // 邮箱格式
  url: z.string().url(),                      // URL格式
  date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/) // 正则匹配
});
可选和默认值
ts 复制代码
const optionalSchema = z.object({
  required: z.string(),                        // 必填
  optional: z.string().optional(),             // 可选
  withDefault: z.string().default("默认值")     // 带默认值
});
枚举类型
ts 复制代码
const enumSchema = z.object({
  status: z.enum(["pending", "done", "cancelled"]),
  priority: z.enum(["low", "normal", "high"]).default("normal")
});

tool 错误处理

js 复制代码
import { DynamicStructuredTool } from "langchain";
import z from "zod";

const robustTool = new DynamicStructuredTool({
  name: "read_file",
  description: "读取文件内容",
  schema: z.object({
    path: z.string().describe("文件路径")
  }),
  func: async ({ path }) => {
    try {
      const content = await fs.readFile(path, "utf-8");
      
      // 限制返回长度,避免Token超限
      if (content.length > 5000) {
        return `${content.slice(0, 5000)}\n...(文件内容过长,已截断)`;
      }
      
      return content;
    } catch (error) {
      // 返回结构化错误,让AI能理解
      if (error.code === "ENOENT") {
        return `错误:文件 "${path}" 不存在。请检查文件路径是否正确。`;
      }
      if (error.code === "EACCES") {
        return `错误:没有权限读取文件 "${path}"。`;
      }
      return `错误:读取文件失败 - ${error.message}`;
    }
  }
});

const res = await robustTool.func({ path: './package.json' });
console.log(res);

自定义工具类

js 复制代码
import { Tool } from "@langchain/core/tools";

class CurrentTimeTool extends Tool {
  name = "get_current_time";
  description = "获取当前时间,输入时区(可选),返回当前日期和时间";
  
  async _call(input: string): Promise<string> {
    const timezone = input || "Asia/Shanghai";
    const now = new Date();
    return `当前时间 (${timezone}): ${now.toLocaleString('zh-CN', { timeZone: timezone })}`;
  }
}

const timeTool = new CurrentTimeTool();

const result = await timeTool.invoke("Asia/Shanghai");
console.log(result); // 输出: 当前时间 (Asia/Shanghai): 2026/3/31 7:19:16

记忆Memory

js 复制代码
import { InMemoryChatMessageHistory } from "@langchain/core/chat_history";
import { HumanMessage } from "@langchain/core/messages";
import { ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts";
import { RunnableWithMessageHistory } from "@langchain/core/runnables";
import { ChatOpenAI } from "@langchain/openai";
import dotenv from 'dotenv';
dotenv.config();

// 配置 DeepSeek 模型
const model = new ChatOpenAI({ 
  model: "deepseek-chat",
  temperature: 0.7,
  apiKey: process.env.DEEPSEEK_API_KEY,
  configuration: {
    baseURL: process.env.DEEPSEEK_API_BASE_URL,
  }
});

// 创建消息历史存储
const messageHistories = {};

// 创建带历史记录的链
const prompt = ChatPromptTemplate.fromMessages([
  ["system", "你是一个友好的AI助手。"],
  new MessagesPlaceholder("history"),
  ["human", "{input}"],
]);
const chain = prompt.pipe(model);

const chainWithHistory = new RunnableWithMessageHistory({
  runnable: chain,
  getMessageHistory: async (sessionId) => {
    if (!messageHistories[sessionId]) {
      messageHistories[sessionId] = new InMemoryChatMessageHistory();
    }
    return messageHistories[sessionId];
  },
  inputMessagesKey: "input",
  historyMessagesKey: "history",
});

// 多轮对话测试
async function runConversation() {
  const sessionId = "user-123";
  
  // 第一轮对话
  const result1 = await chainWithHistory.invoke(
    { input: "你好,我叫小明" },
    { configurable: { sessionId } }
  );
  console.log("AI:", result1.content);
  
  // 第二轮对话
  const result2 = await chainWithHistory.invoke(
    { input: "我是一名程序员" },
    { configurable: { sessionId } }
  );
  console.log("AI:", result2.content);
  
  // 第三轮对话 - AI 会记住前面的信息
  const result3 = await chainWithHistory.invoke(
    { input: "我叫什么名字?做什么工作?" },
    { configurable: { sessionId } }
  );
  console.log("AI:", result3.content);
  
  // 查看记忆内容
  const history = messageHistories[sessionId];
  const messages = await history?.getMessages();
  console.log("\n=== 记忆内容 ===");
  messages?.forEach((msg, idx) => {
    const role = msg instanceof HumanMessage ? "用户" : "AI";
    console.log(`${idx + 1}. ${role}: ${msg.content}`);
  });
}

runConversation();

Agents:智能体

js 复制代码
import { AgentExecutor, createToolCallingAgent } from "@langchain/classic/agents";
import { ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts";
import { DynamicTool } from "@langchain/core/tools";
import { ChatOpenAI } from "@langchain/openai";
import dotenv from 'dotenv';
dotenv.config();

// 配置 DeepSeek 模型
const model = new ChatOpenAI({ 
  model: "deepseek-chat",
  temperature: 0.7,
  apiKey: process.env.DEEPSEEK_API_KEY,
  configuration: {
    baseURL: process.env.DEEPSEEK_API_BASE_URL,
  }
});

// 1. 定义工具
// 天气工具
const weatherTool = new DynamicTool({
  name: "get_weather",
  description: "获取指定城市的天气信息。输入城市名称,返回天气情况。",
  func: async (city) => {
    const weatherData = {
      "北京": "晴天,25°C,微风",
      "上海": "多云,28°C,湿度60%",
      "广州": "雷阵雨,30°C,注意带伞"
    };
    return weatherData[city] || `${city}的天气:晴转多云,温度适中`;
  }
});

// 计算器工具
const calculatorTool = new DynamicTool({
  name: "calculator",
  description: "计算数学表达式。输入数学表达式如 '23 * 45',返回计算结果。",
  func: async (expression) => {
    try {
      const result = eval(expression);
      return `${expression} = ${result}`;
    } catch (error) {
      return `计算错误:${error.message}`;
    }
  }
});

// 2. 定义提示模板
// 注意:不能自定义 system 提示,必须使用 MessagesPlaceholder("agent_scratchpad")
const prompt = ChatPromptTemplate.fromMessages([
  ["system", "You are a helpful assistant"], // 必须是英文基础提示(兼容工具调用)
  new MessagesPlaceholder("chat_history"),
  ["human", "{input}"],
  new MessagesPlaceholder("agent_scratchpad"), // 必须保留,不能修改
]);

// 3. 创建 Agent
const agent = await createToolCallingAgent({
  llm: model,
  tools: [weatherTool, calculatorTool], 
  prompt,
});

// 4. 创建 Agent 执行器
const executor = new AgentExecutor({
  agent,
  tools: [weatherTool, calculatorTool], 
  maxIterations: 5,
  verbose: true,
  returnIntermediateSteps: true,
});

// 5. 执行
const result = await executor.invoke({
  input: "北京天气怎么样?然后帮我算一下 23*45",
  chat_history: [] // 传入 chat_history,用于支持聊天历史
});

console.log("\n最终答案:", result);

Langchain实现一个简单的带记忆的对话

ts 复制代码
import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage, AIMessage, BaseMessage } from "@langchain/core/messages";
import { ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts";
import dotenv from "dotenv";

dotenv.config();

// 自定义 BufferMemory 类
class BufferMemory {
  private messages: BaseMessage[] = [];
  private maxMessages: number = 20;

  constructor(options?: { maxMessages?: number }) {
    this.maxMessages = options?.maxMessages || 20;
  }

  async addUserMessage(content: string) {
    this.messages.push(new HumanMessage(content));
    this.trimMessages();
  }

  async addAIMessage(content: string) {
    this.messages.push(new AIMessage(content));
    this.trimMessages();
  }

  private trimMessages() {
    if (this.messages.length > this.maxMessages) {
      this.messages = this.messages.slice(-this.maxMessages);
    }
  }

  async getHistory() {
    return this.messages;
  }

  async clear() {
    this.messages = [];
  }
}

async function chatWithMemory() {
  // 配置模型
  const model = new ChatOpenAI({
    model: "deepseek-chat",
    temperature: 0.7,
    apiKey: process.env.DEEPSEEK_API_KEY,
    configuration: {
      baseURL: process.env.DEEPSEEK_API_BASE_URL
    }
  });

  // 创建记忆实例
  const memory = new BufferMemory({ maxMessages: 10 });

  // 创建提示模板
  const prompt = ChatPromptTemplate.fromMessages([
    ["system", "你是一个友好的AI助手,用中文回答问题。"],
    new MessagesPlaceholder("history"),
    ["human", "{input}"]
  ]);

  // 创建对话函数
  async function chat(input: string): Promise<string> {
    // 获取历史消息
    const history = await memory.getHistory();
    
    // 构建输入
    const formattedPrompt = await prompt.formatMessages({
      input,
      history
    });
    
    // 调用模型
    const response = await model.invoke(formattedPrompt);
    const responseText = response.content as string;
    
    // 保存到记忆
    await memory.addUserMessage(input);
    await memory.addAIMessage(responseText);
    
    return responseText;
  }

  // 多轮对话
  const response1 = await chat("我叫张三");
  console.log("AI:", response1);

  const response2 = await chat("我叫什么名字?");
  console.log("AI:", response2); // 会记得名字

  // 查看历史
  const history = await memory.getHistory();
  console.log("\n=== 对话历史 ===");
  history.forEach((msg, idx) => {
    const role = msg instanceof HumanMessage ? "用户" : "AI";
    console.log(`${idx + 1}. ${role}: ${msg.content}`);
  });
}

chatWithMemory();

什么时候用 LangChain?

场景 推荐度 原因
多轮对话 + 记忆管理 ✅ 强烈推荐 Memory组件非常方便
多工具 Agent 系统 ✅ 强烈推荐 省去大量循环代码
RAG 应用(文档+检索) ✅ 强烈推荐 内置检索器、向量存储
生产级应用(需可观测性) ✅ 强烈推荐 LangSmith追踪、回调
快速原型开发 ✅ 强烈推荐 组件组合,快速迭代
相关推荐
Developer_Niuge2 小时前
告别翻不动的 1000+ 书签:开源 Chrome / Edge 浏览器书签管理插件 Smart Bookmark 0.2 发布
前端·后端
用户52709648744902 小时前
前端性能指标速查手册
前端
淹死在鱼塘的程序猿2 小时前
🚀 告别"一次性聊天":揭秘让 AI 智能体越用越聪明的秘密武器 —— Skills
前端·人工智能·agent
掘金安东尼2 小时前
OpenMUSE 全面详解:非扩散Transformer文生图开源基座(对标GPT Image 2)
前端·javascript·面试
~ rainbow~2 小时前
前端转型全栈(六)——深入浅出:文件上传的原理与进阶
前端·http·文件上传
我就是马云飞2 小时前
我废了!大厂10年的我面了20家公司,面试官让我回去等通知!
android·前端·程序员
yizhiyang3 小时前
ECharts实战:滑动缩放+选中背景高亮,打造高颜值统计图表
前端
猫山月3 小时前
Flutter路由演进路线(2026)
前端·flutter
We་ct3 小时前
LeetCode 322. 零钱兑换:动态规划入门实战
前端·算法·leetcode·typescript·动态规划