Install
Terminal · npx$
npx skills add https://github.com/waynesutton/convexskills --skill convex-agentsWorks with Paperclip
How Convex Agents fits into a Paperclip company.
Convex Agents drops into any Paperclip agent that handles convex and agents work. Assign it to a specialist inside a pre-configured PaperclipOrg company and the skill becomes available on every heartbeat — no prompt engineering, no tool wiring.
S
SaaS FactoryPaired
Pre-configured AI company — 18 agents, 18 skills, one-time purchase.
$27$59
Explore packSource file
SKILL.md516 linesExpandCollapse
---name: convex-agentsdisplayName: Convex Agentsdescription: Building AI agents with the Convex Agent component including thread management, tool integration, streaming responses, RAG patterns, and workflow orchestrationversion: 1.0.0author: Convextags: [convex, agents, ai, llm, tools, rag, workflows]--- # Convex Agents Build persistent, stateful AI agents with Convex including thread management, tool integration, streaming responses, RAG patterns, and workflow orchestration. ## Documentation Sources Before implementing, do not assume; fetch the latest documentation: - Primary: https://docs.convex.dev/ai- Convex Agent Component: https://www.npmjs.com/package/@convex-dev/agent- For broader context: https://docs.convex.dev/llms.txt ## Instructions ### Why Convex for AI Agents - **Persistent State** - Conversation history survives restarts- **Real-time Updates** - Stream responses to clients automatically- **Tool Execution** - Run Convex functions as agent tools- **Durable Workflows** - Long-running agent tasks with reliability- **Built-in RAG** - Vector search for knowledge retrieval ### Setting Up Convex Agent ```bashnpm install @convex-dev/agent ai openai``` ```typescript// convex/agent.tsimport { Agent } from "@convex-dev/agent";import { components } from "./_generated/api";import { OpenAI } from "openai"; const openai = new OpenAI(); export const agent = new Agent(components.agent, { chat: openai.chat, textEmbedding: openai.embeddings,});``` ### Thread Management ```typescript// convex/threads.tsimport { mutation, query } from "./_generated/server";import { v } from "convex/values";import { agent } from "./agent"; // Create a new conversation threadexport const createThread = mutation({ args: { userId: v.id("users"), title: v.optional(v.string()), }, returns: v.id("threads"), handler: async (ctx, args) => { const threadId = await agent.createThread(ctx, { userId: args.userId, metadata: { title: args.title ?? "New Conversation", createdAt: Date.now(), }, }); return threadId; },}); // List user's threadsexport const listThreads = query({ args: { userId: v.id("users") }, returns: v.array(v.object({ _id: v.id("threads"), title: v.string(), lastMessageAt: v.optional(v.number()), })), handler: async (ctx, args) => { return await agent.listThreads(ctx, { userId: args.userId, }); },}); // Get thread messagesexport const getMessages = query({ args: { threadId: v.id("threads") }, returns: v.array(v.object({ role: v.string(), content: v.string(), createdAt: v.number(), })), handler: async (ctx, args) => { return await agent.getMessages(ctx, { threadId: args.threadId, }); },});``` ### Sending Messages and Streaming Responses ```typescript// convex/chat.tsimport { action } from "./_generated/server";import { v } from "convex/values";import { agent } from "./agent";import { internal } from "./_generated/api"; export const sendMessage = action({ args: { threadId: v.id("threads"), message: v.string(), }, returns: v.null(), handler: async (ctx, args) => { // Add user message to thread await ctx.runMutation(internal.chat.addUserMessage, { threadId: args.threadId, content: args.message, }); // Generate AI response with streaming const response = await agent.chat(ctx, { threadId: args.threadId, messages: [{ role: "user", content: args.message }], stream: true, onToken: async (token) => { // Stream tokens to client via mutation await ctx.runMutation(internal.chat.appendToken, { threadId: args.threadId, token, }); }, }); // Save complete response await ctx.runMutation(internal.chat.saveResponse, { threadId: args.threadId, content: response.content, }); return null; },});``` ### Tool Integration Define tools that agents can use: ```typescript// convex/tools.tsimport { tool } from "@convex-dev/agent";import { v } from "convex/values";import { api } from "./_generated/api"; // Tool to search knowledge baseexport const searchKnowledge = tool({ name: "search_knowledge", description: "Search the knowledge base for relevant information", parameters: v.object({ query: v.string(), limit: v.optional(v.number()), }), handler: async (ctx, args) => { const results = await ctx.runQuery(api.knowledge.search, { query: args.query, limit: args.limit ?? 5, }); return results; },}); // Tool to create a taskexport const createTask = tool({ name: "create_task", description: "Create a new task for the user", parameters: v.object({ title: v.string(), description: v.optional(v.string()), dueDate: v.optional(v.string()), }), handler: async (ctx, args) => { const taskId = await ctx.runMutation(api.tasks.create, { title: args.title, description: args.description, dueDate: args.dueDate ? new Date(args.dueDate).getTime() : undefined, }); return { success: true, taskId }; },}); // Tool to get weatherexport const getWeather = tool({ name: "get_weather", description: "Get current weather for a location", parameters: v.object({ location: v.string(), }), handler: async (ctx, args) => { const response = await fetch( `https://api.weather.com/current?location=${encodeURIComponent(args.location)}` ); return await response.json(); },});``` ### Agent with Tools ```typescript// convex/assistant.tsimport { action } from "./_generated/server";import { v } from "convex/values";import { agent } from "./agent";import { searchKnowledge, createTask, getWeather } from "./tools"; export const chat = action({ args: { threadId: v.id("threads"), message: v.string(), }, returns: v.string(), handler: async (ctx, args) => { const response = await agent.chat(ctx, { threadId: args.threadId, messages: [{ role: "user", content: args.message }], tools: [searchKnowledge, createTask, getWeather], systemPrompt: `You are a helpful assistant. You have access to tools to: - Search the knowledge base for information - Create tasks for the user - Get weather information Use these tools when appropriate to help the user.`, }); return response.content; },});``` ### RAG (Retrieval Augmented Generation) ```typescript// convex/knowledge.tsimport { mutation, query } from "./_generated/server";import { v } from "convex/values";import { agent } from "./agent"; // Add document to knowledge baseexport const addDocument = mutation({ args: { title: v.string(), content: v.string(), metadata: v.optional(v.object({ source: v.optional(v.string()), category: v.optional(v.string()), })), }, returns: v.id("documents"), handler: async (ctx, args) => { // Generate embedding const embedding = await agent.embed(ctx, args.content); return await ctx.db.insert("documents", { title: args.title, content: args.content, embedding, metadata: args.metadata ?? {}, createdAt: Date.now(), }); },}); // Search knowledge baseexport const search = query({ args: { query: v.string(), limit: v.optional(v.number()), }, returns: v.array(v.object({ _id: v.id("documents"), title: v.string(), content: v.string(), score: v.number(), })), handler: async (ctx, args) => { const results = await agent.search(ctx, { query: args.query, table: "documents", limit: args.limit ?? 5, }); return results.map((r) => ({ _id: r._id, title: r.title, content: r.content, score: r._score, })); },});``` ### Workflow Orchestration ```typescript// convex/workflows.tsimport { action, internalMutation } from "./_generated/server";import { v } from "convex/values";import { agent } from "./agent";import { internal } from "./_generated/api"; // Multi-step research workflowexport const researchTopic = action({ args: { topic: v.string(), userId: v.id("users"), }, returns: v.id("research"), handler: async (ctx, args) => { // Create research record const researchId = await ctx.runMutation(internal.workflows.createResearch, { topic: args.topic, userId: args.userId, status: "searching", }); // Step 1: Search for relevant documents const searchResults = await agent.search(ctx, { query: args.topic, table: "documents", limit: 10, }); await ctx.runMutation(internal.workflows.updateStatus, { researchId, status: "analyzing", }); // Step 2: Analyze and synthesize const analysis = await agent.chat(ctx, { messages: [{ role: "user", content: `Analyze these sources about "${args.topic}" and provide a comprehensive summary:\n\n${ searchResults.map((r) => r.content).join("\n\n---\n\n") }`, }], systemPrompt: "You are a research assistant. Provide thorough, well-cited analysis.", }); // Step 3: Generate key insights await ctx.runMutation(internal.workflows.updateStatus, { researchId, status: "summarizing", }); const insights = await agent.chat(ctx, { messages: [{ role: "user", content: `Based on this analysis, list 5 key insights:\n\n${analysis.content}`, }], }); // Save final results await ctx.runMutation(internal.workflows.completeResearch, { researchId, analysis: analysis.content, insights: insights.content, sources: searchResults.map((r) => r._id), }); return researchId; },});``` ## Examples ### Complete Chat Application Schema ```typescript// convex/schema.tsimport { defineSchema, defineTable } from "convex/server";import { v } from "convex/values"; export default defineSchema({ threads: defineTable({ userId: v.id("users"), title: v.string(), lastMessageAt: v.optional(v.number()), metadata: v.optional(v.any()), }).index("by_user", ["userId"]), messages: defineTable({ threadId: v.id("threads"), role: v.union(v.literal("user"), v.literal("assistant"), v.literal("system")), content: v.string(), toolCalls: v.optional(v.array(v.object({ name: v.string(), arguments: v.any(), result: v.optional(v.any()), }))), createdAt: v.number(), }).index("by_thread", ["threadId"]), documents: defineTable({ title: v.string(), content: v.string(), embedding: v.array(v.float64()), metadata: v.object({ source: v.optional(v.string()), category: v.optional(v.string()), }), createdAt: v.number(), }).vectorIndex("by_embedding", { vectorField: "embedding", dimensions: 1536, }),});``` ### React Chat Component ```typescriptimport { useQuery, useMutation, useAction } from "convex/react";import { api } from "../convex/_generated/api";import { useState, useRef, useEffect } from "react"; function ChatInterface({ threadId }: { threadId: Id<"threads"> }) { const messages = useQuery(api.threads.getMessages, { threadId }); const sendMessage = useAction(api.chat.sendMessage); const [input, setInput] = useState(""); const [sending, setSending] = useState(false); const messagesEndRef = useRef<HTMLDivElement>(null); useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); }, [messages]); const handleSend = async (e: React.FormEvent) => { e.preventDefault(); if (!input.trim() || sending) return; const message = input.trim(); setInput(""); setSending(true); try { await sendMessage({ threadId, message }); } finally { setSending(false); } }; return ( <div className="chat-container"> <div className="messages"> {messages?.map((msg, i) => ( <div key={i} className={`message ${msg.role}`}> <strong>{msg.role === "user" ? "You" : "Assistant"}:</strong> <p>{msg.content}</p> </div> ))} <div ref={messagesEndRef} /> </div> <form onSubmit={handleSend} className="input-form"> <input value={input} onChange={(e) => setInput(e.target.value)} placeholder="Type your message..." disabled={sending} /> <button type="submit" disabled={sending || !input.trim()}> {sending ? "Sending..." : "Send"} </button> </form> </div> );}``` ## Best Practices - Never run `npx convex deploy` unless explicitly instructed- Never run any git commands unless explicitly instructed- Store conversation history in Convex for persistence- Use streaming for better user experience with long responses- Implement proper error handling for tool failures- Use vector indexes for efficient RAG retrieval- Rate limit agent interactions to control costs- Log tool usage for debugging and analytics ## Common Pitfalls 1. **Not persisting threads** - Conversations lost on refresh2. **Blocking on long responses** - Use streaming instead3. **Tool errors crashing agents** - Add proper error handling4. **Large context windows** - Summarize old messages5. **Missing embeddings for RAG** - Generate embeddings on insert ## References - Convex Documentation: https://docs.convex.dev/- Convex LLMs.txt: https://docs.convex.dev/llms.txt- Convex AI: https://docs.convex.dev/ai- Agent Component: https://www.npmjs.com/package/@convex-dev/agentRelated skills
Avoid Feature Creep
Install Avoid Feature Creep skill for Claude Code from waynesutton/convexskills.
Convex
This is an umbrella skill that routes you to 11 specialized Convex development skills instead of trying to cram everything into one giant prompt. Hit `/convex-f
Convex Best Practices
Install Convex Best Practices skill for Claude Code from waynesutton/convexskills.