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.

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

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

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.