glownight

返回

如何使用 LangChain (TypeScript) 开发一个处理 PDF 文件的 Agent。这是一个完整的实战教程。


第一步:环境搭建#

1.1 初始化项目#

# 创建项目目录
mkdir pdf-agent-ts
cd pdf-agent-ts

# 初始化 npm 项目
npm init -y

# 安装 TypeScript 及相关工具
npm install -D typescript ts-node @types/node

# 初始化 TypeScript 配置
npx tsc --init
bash

1.2 生成的 tsconfig.json 需要修改以下配置#

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "commonjs",
    "lib": ["ES2022"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "node",
    "resolveJsonModule": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}
json

1.3 安装 LangChain 核心依赖#

# 核心框架
npm install langchain

# PDF 处理(PDFLoader 依赖)
npm install pdf-parse

# 向量数据库(本地开发用)
npm install @langchain/community

# OpenAI 相关(用于嵌入和对话模型)
npm install @langchain/openai

# 环境变量管理
npm install dotenv
bash

1.4 安装可选依赖(推荐)#

# 更好的文本分割(支持中文)
npm install @langchain/textsplitters

# 本地向量存储(开发测试用)
npm install faiss-node

# 或者使用内存向量存储(无需额外安装)
bash

1.5 项目目录结构#

pdf-agent-ts/
├── src/
│   ├── config/           # 配置文件
│   ├── loaders/          # 文档加载器
│   ├── splitters/        # 文本分割器
│   ├── vectorstores/     # 向量存储
│   ├── chains/           # 链定义
│   ├── agents/           # Agent 定义
│   ├── tools/            # 自定义工具
│   ├── utils/            # 工具函数
│   └── index.ts          # 入口文件
├── data/                 # 存放 PDF 文件
│   └── sample.pdf
├── .env                  # 环境变量
├── .env.example          # 环境变量示例
├── tsconfig.json
└── package.json
plaintext

1.6 创建环境变量文件#

.env

# OpenAI API 密钥(必填)
OPENAI_API_KEY=sk-your-openai-api-key-here

# 可选:自定义 OpenAI 基础 URL(如果需要代理)
# OPENAI_BASE_URL=https://api.openai.com/v1

# 可选:LangSmith 追踪(用于调试)
# LANGCHAIN_TRACING_V2=true
# LANGCHAIN_API_KEY=ls-your-langsmith-key
# LANGCHAIN_PROJECT=pdf-agent-project
plaintext

.env.example(用于团队协作模板)

OPENAI_API_KEY=
OPENAI_BASE_URL=
LANGCHAIN_TRACING_V2=
LANGCHAIN_API_KEY=
LANGCHAIN_PROJECT=
plaintext

第二步:配置模块#

2.1 创建配置文件 src/config/env.ts#

import { config } from "dotenv";
config();

/**
 * 环境变量配置集中管理
 * 所有配置项都在这里定义,避免散落在代码各处
 */
export const env = {
  // OpenAI 配置
  openai: {
    apiKey: process.env.OPENAI_API_KEY!,
    baseURL: process.env.OPENAI_BASE_URL || "https://api.openai.com/v1",
    modelName: "gpt-4o-mini", // 性价比高的模型,可替换为 gpt-4o
    embeddingModel: "text-embedding-3-small",
  },

  // LangSmith 追踪配置(可选,用于调试)
  langsmith: {
    tracing: process.env.LANGCHAIN_TRACING_V2 === "true",
    apiKey: process.env.LANGCHAIN_API_KEY,
    project: process.env.LANGCHAIN_PROJECT || "pdf-agent",
  },

  // 应用配置
  app: {
    chunkSize: 1000,      // 文本分割块大小
    chunkOverlap: 200,    // 块之间重叠大小
    topK: 4,              // 检索返回的最相似文档数
    temperature: 0.1,     // 模型温度(越低越确定)
  },
};

// 启动时验证必要环境变量
if (!env.openai.apiKey) {
  throw new Error("❌ 错误:未设置 OPENAI_API_KEY 环境变量,请在 .env 文件中配置");
}

console.log("✅ 环境配置加载成功");
typescript

第三步:PDF 文档加载模块#

3.1 创建 PDF 加载器 src/loaders/pdfLoader.ts#

import { PDFLoader } from "@langchain/community/document_loaders/fs/pdf";
import { Document } from "@langchain/core/documents";
import * as path from "path";

/**
 * PDF 文档加载器
 * 负责将 PDF 文件转换为 LangChain Document 对象
 */
export class PDFDocumentLoader {
  private filePath: string;

  constructor(filePath: string) {
    this.filePath = path.resolve(filePath);
  }

  /**
   * 加载 PDF 文件
   * @param splitPages 是否按页分割(true=每页一个Document,false=整个PDF一个Document)
   */
  async load(splitPages: boolean = true): Promise<Document[]> {
    try {
      console.log(`📄 正在加载 PDF: ${this.filePath}`);

      const loader = new PDFLoader(this.filePath, {
        splitPages,           // 按页分割
        parsedItemSeparator: "", // 解析项之间的分隔符
      });

      const documents = await loader.load();

      console.log(`✅ 成功加载 ${documents.length} 个文档片段`);

      // 打印前3个片段的信息用于调试
      documents.slice(0, 3).forEach((doc, i) => {
        console.log(`\n--- 片段 ${i + 1} ---`);
        console.log(`页码: ${doc.metadata.loc?.pageNumber || "N/A"}`);
        console.log(`内容长度: ${doc.pageContent.length} 字符`);
        console.log(`内容预览: ${doc.pageContent.substring(0, 100)}...`);
      });

      return documents;
    } catch (error) {
      console.error(`❌ PDF 加载失败: ${error}`);
      throw error;
    }
  }

  /**
   * 批量加载多个 PDF 文件
   */
  static async loadMultiple(filePaths: string[]): Promise<Document[]> {
    const allDocs: Document[] = [];

    for (const filePath of filePaths) {
      const loader = new PDFDocumentLoader(filePath);
      const docs = await loader.load(true);
      allDocs.push(...docs);
    }

    return allDocs;
  }
}
typescript

第四步:文本分割模块#

4.1 创建文本分割器 src/splitters/textSplitter.ts#

import { RecursiveCharacterTextSplitter } from "@langchain/textsplitters";
import { Document } from "@langchain/core/documents";
import { env } from "../config/env";

/**
 * 文本分割器
 * 将长文档切分为适合向量化的 chunks
 */
export class DocumentSplitter {
  private splitter: RecursiveCharacterTextSplitter;

  constructor() {
    // RecursiveCharacterTextSplitter 会按以下顺序尝试分割:
    // 1. 段落(\n\n)
    // 2. 换行(\n)
    // 3. 句子(.!?)
    // 4. 单词(空格)
    // 5. 字符
    // 这样可以尽量保持语义完整性
    this.splitter = new RecursiveCharacterTextSplitter({
      chunkSize: env.app.chunkSize,       // 每个块的最大字符数
      chunkOverlap: env.app.chunkOverlap,  // 块之间的重叠字符数(保持上下文连贯)
      separators: ["\n\n", "\n", ".", "!", "?", " ", ""], // 分割优先级
    });
  }

  /**
   * 分割文档
   * @param documents 原始文档列表
   */
  async split(documents: Document[]): Promise<Document[]> {
    console.log(`✂️ 正在分割 ${documents.length} 个文档...`);

    const splitDocs = await this.splitter.splitDocuments(documents);

    console.log(`✅ 分割完成,共 ${splitDocs.length} 个片段`);

    // 显示统计信息
    const avgLength = splitDocs.reduce((sum, d) => sum + d.pageContent.length, 0) / splitDocs.length;
    console.log(`📊 平均片段长度: ${Math.round(avgLength)} 字符`);

    return splitDocs;
  }

  /**
   * 自定义参数分割(用于不同场景)
   */
  async splitWithOptions(
    documents: Document[],
    chunkSize: number,
    chunkOverlap: number
  ): Promise<Document[]> {
    const customSplitter = new RecursiveCharacterTextSplitter({
      chunkSize,
      chunkOverlap,
    });

    return await customSplitter.splitDocuments(documents);
  }
}
typescript

第五步:向量存储模块#

5.1 创建向量存储 src/vectorstores/vectorStore.ts#

import { OpenAIEmbeddings } from "@langchain/openai";
import { MemoryVectorStore } from "langchain/vectorstores/memory";
import { Document } from "@langchain/core/documents";
import { env } from "../config/env";

/**
 * 向量存储管理器
 * 负责文档的嵌入和向量检索
 */
export class VectorStoreManager {
  private embeddings: OpenAIEmbeddings;
  private vectorStore: MemoryVectorStore | null = null;

  constructor() {
    // 初始化 OpenAI 嵌入模型
    this.embeddings = new OpenAIEmbeddings({
      apiKey: env.openai.apiKey,
      modelName: env.openai.embeddingModel,
      // 可选:配置重试
      maxRetries: 3,
    });
  }

  /**
   * 将文档添加到向量存储
   * @param documents 要嵌入的文档片段
   */
  async addDocuments(documents: Document[]): Promise<void> {
    console.log(`🔢 正在为 ${documents.length} 个片段生成嵌入向量...`);

    // MemoryVectorStore 适合开发和测试,数据存在内存中
    this.vectorStore = await MemoryVectorStore.fromDocuments(
      documents,
      this.embeddings
    );

    console.log("✅ 向量存储构建完成");
  }

  /**
   * 相似性搜索
   * @param query 查询文本
   * @param k 返回结果数量
   */
  async similaritySearch(query: string, k: number = env.app.topK): Promise<Document[]> {
    if (!this.vectorStore) {
      throw new Error("❌ 向量存储未初始化,请先调用 addDocuments()");
    }

    console.log(`🔍 执行相似性搜索: "${query}"`);

    const results = await this.vectorStore.similaritySearch(query, k);

    console.log(`📋 找到 ${results.length} 个相关片段`);

    results.forEach((doc, i) => {
      console.log(`\n--- 结果 ${i + 1} ---`);
      console.log(`来源: ${doc.metadata.source || "unknown"}`);
      console.log(`页码: ${doc.metadata.loc?.pageNumber || "N/A"}`);
      console.log(`内容: ${doc.pageContent.substring(0, 150)}...`);
    });

    return results;
  }

  /**
   * 带分数的相似性搜索(返回相似度分数)
   */
  async similaritySearchWithScore(query: string, k: number = env.app.topK) {
    if (!this.vectorStore) {
      throw new Error("❌ 向量存储未初始化");
    }

    return await this.vectorStore.similaritySearchWithScore(query, k);
  }

  /**
   * 作为检索器使用(供 Chain/Agent 调用)
   */
  asRetriever(k: number = env.app.topK) {
    if (!this.vectorStore) {
      throw new Error("❌ 向量存储未初始化");
    }
    return this.vectorStore.asRetriever({ k });
  }
}
typescript

第六步:自定义工具模块#

6.1 创建 PDF 搜索工具 src/tools/pdfSearchTool.ts#

import { DynamicTool } from "@langchain/core/tools";
import { VectorStoreManager } from "../vectorstores/vectorStore";

/**
 * 创建 PDF 内容搜索工具
 * 供 Agent 调用,用于在 PDF 中查找相关信息
 */
export function createPDFSearchTool(vectorStoreManager: VectorStoreManager) {
  return new DynamicTool({
    name: "pdf_search",
    description: `
      在已上传的 PDF 文档中搜索相关信息。
      输入应该是一个具体的搜索查询(问题或关键词)。
      返回与查询最相关的文档片段。
      当用户询问 PDF 内容时使用此工具。
    `.trim(),

    // 工具执行函数
    func: async (input: string) => {
      try {
        const results = await vectorStoreManager.similaritySearch(input, 4);

        // 将检索结果格式化为字符串返回给 Agent
        const formattedResults = results
          .map((doc, i) => {
            const pageNum = doc.metadata.loc?.pageNumber || "未知";
            return `[片段 ${i + 1}] (第 ${pageNum} 页)\n${doc.pageContent}`;
          })
          .join("\n\n---\n\n");

        return formattedResults || "未找到相关信息";
      } catch (error) {
        return `搜索出错: ${error}`;
      }
    },
  });
}

/**
 * 创建文档摘要工具(简单版)
 */
export function createDocumentSummaryTool() {
  return new DynamicTool({
    name: "document_summary",
    description: "提供当前已加载文档的基本信息和摘要。用于回答关于文档整体的问题。",

    func: async () => {
      return "当前已加载 PDF 文档,可以使用 pdf_search 工具查询具体内容。";
    },
  });
}
typescript

第七步:Agent 核心模块#

7.1 创建 PDF Agent src/agents/pdfAgent.ts#

import { ChatOpenAI } from "@langchain/openai";
import { createOpenAIFunctionsAgent, AgentExecutor } from "langchain/agents";
import {
  ChatPromptTemplate,
  MessagesPlaceholder,
} from "@langchain/core/prompts";
import { VectorStoreManager } from "../vectorstores/vectorStore";
import { createPDFSearchTool, createDocumentSummaryTool } from "../tools/pdfSearchTool";
import { env } from "../config/env";

/**
 * PDF Agent 构建器
 * 整合所有组件,构建可回答 PDF 相关问题的 Agent
 */
export class PDFAgentBuilder {
  private vectorStoreManager: VectorStoreManager;
  private model: ChatOpenAI;

  constructor(vectorStoreManager: VectorStoreManager) {
    this.vectorStoreManager = vectorStoreManager;

    // 初始化对话模型
    this.model = new ChatOpenAI({
      apiKey: env.openai.apiKey,
      modelName: env.openai.modelName,
      temperature: env.app.temperature,
      streaming: true, // 启用流式输出
    });
  }

  /**
   * 构建 Agent
   */
  async build() {
    // 1. 定义工具
    const tools = [
      createPDFSearchTool(this.vectorStoreManager),
      createDocumentSummaryTool(),
    ];

    // 2. 构建 Prompt 模板
    // 使用 OpenAI Functions 格式的 Agent Prompt
    const prompt = ChatPromptTemplate.fromMessages([
      // 系统提示:定义 Agent 的角色和行为
      [
        "system",
        `你是一个专业的 PDF 文档分析助手。你的任务是基于已上传的 PDF 文档内容回答用户问题。

规则:
1. 只使用提供给你的工具获取信息,不要编造答案
2. 如果文档中没有相关信息,请明确告知用户
3. 回答时引用具体的页码信息(如果有)
4. 保持回答简洁、准确、专业

当前可用工具:
- pdf_search: 在 PDF 中搜索具体内容
- document_summary: 获取文档基本信息
`,
      ],
      // 历史消息占位符(支持多轮对话)
      new MessagesPlaceholder("chat_history"),
      // 用户输入
      ["human", "{input}"],
      // Agent 思考过程占位符(OpenAI Functions Agent 需要)
      new MessagesPlaceholder("agent_scratchpad"),
    ]);

    // 3. 创建 Agent
    const agent = await createOpenAIFunctionsAgent({
      llm: this.model,
      tools,
      prompt,
    });

    // 4. 创建 Agent 执行器
    const agentExecutor = new AgentExecutor({
      agent,
      tools,
      verbose: true, // 打印详细执行日志(开发时开启)
      maxIterations: 5, // 最大迭代次数,防止无限循环
      returnIntermediateSteps: true, // 返回中间步骤(用于调试)
    });

    console.log("🤖 PDF Agent 构建完成");

    return agentExecutor;
  }
}
typescript

第八步:主流程编排#

8.1 创建主入口 src/index.ts#

import { PDFDocumentLoader } from "./loaders/pdfLoader";
import { DocumentSplitter } from "./splitters/textSplitter";
import { VectorStoreManager } from "./vectorstores/vectorStore";
import { PDFAgentBuilder } from "./agents/pdfAgent";
import { HumanMessage, AIMessage } from "@langchain/core/messages";
import * as readline from "readline";

/**
 * PDF Agent 主程序
 * 完整流程:加载 PDF → 分割 → 向量化 → 构建 Agent → 交互问答
 */
class PDFAgentApp {
  private vectorStoreManager: VectorStoreManager;
  private chatHistory: (HumanMessage | AIMessage)[] = [];

  constructor() {
    this.vectorStoreManager = new VectorStoreManager();
  }

  /**
   * 初始化:加载并处理 PDF
   */
  async initialize(pdfPath: string): Promise<void> {
    console.log("🚀 开始初始化 PDF Agent...\n");

    // 步骤 1: 加载 PDF
    const loader = new PDFDocumentLoader(pdfPath);
    const rawDocuments = await loader.load(true);

    // 步骤 2: 分割文档
    const splitter = new DocumentSplitter();
    const splitDocuments = await splitter.split(rawDocuments);

    // 步骤 3: 构建向量存储
    await this.vectorStoreManager.addDocuments(splitDocuments);

    console.log("\n✅ 初始化完成!可以开始提问了\n");
  }

  /**
   * 单轮问答(非流式)
   */
  async ask(question: string): Promise<string> {
    // 构建 Agent
    const builder = new PDFAgentBuilder(this.vectorStoreManager);
    const agent = await builder.build();

    // 执行
    const result = await agent.invoke({
      input: question,
      chat_history: this.chatHistory,
    });

    // 保存对话历史
    this.chatHistory.push(new HumanMessage(question));
    this.chatHistory.push(new AIMessage(result.output));

    // 限制历史长度(防止超出 Token 限制)
    if (this.chatHistory.length > 10) {
      this.chatHistory = this.chatHistory.slice(-10);
    }

    return result.output;
  }

  /**
   * 流式问答(实时输出)
   */
  async askStream(question: string): Promise<void> {
    const builder = new PDFAgentBuilder(this.vectorStoreManager);
    const agent = await builder.build();

    console.log("🤖 Agent 思考中...\n");

    const result = await agent.invoke({
      input: question,
      chat_history: this.chatHistory,
    });

    console.log("\n💡 回答:");
    console.log(result.output);

    // 保存历史
    this.chatHistory.push(new HumanMessage(question));
    this.chatHistory.push(new AIMessage(result.output));
  }

  /**
   * 交互式命令行界面
   */
  async startInteractive(): Promise<void> {
    const rl = readline.createInterface({
      input: process.stdin,
      output: process.stdout,
    });

    console.log("💬 交互模式已启动");
    console.log("输入问题直接提问,输入 'exit' 退出,输入 'clear' 清空历史\n");

    const askQuestion = () => {
      rl.question("你: ", async (input) => {
        const trimmed = input.trim();

        if (trimmed.toLowerCase() === "exit") {
          console.log("👋 再见!");
          rl.close();
          return;
        }

        if (trimmed.toLowerCase() === "clear") {
          this.chatHistory = [];
          console.log("🧹 对话历史已清空\n");
          askQuestion();
          return;
        }

        if (!trimmed) {
          askQuestion();
          return;
        }

        try {
          await this.askStream(trimmed);
        } catch (error) {
          console.error("❌ 出错了:", error);
        }

        console.log(); // 空行分隔
        askQuestion();
      });
    };

    askQuestion();
  }
}

// ==================== 启动程序 ====================

async function main() {
  // PDF 文件路径(可以是相对路径或绝对路径)
  const pdfPath = process.argv[2] || "./data/sample.pdf";

  const app = new PDFAgentApp();

  try {
    // 初始化
    await app.initialize(pdfPath);

    // 启动交互模式
    await app.startInteractive();
  } catch (error) {
    console.error("❌ 程序启动失败:", error);
    process.exit(1);
  }
}

// 运行
main();
typescript

第九步:运行与测试#

9.1 准备测试 PDF#

将任意 PDF 文件放入 data/ 目录,命名为 sample.pdf,或者通过命令行参数指定路径。

9.2 修改 package.json 添加启动脚本#

{
  "scripts": {
    "start": "ts-node src/index.ts",
    "start:file": "ts-node src/index.ts",
    "build": "tsc",
    "dev": "ts-node src/index.ts ./data/sample.pdf"
  }
}
json

9.3 运行程序#

# 使用默认路径(./data/sample.pdf)
npm run dev

# 或指定 PDF 文件路径
npm run start -- ./data/another-document.pdf
bash

9.4 预期输出示例#

🚀 开始初始化 PDF Agent...

📄 正在加载 PDF: /path/to/pdf-agent-ts/data/sample.pdf
✅ 成功加载 5 个文档片段

--- 片段 1 ---
页码: 1
内容长度: 2450 字符
内容预览: 第一章 概述
本文档详细说明了...

✂️ 正在分割 5 个文档...
✅ 分割完成,共 12 个片段
📊 平均片段长度: 980 字符

🔢 正在为 12 个片段生成嵌入向量...
✅ 向量存储构建完成

✅ 初始化完成!可以开始提问了

💬 交互模式已启动
输入问题直接提问,输入 'exit' 退出,输入 'clear' 清空历史

你: 这份文档的主要内容是什么?
🤖 Agent 思考中...

[verbose 日志显示 Agent 调用 pdf_search 工具]

💡 回答:
根据文档内容,这是一份关于...

你: 第三章提到了哪些关键点?
...
plaintext

第十步:进阶优化(可选)#

10.1 添加更多工具 src/tools/advancedTools.ts#

import { DynamicTool } from "@langchain/core/tools";

/**
 * 数学计算工具(处理 PDF 中的数据计算)
 */
export function createCalculatorTool() {
  return new DynamicTool({
    name: "calculator",
    description: "进行数学计算。输入应该是一个数学表达式,如 '100 * 0.15' 或 '(500 + 300) / 2'",
    func: async (input: string) => {
      try {
        // 安全计算:只允许基本运算符
        const sanitized = input.replace(/[^0-9+\-*/().\s]/g, "");
        // 使用 Function 构造器计算(生产环境建议使用更安全的计算库)
        const result = new Function(`return (${sanitized})`)();
        return `计算结果: ${result}`;
      } catch {
        return "计算失败,请检查表达式格式";
      }
    },
  });
}

/**
 * 当前日期工具
 */
export function createDateTool() {
  return new DynamicTool({
    name: "current_date",
    description: "获取当前日期和时间,用于时间相关的回答",
    func: async () => {
      return new Date().toLocaleString("zh-CN");
    },
  });
}
typescript

10.2 使用 LangChain Expression Language (LCEL) 简化版本#

如果你想用更现代的 LCEL 方式(推荐用于生产环境),可以创建 src/chains/ragChain.ts

import { ChatOpenAI, OpenAIEmbeddings } from "@langchain/openai";
import { MemoryVectorStore } from "langchain/vectorstores/memory";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { createStuffDocumentsChain } from "langchain/chains/combine_documents";
import { createRetrievalChain } from "langchain/chains/retrieval";
import { Document } from "@langchain/core/documents";
import { env } from "../config/env";

/**
 * 使用 LCEL 构建 RAG Chain(更简洁,性能更好)
 */
export async function buildRAGChain(documents: Document[]) {
  // 1. 嵌入并存储
  const embeddings = new OpenAIEmbeddings({
    apiKey: env.openai.apiKey,
    modelName: env.openai.embeddingModel,
  });
  const vectorStore = await MemoryVectorStore.fromDocuments(documents, embeddings);
  const retriever = vectorStore.asRetriever({ k: 4 });

  // 2. 定义 Prompt
  const prompt = ChatPromptTemplate.fromTemplate(`
    基于以下上下文回答问题。如果上下文没有相关信息,请说"根据文档内容,我无法找到相关信息"。

    上下文:
    {context}

    问题: {input}

    请用中文回答,并标注信息来源页码。
  `);

  // 3. 创建文档组合 Chain
  const combineDocsChain = await createStuffDocumentsChain({
    llm: new ChatOpenAI({
      apiKey: env.openai.apiKey,
      modelName: env.openai.modelName,
      temperature: 0.1,
    }),
    prompt,
  });

  // 4. 创建检索 Chain
  const retrievalChain = await createRetrievalChain({
    retriever,
    combineDocsChain,
  });

  return retrievalChain;
}
typescript

10.3 持久化向量存储(生产环境)#

如果需要持久化数据,可以使用 Chroma 或 Pinecone:

# 安装 Chroma
npm install chromadb @langchain/community

# 或使用 Pinecone
npm install @pinecone-database/pinecone
bash
// src/vectorstores/chromaStore.ts(示例)
import { Chroma } from "@langchain/community/vectorstores/chroma";
import { OpenAIEmbeddings } from "@langchain/openai";

export async function createChromaStore(documents: Document[]) {
  return await Chroma.fromDocuments(documents, new OpenAIEmbeddings(), {
    collectionName: "pdf-collection",
    url: "http://localhost:8000", // Chroma 服务地址
  });
}
typescript

完整文件清单#

文件路径作用
src/config/env.ts环境变量与配置
src/loaders/pdfLoader.tsPDF 文档加载
src/splitters/textSplitter.ts文本分割
src/vectorstores/vectorStore.ts向量存储管理
src/tools/pdfSearchTool.ts自定义搜索工具
src/agents/pdfAgent.tsAgent 构建
src/index.ts主程序入口

关键概念总结#

组件作用类比
Document Loader读取各种格式文件图书管理员取书
Text Splitter长文本切分把书拆成章节
Embeddings文本向量化翻译成机器语言
Vector Store存储和检索向量智能图书馆索引
Retriever相似性搜索根据问题找相关章节
Agent决策+工具调用会查资料的研究员
Tool具体功能模块研究员的工具箱

如需进一步讲解某个模块(如流式输出、多 PDF 管理、部署到 Web 服务等),请告诉我!

如何使用 LangChain (TypeScript) 开发一个处理 PDF 文件的 Agent
作者 glownight
发布于 2026年4月29日