Files
2025-11-30 08:25:20 +08:00

14 KiB

Example: AI-Powered Research Assistant

This example shows planning docs for an AI-powered research assistant with agents and tools.

User Request: "I want to build an AI research assistant that can search the web, summarize articles, and help me organize research notes. Users should be able to chat with the AI and have it perform actions on their behalf."


IMPLEMENTATION_PHASES.md (Excerpt)

Implementation Phases: AI Research Assistant

Project Type: AI-Powered Web App with Agents Stack: Cloudflare Workers + Vite + React + D1 + Clerk + OpenAI Estimated Total: 30 hours (~30 minutes human time) Created: 2025-10-25


Phase 1-3: [Standard setup phases - Infrastructure, Database, Auth]

[Similar to auth-web-app.md example]


Phase 4: OpenAI Integration

Type: Integration Estimated: 3 hours Files: src/lib/openai-client.ts, src/routes/ai.ts

Tasks

  • Create OpenAI account and get API key
  • Install openai package
  • Create AI client wrapper
  • Test basic chat completion
  • Implement streaming responses
  • Add error handling and retries
  • Test token usage tracking

Verification Criteria

  • Can send message to OpenAI
  • Receives streaming response
  • Streams correctly to frontend
  • Errors handled gracefully (rate limits, API down)
  • Token usage logged

Exit Criteria

OpenAI integration working with streaming responses.


Phase 5: AI Tools (Functions)

Type: Integration Estimated: 5 hours Files: src/lib/ai-tools.ts, src/lib/web-search.ts

Tasks

  • Define tool: search_web (using Brave Search API or similar)
  • Define tool: save_note (save to user's notes in D1)
  • Define tool: search_notes (search user's existing notes)
  • Define tool: summarize_url (fetch and summarize webpage)
  • Implement tool execution logic
  • Add tool result formatting
  • Test each tool independently

Verification Criteria

  • search_web returns relevant results
  • save_note creates note in database
  • search_notes finds user's notes
  • summarize_url fetches and summarizes content
  • Tools work when called by AI
  • Tool errors handled (404, timeout, etc)

Exit Criteria

All AI tools implemented and tested.


Phase 6: Research Agent

Type: Integration Estimated: 4 hours Files: src/agents/research-agent.ts, src/routes/agents.ts

Tasks

  • Design research agent system prompt
  • Implement agent with tool calling
  • Add conversation state (Durable Object or in-memory)
  • Implement streaming tool calls to frontend
  • Add agent endpoint: POST /api/agents/research
  • Test multi-step research workflow

Verification Criteria

  • Agent responds to questions
  • Agent calls tools when needed
  • Tool results incorporated into response
  • Multi-turn conversation works
  • Streaming updates show tool usage to user

Exit Criteria

Research agent working with tool integration.


Phase 7: Notes Management

Type: API + UI Estimated: 6 hours Files: src/routes/notes.ts, src/components/NotesList.tsx, etc.

Tasks

  • Create notes CRUD API
  • Build notes list UI
  • Build note viewer/editor
  • Add markdown rendering
  • Add note search functionality
  • Integrate AI-saved notes with manual notes

Verification Criteria

  • Can view all notes
  • Can create/edit/delete notes manually
  • AI-saved notes appear in list
  • Markdown renders correctly
  • Search finds notes

Exit Criteria

Complete notes management system.


Phase 8: Chat Interface

Type: UI Estimated: 8 hours Files: src/components/ChatInterface.tsx, src/components/Message.tsx, etc.

Tasks

  • Install Vercel AI SDK (@ai-sdk/react)
  • Build chat UI with message list
  • Implement message input with auto-resize
  • Add streaming message display
  • Show tool call indicators (loading states)
  • Display tool results inline
  • Add conversation history persistence
  • Style with shadcn/ui components

Verification Criteria

  • Messages stream correctly
  • Tool calls show loading indicators
  • Tool results displayed clearly
  • Can continue conversation
  • Conversation history persists
  • UI handles errors gracefully

Exit Criteria

Polished chat interface with full AI interaction.


AGENTS_CONFIG.md (Excerpt)

AI Agents Configuration: AI Research Assistant

AI Provider: OpenAI Model: gpt-5 Framework: Vercel AI SDK


Agents

Research Agent

Purpose: Help users research topics by searching the web, summarizing content, and organizing notes.

Model: gpt-5 (for complex reasoning, tool use)

System Prompt:

You are a research assistant helping users gather and organize information.

Your capabilities:
- Search the web for information on any topic
- Summarize articles and web pages
- Save important findings as notes
- Search through saved notes
- Provide citations and sources

Your workflow:
1. When asked about a topic, search the web for recent information
2. Summarize key findings concisely
3. Offer to save important information as notes
4. Always cite sources with URLs

Guidelines:
- Be thorough but concise
- Verify information from multiple sources when possible
- Organize information logically
- Ask clarifying questions if user request is vague
- Always provide sources for claims

Response format:
- Use markdown for structure
- Use bullet points for lists
- Bold important findings
- Include clickable links to sources

Available Tools:

  • search_web - Search the web using Brave Search API
  • summarize_url - Fetch and summarize a specific webpage
  • save_note - Save information as a note in user's database
  • search_notes - Search user's existing notes

Example Conversation:

User: "What are the latest developments in quantum computing?"

Agent: I'll search for recent information on quantum computing.

[Calls: search_web({ query: "latest quantum computing developments 2025" })]

Based on recent sources, here are the latest developments:

1. **IBM's 1000-qubit processor** - Announced in Q3 2025
   - Source: [IBM Research](https://...)

2. **Google's error correction breakthrough** - Published in Nature
   - Reduced error rates by 50%
   - Source: [Nature article](https://...)

3. **Commercial applications emerging**
   - Drug discovery (Pfizer partnership)
   - Financial modeling (Goldman Sachs)

Would you like me to save any of these findings as notes?

User: "Yes, save the IBM development"

Agent: [Calls: save_note({ title: "IBM 1000-qubit processor", content: "...", source: "..." })]

✓ Saved note: "IBM 1000-qubit processor"

Tools

Tool: search_web

Purpose: Search the web for information

Parameters:

{
  query: string,       // Search query
  limit?: number       // Max results (default 5)
}

Implementation:

export async function search_web(
  { query, limit = 5 }: SearchWebParams,
  context: Context
) {
  const response = await fetch('https://api.search.brave.com/res/v1/web/search', {
    headers: {
      'X-Subscription-Token': context.env.BRAVE_API_KEY
    },
    params: { q: query, count: limit }
  })

  const data = await response.json()

  return {
    success: true,
    results: data.web.results.map(r => ({
      title: r.title,
      url: r.url,
      description: r.description
    }))
  }
}

Example:

// Agent calls
const results = await search_web({
  query: "quantum computing 2025",
  limit: 5
})

// Returns
{
  success: true,
  results: [
    {
      title: "IBM announces 1000-qubit processor",
      url: "https://...",
      description: "IBM has unveiled..."
    }
  ]
}

Tool: summarize_url

Purpose: Fetch and summarize a webpage

Parameters:

{
  url: string          // URL to fetch and summarize
}

Implementation:

export async function summarize_url(
  { url }: SummarizeUrlParams,
  context: Context
) {
  // Fetch webpage
  const response = await fetch(url)
  const html = await response.text()

  // Extract main content (simplified - use Readability or similar in production)
  const text = extractMainContent(html)

  // Summarize with OpenAI
  const summary = await context.env.AI.run('@cf/meta/llama-3-8b-instruct', {
    prompt: `Summarize this article in 3-5 bullet points:\n\n${text}`
  })

  return {
    success: true,
    summary: summary.response,
    url
  }
}

Tool: save_note

Purpose: Save information as a note

Parameters:

{
  title: string,       // Note title
  content: string,     // Note content (markdown)
  source?: string,     // Optional source URL
  tags?: string[]      // Optional tags
}

Implementation:

export async function save_note(
  { title, content, source, tags }: SaveNoteParams,
  context: Context
) {
  const userId = context.get('userId')

  const result = await context.env.DB.prepare(`
    INSERT INTO notes (user_id, title, content, source, created_at, updated_at)
    VALUES (?, ?, ?, ?, ?, ?)
  `).bind(userId, title, content, source, Date.now(), Date.now()).run()

  // Add tags if provided
  if (tags?.length) {
    // ... insert tag associations
  }

  return {
    success: true,
    noteId: result.meta.last_row_id
  }
}

Tool: search_notes

Purpose: Search user's existing notes

Parameters:

{
  query: string,       // Search query
  limit?: number       // Max results (default 5)
}

Implementation:

export async function search_notes(
  { query, limit = 5 }: SearchNotesParams,
  context: Context
) {
  const userId = context.get('userId')

  // Simple full-text search (use Vectorize for semantic search in production)
  const results = await context.env.DB.prepare(`
    SELECT id, title, content, source, created_at
    FROM notes
    WHERE user_id = ? AND (title LIKE ? OR content LIKE ?)
    ORDER BY created_at DESC
    LIMIT ?
  `).bind(userId, `%${query}%`, `%${query}%`, limit).all()

  return {
    success: true,
    notes: results.results
  }
}

Streaming Responses

Server (POST /api/agents/research):

import { openai } from '@ai-sdk/openai'
import { streamText } from 'ai'

app.post('/api/agents/research', async (c) => {
  const { message, conversationId } = await c.req.json()
  const userId = c.get('userId')

  // Get conversation history (from Durable Object or DB)
  const history = await getConversationHistory(conversationId)

  const result = await streamText({
    model: openai('gpt-5'),
    messages: [
      { role: 'system', content: RESEARCH_AGENT_PROMPT },
      ...history,
      { role: 'user', content: message }
    ],
    tools: {
      search_web,
      summarize_url,
      save_note,
      search_notes
    },
    onFinish: async ({ text }) => {
      // Save conversation
      await saveMessage(conversationId, { role: 'assistant', content: text })
    }
  })

  return result.toAIStreamResponse()
})

Client (React):

import { useChat } from '@ai-sdk/react'

export function ChatInterface() {
  const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
    api: '/api/agents/research'
  })

  return (
    <div className="flex flex-col h-screen">
      <div className="flex-1 overflow-y-auto p-4 space-y-4">
        {messages.map(m => (
          <Message key={m.id} message={m} />
        ))}
      </div>

      <form onSubmit={handleSubmit} className="border-t p-4">
        <textarea
          value={input}
          onChange={handleInputChange}
          placeholder="Ask me anything..."
          className="w-full p-2 border rounded"
        />
        <button type="submit" disabled={isLoading}>
          Send
        </button>
      </form>
    </div>
  )
}

Tool Call Display:

function Message({ message }) {
  return (
    <div className={cn('flex', message.role === 'user' ? 'justify-end' : 'justify-start')}>
      <div className="max-w-[80%] rounded-lg p-4 bg-muted">
        <ReactMarkdown>{message.content}</ReactMarkdown>

        {message.toolInvocations?.map((tool, i) => (
          <div key={i} className="mt-2 text-sm border-l-2 pl-2">
            <span className="font-semibold">🔧 {tool.toolName}</span>
            {tool.state === 'result' && (
              <div className="text-muted-foreground">
                 {tool.result.success ? 'Success' : 'Failed'}
              </div>
            )}
          </div>
        ))}
      </div>
    </div>
  )
}

DATABASE_SCHEMA.md (Additions)

notes

Column Type Constraints Notes
id INTEGER PRIMARY KEY
user_id INTEGER FOREIGN KEY References users(id)
title TEXT NOT NULL Note title
content TEXT NOT NULL Note content (markdown)
source TEXT NULL Source URL if from web
created_at INTEGER NOT NULL
updated_at INTEGER NOT NULL

conversations

Column Type Constraints Notes
id INTEGER PRIMARY KEY
user_id INTEGER FOREIGN KEY References users(id)
title TEXT NULL Auto-generated from first message
created_at INTEGER NOT NULL
updated_at INTEGER NOT NULL

messages

Column Type Constraints Notes
id INTEGER PRIMARY KEY
conversation_id INTEGER FOREIGN KEY References conversations(id)
role TEXT NOT NULL 'user' or 'assistant'
content TEXT NOT NULL Message content
tool_calls TEXT NULL JSON array of tool calls
created_at INTEGER NOT NULL

INTEGRATION.md (Additions)

OpenAI

Purpose: AI-powered chat and tool use

Environment Variables:

OPENAI_API_KEY=sk-...

API Client:

import { openai } from '@ai-sdk/openai'

const model = openai('gpt-5')

Rate Limits: 10,000 requests/minute (Tier 5)

Token Management:

  • Use gpt-5-mini for simple tasks (cheaper)
  • Use gpt-5 for complex research (better reasoning)
  • Limit conversation history to last 20 messages
  • Track token usage per user (optional billing)

Brave Search API

Purpose: Web search for research agent

Environment Variables:

BRAVE_API_KEY=...

Rate Limits: 15,000 queries/month (free tier)


Note: This example demonstrates a complete AI-powered application with agents, tools, and streaming responses. Adjust complexity based on actual requirements.