flare-agent Bài 2: Thiết kế Types & Interfaces — Nền tảng của mọi abstraction

Hướng dẫn thiết kế @flare-agent/types — package zero-dependency định nghĩa toàn bộ interfaces cho LLM, Agent, Tool, Memory và Runtime của framework.

4 phút đọc
Tập này đang được chuẩn bị, quay lại sau nhé.

flare-agent Bài 2: Thiết kế Types & Interfaces

Series: Build Your Own AI Agent Framework trên Cloudflare Bài: 2 / 7 — @flare-agent/types


Tại sao Types là bài đầu tiên?

Trong một framework, interfaces là hợp đồng giữa các layer. Nếu interfaces sai, mọi thứ phía trên sẽ sai theo.

@flare-agent/types là package duy nhất không có dependency nào — nó chỉ export TypeScript interfaces và types. Mọi package khác đều import từ đây.


Cấu trúc package

packages/types/
  src/
    llm.ts       # Message, LLMResponse, LLMProvider
    agent.ts     # AgentConfig, AgentContext
    tool.ts      # ToolSchema, ToolDefinition
    memory.ts    # MemoryAdapter
    runtime.ts   # AgentRuntime (Cloudflare abstraction)
    workflow.ts  # WorkflowDefinition, Step
    index.ts
  package.json
  tsconfig.json

LLM Types

// src/llm.ts

export type Role = 'system' | 'user' | 'assistant' | 'tool';

export interface Message {
  role: Role;
  content: string;
  toolCallId?: string;  // khi role = 'tool'
  name?: string;
}

export interface ToolCall {
  id: string;
  name: string;
  args: Record<string, unknown>;
}

export interface LLMResponse {
  type: 'text' | 'tool_call';
  content?: string;
  toolCall?: ToolCall;
}

// Interface mọi provider phải implement
export interface LLMProvider {
  chat(
    messages: Message[],
    tools?: ToolSchema[]
  ): Promise<LLMResponse>;

  stream(
    messages: Message[],
    tools?: ToolSchema[]
  ): ReadableStream<string>;
}

Tool Types

// src/tool.ts

export interface ToolSchema {
  name: string;
  description: string;
  parameters: {
    type: 'object';
    properties: Record<string, unknown>;
    required?: string[];
  };
}

// Generic để type-safe args và result
export interface ToolDefinition<
  TArgs = Record<string, unknown>,
  TResult = unknown
> {
  schema: ToolSchema;
  execute: (args: TArgs, ctx: AgentContext) => Promise<TResult>;
}

Agent Types

// src/agent.ts
import type { ToolDefinition } from './tool';

export type ProviderName = 'workersai' | 'groq' | 'ollama';

export interface ModelConfig {
  provider: ProviderName;
  model: string;
}

export interface AgentConfig {
  name: string;
  model: ModelConfig;
  systemPrompt?: string | ((ctx: AgentContext) => string);
  tools?: ToolDefinition[];
  memory?: 'kv' | 'd1' | 'none';
  maxIterations?: number;
}

// Context được inject vào mỗi agent run
export interface AgentContext {
  sessionId: string;
  userId?: string;
  env: Record<string, unknown>;
  metadata?: Record<string, unknown>;
}

export interface AgentResult {
  output: string;
  iterations: number;
  toolCalls: Array<{ name: string; args: unknown; result: unknown }>;
}

Memory Types

// src/memory.ts
import type { Message } from './llm';

export interface MemoryAdapter {
  getMessages(sessionId: string): Promise<Message[]>;
  addMessage(sessionId: string, message: Message): Promise<void>;
  clearSession(sessionId: string): Promise<void>;
}

export interface VectorAdapter {
  insert(vectors: VectorItem[]): Promise<void>;
  query(vector: number[], topK: number): Promise<VectorMatch[]>;
  delete(ids: string[]): Promise<void>;
}

export interface VectorItem {
  id: string;
  values: number[];
  metadata?: Record<string, unknown>;
}

export interface VectorMatch {
  id: string;
  score: number;
  metadata?: Record<string, unknown>;
}

Runtime Types — chống lock-in Cloudflare

Đây là interface quan trọng nhất — nó abstract hóa Cloudflare primitives để core không phụ thuộc trực tiếp:

// src/runtime.ts

export interface AgentRuntime {
  // Persistent state (Durable Object storage)
  getState<T>(key: string): Promise<T | null>;
  setState<T>(key: string, value: T): Promise<void>;
  deleteState(key: string): Promise<void>;

  // Scheduling
  scheduleTask(
    delayMs: number,
    taskName: string,
    payload: unknown
  ): Promise<string>; // returns taskId
  cancelTask(taskId: string): Promise<void>;

  // Real-time messaging
  sendToClient(connectionId: string, data: unknown): Promise<void>;
  broadcast(data: unknown): Promise<void>;
}

Khi Cloudflare thay đổi API, chỉ cần sửa CloudflareRuntime implements AgentRuntime — core không đổi gì.


Workflow Types

// src/workflow.ts

export interface WorkflowStep<TInput = unknown, TOutput = unknown> {
  name: string;
  execute(
    input: TInput,
    ctx: WorkflowContext
  ): Promise<TOutput>;
}

export interface WorkflowContext {
  agentCtx: AgentContext;
  results: Record<string, unknown>; // kết quả từ steps trước
  metadata: Record<string, unknown>;
}

export interface WorkflowDefinition {
  name: string;
  steps: WorkflowStep[];
  // Hàm quyết định step tiếp theo dựa trên kết quả
  next?: (
    stepName: string,
    result: unknown,
    ctx: WorkflowContext
  ) => string | null; // null = kết thúc
}

package.json

{
  "name": "@flare-agent/types",
  "version": "0.1.0",
  "type": "module",
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "types": "./dist/index.d.ts"
    }
  },
  "scripts": {
    "build": "tsup src/index.ts --format esm --dts",
    "dev": "tsup src/index.ts --format esm --dts --watch"
  },
  "devDependencies": {
    "tsup": "^8.0.0",
    "typescript": "^5.0.0"
  }
}

Chú ý: Package này zero runtime dependencies — chỉ có devDependencies.


Checklist trước khi sang bài 3

  • Scaffold Turborepo monorepo với pnpm
  • Tạo packages/types/
  • Viết đủ 6 files: llm.ts, tool.ts, agent.ts, memory.ts, runtime.ts, workflow.ts
  • Build thành công, không có TypeScript error

Bài tiếp theo: Bài 3 — LLM Router: Groq, Workers AI, Ollama trong 1 interface