본문으로 건너뛰기
cccskills
무료GitHub에 공개

agent-builder-vercel-sdk

Build conversational AI agents using Vercel AI SDK + OpenRouter. Use when creating Next.js frontends with streaming UI, tool calling, and multi-provider support.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

연동 도구VercelOpenAI
설치 방법 보기

포함된 파일(2)

  • SKILL.md8.4 KB
  • metadata.json364 B

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Vercel AI SDK Agent Builder

Purpose

Create streaming AI chat interfaces with minimal code using Vercel AI SDK and OpenRouter provider.

When to Use

  • Building Next.js frontend with chat UI
  • Need streaming responses with SSE
  • Want type-safe tool calling in TypeScript
  • Switching between multiple AI providers
  • Building agentic loops with stopWhen/prepareStep

Quick Start

Installation

npm install ai @openrouter/ai-sdk-provider zod

Environment Variables

OPENROUTER_API_KEY=sk-or-v1-...
NEXT_PUBLIC_SITE_URL=http://localhost:3000

Backend Setup (Route Handler)

Basic Chat Endpoint

// app/api/chat/route.ts
import { OpenRouter } from '@openrouter/ai-sdk-provider'
import { streamText } from 'ai'

const openrouter = new OpenRouter({
  apiKey: process.env.OPENROUTER_API_KEY
})

export async function POST(req: Request) {
  const { messages } = await req.json()

  const result = streamText({
    model: openrouter('openai/gpt-4o'),
    system: 'You are a helpful assistant',
    messages,
  })

  return result.toDataStreamResponse()
}

With Tool Calling

import { z } from 'zod'
import { tool } from 'ai'

const tools = {
  generateImage: tool({
    description: 'Generate images using AI',
    parameters: z.object({
      prompt: z.string().describe('Image description'),
      numImages: z.number().min(1).max(10).default(1)
    }),
    execute: async ({ prompt, numImages }) => {
      // Your implementation
      const images = await generateImages(prompt, numImages)
      return { images }
    }
  })
}

export async function POST(req: Request) {
  const { messages } = await req.json()

  const result = streamText({
    model: openrouter('openai/gpt-4o'),
    system: 'You are a helpful assistant',
    messages,
    tools,
    maxSteps: 5 // Enable agentic loop
  })

  return result.toDataStreamResponse()
}

Frontend Integration

Using useChat Hook

'use client'

import { useChat } from 'ai/react'

export default function Chat() {
  const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat()

  return (
    <div className="flex flex-col h-screen">
      {/* Messages */}
      <div className="flex-1 overflow-y-auto p-4">
        {messages.map(m => (
          <div key={m.id} className={m.role === 'user' ? 'text-right' : 'text-left'}>
            <div className="inline-block p-3 rounded-lg">
              {m.content}
            </div>
          </div>
        ))}
      </div>

      {/* Input */}
      <form onSubmit={handleSubmit} className="p-4 border-t">
        <input
          value={input}
          onChange={handleInputChange}
          placeholder="Type a message..."
          disabled={isLoading}
          className="w-full px-4 py-2 border rounded"
        />
      </form>
    </div>
  )
}

With Tool Results Display

'use client'

import { useChat } from 'ai/react'

export default function ChatWithTools() {
  const { messages, input, handleInputChange, handleSubmit } = useChat()

  return (
    <div>
      {messages.map(m => (
        <div key={m.id}>
          {m.content}

          {/* Display tool calls */}
          {m.toolInvocations?.map(tool => (
            <div key={tool.toolCallId} className="bg-gray-100 p-2 rounded">
              <strong>{tool.toolName}</strong>
              {tool.state === 'result' && (
                <pre>{JSON.stringify(tool.result, null, 2)}</pre>
              )}
            </div>
          ))}
        </div>
      ))}

      <form onSubmit={handleSubmit}>
        <input value={input} onChange={handleInputChange} />
      </form>
    </div>
  )
}

Advanced Patterns

Multi-Step Agentic Loop

const result = streamText({
  model: openrouter('openai/gpt-4o'),
  messages,
  tools,
  maxSteps: 5,

  // Control loop behavior
  onStepFinish: ({ stepType, text, toolCalls }) => {
    console.log(`Step finished: ${stepType}`)
  },

  // Stop condition
  experimental_continueSteps: true
})

Custom Streaming with streamUI

import { streamUI } from 'ai/rsc'

export async function generateUI(prompt: string) {
  const result = streamUI({
    model: openrouter('openai/gpt-4o'),
    prompt,
    text: ({ content }) => <p>{content}</p>,
    tools: {
      showImage: {
        description: 'Display an image',
        parameters: z.object({ url: z.string() }),
        generate: async ({ url }) => <img src={url} />
      }
    }
  })

  return result.value
}

tldraw Agent Pattern

Based on: /Users/danielcarreon/Documents/AI/software/tldraw-agent/

// Incremental JSON parsing pattern
async function* streamActions(model, prompt) {
  const { textStream } = streamText({
    model,
    system: systemPrompt,
    messages,
    maxOutputTokens: 8192,
    temperature: 0
  })

  let buffer = '{"actions": [{"_type":'

  for await (const text of textStream) {
    buffer += text

    // Parse incremental JSON
    const partialObject = closeAndParseJson(buffer)
    if (!partialObject) continue

    const actions = partialObject.actions
    if (!Array.isArray(actions)) continue

    // Yield actions as they complete
    for (const action of actions) {
      if (action.complete) {
        yield action
      }
    }
  }
}

OpenRouter Provider Setup

import { OpenRouter } from '@openrouter/ai-sdk-provider'

const openrouter = new OpenRouter({
  apiKey: process.env.OPENROUTER_API_KEY,
  // Optional: customize
  baseURL: 'https://openrouter.ai/api/v1',
  headers: {
    'HTTP-Referer': process.env.NEXT_PUBLIC_SITE_URL,
    'X-Title': 'My App'
  }
})

// Use different models
const gpt4 = openrouter('openai/gpt-4o')
const claude = openrouter('anthropic/claude-3-5-sonnet')
const gemini = openrouter('google/gemini-2.0-flash-exp')

Error Handling

export async function POST(req: Request) {
  try {
    const { messages } = await req.json()

    const result = streamText({
      model: openrouter('openai/gpt-4o'),
      messages,
      onError: (error) => {
        console.error('Stream error:', error)
      }
    })

    return result.toDataStreamResponse()
  } catch (error) {
    return new Response(
      JSON.stringify({ error: error.message }),
      { status: 500 }
    )
  }
}

Testing

import { streamText } from 'ai'
import { OpenRouter } from '@openrouter/ai-sdk-provider'

describe('Chat API', () => {
  it('should stream response', async () => {
    const openrouter = new OpenRouter({
      apiKey: process.env.OPENROUTER_API_KEY
    })

    const result = streamText({
      model: openrouter('openai/gpt-4o'),
      prompt: 'Say hello'
    })

    const chunks = []
    for await (const chunk of result.textStream) {
      chunks.push(chunk)
    }

    expect(chunks.length).toBeGreaterThan(0)
  })
})

Best Practices

  1. Type Safety: Use Zod for tool parameters
  2. Error Boundaries: Wrap chat UI in ErrorBoundary
  3. Loading States: Show loading UI during streaming
  4. Optimistic Updates: Update UI before server response
  5. Tool Results: Display tool executions to user
  6. Rate Limiting: Implement rate limits on API routes
  7. Context Management: Limit message history to avoid token overflow

Common Patterns

Image Generation Agent

const tools = {
  generateAvatar: tool({
    description: 'Generate avatar with DANI identity',
    parameters: z.object({
      prompt: z.string(),
      numImages: z.number().default(3)
    }),
    execute: async ({ prompt, numImages }) => {
      const response = await fetch('/api/generate', {
        method: 'POST',
        body: JSON.stringify({ prompt, numImages })
      })
      return await response.json()
    }
  }),

  combineImages: tool({
    description: 'Combine multiple images',
    parameters: z.object({
      imageUrls: z.array(z.string()),
      prompt: z.string()
    }),
    execute: async ({ imageUrls, prompt }) => {
      // Nano Banana integration
      return await combineWithNanoBanana(imageUrls, prompt)
    }
  })
}

Resources

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Use when you need to install the embedded robot agents into either .cursor/agents or .claude/agents, selecting the destination interactively and copying the embedded agent definitions from project assets. This should trigger for requests such as Install embedded agents; Bootstrap .cursor/agents; Bootstrap .claude/agents; Copy robot agents. Part of cursor-rules-java project

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

majiayu000/claude-skill-registry-data272026년 10월 21일 업데이트

Use when you need to generate an AGENTS.md file for a Java repository — covering project conventions, tech stack, file structure, commands, Git workflow, and contributor boundaries — through a modular, step-based interactive process that adapts to your specific project needs. This should trigger for requests such as Create AGENTS.md; Update AGENTS.md file; Add agent instructions. Part of cursor-rules-java project

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

majiayu000/claude-skill-registry-data272026년 10월 21일 업데이트

Expert legal research agent for finding and scraping expungement data state by state. Knows authoritative sources, URL patterns, Firecrawl configuration, and 2026 legal landscape. Activate on "find expungement data", "scrape state laws", "legal research", "court URLs", "statute sources", "Clean Slate laws", "automatic expungement research". NOT for interpreting laws (use national-expungement-expert), building UI, or legal advice.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

majiayu000/claude-skill-registry-data272026년 10월 21일 업데이트

Generated by Gremlin Forge Meta-Agent 🍆👾⚡

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

majiayu000/claude-skill-registry-data272026년 10월 21일 업데이트

Work autonomously without waiting for hand-holding — complete tasks end-to-end without asking for permission at every step.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

majiayu000/claude-skill-registry-data272026년 10월 21일 업데이트

abmahnung

무료

Entwurf einer BAG-konformen Abmahnung im Arbeitsrecht – konkrete Schilderung der Pflichtverletzung, klare Verhaltenserwartung, ausdrückliche Sanktionsandrohung. Use when AG eine verhaltensbedingte Sanktion vor einer Kündigung dokumentieren muss oder AN die Wirksamkeit einer empfangenen Abmahnung prüft.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

majiayu000/claude-skill-registry-data272026년 10월 21일 업데이트

majiayu000의 스킬 모두 보기

이 스킬의 문제 신고