flare-agent Bài 7: Áp dụng vào edu-ai-platform — End-to-end

Kết hợp tất cả packages của flare-agent để xây dựng edu-ai-platform hoàn chỉnh — vocabulary learning agent với quiz workflow, RAG memory và SSE streaming trên Cloudflare.

Nghe bài viết này dưới dạng podcast

flare-agent Bài 7: Áp dụng vào edu-ai-platform

Series: Build Your Own AI Agent Framework trên Cloudflare Bài: 7 / 7 — End-to-end


Tổng kết những gì đã build

Package Vai trò
@flare-agent/types Interfaces — hợp đồng giữa các layer
@flare-agent/providers LLM Router — Groq, WorkersAI, Ollama
@flare-agent/memory KV + D1 + Vectorize adapters
@flare-agent/core Agent Loop + Tool Registry
@flare-agent/workflow Graph-based Workflow Engine

Bài này kết nối tất cả vào edu-ai-platform thực tế.


Kiến trúc edu-ai-platform

React/Vite (Cloudflare Pages)
  ↓ POST /api/chat
  ↓ GET  /api/quiz/start
Hono Worker
  ├── /api/chat     → VocabularyAgent (Agent Loop)
  ├── /api/quiz/*   → QuizWorkflow (Workflow Engine)
  └── /api/recall   → RAG query
Durable Object (session state)
  └── AgentSession
Storage
  ├── KV  → conversation history
  ├── D1  → vocabulary, progress, users
  └── Vectorize → semantic word search

Project Structure

edu-ai-platform/
├── apps/
│   ├── web/                    # React + Vite
│   │   └── src/
│   │       ├── hooks/
│   │       │   └── useChat.ts  # SSE consumer
│   │       └── components/
│   │           └── ChatBox.tsx
│   └── worker/                 # Hono Worker
│       └── src/
│           ├── index.ts        # routes
│           ├── agents/
│           │   └── vocabulary.ts
│           ├── workflows/
│           │   └── quiz.ts
│           └── tools/
│               ├── vocabulary.ts
│               └── progress.ts
└── packages/                   # flare-agent packages
    ├── types/
    ├── providers/
    ├── memory/
    ├── core/
    └── workflow/

Tools

// apps/worker/src/tools/vocabulary.ts
import { tool } from '@flare-agent/core';

export const getVocabulary = tool({
  schema: {
    name: 'get_vocabulary',
    description: 'Lấy danh sách từ vựng theo chủ đề và level',
    parameters: {
      type: 'object',
      properties: {
        topic: { type: 'string', description: 'Chủ đề (business, travel, ...)' },
        level: {
          type: 'string',
          enum: ['beginner', 'intermediate', 'advanced'],
        },
        limit: { type: 'number', default: 5 },
      },
      required: ['topic'],
    },
  },
  execute: async ({ topic, level, limit }, ctx) => {
    const db = ctx.env.DB as D1Database;
    const { results } = await db
      .prepare(
        `SELECT word, definition, example
         FROM vocabulary
         WHERE topic = ? AND level = ?
         ORDER BY RANDOM() LIMIT ?`
      )
      .bind(topic, level ?? 'beginner', limit ?? 5)
      .all();
    return results;
  },
});

export const saveProgress = tool({
  schema: {
    name: 'save_progress',
    description: 'Lưu tiến độ học của user',
    parameters: {
      type: 'object',
      properties: {
        wordId: { type: 'string' },
        status: { type: 'string', enum: ['learned', 'reviewing', 'skipped'] },
        score: { type: 'number' },
      },
      required: ['wordId', 'status'],
    },
  },
  execute: async ({ wordId, status, score }, ctx) => {
    const db = ctx.env.DB as D1Database;
    await db
      .prepare(
        `INSERT OR REPLACE INTO progress (user_id, word_id, status, score, updated_at)
         VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)`
      )
      .bind(ctx.userId, wordId, status, score ?? 0)
      .run();
    return { success: true };
  },
});

Vocabulary Agent

// apps/worker/src/agents/vocabulary.ts
import { Agent } from '@flare-agent/core';
import { getVocabulary, saveProgress } from '../tools/vocabulary';

export const vocabularyAgent = new Agent({
  name: 'vocabulary-tutor',
  model: { provider: 'groq', model: 'llama-3.3-70b-versatile' },
  memory: 'd1',
  maxIterations: 8,
  systemPrompt: (ctx) =>
    `Bạn là trợ lý học từ vựng tiếng Anh thân thiện.
     Hỗ trợ người dùng ${ctx.userId ?? 'ẩn danh'} học từ vựng hiệu quả.
     Luôn giải thích bằng tiếng Việt, ví dụ bằng tiếng Anh.
     Khuyến khích và động viên khi user trả lời đúng.`,
})
.use(getVocabulary, saveProgress);

Hono Routes

// apps/worker/src/index.ts
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { createSSEResponse } from '@flare-agent/core';
import { vocabularyAgent } from './agents/vocabulary';
import { QuizWorkflow } from './workflows/quiz';
import { MemoryManager, KVMemoryAdapter, D1MemoryAdapter, VectorizeAdapter } from '@flare-agent/memory';

const app = new Hono<{ Bindings: Env }>();

app.use('*', cors());

// Chat endpoint — stream response
app.post('/api/chat/stream', async (c) => {
  const { input, sessionId, userId } = await c.req.json();

  const ctx = {
    sessionId,
    userId,
    env: c.env as any,
  };

  const provider = /* resolve từ config */;
  const stream = provider.stream(
    [{ role: 'user', content: input }]
  );

  return createSSEResponse(stream);
});

// Chat endpoint — full response
app.post('/api/chat', async (c) => {
  const { input, sessionId, userId } = await c.req.json();

  const result = await vocabularyAgent.run(input, {
    sessionId,
    userId,
    env: c.env as any,
  });

  return c.json(result);
});

// Quiz workflow
app.post('/api/quiz/answer', async (c) => {
  const { answer, sessionId, userId } = await c.req.json();

  const workflow = new QuizWorkflow(c.env);
  const result = await workflow.run(answer, {
    sessionId,
    userId,
    env: c.env as any,
  });

  return c.json(result);
});

// RAG — tìm từ theo nghĩa
app.get('/api/recall', async (c) => {
  const query = c.req.query('q') ?? '';

  const memory = new MemoryManager({
    vector: new VectorizeAdapter(c.env.VECTORIZE, c.env.AI),
  });

  const results = await memory.recall(query, 5);
  return c.json({ results });
});

export const AgentSession = vocabularyAgent.toDurableObject();
export default app;

React Hook — SSE Consumer

// apps/web/src/hooks/useChat.ts
import { useState, useCallback } from 'react';

export function useChat() {
  const [messages, setMessages] = useState<Array<{
    role: 'user' | 'assistant';
    content: string;
  }>>([]);
  const [streaming, setStreaming] = useState('');
  const [loading, setLoading] = useState(false);

  const send = useCallback(async (input: string, sessionId: string) => {
    setLoading(true);
    setMessages((prev) => [...prev, { role: 'user', content: input }]);

    const res = await fetch('/api/chat/stream', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ input, sessionId }),
    });

    const reader = res.body!.getReader();
    const decoder = new TextDecoder();
    let fullText = '';

    while (true) {
      const { done, value } = await reader.read();
      if (done) break;

      const lines = decoder.decode(value).split('\n');
      for (const line of lines) {
        if (!line.startsWith('data: ')) continue;
        const data = line.slice(6);
        if (data === '[DONE]') break;
        try {
          const { text } = JSON.parse(data);
          fullText += text;
          setStreaming(fullText);
        } catch { /* skip */ }
      }
    }

    setMessages((prev) => [...prev, { role: 'assistant', content: fullText }]);
    setStreaming('');
    setLoading(false);
  }, []);

  return { messages, streaming, loading, send };
}

wrangler.toml

name = "edu-ai-worker"
main = "src/index.ts"
compatibility_date = "2025-01-01"

[[durable_objects.bindings]]
name = "AGENT_SESSION"
class_name = "AgentSession"

[[migrations]]
tag = "v1"
new_classes = ["AgentSession"]

[[kv_namespaces]]
binding = "KV"
id = "YOUR_KV_ID"

[[d1_databases]]
binding = "DB"
database_name = "edu-ai"
database_id = "YOUR_D1_ID"

[[vectorize]]
binding = "VECTORIZE"
index_name = "edu-vocabulary"

[ai]
binding = "AI"

[vars]
OLLAMA_BASE_URL = "https://your-oracle-vm/ollama"

[secrets]
GROQ_API_KEY = ""

Kết quả

Sau 7 bài, bạn có:

  1. Framework ~800 lines TypeScript — hiểu 100% internals
  2. Zero lock-in — swap provider, memory, runtime bằng 1 dòng
  3. edu-ai-platform chạy native trên Cloudflare
  4. Tái sử dụng được cho bất kỳ project nào khác

Bước tiếp theo

  • Extract framework thành npm packages khi cần dùng cho project thứ 2
  • Thêm Observability / Tracing (OpenTelemetry)
  • Thêm Multi-agent support
  • Viết tests cho từng package

Series hoàn tất. Nếu bài viết hữu ích, hãy star repo và chia sẻ với cộng đồng developer Việt Nam! 🇻🇳

Chưa có bình luận

Để lại bình luận

Bình luận sẽ được phê duyệt trước khi hiển thị.

Nhận bài viết mới

Mình sẽ gửi email khi có bài mới. Không spam.