> ## Documentation Index
> Fetch the complete documentation index at: https://langchain-zh.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# 构建 SQL 代理

## 概述

在本教程中，你将学习如何使用 LangChain [代理](/oss/javascript/langchain/agents)构建一个能够回答关于 SQL 数据库问题的代理。

从高层次来看，该代理将：

<Steps>
  <Step title="从数据库中获取可用的表和模式" />

  <Step title="确定哪些表与问题相关" />

  <Step title="获取相关表的模式" />

  <Step title="根据问题和模式信息生成查询" />

  <Step title="使用 LLM 双重检查查询中的常见错误" />

  <Step title="执行查询并返回结果" />

  <Step title="纠正数据库引擎暴露的错误，直到查询成功" />

  <Step title="根据结果制定响应" />
</Steps>

<Warning>
  构建针对 SQL 数据库的问答系统需要执行模型生成的 SQL 查询。这样做存在固有风险。请确保你的数据库连接权限始终根据代理的需求尽可能缩小范围。这将减轻（尽管不能消除）构建模型驱动系统的风险。
</Warning>

### 概念

我们将涵盖以下概念：

* 用于从 SQL 数据库读取的[工具](/oss/javascript/langchain/tools)
* LangChain [代理](/oss/javascript/langchain/agents)
* [人工介入](/oss/javascript/langchain/human-in-the-loop)流程

## 设置

### 安装

<CodeGroup>
  ```bash npm theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  npm i langchain @langchain/core typeorm sqlite3 zod
  ```

  ```bash yarn theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  yarn add langchain @langchain/core typeorm sqlite3 zod
  ```

  ```bash pnpm theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  pnpm add langchain @langchain/core typeorm sqlite3 zod
  ```
</CodeGroup>

### LangSmith

设置 [LangSmith](https://smith.langchain.com) 以检查你的链或代理内部发生的情况。然后设置以下环境变量：

```shell theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
export LANGSMITH_TRACING="true"
export LANGSMITH_API_KEY="..."
```

## 1. 选择 LLM

选择一个支持[工具调用](/oss/javascript/integrations/providers/overview)的模型：

<Tabs>
  <Tab title="OpenAI">
    👉 阅读 [OpenAI 聊天模型集成文档](/oss/javascript/integrations/chat/openai/)

    <CodeGroup>
      ```bash npm theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      npm install @langchain/openai
      ```

      ```bash pnpm theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      pnpm install @langchain/openai
      ```

      ```bash yarn theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      yarn add @langchain/openai
      ```

      ```bash bun theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      bun add @langchain/openai
      ```
    </CodeGroup>

    <CodeGroup>
      ```typescript initChatModel theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      import { initChatModel } from "langchain";

      process.env.OPENAI_API_KEY = "your-api-key";

      const model = await initChatModel("gpt-5.2");
      ```

      ```typescript Model Class theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      import { ChatOpenAI } from "@langchain/openai";

      const model = new ChatOpenAI({
        model: "gpt-5.2",
        apiKey: "your-api-key"
      });
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Anthropic">
    👉 阅读 [Anthropic 聊天模型集成文档](/oss/javascript/integrations/chat/anthropic/)

    <CodeGroup>
      ```bash npm theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      npm install @langchain/anthropic
      ```

      ```bash pnpm theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      pnpm install @langchain/anthropic
      ```

      ```bash yarn theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      yarn add @langchain/anthropic
      ```

      ```bash pnpm theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      pnpm add @langchain/anthropic
      ```
    </CodeGroup>

    <CodeGroup>
      ```typescript initChatModel theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      import { initChatModel } from "langchain";

      process.env.ANTHROPIC_API_KEY = "your-api-key";

      const model = await initChatModel("claude-sonnet-4-6");
      ```

      ```typescript Model Class theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      import { ChatAnthropic } from "@langchain/anthropic";

      const model = new ChatAnthropic({
        model: "claude-sonnet-4-6",
        apiKey: "your-api-key"
      });
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Azure">
    👉 阅读 [Azure 聊天模型集成文档](/oss/javascript/integrations/chat/azure/)

    <CodeGroup>
      ```bash npm theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      npm install @langchain/azure
      ```

      ```bash pnpm theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      pnpm install @langchain/azure
      ```

      ```bash yarn theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      yarn add @langchain/azure
      ```

      ```bash bun theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      bun add @langchain/azure
      ```
    </CodeGroup>

    <CodeGroup>
      ```typescript initChatModel theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      import { initChatModel } from "langchain";

      process.env.AZURE_OPENAI_API_KEY = "your-api-key";
      process.env.AZURE_OPENAI_ENDPOINT = "your-endpoint";
      process.env.OPENAI_API_VERSION = "your-api-version";

      const model = await initChatModel("azure_openai:gpt-5.2");
      ```

      ```typescript Model Class theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      import { AzureChatOpenAI } from "@langchain/openai";

      const model = new AzureChatOpenAI({
        model: "gpt-5.2",
        azureOpenAIApiKey: "your-api-key",
        azureOpenAIApiEndpoint: "your-endpoint",
        azureOpenAIApiVersion: "your-api-version"
      });
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Google Gemini">
    👉 阅读 [Google GenAI 聊天模型集成文档](/oss/javascript/integrations/chat/google_generative_ai/)

    <CodeGroup>
      ```bash npm theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      npm install @langchain/google-genai
      ```

      ```bash pnpm theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      pnpm install @langchain/google-genai
      ```

      ```bash yarn theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      yarn add @langchain/google-genai
      ```

      ```bash bun theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      bun add @langchain/google-genai
      ```
    </CodeGroup>

    <CodeGroup>
      ```typescript initChatModel theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      import { initChatModel } from "langchain";

      process.env.GOOGLE_API_KEY = "your-api-key";

      const model = await initChatModel("google-genai:gemini-2.5-flash-lite");
      ```

      ```typescript Model Class theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      import { ChatGoogleGenerativeAI } from "@langchain/google-genai";

      const model = new ChatGoogleGenerativeAI({
        model: "gemini-2.5-flash-lite",
        apiKey: "your-api-key"
      });
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Bedrock Converse">
    👉 阅读 [AWS Bedrock 聊天模型集成文档](/oss/javascript/integrations/chat/bedrock_converse/)

    <CodeGroup>
      ```bash npm theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      npm install @langchain/aws
      ```

      ```bash pnpm theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      pnpm install @langchain/aws
      ```

      ```bash yarn theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      yarn add @langchain/aws
      ```

      ```bash bun theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      bun add @langchain/aws
      ```
    </CodeGroup>

    <CodeGroup>
      ```typescript initChatModel theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      import { initChatModel } from "langchain";

      // 按照以下步骤配置您的凭据：
      // https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html

      const model = await initChatModel("bedrock:gpt-5.2");
      ```

      ```typescript Model Class theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      import { ChatBedrockConverse } from "@langchain/aws";

      // 按照以下步骤配置您的凭据：
      // https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html

      const model = new ChatBedrockConverse({
        model: "gpt-5.2",
        region: "us-east-2"
      });
      ```
    </CodeGroup>
  </Tab>
</Tabs>

下面示例中显示的输出使用了 OpenAI。

## 2. 配置数据库

你将为本教程创建一个 [SQLite 数据库](https://www.sqlitetutorial.net/sqlite-sample-database/)。SQLite 是一个轻量级数据库，易于设置和使用。我们将加载 `chinook` 数据库，这是一个代表数字媒体商店的示例数据库。

为了方便起见，我们已将数据库 (`Chinook.db`) 托管在一个公共的 GCS 存储桶上。

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import fs from "node:fs/promises";
import path from "node:path";

const url = "https://storage.googleapis.com/benchmarks-artifacts/chinook/Chinook.db";
const localPath = path.resolve("Chinook.db");

async function resolveDbPath() {
  if (await fs.exists(localPath)) {
    return localPath;
  }
  const resp = await fetch(url);
  if (!resp.ok) throw new Error(`下载数据库失败。状态码: ${resp.status}`);
  const buf = Buffer.from(await resp.arrayBuffer());
  await fs.writeFile(localPath, buf);
  return localPath;
}
```

## 3. 添加数据库交互工具

使用 `langchain/sql_db` 中提供的 `SqlDatabase` 包装器来与数据库交互。该包装器提供了一个简单的接口来执行 SQL 查询和获取结果：

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { SqlDatabase } from "@langchain/classic/sql_db";
import { DataSource } from "typeorm";

let db: SqlDatabase | undefined;
async function getDb() {
  if (!db) {
    const dbPath = await resolveDbFile();
    const datasource = new DataSource({ type: "sqlite", database: dbPath });
    db = await SqlDatabase.fromDataSourceParams({ appDataSource: datasource });
  }
  return db;
}

async function getSchema() {
  const db = await getDb();
  return await db.getTableInfo();
}
```

## 4. 执行 SQL 查询

在运行命令之前，在 `_safe_sql` 中进行检查以验证 LLM 生成的命令：

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}

const DENY_RE = /\b(INSERT|UPDATE|DELETE|ALTER|DROP|CREATE|REPLACE|TRUNCATE)\b/i;
const HAS_LIMIT_TAIL_RE = /\blimit\b\s+\d+(\s*,\s*\d+)?\s*;?\s*$/i;

function sanitizeSqlQuery(q) {
  let query = String(q ?? "").trim();

  // 阻止多条语句（允许一个可选的尾随 ;）
  const semis = [...query].filter((c) => c === ";").length;
  if (semis > 1 || (query.endsWith(";") && query.slice(0, -1).includes(";"))) {
    throw new Error("不允许多条语句。")
  }
  query = query.replace(/;+\s*$/g, "").trim();

  // 只读门控
  if (!query.toLowerCase().startsWith("select")) {
    throw new Error("只允许 SELECT 语句")
  }
  if (DENY_RE.test(query)) {
    throw new Error("检测到 DML/DDL。只允许只读查询。")
  }

  // 仅当不存在时才附加 LIMIT
  if (!HAS_LIMIT_TAIL_RE.test(query)) {
    query += " LIMIT 5";
  }
  return query;
}

```

然后，使用 `SQLDatabase` 的 `run` 方法通过 `execute_sql` 工具执行命令：

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { tool } from "langchain"
import * as z from "zod";

const executeSql = tool(
  async ({ query }) => {
    const q = sanitizeSqlQuery(query);
    try {
      const result = await db.run(q);
      return typeof result === "string" ? result : JSON.stringify(result, null, 2);
    } catch (e) {
      throw new Error(e?.message ?? String(e))
    }
  },
  {
    name: "execute_sql",
    description: "执行一个只读的 SQLite SELECT 查询并返回结果。",
    schema: z.object({
      query: z.string().describe("要执行的 SQLite SELECT 查询（只读）。"),
    }),
  }
);

```

## 5. 使用 `createAgent`

使用 `createAgent` 以最少的代码构建一个 [ReAct 代理](https://arxiv.org/pdf/2210.03629)。代理将解释请求并生成 SQL 命令。工具将检查命令的安全性，然后尝试执行命令。如果命令有错误，错误消息将返回给模型。模型随后可以检查原始请求和新的错误消息，并生成新的命令。这可以持续进行，直到 LLM 成功生成命令或达到结束计数。这种向模型提供反馈（在本例中是错误消息）的模式非常强大。

使用描述性的系统提示初始化代理以自定义其行为：

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { SystemMessage } from "langchain";

const getSystemPrompt = async () => new SystemMessage(`你是一个谨慎的 SQLite 分析师。

权威模式（不要编造列/表）:
${await getSchema()}

规则:
- 逐步思考。
- 当你需要数据时，使用一个 SELECT 查询调用工具 \`execute_sql\`。
- 只读；不允许 INSERT/UPDATE/DELETE/ALTER/DROP/CREATE/REPLACE/TRUNCATE。
- 除非用户明确要求，否则限制为 5 行。
- 如果工具返回 'Error:'，请修改 SQL 并重试。
- 将尝试次数限制为 5 次。
- 如果 5 次尝试后仍未成功，请向用户返回一条说明。
- 优先使用显式列列表；避免使用 SELECT *。
`);
```

现在，使用模型、工具和提示创建一个代理：

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { createAgent } from "langchain";

const agent = createAgent({
  model: "gpt-5",
  tools: [executeSql],
  systemPrompt: getSystemPrompt,
});

```

## 6. 运行代理

在示例查询上运行代理并观察其行为：

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
const question = "哪种流派的曲目平均长度最长？";
const stream = await agent.stream(
  { messages: [{ role: "user", content: question }] },
  { streamMode: "values" }
);
for await (const step of stream) {
  const message = step.messages.at(-1);
  console.log(`${message.role}: ${JSON.stringify(message.content, null, 2)}`);
}
```

```
human: 哪种流派的曲目平均长度最长？
ai:
tool: [{"Genre":"Sci Fi & Fantasy","AvgMilliseconds":2911783.0384615385}]
ai: Sci Fi & Fantasy — 平均曲目长度 ≈ 48.5 分钟（约 2,911,783 毫秒）。
```

代理正确地编写了查询，检查了查询，并运行它以形成最终响应。

<Note>
  你可以在 [LangSmith 跟踪记录](https://smith.langchain.com/public/653d218b-af67-4854-95ca-6abecb9b2520/r) 中检查上述运行的所有方面，包括采取的步骤、调用的工具、LLM 看到的提示等。
</Note>

#### （可选）使用 Studio

[Studio](/langsmith/studio) 提供了一个“客户端”循环以及内存，因此你可以将其作为聊天界面运行并查询数据库。你可以提出诸如“告诉我数据库的模式”或“显示前 5 名客户的发票”之类的问题。你将看到生成的 SQL 命令以及结果输出。如何开始的详细信息如下。

<Accordion title="在 Studio 中运行你的代理">
  除了前面提到的包，你还需要：

  ```shell theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  npm i -g @langchain/langgraph-cli@latest
  ```

  在你将运行的目录中，你需要一个包含以下内容的 `langgraph.json` 文件：

  ```json theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  {
    "dependencies": ["."],
    "graphs": {
        "agent": "./sqlAgent.ts:agent",
        "graph": "./sqlAgentLanggraph.ts:graph"
    },
    "env": ".env"
  }
  ```

  ```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import fs from "node:fs/promises";
  import path from "node:path";
  import { SqlDatabase } from "@langchain/classic/sql_db";
  import { DataSource } from "typeorm";
  import { SystemMessage, createAgent, tool } from "langchain"
  import * as z from "zod";

  const url = "https://storage.googleapis.com/benchmarks-artifacts/chinook/Chinook.db";
  const localPath = path.resolve("Chinook.db");

  async function resolveDbPath() {
    if (await fs.exists(localPath)) {
      return localPath;
    }
    const resp = await fetch(url);
    if (!resp.ok) throw new Error(`下载数据库失败。状态码: ${resp.status}`);
    const buf = Buffer.from(await resp.arrayBuffer());
    await fs.writeFile(localPath, buf);
    return localPath;
  }

  let db: SqlDatabase | undefined;
  async function getDb() {
    if (!db) {
      const dbPath = await resolveDbPath();
      const datasource = new DataSource({ type: "sqlite", database: dbPath });
      db = await SqlDatabase.fromDataSourceParams({ appDataSource: datasource });
    }
    return db;
  }

  async function getSchema() {
    const db = await getDb();
    return await db.getTableInfo();
  }

  const DENY_RE = /\b(INSERT|UPDATE|DELETE|ALTER|DROP|CREATE|REPLACE|TRUNCATE)\b/i;
  const HAS_LIMIT_TAIL_RE = /\blimit\b\s+\d+(\s*,\s*\d+)?\s*;?\s*$/i;

  function sanitizeSqlQuery(q) {
    let query = String(q ?? "").trim();

    // 阻止多条语句（允许一个可选的尾随 ;）
    const semis = [...query].filter((c) => c === ";").length;
    if (semis > 1 || (query.endsWith(";") && query.slice(0, -1).includes(";"))) {
      throw new Error("不允许多条语句。")
    }
    query = query.replace(/;+\s*$/g, "").trim();

    // 只读门控
    if (!query.toLowerCase().startsWith("select")) {
      throw new Error("只允许 SELECT 语句")
    }
    if (DENY_RE.test(query)) {
      throw new Error("检测到 DML/DDL。只允许只读查询。")
    }

    // 仅当不存在时才附加 LIMIT
    if (!HAS_LIMIT_TAIL_RE.test(query)) {
      query += " LIMIT 5";
    }
    return query;
  }

  const executeSql = tool(
    async ({ query }) => {
      const q = sanitizeSqlQuery(query);
      try {
        const result = await db.run(q);
        return typeof result === "string" ? result : JSON.stringify(result, null, 2);
      } catch (e) {
        throw new Error(e?.message ?? String(e))
      }
    },
    {
      name: "execute_sql",
      description: "执行一个只读的 SQLite SELECT 查询并返回结果。",
      schema: z.object({
        query: z.string().describe("要执行的 SQLite SELECT 查询（只读）。"),
      }),
    }
  );

  const getSystemPrompt = async () => new SystemMessage(`你是一个谨慎的 SQLite 分析师。

  权威模式（不要编造列/表）:
  ${await getSchema()}

  规则:
  - 逐步思考。
  - 当你需要数据时，使用一个 SELECT 查询调用工具 \`execute_sql\`。
  - 只读；不允许 INSERT/UPDATE/DELETE/ALTER/DROP/CREATE/REPLACE/TRUNCATE。
  - 除非用户明确要求，否则限制为 5 行。
  - 如果工具返回 'Error:'，请修改 SQL 并重试。
  - 将尝试次数限制为 5 次。
  - 如果 5 次尝试后仍未成功，请向用户返回一条说明。
  - 优先使用显式列列表；避免使用 SELECT *。
  `);

  export const agent = createAgent({
    model: "gpt-5",
    tools: [executeSql],
    systemPrompt: getSystemPrompt,
  });
  ```
</Accordion>

## 后续步骤

要进行更深入的定制，请查看[本教程](/oss/javascript/langgraph/sql-agent)，了解如何使用 LangGraph 原语直接实现 SQL 代理。

***

<div className="source-links">
  <Callout icon="edit">
    [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/i18n\zh-CN\oss\langchain\sql-agent.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
  </Callout>

  <Callout icon="terminal-2">
    [Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers.
  </Callout>
</div>
