convex-agent

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Convex Agent Component

Convex Agent 组件

Build AI agents with persistent message history, tool calling, real-time streaming, and durable workflows.
构建具备持久消息历史、工具调用、实时流式输出和可靠工作流的AI Agent。

Installation

安装

bash
npm install @convex-dev/agent
typescript
// convex/convex.config.ts
import { defineApp } from 'convex/server';
import agent from '@convex-dev/agent/convex.config';

const app = defineApp();
app.use(agent);
export default app;
Run
npx convex dev
to generate component code before defining agents.
bash
npm install @convex-dev/agent
typescript
// convex/convex.config.ts
import { defineApp } from 'convex/server';
import agent from '@convex-dev/agent/convex.config';

const app = defineApp();
app.use(agent);
export default app;
在定义Agent之前,运行
npx convex dev
生成组件代码。

Core Concepts

核心概念

Agent Definition

Agent 定义

typescript
// convex/agents.ts
import { Agent } from '@convex-dev/agent';
import { openai } from '@ai-sdk/openai';
import { components } from './_generated/api';

const supportAgent = new Agent(components.agent, {
  name: 'Support Agent',
  languageModel: openai.chat('gpt-4o-mini'),
  textEmbeddingModel: openai.embedding('text-embedding-3-small'), // For vector search
  instructions: 'You are a helpful support assistant.',
  tools: { lookupAccount, createTicket },
  stopWhen: stepCountIs(10) // Or use maxSteps: 10
});
typescript
// convex/agents.ts
import { Agent } from '@convex-dev/agent';
import { openai } from '@ai-sdk/openai';
import { components } from './_generated/api';

const supportAgent = new Agent(components.agent, {
  name: 'Support Agent',
  languageModel: openai.chat('gpt-4o-mini'),
  textEmbeddingModel: openai.embedding('text-embedding-3-small'), // For vector search
  instructions: 'You are a helpful support assistant.',
  tools: { lookupAccount, createTicket },
  stopWhen: stepCountIs(10) // Or use maxSteps: 10
});

Basic Usage (Two Approaches)

基础用法(两种方式)

Approach 1: Direct generation (simpler)
typescript
import { createThread } from '@convex-dev/agent';

export const chat = action({
  args: { prompt: v.string() },
  handler: async (ctx, { prompt }) => {
    const threadId = await createThread(ctx, components.agent);
    const result = await agent.generateText(ctx, { threadId }, { prompt });
    return result.text;
  }
});
Approach 2: Thread object (more features)
typescript
export const chat = action({
  args: { prompt: v.string() },
  handler: async (ctx, { prompt }) => {
    const { threadId, thread } = await agent.createThread(ctx);
    const result = await thread.generateText({ prompt });
    return { threadId, text: result.text };
  }
});
方式1:直接生成(更简单)
typescript
import { createThread } from '@convex-dev/agent';

export const chat = action({
  args: { prompt: v.string() },
  handler: async (ctx, { prompt }) => {
    const threadId = await createThread(ctx, components.agent);
    const result = await agent.generateText(ctx, { threadId }, { prompt });
    return result.text;
  }
});
方式2:线程对象(功能更丰富)
typescript
export const chat = action({
  args: { prompt: v.string() },
  handler: async (ctx, { prompt }) => {
    const { threadId, thread } = await agent.createThread(ctx);
    const result = await thread.generateText({ prompt });
    return { threadId, text: result.text };
  }
});

Continue Existing Thread

继续现有对话线程

typescript
export const continueChat = action({
  args: { threadId: v.string(), prompt: v.string() },
  handler: async (ctx, { threadId, prompt }) => {
    // Message history included automatically
    const result = await agent.generateText(ctx, { threadId }, { prompt });
    return result.text;
  }
});
typescript
export const continueChat = action({
  args: { threadId: v.string(), prompt: v.string() },
  handler: async (ctx, { threadId, prompt }) => {
    // 消息历史会自动包含在内
    const result = await agent.generateText(ctx, { threadId }, { prompt });
    return result.text;
  }
});

Asynchronous Pattern (Recommended)

异步模式(推荐)

Best practice: save message in mutation, generate response asynchronously.
typescript
import { saveMessage } from '@convex-dev/agent';

// Step 1: Mutation saves message and schedules generation
export const sendMessage = mutation({
  args: { threadId: v.string(), prompt: v.string() },
  handler: async (ctx, { threadId, prompt }) => {
    const { messageId } = await saveMessage(ctx, components.agent, {
      threadId,
      prompt
    });
    await ctx.scheduler.runAfter(0, internal.chat.generateResponse, {
      threadId,
      promptMessageId: messageId
    });
    return messageId;
  }
});

// Step 2: Action generates response
export const generateResponse = internalAction({
  args: { threadId: v.string(), promptMessageId: v.string() },
  handler: async (ctx, { threadId, promptMessageId }) => {
    await agent.generateText(ctx, { threadId }, { promptMessageId });
  }
});

// Shorthand for Step 2:
export const generateResponse = agent.asTextAction();
最佳实践:在mutation中保存消息,异步生成响应。
typescript
import { saveMessage } from '@convex-dev/agent';

// 步骤1:Mutation保存消息并调度生成任务
export const sendMessage = mutation({
  args: { threadId: v.string(), prompt: v.string() },
  handler: async (ctx, { threadId, prompt }) => {
    const { messageId } = await saveMessage(ctx, components.agent, {
      threadId,
      prompt
    });
    await ctx.scheduler.runAfter(0, internal.chat.generateResponse, {
      threadId,
      promptMessageId: messageId
    });
    return messageId;
  }
});

// 步骤2:Action生成响应
export const generateResponse = internalAction({
  args: { threadId: v.string(), promptMessageId: v.string() },
  handler: async (ctx, { threadId, promptMessageId }) => {
    await agent.generateText(ctx, { threadId }, { promptMessageId });
  }
});

// 步骤2的简写形式:
export const generateResponse = agent.asTextAction();

Generation Methods

生成方法

typescript
// Text generation
const result = await agent.generateText(ctx, { threadId }, { prompt });

// Structured output
const result = await agent.generateObject(
  ctx,
  { threadId },
  {
    prompt: 'Extract user info',
    schema: z.object({ name: z.string(), email: z.string() })
  }
);

// Stream text (see STREAMING.md)
const result = await agent.streamText(ctx, { threadId }, { prompt });

// Multiple messages
const result = await agent.generateText(
  ctx,
  { threadId },
  {
    messages: [
      { role: 'user', content: 'Context message' },
      { role: 'user', content: 'Actual question' }
    ]
  }
);
typescript
// 文本生成
const result = await agent.generateText(ctx, { threadId }, { prompt });

// 结构化输出
const result = await agent.generateObject(
  ctx,
  { threadId },
  {
    prompt: 'Extract user info',
    schema: z.object({ name: z.string(), email: z.string() })
  }
);

// 流式文本输出(参见STREAMING.md)
const result = await agent.streamText(ctx, { threadId }, { prompt });

// 多条消息
const result = await agent.generateText(
  ctx,
  { threadId },
  {
    messages: [
      { role: 'user', content: 'Context message' },
      { role: 'user', content: 'Actual question' }
    ]
  }
);

Querying Messages

查询消息

typescript
import { listUIMessages, paginationOptsValidator } from '@convex-dev/agent';

export const listMessages = query({
  args: { threadId: v.string(), paginationOpts: paginationOptsValidator },
  handler: async (ctx, args) => {
    return await listUIMessages(ctx, components.agent, args);
  }
});
React Hook:
typescript
import { useUIMessages } from '@convex-dev/agent/react';

const { results, status, loadMore } = useUIMessages(
  api.chat.listMessages,
  { threadId },
  { initialNumItems: 20 }
);
typescript
import { listUIMessages, paginationOptsValidator } from '@convex-dev/agent';

export const listMessages = query({
  args: { threadId: v.string(), paginationOpts: paginationOptsValidator },
  handler: async (ctx, args) => {
    return await listUIMessages(ctx, components.agent, args);
  }
});
React Hook:
typescript
import { useUIMessages } from '@convex-dev/agent/react';

const { results, status, loadMore } = useUIMessages(
  api.chat.listMessages,
  { threadId },
  { initialNumItems: 20 }
);

Agent Configuration Options

Agent 配置选项

typescript
const agent = new Agent(components.agent, {
  name: 'Agent Name',
  languageModel: openai.chat('gpt-4o-mini'),
  textEmbeddingModel: openai.embedding('text-embedding-3-small'),
  instructions: 'System prompt...',
  tools: {
    /* tools */
  },
  stopWhen: stepCountIs(10), // Or maxSteps: 10

  // Context options (see CONTEXT.md)
  contextOptions: {
    recentMessages: 100,
    excludeToolMessages: true,
    searchOptions: { limit: 10, textSearch: false, vectorSearch: false }
  },

  // Storage options
  storageOptions: { saveMessages: 'promptAndOutput' }, // 'all' | 'none'

  // Handlers
  usageHandler: async (ctx, { usage, model, provider, agentName }) => {},
  contextHandler: async (ctx, { allMessages }) => allMessages,
  rawRequestResponseHandler: async (ctx, { request, response }) => {},

  // Call settings
  callSettings: { maxRetries: 3, temperature: 1.0 }
});
typescript
const agent = new Agent(components.agent, {
  name: 'Agent Name',
  languageModel: openai.chat('gpt-4o-mini'),
  textEmbeddingModel: openai.embedding('text-embedding-3-small'),
  instructions: 'System prompt...',
  tools: {
    /* tools */
  },
  stopWhen: stepCountIs(10), // Or use maxSteps: 10

  // 上下文选项(参见CONTEXT.md)
  contextOptions: {
    recentMessages: 100,
    excludeToolMessages: true,
    searchOptions: { limit: 10, textSearch: false, vectorSearch: false }
  },

  // 存储选项
  storageOptions: { saveMessages: 'promptAndOutput' }, // 'all' | 'none'

  // 处理器
  usageHandler: async (ctx, { usage, model, provider, agentName }) => {},
  contextHandler: async (ctx, { allMessages }) => allMessages,
  rawRequestResponseHandler: async (ctx, { request, response }) => {},

  // 调用设置
  callSettings: { maxRetries: 3, temperature: 1.0 }
});

Key References

关键参考文档

  • Streaming - Delta streaming, HTTP streaming, text smoothing
  • Tools - Defining and using tools with Convex context
  • Context - Customizing LLM context and RAG
  • Threads - Thread management and deletion
  • Messages - Message storage, ordering, UIMessage type
  • Workflows - Durable multi-step workflows
  • Human Agents - Mixing human and AI responses
  • Files - Images and files in messages
  • RAG - Retrieval-augmented generation patterns
  • Rate Limiting - Controlling request rates
  • Usage Tracking - Token usage and billing
  • Debugging - Troubleshooting and playground
  • Streaming - Delta流式输出、HTTP流式输出、文本平滑
  • Tools - 结合Convex上下文定义和使用工具
  • Context - 自定义LLM上下文和RAG
  • Threads - 线程管理与删除
  • Messages - 消息存储、排序、UIMessage类型
  • Workflows - 可靠的多步骤工作流
  • Human Agents - 混合人工与AI响应
  • Files - 消息中的图片与文件
  • RAG - 检索增强生成模式
  • Rate Limiting - 控制请求速率
  • Usage Tracking - Token使用量与计费
  • Debugging - 故障排查与测试环境

Best Practices

最佳实践

  1. Define agents at module level - Reuse across functions
  2. Use
    userId
    on threads
    - Enables cross-thread search and per-user data
  3. Set appropriate
    stopWhen
    /
    maxSteps
    - Prevents runaway tool loops
  4. Use
    promptMessageId
    for async
    - Enables safe retries without duplicates
  5. Save messages in mutations - Use optimistic updates, schedule actions
  6. Use
    textEmbeddingModel
    for RAG
    - Required for vector search
  7. Handle streaming via deltas - Better UX than HTTP streaming alone
  1. 在模块级别定义Agent - 在多个函数中复用
  2. 为线程设置
    userId
    - 支持跨线程搜索和按用户划分数据
  3. 设置合适的
    stopWhen
    /
    maxSteps
    - 防止工具调用进入无限循环
  4. 异步场景使用
    promptMessageId
    - 支持安全重试,避免重复
  5. 在mutation中保存消息 - 使用乐观更新,调度Action
  6. 使用
    textEmbeddingModel
    实现RAG
    - 向量搜索必需
  7. 通过Delta处理流式输出 - 比仅使用HTTP流式输出的用户体验更好