---
title: "使用模式"
description: "streamText、generateText、多步骤代理、RAG 和多个模型——每一种常见的 AI SDK 模式都已接入 evlog。"
canonical_url: "https://evlog.zhcndoc.com/use-cases/ai-sdk/usage"
---
# 使用模式

> streamText、generateText、多步骤代理、RAG 和多个模型——每一种常见的 AI SDK 模式都已接入 evlog。

下面的每种模式都使用相同的 `createAILogger(log)` 设置。用 `ai.wrap()` 包裹模型，middleware 会自动在 wide event 上累积 token、工具和耗时。

在 Next.js、Nuxt/Nitro、SvelteKit、Hono、React Router 和 oRPC 中，evlog 会延迟流式响应的 wide-event 发送（例如 `text/event-stream` 和 AI SDK UI 流）直到正文完成，因此较晚的 `ai` 元数据会保留在同一个请求事件上。

## streamText

最常见的模式是具有完整可观测性的流式聊天：

<framework-tabs>

```typescript [Nuxt]server/api/chat.post.ts
import { useLogger } from 'evlog'
import { streamText } from 'ai'
import { createAILogger } from 'evlog/ai'

export default defineEventHandler(async (event) => {
  const log = useLogger(event)

  const ai = createAILogger(log)
  const { messages } = await readBody(event)

  log.set({ action: 'chat', messagesCount: messages.length })

  const result = streamText({
    model: ai.wrap('anthropic/claude-sonnet-4.6'),
    messages,
    onFinish: ({ text }) => {
      saveConversation(text)
    },
  })

  return result.toTextStreamResponse()
})
```

```typescript [Nitro]routes/api/chat.post.ts
import { defineHandler } from 'nitro/h3'
import { useLogger } from 'evlog/nitro/v3'
import { streamText } from 'ai'
import { createAILogger } from 'evlog/ai'

export default defineHandler(async (event) => {
  const log = useLogger(event)

  const ai = createAILogger(log)
  const { messages } = await readBody(event)

  log.set({ action: 'chat', messagesCount: messages.length })

  const result = streamText({
    model: ai.wrap('anthropic/claude-sonnet-4.6'),
    messages,
    onFinish: ({ text }) => {
      saveConversation(text)
    },
  })

  return result.toTextStreamResponse()
})
```

```typescript [Next.js]app/api/chat/route.ts
import { withEvlog, useLogger } from '@/lib/evlog'
import { streamText } from 'ai'
import { createAILogger } from 'evlog/ai'

export const POST = withEvlog(async (request: Request) => {
  const log = useLogger()

  const ai = createAILogger(log)
  const { messages } = await request.json()

  log.set({ action: 'chat', messagesCount: messages.length })

  const result = streamText({
    model: ai.wrap('anthropic/claude-sonnet-4.6'),
    messages,
    onFinish: ({ text }) => {
      saveConversation(text)
    },
  })

  return result.toTextStreamResponse()
})
```

```typescript [Hono]src/index.ts
import { streamText } from 'ai'
import { createAILogger } from 'evlog/ai'

app.post('/api/chat', async (c) => {
  const log = c.get('log')

  const ai = createAILogger(log)
  const { messages } = await c.req.json()

  log.set({ action: 'chat', messagesCount: messages.length })

  const result = streamText({
    model: ai.wrap('anthropic/claude-sonnet-4.6'),
    messages,
    onFinish: ({ text }) => {
      saveConversation(text)
    },
  })

  return result.toTextStreamResponse()
})
```

```typescript [Express]src/index.ts
import { streamText } from 'ai'
import { createAILogger } from 'evlog/ai'

app.post('/api/chat', async (req, res) => {
  const log = req.log

  const ai = createAILogger(log)
  const { messages } = req.body

  log.set({ action: 'chat', messagesCount: messages.length })

  const result = streamText({
    model: ai.wrap('anthropic/claude-sonnet-4.6'),
    messages,
    onFinish: ({ text }) => {
      saveConversation(text)
    },
  })

  result.pipeTextStreamToResponse(res)
})
```

</framework-tabs>

middleware 不会触碰你的 `onFinish` 回调，因此你的代码会照常运行。

## 生成文本

同步生成。middleware 会自动捕获结果：

<framework-tabs>

```typescript [Nuxt]server/api/summarize.post.ts
import { useLogger } from 'evlog'
import { generateText } from 'ai'
import { createAILogger } from 'evlog/ai'

export default defineEventHandler(async (event) => {
  const log = useLogger(event)

  const ai = createAILogger(log)

  const result = await generateText({
    model: ai.wrap('anthropic/claude-sonnet-4.6'),
    prompt: '总结这份文档',
  })

  return { text: result.text }
})
```

```typescript [Nitro]routes/api/summarize.post.ts
import { defineHandler } from 'nitro/h3'
import { useLogger } from 'evlog/nitro/v3'
import { generateText } from 'ai'
import { createAILogger } from 'evlog/ai'

export default defineHandler(async (event) => {
  const log = useLogger(event)

  const ai = createAILogger(log)

  const result = await generateText({
    model: ai.wrap('anthropic/claude-sonnet-4.6'),
    prompt: 'Summarize this document',
  })

  return { text: result.text }
})
```

```typescript [Next.js]app/api/summarize/route.ts
import { withEvlog, useLogger } from '@/lib/evlog'
import { generateText } from 'ai'
import { createAILogger } from 'evlog/ai'

export const POST = withEvlog(async () => {
  const log = useLogger()

  const ai = createAILogger(log)

  const result = await generateText({
    model: ai.wrap('anthropic/claude-sonnet-4.6'),
    prompt: 'Summarize this document',
  })

  return Response.json({ text: result.text })
})
```

```typescript [Hono]src/index.ts
import { generateText } from 'ai'
import { createAILogger } from 'evlog/ai'

app.post('/api/summarize', async (c) => {
  const log = c.get('log')

  const ai = createAILogger(log)

  const result = await generateText({
    model: ai.wrap('anthropic/claude-sonnet-4.6'),
    prompt: 'Summarize this document',
  })

  return c.json({ text: result.text })
})
```

```typescript [Express]src/index.ts
import { generateText } from 'ai'
import { createAILogger } from 'evlog/ai'

app.post('/api/summarize', async (req, res) => {
  const log = req.log

  const ai = createAILogger(log)

  const result = await generateText({
    model: ai.wrap('anthropic/claude-sonnet-4.6'),
    prompt: 'Summarize this document',
  })

  return res.json({ text: result.text })
})
```

</framework-tabs>

## 跟踪多步骤 agent

middleware 会自动为每一步触发。步骤、工具调用和 token 会在整个 agent 循环中累积：

<framework-tabs>

```typescript [Nuxt]server/api/agent.post.ts
import { useLogger } from 'evlog'
import { ToolLoopAgent, createAgentUIStreamResponse, stepCountIs } from 'ai'
import { createAILogger } from 'evlog/ai'

export default defineEventHandler(async (event) => {
  const log = useLogger(event)

  const { messages } = await readBody(event)
  const ai = createAILogger(log, {
    toolInputs: { maxLength: 500 },
  })

  const agent = new ToolLoopAgent({
    model: ai.wrap('anthropic/claude-sonnet-4.6'),
    tools: { searchWeb, queryDatabase },
    stopWhen: stepCountIs(5),
  })

  return createAgentUIStreamResponse({
    agent,
    uiMessages: messages,
  })
})
```

```typescript [Nitro]routes/api/agent.post.ts
import { defineHandler } from 'nitro/h3'
import { useLogger } from 'evlog/nitro/v3'
import { ToolLoopAgent, createAgentUIStreamResponse, stepCountIs } from 'ai'
import { createAILogger } from 'evlog/ai'

export default defineHandler(async (event) => {
  const log = useLogger(event)

  const { messages } = await readBody(event)
  const ai = createAILogger(log, {
    toolInputs: { maxLength: 500 },
  })

  const agent = new ToolLoopAgent({
    model: ai.wrap('anthropic/claude-sonnet-4.6'),
    tools: { searchWeb, queryDatabase },
    stopWhen: stepCountIs(5),
  })

  return createAgentUIStreamResponse({
    agent,
    uiMessages: messages,
  })
})
```

```typescript [Next.js]app/api/agent/route.ts
import { withEvlog, useLogger } from '@/lib/evlog'
import { ToolLoopAgent, createAgentUIStreamResponse, stepCountIs } from 'ai'
import { createAILogger } from 'evlog/ai'

export const POST = withEvlog(async (request: Request) => {
  const log = useLogger()

  const { messages } = await request.json()
  const ai = createAILogger(log, {
    toolInputs: { maxLength: 500 },
  })

  const agent = new ToolLoopAgent({
    model: ai.wrap('anthropic/claude-sonnet-4.6'),
    tools: { searchWeb, queryDatabase },
    stopWhen: stepCountIs(5),
  })

  return createAgentUIStreamResponse({
    agent,
    uiMessages: messages,
  })
})
```

```typescript [Hono]src/index.ts
import { ToolLoopAgent, createAgentUIStreamResponse, stepCountIs } from 'ai'
import { createAILogger } from 'evlog/ai'

app.post('/api/agent', async (c) => {
  const log = c.get('log')

  const { messages } = await c.req.json()
  const ai = createAILogger(log, {
    toolInputs: { maxLength: 500 },
  })

  const agent = new ToolLoopAgent({
    model: ai.wrap('anthropic/claude-sonnet-4.6'),
    tools: { searchWeb, queryDatabase },
    stopWhen: stepCountIs(5),
  })

  return createAgentUIStreamResponse({
    agent,
    uiMessages: messages,
  })
})
```

```typescript [Express]src/index.ts
import { ToolLoopAgent, pipeAgentUIStreamToResponse, stepCountIs } from 'ai'
import { createAILogger } from 'evlog/ai'

app.post('/api/agent', async (req, res) => {
  const log = req.log

  const { messages } = req.body
  const ai = createAILogger(log, {
    toolInputs: { maxLength: 500 },
  })

  const agent = new ToolLoopAgent({
    model: ai.wrap('anthropic/claude-sonnet-4.6'),
    tools: { searchWeb, queryDatabase },
    stopWhen: stepCountIs(5),
  })

  await pipeAgentUIStreamToResponse({
    response: res,
    agent,
    uiMessages: messages,
  })
})
```

</framework-tabs>

3 步代理运行后的宽事件：

```json [Wide Event]
{
  "ai": {
    "calls": 3,
    "steps": 3,
    "model": "claude-sonnet-4.6",
    "provider": "anthropic",
    "inputTokens": 4500,
    "outputTokens": 1200,
    "totalTokens": 5700,
    "finishReason": "stop",
    "toolCalls": [
      { "name": "searchWeb", "input": { "query": "TypeScript 6.0 features" } },
      { "name": "queryDatabase", "input": { "sql": "SELECT * FROM docs WHERE topic = 'typescript'" } },
      { "name": "searchWeb", "input": { "query": "TypeScript 6.0 release date" } }
    ],
    "responseId": "msg_01XFDUDYJgAACzvnptvVoYEL",
    "stepsUsage": [
      { "model": "claude-sonnet-4.6", "inputTokens": 1200, "outputTokens": 300, "toolCalls": ["searchWeb"] },
      { "model": "claude-sonnet-4.6", "inputTokens": 1500, "outputTokens": 400, "toolCalls": ["queryDatabase", "searchWeb"] },
      { "model": "claude-sonnet-4.6", "inputTokens": 1800, "outputTokens": 500 }
    ],
    "msToFirstChunk": 312,
    "msToFinish": 8200,
    "tokensPerSecond": 146
  }
}
```

<tip>

结合 [`createEvlogIntegration`](https://evlog.zhcndoc.com/use-cases/ai-sdk/telemetry) 一起使用，还可以捕获每个工具的执行耗时以及 agent 的总墙钟时间。

</tip>

## RAG（嵌入 + 生成）

嵌入模型使用不同的类型，不能通过中间件包裹。请改用 `captureEmbed`：

<framework-tabs>

```typescript [Nuxt]server/api/rag.post.ts
import { useLogger } from 'evlog'
import { embed, generateText } from 'ai'
import { createAILogger } from 'evlog/ai'

export default defineEventHandler(async (event) => {
  const log = useLogger(event)

  const ai = createAILogger(log)

  const { embedding, usage } = await embed({
    model: openai.embedding('text-embedding-3-small'),
    value: query,
  })
  ai.captureEmbed({
    usage,
    model: 'text-embedding-3-small',
    dimensions: 1536,
  })

  const docs = await findSimilar(embedding)

  const result = await generateText({
    model: ai.wrap('anthropic/claude-sonnet-4.6'),
    prompt: buildPrompt(docs),
  })

  return { text: result.text }
})
```

```typescript [Nitro]routes/api/rag.post.ts
import { defineHandler } from 'nitro/h3'
import { useLogger } from 'evlog/nitro/v3'
import { embed, generateText } from 'ai'
import { createAILogger } from 'evlog/ai'

export default defineHandler(async (event) => {
  const log = useLogger(event)

  const ai = createAILogger(log)

  const { embedding, usage } = await embed({
    model: openai.embedding('text-embedding-3-small'),
    value: query,
  })
  ai.captureEmbed({
    usage,
    model: 'text-embedding-3-small',
    dimensions: 1536,
  })

  const docs = await findSimilar(embedding)

  const result = await generateText({
    model: ai.wrap('anthropic/claude-sonnet-4.6'),
    prompt: buildPrompt(docs),
  })

  return { text: result.text }
})
```

```typescript [Next.js]app/api/rag/route.ts
import { withEvlog, useLogger } from '@/lib/evlog'
import { embed, generateText } from 'ai'
import { createAILogger } from 'evlog/ai'

export const POST = withEvlog(async () => {
  const log = useLogger()

  const ai = createAILogger(log)

  const { embedding, usage } = await embed({
    model: openai.embedding('text-embedding-3-small'),
    value: query,
  })
  ai.captureEmbed({
    usage,
    model: 'text-embedding-3-small',
    dimensions: 1536,
  })

  const docs = await findSimilar(embedding)

  const result = await generateText({
    model: ai.wrap('anthropic/claude-sonnet-4.6'),
    prompt: buildPrompt(docs),
  })

  return Response.json({ text: result.text })
})
```

```typescript [Hono]src/index.ts
import { embed, generateText } from 'ai'
import { createAILogger } from 'evlog/ai'

app.post('/api/rag', async (c) => {
  const log = c.get('log')

  const ai = createAILogger(log)

  const { embedding, usage } = await embed({
    model: openai.embedding('text-embedding-3-small'),
    value: query,
  })
  ai.captureEmbed({
    usage,
    model: 'text-embedding-3-small',
    dimensions: 1536,
  })

  const docs = await findSimilar(embedding)

  const result = await generateText({
    model: ai.wrap('anthropic/claude-sonnet-4.6'),
    prompt: buildPrompt(docs),
  })

  return c.json({ text: result.text })
})
```

```typescript [Express]src/index.ts
import { embed, generateText } from 'ai'
import { createAILogger } from 'evlog/ai'

app.post('/api/rag', async (req, res) => {
  const log = req.log

  const ai = createAILogger(log)

  const { embedding, usage } = await embed({
    model: openai.embedding('text-embedding-3-small'),
    value: query,
  })
  ai.captureEmbed({
    usage,
    model: 'text-embedding-3-small',
    dimensions: 1536,
  })

  const docs = await findSimilar(embedding)

  const result = await generateText({
    model: ai.wrap('anthropic/claude-sonnet-4.6'),
    prompt: buildPrompt(docs),
  })

  return res.json({ text: result.text })
})
```

</framework-tabs>

对于 `embedMany`，传入批量数量：

```typescript
const { embeddings, usage } = await embedMany({
  model: openai.embedding('text-embedding-3-small'),
  values: documents,
})
ai.captureEmbed({ usage, model: 'text-embedding-3-small', count: documents.length })
```

## 包裹多个模型

分别包裹每个模型。它们共享同一个累加器。当使用多个模型时，wide event 会同时包含 `model`（最后使用的模型）和 `models`（所有不重复的模型）：

<code-group>

```typescript [server/api/chat.post.ts]
const ai = createAILogger(log)

const fast = ai.wrap('anthropic/claude-haiku-4.5')
const smart = ai.wrap('anthropic/claude-sonnet-4.6')

const classification = await generateText({ model: fast, prompt: classifyPrompt })
const response = await generateText({ model: smart, prompt: detailedPrompt })
```

```json [Wide Event]
{
  "ai": {
    "calls": 2,
    "model": "claude-sonnet-4.6",
    "models": ["claude-haiku-4.5", "claude-sonnet-4.6"],
    "provider": "anthropic",
    "inputTokens": 450,
    "outputTokens": 300,
    "totalTokens": 750
  }
}
```

</code-group>

## 传入来自任意 provider 的模型对象

`wrap()` 接受来自 provider SDK 的模型对象：包括 `LanguageModelV3`（AI SDK v6）和 `LanguageModelV4`（AI SDK v7）：

```typescript [server/api/chat.post.ts]
import { anthropic } from '@ai-sdk/anthropic'

const model = ai.wrap(anthropic('claude-sonnet-4.6'))
```

---

- [概览](https://evlog.zhcndoc.com/use-cases/ai-sdk/overview)
- [选项](https://evlog.zhcndoc.com/use-cases/ai-sdk/options)


## Sitemap

See the full [sitemap](https://evlog.zhcndoc.com/sitemap.md) for all pages.
