---
title: "Vercel AI SDK"
description: "Vercel AI SDK 为 TypeScript 提供一套对接多家模型 provider 的 API，并支持有类型的 tool。Dex adapter 把 AI SDK 的 Agent 定义放进 durable session 中运行。它支持 TypeScript。"
canonical_url: "https://docs.superdurable.io/zh-Hans/agent-frameworks/vercel-ai-sdk/"
source: "docs/i18n/zh-Hans/docusaurus-plugin-content-docs/current/agent-frameworks/vercel-ai-sdk.mdx"
locale: "zh-Hans"
---

# Vercel AI SDK

Vercel AI SDK 为 TypeScript 提供一套对接多家模型 provider 的 API，并支持有类型的 tool。Dex adapter 把 AI SDK 的 Agent 定义放进 durable session 中运行。它支持 TypeScript。

<a id="when-to-use-it"></a>

## 何时使用

TypeScript 应用已经在使用 AI SDK 的模型和 tool 时，使用它。保留模型、tool 和 **stopWhen**，由 Dex 管理 session。

<a id="how-a-session-runs"></a>

## Session 如何运行

每个 tool 交给模型时都不带 **execute**，因此 AI SDK 会返回 tool call，而不是运行它们。每个 **RunModel** Step 用 **streamText** 调用一次模型。

每个 tool call 在自己的 Step 中并行运行 tool 原本的 **execute**：read 或 idempotent write 用 **CallTool**，其他写操作用 **CallSingleAttemptTool**。需要审批的调用先在 **AwaitToolApproval** 中等待。下一个 **RunModel** 加入记录下来的结果，检查 **stopWhen**，然后再次调用模型。

查询订单的 turn 依次运行 **RunModel**、**CallTool**、**ResumeTurn** 和 **RunModel**。

<a id="install"></a>

## 安装

把 adapter 与它的 framework 一起安装。每个 adapter 都会带上 Dex Agents core。

**TypeScript**

```bash
npm i @superdurable/dex-agents-vercel-ai @superdurable/dex-agents @superdurable/dex ai @ai-sdk/openai zod
```

例子: [agents/typescript/vercel-ai/README.md](https://github.com/superdurable/dex/tree/main/agents/typescript/vercel-ai/README.md)

<a id="the-example"></a>

## Example

这个 example 就是每个 framework 页面共用的[订单客服 Agent](https://docs.superdurable.io/zh-Hans/agent-frameworks.md#the-examples)。

Flow type 是 **VercelAiOrderSupportAgent**（TypeScript）。完整 example 位于 [examples/typescript/src/products/agent-harnesses](https://github.com/superdurable/dex/tree/main/examples/typescript/src/products/agent-harnesses)。

<a id="agent-and-tools"></a>

### Agent 与 tool

定义中包含模型、instructions、AI SDK tool 和 tool policy。Dex 根据 policy 决定是否需要审批。

**TypeScript**

```typescript
export function createVercelAiOrderSupportFlow(
  orderDesk: OrderDesk,
  model: LanguageModel,
  slack: Connection | undefined,
): AgentSessionFlow {
  return createVercelAiAgentSessionFlow(
    {
      model,
      instructions: orderSupportInstructions,
      tools: {
        look_up_order: tool({
          description: "Look up one order by its order ID and return its items and status.",
          inputSchema: z.object({ order_id: z.string() }),
          execute: ({ order_id }) => orderDesk.describeOrder(order_id),
        }),
        cancel_order: tool({
          description: "Cancel one order that has not shipped yet.",
          inputSchema: z.object({ order_id: z.string(), reason: z.string() }),
          execute: ({ order_id, reason }) => orderDesk.cancelOrder(order_id, reason),
        }),
      },
      toolPolicies: {
        look_up_order: { effect: "read" },
        cancel_order: { effect: "non_idempotent_write" },
      },
      toolSources: createSlackConnectorToolSources(slack),
    },
    vercelAiOrderSupportFlowType,
  );
}
```

例子: [examples/typescript/src/products/agent-harnesses/vercel-ai/vercel-ai-order-support-flow.ts](https://github.com/superdurable/dex/tree/main/examples/typescript/src/products/agent-harnesses/vercel-ai/vercel-ai-order-support-flow.ts)

<a id="worker"></a>

### Worker

一个 Worker 为 example 中所有 harness 的 session Flow 提供服务。每个启动 session 或调用其 RPC 的进程，都要构建同一个 session Flow。

**TypeScript**

```typescript
// One Slack connection for the agent examples; without it they offer no Slack tools.
const slack = await openSlackConnectionFromProject();
// ...
// One order desk for the four agent harness examples; each harness registers its own session Flow type.
const orderDesk = new OrderDesk();
// ...
const vercelAiOrderSupportFlow = createVercelAiOrderSupportFlow(orderDesk, loadVercelAiOrderSupportModel(), slack);
```

例子: [examples/typescript/src/registry.ts](https://github.com/superdurable/dex/tree/main/examples/typescript/src/registry.ts)

<a id="session-routes"></a>

### Session route

每个 harness 都提供这同一组 route。这些 route 和 session token 的说明见 [Session route](https://docs.superdurable.io/zh-Hans/agent-frameworks.md#session-routes)。

**TypeScript**

```typescript
  router.post("/sessions", async (request, response) => {
    const initialMessage = optionalString(requireBody(request), "message");
    // The server picks the session ID, so a caller cannot name another session.
    const sessionId = `order-support-${randomUUID()}`;
    await startAgentSession(client, flow, sessionId, { initialMessage }, { timeoutMs: 30 * DAY_MS });
    response.json({ session_id: sessionId, session_token: issueSessionToken(sessionId) });
  });
  router.get("/sessions/:id/snapshot", async (request, response) => {
    response.json(convertAgentSnapshotToJson(await client.invokeRPC(flow.getAgentSnapshot, sessionIdOf(request))));
  });
  router.post("/sessions/:id/messages", async (request, response) => {
    const sessionId = sessionIdOf(request);
    const message = { text: requireString(requireBody(request), "text") };
    sendAccepted(response, await client.invokeRPC(flow.sendUserMessage, sessionId, message));
  });
  router.post("/sessions/:id/interrupts/:interruptId/approve", async (request, response) => {
    const sessionId = sessionIdOf(request);
    const approval = { interruptId: String(request.params.interruptId), approved: true, rejectionMessage: null };
    sendAccepted(response, await client.invokeRPC(flow.resolveInterrupt, sessionId, approval));
  });
```

例子: [examples/typescript/src/products/agent-harnesses/order-support/order-support-routes.ts](https://github.com/superdurable/dex/tree/main/examples/typescript/src/products/agent-harnesses/order-support/order-support-routes.ts)

<a id="run-it-locally"></a>

## 在本地运行

<a id="start-dex-and-the-example"></a>

### 启动 Dex 与 example

在第一个终端中，先渲染 session graph，再启动 Dex。Dex Web 启动时读取这个 graph，用它显示 session 和审批控件。

**TypeScript**

```bash
cd examples/typescript
npm install
mkdir -p definitions
npx dex-agents-session-graph --flow-type VercelAiOrderSupportAgent \
  --permission order-support.operate --out definitions/vercel-ai-sdk.json
dexcli dev --flow-rendering-dir ./definitions
```

例子: [examples/typescript/src/products/agent-harnesses/README.md](https://github.com/superdurable/dex/tree/main/examples/typescript/src/products/agent-harnesses/README.md)

**dexcli dev** 会一直在前台运行。在第二个终端中启动 example：

**TypeScript**

```bash
cd examples/typescript
OPENAI_API_KEY=... npm start
```

例子: [examples/typescript/src/products/agent-harnesses/README.md](https://github.com/superdurable/dex/tree/main/examples/typescript/src/products/agent-harnesses/README.md)

应用监听 **127.0.0.1:8080**。Dex Web 位于 **127.0.0.1:8802**。

这个 example 没有 demo model。它通过 **@ai-sdk/openai** 调用 OpenAI，因此需要 **OPENAI_API_KEY**。

| 变量                        | 默认值            | 含义                       |
| ------------------------- | -------------- | ------------------------ |
| **DEX_VERCEL_AI_MODEL**   | **gpt-5-mini** | OpenAI 模型名称。             |
| **OPENAI_API_KEY**        | 无；必填           | Worker 的 OpenAI API key。 |
| **DEX_AGENT_SESSION_KEY** | 随机 key         | 签发 session token 的 key。  |

<a id="call-the-session"></a>

### 调用 session

启动一个 session，让 Agent 取消订单 1002，然后批准这次调用：

**TypeScript**

```bash
base=http://127.0.0.1:8080/products/agent-harnesses/vercel-ai
session=$(curl -s -X POST "$base/sessions" -H 'Content-Type: application/json' \
  -d '{"message": "Please cancel order 1002"}')
id=$(echo "$session" | jq -r .session_id)
token=$(echo "$session" | jq -r .session_token)
until interrupt=$(curl -s "$base/sessions/$id/snapshot" -H "X-Agent-Session-Token: $token" \
  | jq -re '.pending_interrupts[0].interrupt_id'); do sleep 1; done
curl -s -X POST "$base/sessions/$id/interrupts/$interrupt/approve" -H "X-Agent-Session-Token: $token"
```

例子: [examples/typescript/src/products/agent-harnesses/README.md](https://github.com/superdurable/dex/tree/main/examples/typescript/src/products/agent-harnesses/README.md)

循环会反复读取 snapshot，直到 Agent 请求运行 **cancel_order**。这次调用先等待审批，批准后只运行一次。再读一次 snapshot 即可看到 Agent 的回答。

<a id="approve-in-dex-web"></a>

### 在 Dex Web 中审批

也可以不执行最后一条命令，改在 Dex Web 中审批，步骤见 [在 Dex Web 中审批](https://docs.superdurable.io/zh-Hans/agent-frameworks.md#approve-in-dex-web)。

<a id="connect-connectors"></a>

## 连接 Connector

Agent 可以把 [Connector](https://docs.superdurable.io/zh-Hans/concepts/connector.md) 的 operation 当作 tool 调用，见 [把 Connector 用作 tool](https://docs.superdurable.io/zh-Hans/agent-frameworks.md#connectors-as-tools)。这个 example 加入了 Slack tool **slack_post_thread_reply**，用来在 Slack thread 中回复。

<a id="offer-connector-tools"></a>

### 提供 tool

安装 **@superdurable/connectors-sdk** 以及 Connector 包，例如 **@superdurable/connector-slack**。安装命令见 [Connector tools](https://docs.superdurable.io/zh-Hans/reference/agents/connector-tools.md#install)。

Worker 从项目配置中打开 Slack connection **support-slack**。**connectorTool** 把 operation 变成 tool，由 **ConnectorToolSource** 持有。没有这个 connection 时，agent 不会得到 Slack tool。

**TypeScript**

```typescript
export async function openSlackConnectionFromProject(
  environment: NodeJS.ProcessEnv = process.env,
): Promise<Connection | undefined> {
  // ...
  const project = await loadFromEnvironment(environment);
  // ...
  return newProjectConnection(project, slackConnectionName);
}

// ...

export function createSlackConnectorToolSources(slack: Connection | undefined): ToolSource[] {
  if (slack === undefined) {
    return [];
  }
  return [
    new ConnectorToolSource([
      connectorTool(slack.operations.postThreadReply(), slack.reference, {
        manifest,
        inputValidator: PostThreadReplyInputSchema,
        // The manifest does not ask for approval; this agent asks before every post.
        requiresApproval: true,
      }),
    ]),
  ];
}
```

例子: [examples/typescript/src/shared/slack-connector-tools.ts](https://github.com/superdurable/dex/tree/main/examples/typescript/src/shared/slack-connector-tools.ts)

Worker 在启动时打开一次 connection，每个 agent 都使用它：

**TypeScript**

```typescript
// One Slack connection for the agent examples; without it they offer no Slack tools.
const slack = await openSlackConnectionFromProject();
```

例子: [examples/typescript/src/registry.ts](https://github.com/superdurable/dex/tree/main/examples/typescript/src/registry.ts)

Agent 把 tool source 和自己的 tool 一起传入：

**TypeScript**

```typescript
  return createVercelAiAgentSessionFlow(
    {
      // ...
      toolSources: createSlackConnectorToolSources(slack),
    },
    vercelAiOrderSupportFlowType,
  );
```

例子: [examples/typescript/src/products/agent-harnesses/vercel-ai/vercel-ai-order-support-flow.ts](https://github.com/superdurable/dex/tree/main/examples/typescript/src/products/agent-harnesses/vercel-ai/vercel-ai-order-support-flow.ts)

<a id="connector-tool-calls"></a>

### 调用如何运行

每次调用 **slack_post_thread_reply** 都要先经人批准，然后在自己的 **CallSingleAttemptTool** Step 中只运行一次。原因见 [Slack Connector tool](https://docs.superdurable.io/zh-Hans/agent-frameworks.md#slack-connector-tool)。

Adapter 在 agent 的 tool 之后提供 Connector tool，它们是没有 **execute** 的 AI SDK tool。调用从不在模型调用内运行。AI SDK 的 **needsApproval** 对它不起作用；审批由 source 决定。

<a id="configure-connector-connection"></a>

### 配置 connection

Worker 从哪里找到 connection **support-slack**、Slack app 需要什么，见 [Connection](https://docs.superdurable.io/zh-Hans/reference/agents/connector-tools.md#connector-tools-connection)。

试用时，先设置 **DEX_CONNECTOR_CONFIG_FILE** 再启动 example。然后让 agent 在 Slack thread 中回复，并给出 channel ID 和 thread timestamp：

```text
Reply in Slack thread C0123456789 1710000000.000100: Order 1002 ships on Thursday.
```

这次发帖和 **cancel_order** 一样，会先等待审批。

<a id="limits"></a>

## 限制

- **RunModel** 中发生崩溃时，会重跑那次模型调用以及其中由 provider 执行的 tool。
- Tool 的 **execute** 会收到 **toolCallId** 和 **abortSignal**。它的 **messages** 为空，**context** 为 **undefined**。
- 审批由 Dex 负责。不开放 AI SDK 的 **toolApproval**，**needsApproval** 函数会被拒绝。由 provider 执行的 tool 永远不会等待审批。
- 每个 tool 都在 Dex 中运行，因此没有 **execute** 的 tool，例如浏览器端 tool，会被拒绝。
- Tool 的输入和输出必须能经受 JSON 序列化。
- 不开放 **prepareStep**、**activeTools**、**output** 和 stream transform。

Adapter README 列出了全部限制。Server 的限制参见 [Limits for long sessions](https://docs.superdurable.io/zh-Hans/reference/agents/session-limits.md)。

<a id="related"></a>

## 相关内容

- [Agent Framework](https://docs.superdurable.io/zh-Hans/agent-frameworks.md)
- [Agents reference](https://docs.superdurable.io/zh-Hans/reference/agents.md)
- [TypeScript adapter README](https://github.com/superdurable/dex/tree/main/agents/typescript/vercel-ai)
