返回 Skill 列表
extension
分类: AI Agent 能力无需 API Key

speak-reference-architecture

实现Speak参考架构,采用最佳实践项目布局用于语言学习应用程序。在设计新的Speak集成、审查项目结构或为语言学习应用程序建立架构标准时使用。可以通过诸如“speak架构”、“speak最佳实践”、“speak项目结构”、“如何组织speak”、“speak布局”等短语触发。

person作者: jakexiaohubgithub

Speak Reference Architecture

Overview

Production-ready architecture patterns for Speak language learning integrations.

Prerequisites

  • Understanding of layered architecture
  • Speak SDK knowledge
  • TypeScript project setup
  • Testing framework configured

Project Structure

my-speak-app/
├── src/
│   ├── speak/
│   │   ├── client.ts           # Singleton client wrapper
│   │   ├── config.ts           # Environment configuration
│   │   ├── types.ts            # TypeScript types
│   │   ├── errors.ts           # Custom error classes
│   │   └── handlers/
│   │       ├── webhooks.ts     # Webhook handlers
│   │       └── events.ts       # Event processing
│   ├── lessons/
│   │   ├── session.ts          # Session management
│   │   ├── conversation.ts     # Conversation practice
│   │   ├── pronunciation.ts    # Pronunciation training
│   │   └── vocabulary.ts       # Vocabulary exercises
│   ├── speech/
│   │   ├── recognizer.ts       # Speech recognition wrapper
│   │   ├── scorer.ts           # Pronunciation scoring
│   │   └── audio.ts            # Audio processing utilities
│   ├── progress/
│   │   ├── tracker.ts          # Progress tracking
│   │   ├── streaks.ts          # Streak management
│   │   └── achievements.ts     # Gamification
│   ├── services/
│   │   └── speak/
│   │       ├── index.ts        # Service facade
│   │       ├── sync.ts         # Data synchronization
│   │       └── cache.ts        # Caching layer
│   ├── api/
│   │   └── speak/
│   │       ├── lessons.ts      # Lesson endpoints
│   │       ├── progress.ts     # Progress endpoints
│   │       └── webhook.ts      # Webhook endpoint
│   └── jobs/
│       └── speak/
│           ├── sync.ts         # Background sync job
│           └── notifications.ts # Reminder notifications
├── tests/
│   ├── unit/
│   │   └── speak/
│   ├── integration/
│   │   └── speak/
│   └── fixtures/
│       └── audio/              # Test audio files
├── config/
│   ├── speak.development.json
│   ├── speak.staging.json
│   └── speak.production.json
└── docs/
    └── speak/
        ├── SETUP.md
        └── RUNBOOK.md

Layer Architecture

┌─────────────────────────────────────────┐
│             API Layer                    │
│   (Controllers, Routes, Webhooks)        │
├─────────────────────────────────────────┤
│           Lesson Layer                   │
│  (Conversation, Pronunciation, Vocab)    │
├─────────────────────────────────────────┤
│          Speech Layer                    │
│   (Recognition, Scoring, Audio)          │
├─────────────────────────────────────────┤
│          Speak SDK Layer                 │
│   (Client, Types, Error Handling)        │
├─────────────────────────────────────────┤
│         Infrastructure Layer             │
│    (Cache, Queue, Storage, Monitoring)   │
└─────────────────────────────────────────┘

Key Components

Step 1: Client Wrapper

// src/speak/client.ts
import { SpeakClient as SDKClient } from '@speak/language-sdk';

export class SpeakService {
  private client: SDKClient;
  private cache: Cache;
  private monitor: Monitor;

  constructor(config: SpeakConfig) {
    this.client = new SDKClient({
      apiKey: config.apiKey,
      appId: config.appId,
    });
    this.cache = new Cache(config.cacheOptions);
    this.monitor = new Monitor('speak');
  }

  get tutor() {
    return {
      startSession: (config: SessionConfig) =>
        this.monitor.track('tutor.startSession', () =>
          this.client.tutor.startSession(config)
        ),
      getPrompt: (sessionId: string) =>
        this.monitor.track('tutor.getPrompt', () =>
          this.client.tutor.getPrompt(sessionId)
        ),
    };
  }

  get speech() {
    return {
      recognize: (audio: ArrayBuffer) =>
        this.monitor.track('speech.recognize', () =>
          this.client.speech.recognize(audio)
        ),
      score: (audio: ArrayBuffer, text: string) =>
        this.monitor.track('speech.score', () =>
          this.client.speech.score({ audio, expectedText: text })
        ),
    };
  }

  get vocabulary() {
    return {
      lookup: (word: string, language: string) =>
        this.cache.getOrFetch(`vocab:${language}:${word}`, () =>
          this.client.vocabulary.lookup(word, language)
        ),
    };
  }
}

Step 2: Error Boundary

// src/speak/errors.ts
export class SpeakServiceError extends Error {
  constructor(
    message: string,
    public readonly code: string,
    public readonly retryable: boolean,
    public readonly originalError?: Error
  ) {
    super(message);
    this.name = 'SpeakServiceError';
  }

  static fromSDKError(error: unknown): SpeakServiceError {
    if (error instanceof RateLimitError) {
      return new SpeakServiceError(
        'Rate limit exceeded',
        'RATE_LIMITED',
        true,
        error
      );
    }
    if (error instanceof AuthError) {
      return new SpeakServiceError(
        'Authentication failed',
        'AUTH_ERROR',
        false,
        error
      );
    }
    if (error instanceof AudioError) {
      return new SpeakServiceError(
        'Audio processing failed',
        'AUDIO_ERROR',
        true,
        error
      );
    }
    return new SpeakServiceError(
      error instanceof Error ? error.message : 'Unknown error',
      'UNKNOWN',
      true
    );
  }
}

Step 3: Lesson Session Manager

// src/lessons/session.ts
interface ActiveSession {
  id: string;
  userId: string;
  language: string;
  topic: string;
  startedAt: Date;
  exchanges: number;
}

export class LessonSessionManager {
  private sessions: Map<string, ActiveSession> = new Map();
  private speakService: SpeakService;

  constructor(speakService: SpeakService) {
    this.speakService = speakService;
  }

  async startSession(userId: string, config: LessonConfig): Promise<Session> {
    // End existing session if any
    await this.endSession(userId);

    const session = await this.speakService.tutor.startSession({
      language: config.language,
      topic: config.topic,
      difficulty: config.difficulty,
    });

    this.sessions.set(userId, {
      id: session.id,
      userId,
      language: config.language,
      topic: config.topic,
      startedAt: new Date(),
      exchanges: 0,
    });

    // Track session start
    await analytics.track('lesson_started', {
      userId,
      sessionId: session.id,
      ...config,
    });

    return session;
  }

  getActiveSession(userId: string): ActiveSession | null {
    return this.sessions.get(userId) || null;
  }

  async endSession(userId: string): Promise<SessionSummary | null> {
    const active = this.sessions.get(userId);
    if (!active) return null;

    const summary = await this.speakService.tutor.endSession(active.id);
    this.sessions.delete(userId);

    // Track session end
    await analytics.track('lesson_completed', {
      userId,
      sessionId: active.id,
      duration: Date.now() - active.startedAt.getTime(),
      exchanges: active.exchanges,
    });

    return summary;
  }
}

Step 4: Health Check

// src/speak/health.ts
interface SpeakHealthStatus {
  status: 'healthy' | 'degraded' | 'unhealthy';
  latencyMs: number;
  services: {
    api: boolean;
    speech: boolean;
    tutor: boolean;
  };
}

export async function checkSpeakHealth(): Promise<SpeakHealthStatus> {
  const start = Date.now();
  const services = { api: false, speech: false, tutor: false };

  try {
    // Check API
    await speakService.health.check();
    services.api = true;

    // Check speech recognition (with minimal audio)
    services.speech = await testSpeechRecognition();

    // Check tutor availability
    services.tutor = await testTutorAvailability();

    return {
      status: Object.values(services).every(Boolean) ? 'healthy' : 'degraded',
      latencyMs: Date.now() - start,
      services,
    };
  } catch (error) {
    return {
      status: 'unhealthy',
      latencyMs: Date.now() - start,
      services,
    };
  }
}

Data Flow Diagram

User Interaction
     │
     ▼
┌─────────────┐
│   Web/App   │
│   Client    │
└──────┬──────┘
       │ Audio + Text
       ▼
┌─────────────┐    ┌─────────────┐
│   API       │───▶│   Cache     │
│   Gateway   │    │   (Redis)   │
└──────┬──────┘    └─────────────┘
       │
       ▼
┌─────────────┐    ┌─────────────┐
│   Lesson    │───▶│   Audio     │
│   Service   │    │   Storage   │
└──────┬──────┘    └─────────────┘
       │
       ▼
┌─────────────┐
│   Speak     │
│   SDK       │
└──────┬──────┘
       │
       ▼
┌─────────────┐
│   Speak     │
│   API       │
└─────────────┘

Configuration Management

// config/speak.ts
export interface SpeakConfig {
  apiKey: string;
  appId: string;
  environment: 'development' | 'staging' | 'production';
  defaultLanguage: string;
  supportedLanguages: string[];
  cache: {
    enabled: boolean;
    ttlSeconds: number;
  };
  audio: {
    maxDuration: number;
    format: 'wav' | 'mp3';
    sampleRate: number;
  };
}

export function loadSpeakConfig(): SpeakConfig {
  const env = process.env.NODE_ENV || 'development';
  const baseConfig = require('./speak.base.json');
  const envConfig = require(`./speak.${env}.json`);

  return {
    ...baseConfig,
    ...envConfig,
    apiKey: process.env.SPEAK_API_KEY!,
    appId: process.env.SPEAK_APP_ID!,
    environment: env as SpeakConfig['environment'],
  };
}

Output

  • Structured project layout
  • Client wrapper with caching
  • Error boundary implemented
  • Health checks configured
  • Session management

Error Handling

| Issue | Cause | Solution | |-------|-------|----------| | Circular dependencies | Wrong layering | Separate concerns by layer | | Config not loading | Wrong paths | Verify config file locations | | Type errors | Missing types | Add Speak types | | Test isolation | Shared state | Use dependency injection |

Examples

Quick Setup Script

# Create reference structure
mkdir -p src/speak/{handlers}
mkdir -p src/lessons src/speech src/progress
mkdir -p src/services/speak src/api/speak src/jobs/speak
mkdir -p tests/{unit,integration,fixtures}/speak
mkdir -p config docs/speak

touch src/speak/{client,config,types,errors}.ts
touch src/lessons/{session,conversation,pronunciation,vocabulary}.ts
touch src/speech/{recognizer,scorer,audio}.ts

Resources

Flagship Skills

For multi-environment setup, see speak-multi-env-setup.