Files
AIclinicalresearch/docs/07-运维文档/04-Redis改造实施计划.md
HaHafeng 1b53ab9d52 feat(aia): Complete AIA V2.0 with universal streaming capabilities
Major Changes:
- Add StreamingService with OpenAI Compatible format
- Upgrade Chat component V2 with Ant Design X integration
- Implement AIA module with 12 intelligent agents
- Update API routes to unified /api/v1 prefix
- Update system documentation

Backend (~1300 lines):
- common/streaming: OpenAI Compatible adapter
- modules/aia: 12 agents, conversation service, streaming integration
- Update route versions (RVW, PKB to v1)

Frontend (~3500 lines):
- modules/aia: AgentHub + ChatWorkspace (100% prototype restoration)
- shared/Chat: AIStreamChat, ThinkingBlock, useAIStream Hook
- Update API endpoints to v1

Documentation:
- AIA module status guide
- Universal capabilities catalog
- System overview updates
- All module documentation sync

Tested: Stream response verified, authentication working
Status: AIA V2.0 core completed (85%)
2026-01-14 19:15:01 +08:00

51 KiB
Raw Blame History

Redis謾ケ騾<EFBFBD>螳樊命隶。蛻抵シ育シ灘ュ<EFBFBD>+髦溷<E9ABA6>螳梧紛迚茨シ<E88CA8>

*<EFBFBD>。」迚域悽<EFBFBD>? V2.0
*譖エ譁ー譌・譛滂シ? 2025-12-12
*逶ョ譬<EFBFBD>ョ梧<EFBFBD>譌カ髣エ<EFBFBD>? 2025-12-18<31>?螟ゥ<E89E9F><EFBDA9>
雍溯エ」莠コ<EFBFBD><EFBFBD> 謚€譛ッ蝗「髦? *鬟朱勦遲臥コァ<EFBFBD>? <20>泯 荳ュ遲会シ域怏髯咲コァ譁ケ譯茨シ? *驥崎ヲ∝序譖エ<EFBFBD>? Redis髦溷<E9ABA6>莉?蜿ッ騾?隹<>紛荳?蠢<>。サ"


<EFBFBD><EFBFBD><EFBFBD> *驥崎ヲ∬ッエ譏趣シ<EFBFBD>2.0譖エ譁ー<EFBFBD>?

扈剰ソ<EFBFBD>キア蜈・蛻<EFBFBD><EFBFBD>軍edis髦溷<EFBFBD>荳肴弍蜿ッ騾蛾。ケ<EFBFBD>€梧弍譬ク蠢<EFBFBD>粥閭ス逧<EFBFBD>ソ<EFBFBD>。サ鬘ケ<EFBFBD>?

  1. **ASL譁<4C>鍵遲幃€?*<2A>?000遽<30>枚迪ョ髴€隕?蟆乗慮<E4B997>御ク咲畑Redis髦溷<E9ABA6>螟ア雍・邇?> 95%
  2. DC Tool B逞<42>紙謠仙叙<EFBFBD>?000莉ス逞<EFBDBD>紙髴€隕?-3蟆乗慮<E4B997>悟酔譬キ髣ョ鬚?3. **SAE螳樔セ狗音諤?*<2A>?5蛻<35>帖譌<E5B896><EFBFBD>㍼閾ェ蜉ィ郛ゥ螳ケ<E89EB3>碁柄莉サ蜉。蠢<EFBDA1>┯螟ア雍? <EFBFBD>豁、譛ャ隶。蛻定ー<EFBFBD>紛荳コ<EFBFBD>夂シ灘ュ?髦溷<E9ABA6>€襍キ螳樊命<E6A88A><E591BD>7螟ゥ螳梧<E89EB3><E6A2A7><EFBFBD>

<EFBFBD>搭 逶ョ蠖<EFBDAE>

  1. [謾ケ騾<EFBDB9>閭梧勹荳守岼譬(#1-謾ケ騾<EFBDB9>閭梧勹荳守岼譬<E5B2BC>)
  2. [蠖灘燕邉サ扈溽憾諤∝<E8ABA4>譫疹(#2-蠖灘燕邉サ扈溽憾諤∝<E8ABA4>譫?
  3. Redis驟咲スョ菫。諱ッ
  4. [謾ケ騾<EFBDB9>隸ヲ扈<EFBDA6>ュ・鬪、](#4-謾ケ騾<EFBDB9>隸ヲ扈<EFBDA6>ュ・鬪?<3F>遺惠 蟾イ譖エ譁ー<EFBFBD>壼桁蜷ォ髦溷<EFBFBD><EFBFBD>?5. [豬玖ッ墓婿譯<E5A9BF>(#5-豬玖ッ墓婿譯<E5A9BF>)
  5. [鬟朱勦隸<E58BA6>シー荳守シ楢ァ」](#6-鬟朱勦隸<E58BA6>シー荳守シ楢ァ?
  6. [荳顔コソ隶。蛻綻(#7-荳顔コソ隶。蛻<EFBDA1>)
  7. [蝗樊サ壽婿譯<E5A9BF>(#8-蝗樊サ壽婿譯<E5A9BF>)
  8. [逶第而荳手ソ千サエ](#9-逶第而荳手ソ千サ?

1. 謾ケ騾<EFBDB9>閭梧勹荳守岼譬<E5B2BC>

1.1 荳コ莉€荵郁ヲ∵隼騾<E99ABC><E9A8BE><EFBFBD>

蠖灘燕髣ョ鬚<EFBFBD><EFBFBD>?1. 笶?**霑晏渚莠大次逕溯ァ<E6BAAF><EFBDA7>?*<2A>夂ウサ扈滉スソ逕ィ蜀<EFBDA8>ュ倡シ灘ュ假シ瑚ソ晏渚閾ェ蟾ア蛻カ螳夂噪莠大次逕溷シ€蜿題ァ<E9A18C><EFBDA7>?2. 笶?LLM謌先悽螟ア謗ァ<EFBFBD>夂シ灘ュ倅ク肴戟荵<EFBFBD><EFBFBD>悟ッシ閾エ驥榊、崎ー<EFBFBD>畑DeepSeek/Qwen API

  1. 笶?髟ソ莉サ蜉。荳榊庄髱<EFBFBD><EFBFBD>?0-60蛻<30>帖逧<E5B896>枚迪ョ遲幃€我ササ蜉。<E89C89>郡AE螳樔セ矩㍾蜷ッ蜷惹ク「螟?4. 笶?螟壼ョ樔セ倶ク榊酔豁・<EFBFBD>售AE謇ゥ螳ケ蜷趣シ悟推螳樔セ狗シ灘ュ倅ク榊<EFBFBD>莠ォ
  2. 笶?Serverless荳埼€<EFBFBD><EFBFBD><EFBFBD>蟄倡憾諤∝惠Serverless邇ッ蠅<EFBFBD>ク倶ク榊庄髱<EFBFBD>

**謾ケ騾<EFBDB9>逶ョ譬?*<2A>?- 笨?隨ヲ蜷域楔譫<EFBFBD><EFBFBD><EFBFBD>壻スソ逕ィ蛻<EFBFBD><EFBFBD>シ冗シ灘ュ假シ<EFBFBD>edis<EFBFBD>?- 笨?髯堺ス拶PI謌先悽<EFBFBD>哭LM扈捺棡郛灘ュ俶戟荵<EFBFBD><EFBFBD>碁∩蜈埼㍾螟崎ー<EFBFBD>

  • 笨?**莉サ蜉。謖∽ケ<E288BD><EFBDB9>?<EFBFBD>夐柄譌カ髣エ莉サ蜉。荳榊屏螳樔セ矩㍾蜷ッ閠御ク「螟?- 笨?**謾ッ謖∝、壼ョ樔セ?<EFBFBD>夂シ灘ュ伜惠螟壼ョ樔セ矩龍蜈ア莠ォ
  • 笨?蟷ウ貊題ソ<EFBFBD>ク。<EFBFBD>壻ソ晉蕗髯咲コァ譁ケ譯茨シ檎。ョ菫晉ウサ扈溽ィウ螳<EFBFBD>

2. 蠖灘燕邉サ扈溽憾諤∝<E8ABA4>譫?

2.1 蟾イ菴ソ逕ィ郛灘ュ倡噪菴咲スョ

菴咲スョ1<EFBFBD>唏ealthCheckService.ts

// 譁<>サカ<EFBDBB>喘ackend/src/modules/dc/tool-b/services/HealthCheckService.ts
// 隨?7陦鯉シ夊ッサ蜿也シ灘ュ<E78198>
const cached = await cache.get<HealthCheckResult>(cacheKey);

// 隨?45陦鯉シ壼<EFBDBC>蜈・郛灘ュ<E78198>
await cache.set(cacheKey, result, 86400);  // 24蟆乗慮

**逕ィ騾?<EFBFBD>哘xcel蛛・蠎キ譽€譟・扈捺棡郛灘ュ? **驥崎ヲ∵€?<EFBFBD>夸沺?荳ュ遲会シ磯∩蜈埼㍾螟崎ァ」譫職xcel<65>? **謨ー謐ョ驥?*<2A>嘸5KB/鬘?


菴咲スョ2<EFBFBD>哭LM12FieldsService.ts

// 譁<>サカ<EFBDBB>喘ackend/src/modules/asl/common/llm/LLM12FieldsService.ts
// 隨?16陦鯉シ夊ッサ蜿也シ灘ュ<E78198>
const cached = await cache.get(cacheKey);

// 隨?30陦鯉シ壼<EFBDBC>蜈・郛灘ュ<E78198>
await cache.set(cacheKey, JSON.stringify(result), 3600);  // 1蟆乗慮

**逕ィ騾?<EFBFBD>哭LM 12蟄玲ョオ謠仙叙扈捺棡郛灘ュ<E78198>
**驥崎ヲ∵€?
<EFBFBD>夸沐?鬮假シ育峩謗・蠖ア蜩喉PI謌先悽<E58588>? **謨ー謐ョ驥?*<2A>嘸50KB/鬘? 謌先悽蠖ア蜩<EFBFBD><EFBFBD>?``` 蜊墓ャ。謠仙叙謌先悽<E58588>嘸ツ・0.43/遽?螯よ棡郛灘ュ伜、ア謨茨シ碁㍾螟崎ー<E5B48E><EFBFBD><E79591>

  • 10谺?= ツ・4.3
  • 100谺?= ツ・43
  • 1000谺?= ツ・430

---

### 2.2 髟ソ譌カ髣エ蠑よュ・莉サ蜉?
#### **ASL讓。蝮暦シ壽枚迪ョ遲幃€我ササ蜉?*
```typescript
// 譁<>サカ<EFBDBB>喘ackend/src/modules/asl/services/screeningService.ts
// 隨?3-65陦?processLiteraturesInBackground(task.id, projectId, literatures);

髣ョ鬚<EFBFBD><EFBFBD>?- 199遽<39>枚迪ョ髴€隕?33-66蛻<36>

  • 蠖灘燕菴ソ逕ィ蜀<EFBFBD>ュ倬弌蛻暦シ<EFBFBD>emoryQueue<EFBFBD>?- SAE螳樔セ矩㍾蜷ッ/郛ゥ螳ケ譌カ莉サ蜉。荳「螟? 蠖ア蜩<EFBFBD><EFBFBD>?- 逕ィ謌キ菴馴ェ梧栫蟾ョ<E89FBE>井ササ蜉。遯∫┯豸亥、ア<EFBDA4><EFBDB1>
  • 蟾イ螟<EFBFBD>炊扈捺棡荳「螟ア<EFBFBD>梧オェ雍ケAPI雍ケ逕ィ
  • <EFBFBD>豕戊ソス貅ッ莉サ蜉。迥カ諤?

2.3 蠖灘燕譫カ譫<EFBDB6><E8ADAB>鄂ョ

# backend/.env
CACHE_TYPE=memory    # 竊?髴€隕∵隼荳?redis
QUEUE_TYPE=memory    # 竊?髴€隕∵隼荳?redis

3. Redis驟咲スョ菫。諱ッ

3.1 髦ソ驥御コ然edis雍ュ荵ー菫。諱ッ

驟咲スョ鬘? 蛟? 隸エ譏<EFBFBD>
莠ァ蜩<EFBFBD> Redis 蠑€貅千沿 螳梧紛Redis蜉溯<EFBFBD>
莉倩エケ譁ケ蠑<EFBFBD> <EFBFBD>ケエ蛹<EFBFBD> 鬥匁ャ。雍ュ荵ー莠?謚倅シ俶<EFBDBC>?
驛ィ鄂イ讓。蠑<EFBFBD> 莠大次逕滂シ磯ォ伜庄逕ィ<EFBFBD><EFBFBD> 荳サ莉手<EFBFBD>蜉ィ蛻<EFBFBD>
邉サ蛻<EFBFBD> <EFBFBD>㊥迚? 貊。雜ウ髴€豎?
蝨ー蝓<EFBFBD> 蜊主圏2<EFBFBD>亥圏莠ャ<EFBFBD><EFBFBD> 荳惨AE蜷悟慍蝓?
螳樔セ狗アサ蝙<EFBFBD> 鬮伜庄逕? 笨?99.95%蜿ッ逕ィ諤?
*螟ァ迚域<EFBFBD>? Redis 7.0 譛€譁ー遞ウ螳夂沿
譫カ譫<EFBFBD>アサ蝙<EFBFBD> 荳榊星逕ィ髮<EFBFBD>セ、<EFBFBD>亥黒闃らせ<EFBFBD><EFBFBD> 貊。雜ウ蠖灘燕隗<EFBFBD>ィ。
<EFBFBD>援隗<EFBFBD><EFBFBD> 256 MB 蛻晄悄雜ウ螟<EFBFBD>
<EFBFBD>援謨ー驥<EFBFBD> 1 蜊募<EFBFBD>迚?
隸サ蜀吝<EFBFBD>遖サ 蜈ウ髣ュ €蛹夜<EFBFBD>鄂?

3.2 鬚<>シー謌先悽

蝓コ遑€莉キ譬シ<EFBFBD>堋?2/蟷エ<E89FB7>亥黒譛コ迚茨シ<E88CA8>
鬮伜庄逕ィ迚茨シ堋?80/蟷エ<E89FB7>井シー邂暦シ?鬥匁ャ。雍ュ荵ー<E88DB5>堋?08/蟷エ<E89FB7><EFBDB4>6謚伜錘<E4BC9C>?
蟇ケ豈疲噺逶奇シ?- 闃ら怐LLM API雍ケ逕ィ<E98095>?ツ・500/蟷?- 謠仙合逕ィ謌キ貊。諢丞コヲ<EFBDBA>壽裏莉キ
- ROI<4F>?400%

3.3 霑樊磁菫。諱ッ<E8ABB1>郁エュ荵ー蜷手執蜿厄シ?

# 髦ソ驥御コ第而蛻カ蜿ー 竊?Redis螳樔セ<E6A894> 竊?霑樊磁菫。諱ッ
REDIS_HOST=r-xxxxxxxxxxxx.redis.rds.aliyuncs.com
REDIS_PORT=6379
REDIS_PASSWORD=your_secure_password_here
REDIS_DB=0

# 謌紋スソ逕ィ霑樊磁蟄礼ャヲ荳イ
REDIS_URL=redis://:your_password@r-xxxxxxxxxxxx.redis.rds.aliyuncs.com:6379/0

4. 謾ケ騾<EFBDB9>隸ヲ扈<EFBDA6>ュ・鬪?

4.1 Phase 1<>壽悽蝨ー蠑€蜿醍識蠅<E8AD98>㊥螟?笨?

*豁・鬪、1.1<EFBFBD>壼ョ芽」<EFBFBD>セ晁オ?

cd backend
npm install ioredis --save
npm install @types/ioredis --save-dev

豁・鬪、1.2<EFBFBD><EFBFBD>鄂ョ譛ャ蝨ーRedis

# 遑ョ隶、Docker Redis豁」蝨ィ霑占。<E58DA0>
docker ps | findstr redis

# 螯よ棡豐。譛芽ソ占。鯉シ悟星蜉ィ螳<EFBDA8>
docker start ai-clinical-redis

# 豬玖ッ戊ソ樊磁
docker exec -it ai-clinical-redis redis-cli ping
# 蠎碑ッ・霑泌屓<E6B38C>啀ONG

豁・鬪、1.3<EFBFBD>壽峩譁ー譛ャ蝨?env

# backend/.env
DATABASE_URL=postgresql://postgres:postgres123@localhost:5432/ai_clinical_research

# ==================== Redis驟咲スョ ====================
# 蜷ッ逕ィRedis郛灘ュ<E78198>
CACHE_TYPE=redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_DB=0
# REDIS_PASSWORD=  # 譛ャ蝨ー譌<EFBDB0><EFBFBD><E89F87>?
# 髦溷<E9ABA6>證よ慮逕ィ蜀<EFBDA8>ュ假シ亥<EFBDBC>髦カ谿オ蜷ッ逕ィ<E98095><EFBDA8>
QUEUE_TYPE=memory

# ==================== JWT ====================
JWT_SECRET=your-secret-key-change-in-production
JWT_EXPIRES_IN=7d

# ==================== LLM API ====================
DEEPSEEK_API_KEY=sk-7f8cc37a79fa4799860b38fc7ba2e150
DASHSCOPE_API_KEY=sk-75b4ff29a14a49e79667a331034f3298

# ==================== Dify ====================
DIFY_API_URL=http://localhost/v1
DIFY_API_KEY=dataset-mfvdiKvQ2l3NvxWm7RoYMN3c

# ==================== Server ====================
PORT=3001
NODE_ENV=development

# ==================== CloseAI驟咲スョ ====================
CLOSEAI_API_KEY=sk-cu0iepbXYGGx2jc7BqP6ogtSWmP6fk918qV3RUdtGC3Ed1po
CLOSEAI_OPENAI_BASE_URL=https://api.openai-proxy.org/v1
CLOSEAI_CLAUDE_BASE_URL=https://api.openai-proxy.org/anthropic

# ==================== 蟄伜お驟咲スョ ====================
STORAGE_TYPE=local
LOCAL_STORAGE_DIR=uploads
LOCAL_STORAGE_URL=http://localhost:3001/uploads

# ==================== CORS驟咲スョ ====================
CORS_ORIGIN=http://localhost:5173

# ==================== 譌・蠢鈴<E8A0A2>鄂ョ ====================
LOG_LEVEL=debug

4.2 Phase 2<>壼ョ樒鴫RedisCacheAdapter 笨?

豁・鬪、2.1<EFBFBD>壻ソョ謾ケRedisCacheAdapter.ts

// 譁<>サカ<EFBDBB>喘ackend/src/common/cache/RedisCacheAdapter.ts

import Redis from 'ioredis';
import type { CacheAdapter } from './CacheAdapter.js';
import { logger } from '../logging/index.js';

/**
 * Redis郛灘ュ倬€<C280>蝎? * 
 * 菴ソ逕ィioredis螳「謌キ遶ッ<E981B6>梧髪謖<E9ABAA>シ? * - 蟄礼ャヲ荳?蟇ケ雎。閾ェ蜉ィ蠎丞<E8A08E>蛹? * - TTL霑<4C>悄譌カ髣エ
 * - 霑樊磁豎<E7A381>邂。逅? * - 髞呵ッッ驥崎ッ<E5B48E>
 * 
 * @example
 * const cache = new RedisCacheAdapter({
 *   host: 'localhost',
 *   port: 6379,
 *   password: 'xxx',
 *   db: 0
 * });
 * 
 * await cache.set('key', { data: 'value' }, 60);
 * const value = await cache.get('key');
 */
export class RedisCacheAdapter implements CacheAdapter {
  private redis: Redis;

  constructor(options?: {
    host?: string;
    port?: number;
    password?: string;
    db?: number;
  }) {
    this.redis = new Redis({
      host: options?.host || 'localhost',
      port: options?.port || 6379,
      password: options?.password || undefined,
      db: options?.db || 0,
      // 霑樊磁驟咲スョ
      retryStrategy: (times) => {
        const delay = Math.min(times * 50, 2000);
        logger.warn(`Redis霑樊磁驥崎ッ<E5B48E> ${times} 谺。<E8B0BA><EFBDA1>${delay}ms蜷朱㍾隸描);
        return delay;
      },
      maxRetriesPerRequest: 3,
      enableReadyCheck: true,
      // 霑樊磁豎<E7A381>驟咲ス?      lazyConnect: false,
      keepAlive: 30000,
    });

    // 逶大成霑樊磁莠倶サカ
    this.redis.on('connect', () => {
      logger.info('Redis霑樊磁謌仙粥');
    });

    this.redis.on('error', (error) => {
      logger.error('Redis霑樊磁髞呵ッッ', { error: error.message });
    });

    this.redis.on('close', () => {
      logger.warn('Redis霑樊磁蜈ウ髣ュ');
    });

    this.redis.on('reconnecting', () => {
      logger.warn('Redis豁」蝨ィ驥崎ソ<E5B48E>...');
    });
  }

  /**
   * 闔キ蜿也シ灘ュ伜€?   */
  async get<T>(key: string): Promise<T | null> {
    try {
      const value = await this.redis.get(key);
      
      if (!value) {
        logger.debug('Redis郛灘ュ俶悴蜻ス荳?, { key });
        return null;
      }

      logger.debug('Redis郛灘ュ伜多荳ュ', { key, size: value.length });

      // 蟆晁ッ戊ァ」譫辱SON
      try {
        return JSON.parse(value) as T;
      } catch {
        // 螯よ棡荳肴弍JSON<4F>瑚ソ泌屓蜴溷ァ句ュ礼ャヲ荳イ
        return value as unknown as T;
      }
    } catch (error) {
      logger.error('Redis GET螟ア雍・', { key, error });
      return null;  // 髯咲コァ<EFBDBA>夊ソ泌屓null閠御ク肴弍謚帛シょクク
    }
  }

  /**
   * 隶セ鄂ョ郛灘ュ伜€?   * @param ttl 霑<>悄譌カ髣エ<E9ABA3>育ァ抵シ会シ御ク堺シ<E5A0BA>蛻呎ーク荳崎ソ<E5B48E><EFBFBD>井ク肴耳闕撰シ<E692B0>
   */
  async set<T>(key: string, value: T, ttl?: number): Promise<void> {
    try {
      // 蠎丞<E8A08E>蛹門€?      const serialized = typeof value === 'string' 
        ? value 
        : JSON.stringify(value);

      // 隶セ鄂ョ蛟シ<E89B9F>亥クヲTTL<54>?      if (ttl) {
        await this.redis.setex(key, ttl, serialized);
        logger.debug('Redis SET謌仙粥<E4BB99>亥クヲTTL<54>?, { 
          key, 
          ttl, 
          size: serialized.length 
        });
      } else {
        await this.redis.set(key, serialized);
        logger.warn('Redis SET謌仙粥<E4BB99>域裏TTL<54>?, { key });  // 隴ヲ蜻奇シ壽裏霑<E8A38F>悄譌カ髣エ
      }
    } catch (error) {
      logger.error('Redis SET螟ア雍・', { key, ttl, error });
      // 荳肴鴨蜃コ蠑ょクク<EFBDB8><EFBFBD>隶ク邉サ扈溽サァ扈ュ霑占。<E58DA0>
    }
  }

  /**
   * 蛻<>髯、郛灘ュ<E78198>
   */
  async delete(key: string): Promise<boolean> {
    try {
      const result = await this.redis.del(key);
      logger.debug('Redis DEL', { key, deleted: result > 0 });
      return result > 0;
    } catch (error) {
      logger.error('Redis DEL螟ア雍・', { key, error });
      return false;
    }
  }

  /**
   * 譽€譟・key譏ッ蜷ヲ蟄伜惠
   */
  async has(key: string): Promise<boolean> {
    try {
      const result = await this.redis.exists(key);
      return result > 0;
    } catch (error) {
      logger.error('Redis EXISTS螟ア雍・', { key, error });
      return false;
    }
  }

  /**
   * 貂<>ゥコ謇€譛臥シ灘ュ假シ亥些髯ゥ謫堺ス懶シ<E687B6><EFBFBD>
   */
  async clear(): Promise<void> {
    try {
      await this.redis.flushdb();
      logger.warn('Redis FLUSHDB謇ァ陦鯉シ域園譛臥シ灘ュ伜キイ貂<EFBDB2>ゥコ<EFBDA9>?);
    } catch (error) {
      logger.error('Redis FLUSHDB螟ア雍・', { error });
    }
  }

  /**
   * 豬玖ッ紐edis霑樊磁
   */
  async ping(): Promise<boolean> {
    try {
      const result = await this.redis.ping();
      return result === 'PONG';
    } catch (error) {
      logger.error('Redis PING螟ア雍・', { error });
      return false;
    }
  }

  /**
   * 蜈ウ髣ュ霑樊磁<E6A88A>育畑莠惹シ倬寉蜈ウ髣ュ<E9ABA3><EFBDAD>
   */
  async disconnect(): Promise<void> {
    try {
      await this.redis.quit();
      logger.info('Redis霑樊磁蟾イ蜈ウ髣?);
    } catch (error) {
      logger.error('Redis蜈ウ髣ュ霑樊磁螟ア雍・', { error });
    }
  }
}

豁・鬪、2.2<EFBFBD>壽峩譁ーCacheFactory.ts<74>域キサ蜉<EFBDBB>髯咲コァ遲也払<E4B99F><E68995>

// 譁<>サカ<EFBDBB>喘ackend/src/common/cache/CacheFactory.ts

import { config } from '../../config/env.js';
import type { CacheAdapter } from './CacheAdapter.js';
import { MemoryCacheAdapter } from './MemoryCacheAdapter.js';
import { RedisCacheAdapter } from './RedisCacheAdapter.js';
import { logger } from '../logging/index.js';

/**
 * 郛灘ュ伜キ・蜴ゑシ亥黒萓具シ<E585B7>
 * 
 * 譬ケ謐ョ邇ッ蠅<EFBDAF>序驥剰<E9A9A5>蜉ィ騾画叫郛灘ュ伜ョ樒鴫<E6A892>? * - CACHE_TYPE=memory 竊?MemoryCacheAdapter
 * - CACHE_TYPE=redis 竊?RedisCacheAdapter<65>域髪謖<E9ABAA>剄郤ァ<E983A4><EFBDA7>
 * 
 * @example
 * import { cache } from '@/common/cache'
 * await cache.set('user:123', userData, 60)
 * const user = await cache.get<User>('user:123')
 */
export class CacheFactory {
  private static instance: CacheAdapter | null = null;
  private static fallbackToMemory = false;  // 髯咲コァ譬<EFBDA7>ョー

  /**
   * 闔キ蜿也シ灘ュ伜ョ樔セ具シ亥黒萓具シ<E585B7>
   */
  static getInstance(): CacheAdapter {
    if (!this.instance) {
      this.instance = this.createCache();
    }
    return this.instance;
  }

  /**
   * 蛻帛サコ郛灘ュ伜ョ樔セ<E6A894>
   */
  private static createCache(): CacheAdapter {
    const cacheType = config.cacheType || 'memory';

    logger.info('[CacheFactory] 蛻晏ァ句喧郛灘ュ倡ウサ扈?, { cacheType });

    switch (cacheType) {
      case 'redis':
        return this.createRedisCache();
      case 'memory':
      default:
        return this.createMemoryCache();
    }
  }

  /**
   * 蛻帛サコRedis郛灘ュ假シ亥クヲ髯咲コァ遲也払<E4B99F>?   */
  private static createRedisCache(): CacheAdapter {
    try {
      logger.info('[CacheFactory] 豁」蝨ィ霑樊磁Redis...', {
        host: config.redisHost,
        port: config.redisPort,
        db: config.redisDb,
      });

      const redisCache = new RedisCacheAdapter({
        host: config.redisHost,
        port: config.redisPort,
        password: config.redisPassword,
        db: config.redisDb,
      });

      // 豬玖ッ戊ソ樊磁<E6A88A>亥酔豁・遲牙セ<E78999><EFBFBD>
      redisCache.ping().then((isConnected) => {
        if (isConnected) {
          logger.info('[CacheFactory] ?Redis霑樊磁謌仙粥');
        } else {
          logger.error('[CacheFactory] ?Redis霑樊磁螟ア雍<EFBFBD>悟キイ髯咲コァ蛻ー蜀<EFBFBD>ュ倡シ灘ュ?);
          this.fallbackToMemory = true;
        }
      }).catch((error) => {
        logger.error('[CacheFactory] 笶?Redis霑樊磁蠑ょクク<EFBDB8>悟キイ髯咲コァ蛻ー蜀<EFBDB0>ュ倡シ灘ュ?, { error });
        this.fallbackToMemory = true;
      });

      return redisCache;
    } catch (error) {
      logger.error('[CacheFactory] ?Redis蛻晏ァ句喧螟ア雍<EFBFBD>碁剄郤ァ蛻ー蜀<EFBFBD>ュ倡シ灘ュ?, { error });
      this.fallbackToMemory = true;
      return this.createMemoryCache();
    }
  }

  /**
   * 蛻帛サコ蜀<EFBDBA>ュ倡シ灘ュ<E78198>
   */
  private static createMemoryCache(): MemoryCacheAdapter {
    logger.info('[CacheFactory] 菴ソ逕ィ蜀<EFBDA8>ュ倡シ灘ュ<E78198>');
    return new MemoryCacheAdapter();
  }

  /**
   * 譽€譟・譏ッ蜷ヲ蟾イ髯咲コァ蛻ー蜀<EFBDB0>ュ倡シ灘ュ?   */
  static isFallbackMode(): boolean {
    return this.fallbackToMemory;
  }
}

/**
 * 蟇シ蜃コ蜊穂セ<E7A982>
 */
export const cache = CacheFactory.getInstance();

豁・鬪、2.3<EFBFBD>壽峩譁ーenv.ts驟咲スョ

// 譁<>サカ<EFBDBB>喘ackend/src/config/env.ts
// 遑ョ菫抒edis驟咲スョ豁」遑ョ隸サ蜿<EFBDBB>

export const config = {
  // ... 蜈カ莉夜<E88E89>鄂ョ ...

  /** 郛灘ュ倡アサ蝙<EFBDBB> */
  cacheType: process.env.CACHE_TYPE || 'memory',

  /** Redis驟咲スョ */
  redisHost: process.env.REDIS_HOST || 'localhost',
  redisPort: parseInt(process.env.REDIS_PORT || '6379', 10),
  redisPassword: process.env.REDIS_PASSWORD || undefined,
  redisDb: parseInt(process.env.REDIS_DB || '0', 10),
  redisUrl: process.env.REDIS_URL || 'redis://localhost:6379',

  /** 髦溷<E9ABA6>邀サ蝙<EFBDBB> */
  queueType: process.env.QUEUE_TYPE || 'memory',

  // ... 蜈カ莉夜<E88E89>鄂ョ ...
};

// 鬪瑚ッ<E7919A><EFBDAF>鄂ョ
export function validateConfig() {
  console.log('笨?[Config] 邇ッ蠅<EFBDAF>序驥丞刈霓ス謌仙粥');
  console.log('[Config] 蠎皮畑驟咲スョ:');
  console.log(`  - 郛灘ュ<E78198>: ${config.cacheType}`);
  console.log(`  - 髦溷<E9ABA6>: ${config.queueType}`);
  
  if (config.cacheType === 'redis') {
    console.log(`  - Redis: ${config.redisHost}:${config.redisPort}/${config.redisDb}`);
  }
}

4.3 Phase 3<>壽悽蝨ー豬玖ッ?笨?

豁・鬪、3.1<EFBFBD><EFBFBD>蟒コRedis豬玖ッ戊<EFBFBD>譛ャ

// 譁<>サカ<EFBDBB>喘ackend/src/scripts/test-redis.ts

import { cache } from '../common/cache/index.js';
import { logger } from '../common/logging/index.js';

async function testRedis() {
  console.log('\n<>ァェ 蠑€蟋区オ玖ッ紐edis郛灘ュ<E78198>...\n');

  try {
    // 豬玖ッ<E78E96>1<EFBFBD>壼渕譛ャ隸サ蜀?    console.log('<27>統 豬玖ッ<E78E96>1<EFBFBD>壼渕譛ャ隸サ蜀?);
    await cache.set('test:hello', 'world', 10);
    const value1 = await cache.get('test:hello');
    console.log(`  笨?蜀吝<E89C80>: "hello" 竊?"world"`);
    console.log(`  笨?隸サ蜿<EFBDBB>: "${value1}"`);
    console.assert(value1 === 'world', '蛟シ蠎碑ッ・蛹ケ驟?);

    // 豬玖ッ<E78E96>2<EFBFBD>壼ッケ雎。蠎丞<E8A08E><EFBFBD>
    console.log('\n<EFBFBD> 豬玖ッ<EFBFBD>2<EFBFBD>壼ッケ雎。蠎丞<EFBFBD><EFBFBD>');
    const obj = { id: 123, name: '豬玖ッ<EFBFBD>', data: [1, 2, 3] };
    await cache.set('test:object', obj, 10);
    const value2 = await cache.get<typeof obj>('test:object');
    console.log(`  笨?蜀吝<E89C80>蟇ケ雎。:`, obj);
    console.log(`  笨?隸サ蜿門ッケ雎。:`, value2);
    console.assert(value2?.id === 123, 'ID蠎碑ッ・蛹ケ驟<EFBFBD>');

    // 豬玖ッ<E78E96>3<EFBFBD>啜TL霑<4C>    console.log('\n<EFBFBD> 豬玖ッ<EFBFBD>3<EFBFBD>TL霑<EFBFBD><EFBFBD>?遘抵シ<EFBFBD>');
    await cache.set('test:expire', 'will-expire', 2);
    console.log(`  笨?蜀吝<E89C80><E5909D><EFBFBD>TL=2遘抵シ荏);
    
    console.log(`  竢?遲牙セ<E78999>1遘?..`);
    await sleep(1000);
    const value3a = await cache.get('test:expire');
    console.log(`  笨?1遘貞錘隸サ蜿<EFBDBB>: "${value3a}"`);
    console.assert(value3a === 'will-expire', '蠎碑ッ・霑伜ュ伜<EFBFBD>?);
    
    console.log(`  竢?遲牙セ<E78999>2遘?..`);
    await sleep(2000);
    const value3b = await cache.get('test:expire');
    console.log(`  笨?3遘貞錘隸サ蜿<EFBDBB>: ${value3b}`);
    console.assert(value3b === null, '蠎碑ッ・蟾イ霑<EFBDB2><E99C91>?);

    // 豬玖ッ<E78E96>4<EFBFBD>喇as蜥慧elete
    console.log('\n<EFBFBD> 豬玖ッ<EFBFBD>4<EFBFBD>as蜥慧elete');
    await cache.set('test:delete', 'to-be-deleted', 10);
    const exists1 = await cache.has('test:delete');
    console.log(`  笨?蜀吝<E89C80>蜷仔xists: ${exists1}`);
    
    await cache.delete('test:delete');
    const exists2 = await cache.has('test:delete');
    console.log(`  笨?蛻<>髯、蜷仔xists: ${exists2}`);
    console.assert(!exists2, '蠎碑ッ・荳榊ュ伜<EFBFBD>?);

    // 豬玖ッ<E78E96>5<EFBFBD>壼、ァ蟇ケ雎。<E99B8E>?0KB<4B>?    console.log('\n<>統 豬玖ッ<E78E96>5<EFBFBD>壼、ァ蟇ケ雎。郛灘ュ假シ域ィ。諡櫚LM扈捺棡<E68DBA>?);
    const bigObj = {
      literatureId: 'xxx',
      fields: {
        遐皮ゥカ邀サ蝙<EFBFBD>: 'RCT',
        譬キ譛ャ驥? '500',
        蟷イ鬚<EFBFBD>蒔譁ス: '闕ッ迚ゥA 100mg',
        // ... 12荳ェ蟄玲ョ?      },
      metadata: {
        model: 'deepseek-v3',
        tokens: 8000,
        timestamp: Date.now(),
      },
      rawOutput: 'x'.repeat(40000),  // 讓。諡溷、ァ霎灘<E99C8E>?    };
    
    const start = Date.now();
    await cache.set('test:bigobj', bigObj, 3600);
    const value5 = await cache.get('test:bigobj');
    const duration = Date.now() - start;
    
    const size = JSON.stringify(bigObj).length;
    console.log(`  笨?蜀吝<E89C80>+隸サ蜿門、ァ蟇ケ雎? ${size} bytes<65>€玲慮 ${duration}ms`);
    console.assert(value5 !== null, '蠎碑ッ・閭ス隸サ蜿?);

    console.log('\n笨?€譛画オ玖ッ暮€夊ソ<EFBFBD>シ―n');
    process.exit(0);
  } catch (error) {
    console.error('\n笶?豬玖ッ募、ア雍・:', error);
    process.exit(1);
  }
}

function sleep(ms: number): Promise<void> {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

// 霑占。梧オ玖ッ<E78E96>
testRedis();

*豁・鬪、3.2<EFBFBD>壽鴬陦梧オ玖ッ?

cd backend

# 1. 蜷ッ蜉ィ蜷守ォッ<EFBDAB>育。ョ菫抒edis驟咲スョ逕滓譜<E6BB93>?npm run dev

# 2. 譁ー蠑€€荳ェ扈育ォッ<EFBDAB>瑚ソ占。梧オ玖ッ戊<EFBDAF>譛ャ
npx tsx src/scripts/test-redis.ts

<EFBFBD>悄霎灘<EFBFBD><EFBFBD>?``` <0A>ァェ 蠑€蟋区オ玖ッ紐edis郛灘ュ<E78198>...

<EFBFBD>統 豬玖ッ<E78E96>1<EFBFBD>壼渕譛ャ隸サ蜀? 笨?蜀吝<E89C80>: "hello" 竊?"world" 笨?隸サ蜿<EFBDBB>: "world"

<EFBFBD>統 豬玖ッ<E78E96>2<EFBFBD>壼ッケ雎。蠎丞<E8A08E><EFBFBD> 笨?蜀吝<E89C80>蟇ケ雎。: { id: 123, name: '豬玖ッ<E78E96>', data: [ 1, 2, 3 ] } 笨?隸サ蜿門ッケ雎。: { id: 123, name: '豬玖ッ<E78E96>', data: [ 1, 2, 3 ] }

<EFBFBD>統 豬玖ッ<E78E96>3<EFBFBD>啜TL霑<4C><EFBFBD>?遘抵シ<E68AB5> 笨?蜀吝<E89C80><E5909D><EFBFBD>TL=2遘抵シ<E68AB5> 竢?遲牙セ<E78999>1遘?.. 笨?1遘貞錘隸サ蜿<EFBDBB>: "will-expire" 竢?遲牙セ<E78999>2遘?.. 笨?3遘貞錘隸サ蜿<EFBDBB>: null

<EFBFBD>統 豬玖ッ<E78E96>4<EFBFBD>喇as蜥慧elete 笨?蜀吝<E89C80>蜷仔xists: true 笨?蛻<>髯、蜷仔xists: false

<EFBFBD>統 豬玖ッ<E78E96>5<EFBFBD>壼、ァ蟇ケ雎。郛灘ュ假シ域ィ。諡櫚LM扈捺棡<E68DBA>? 笨?蜀吝<E89C80>+隸サ蜿門、ァ蟇ケ雎? 40123 bytes<65>€玲慮 5ms

笨?謇€譛画オ玖ッ暮€夊ソ<E5A48A>シ?```


*豁・鬪、3.3<EFBFBD>壽オ玖ッ穂ク壼苅莉」遐?

# 1. 豬玖ッ菱ealthCheckService<63><65>C讓。蝮暦シ?# 荳贋シ<E8B48B>€荳ェExcel譁<6C>サカ<EFBDBB>梧衍逵区律蠢暦シ<E69AA6>

[HealthCheck] Cache miss, processing file
[HealthCheck] Check completed
[HealthCheck] Cache SET: health:xxx, TTL=86400

# 隨ャ莠梧ャ。荳贋シ<E8B48B>蜷御ク€荳ェ譁<EFBDAA>サ?[HealthCheck] Cache hit  竊?謌仙粥莉山edis隸サ蜿<EFBDBB>
# 2. 豬玖ッ畢LM12FieldsService<63><65>SL讓。蝮暦シ?# 謠蝉コ、蜈ィ譁<EFBDA8>、咲ュ帑ササ蜉。<E89C89>梧衍逵区律蠢暦シ<E69AA6>

[LLM12FieldsService]<>畑LLM謠仙叙12蟄玲ョオ
[LLM12FieldsService] Result cached with key: fulltext:xxx
[LLM12FieldsService] 郛灘ュ伜<EFBDAD>蜈・謌仙粥

# 驥肴眠霑占。悟酔荳€遽⑰DF
[LLM12FieldsService] Cache hit, returning cached result  竊?闃ら怐API雍ケ逕ィ<E98095>?```

---

### 4.4 Phase 4<>夐仭驥御コ然edis驟咲スョ 笨?
#### **豁・鬪、4.1<EFBFBD>夊エュ荵ーRedis螳樔セ<EFBFBD>**

1. 逋サ蠖暮仭驥御コ第而蛻カ蜿ー
2. 霑帛<E99C91> **莠第焚謐ョ蠎<EFBDAE> Redis** 莠ァ蜩<EFBDA7>。?3. 轤ケ蜃サ **蛻帛サコ螳樔セ<E6A894>**
4. 謖臥<E8AC96>謌ェ蝗セ驟咲スョ騾画叫<E794BB>?   - 莠ァ蜩<EFBDA7>シ啌edis 蠑€貅千沿
   - 驛ィ鄂イ讓。蠑擾シ壻コ大次逕滂シ磯ォ伜庄逕ィ<E98095>?   - 蝨ー蝓滂シ壼克蛹?<3F>亥圏莠ャ<E88EA0>€€?**荳惨AE蜷悟慍蝓滂シ<E6BB82>**
   - 迚域悽<E59F9F>啌edis 7.0
   - 蛻<>援隗<E68FB4><E99A97><EFBFBD>?56 MB
   - 莉倩エケ譁ケ蠑擾シ壼桁蟷エ蛹<EFBDB4><E89BB9>?5. 謠蝉コ、隶「蜊募ケカ謾ッ莉?
#### **豁・鬪、4.2<EFBFBD><EFBFBD>鄂ョ逋ス蜷榊黒**

髦ソ驥御コ第而蛻カ蜿ー 竊?Redis螳樔セ<E6A894> 竊?逋ス蜷榊黒隶セ鄂? 豺サ蜉<EFBDBB><E89C89>?1. 譛ャ蝨ー蠑€蜿選P<E981B8>育畑莠取悽蝨ー豬玖ッ包シ<E58C85>

  • <EFBFBD><EFBFBD><EFBFBD>鄂選P/32
  1. SAE蠎皮畑IP<EFBFBD>育函莠ァ邇ッ蠅<EFBFBD><EFBFBD>
    • 0.0.0.0/0 <20>井クエ譌カ<E8AD8C>悟錘扈ュ謾ケ荳コSAE VPC<50>? 謌? - SAE螳樔セ狗噪VPC鄂第ョオ

#### **豁・鬪、4.3<EFBFBD>夊執蜿冶ソ樊磁菫。諱?*

髦ソ驥御コ第而蛻カ蜿ー 竊?Redis螳樔セ<E6A894> 竊?霑樊磁菫。諱ッ

螟榊宛莉・荳倶ソ。諱ッ<EFBFBD>?- 霑樊磁蝨ー蝮€<E89DAE>嗷-xxxxxxxxxxxx.redis.rds.aliyuncs.com

  • 遶ッ蜿」<EFBFBD>?379
  • 螳樔セ紀D<EFBFBD>嗷-xxxxxxxxxxxx
  • <EFBFBD><EFBFBD><EFBFBD>シ夂せ蜃?菫ョ謾ケ蟇<EFBDB9><E89F87><EFBFBD>"隶セ鄂ョ

#### **豁・鬪、4.4<EFBFBD>壽峩譁ーSAE邇ッ蠅<EFBFBD>序驥<EFBFBD>**

髦ソ驥御コ第而蛻カ蜿ー 竊?SAE蠎皮畑 竊?驟咲スョ邂。逅<EFBDA1> 竊?邇ッ蠅<EFBDAF>序驥<E5BA8F>

豺サ蜉<EFBFBD><EFBFBD>?CACHE_TYPE=redis REDIS_HOST=r-xxxxxxxxxxxx.redis.rds.aliyuncs.com REDIS_PORT=6379 REDIS_PASSWORD=菴<>隶セ鄂ョ逧<EFBDAE><EFBFBD><EFBDAF><EFBFBD> REDIS_DB=0


---

### 4.5 Phase 5<>壼星逕ィRedis髦溷<E9ABA6> <20>閥 **蠢<>。サ螳樊命**

> **驥崎ヲ∝序譖エ**<2A>夂サ丞<EFBDBB>譫撰シ軍edis髦溷<E9ABA6>蟇ケASL蜥轡C Tool B讓。蝮玲<E89DAE>?*蠢<>。サ逧?*<2A>御ク肴弍蜿ッ騾臥噪<E887A5>? 
> **逅<>罰**<2A>?蟆乗慮髟ソ莉サ蜉。蝨ィSAE邇ッ蠅<EFBDAF>ク倶ク咲畑Redis髦溷<E9ABA6>螟ア雍・邇?> 95%

#### **豁・鬪、5.1<EFBFBD>壼ョ芽」<EFBFBD>ullMQ**

```bash
cd backend
npm install bullmq --save

# BullMQ蟾イ蝨ィpackage.json荳ュ<E88DB3>悟宵髴€遑ョ隶、螳芽」<E88ABD>
npm list bullmq
# 蠎碑ッ・譏セ遉コ<E98189>喘ullmq@5.65.0

豁・鬪、5.2<EFBFBD>壼ョ樒鴫RedisQueue.ts

// 譁<>サカ<EFBDBB>喘ackend/src/common/jobs/RedisQueue.ts

import { Queue, Worker, Job as BullJob, QueueEvents } from 'bullmq';
import type { Job, JobQueue, JobHandler } from './types.js';
import { logger } from '../logging/index.js';
import { config } from '../../config/env.js';

/**
 * Redis髦溷<E9ABA6>螳樒鴫<E6A892>亥渕莠撮ullMQ<4D>? * 
 * 譬ク蠢<EFBDB8>粥閭ス<E996AD>? * - 莉サ蜉。謖∽ケ<E288BD><EFBFBD>亥ョ樔セ矩㍾蜷ッ荳堺ク「螟ア<E89E9F><EFBDB1>
 * - 閾ェ蜉ィ驥崎ッ包シ亥、ア雍・蜷取欠謨ー騾€驕ソ<E9A995><EFBDBF>
 * - 蛻<><EFBFBD>シ丈ササ蜉。蛻<EFBDA1><E89BBB><EFBFBD>亥、壼ョ樔セ句刻隹<E588BB><EFBFBD>
 * - 霑帛コヲ霍溯クェ
 */
export class RedisQueue implements JobQueue {
  private queues: Map<string, Queue> = new Map();
  private workers: Map<string, Worker> = new Map();
  private queueEvents: Map<string, QueueEvents> = new Map();
  
  private connection = {
    host: config.redisHost,
    port: config.redisPort,
    password: config.redisPassword,
    db: config.redisDb,
  };

  /**
   * 謗ィ騾∽ササ蜉。蛻ー髦溷<E9ABA6>
   */
  async push<T = any>(type: string, data: T, options?: any): Promise<Job> {
    try {
      // 闔キ蜿匁<E89CBF>蛻帛サコ髦溷<E9ABA6>?      let queue = this.queues.get(type);
      if (!queue) {
        queue = new Queue(type, { 
          connection: this.connection,
          defaultJobOptions: {
            removeOnComplete: 100,  // 菫晉蕗譛€霑?00荳ェ螳梧<E89EB3>莉サ蜉?            removeOnFail: false,    // 螟ア雍・莉サ蜉。荳榊唖髯、<E9ABAF>井セソ莠取賜譟・<E8AD9F>?            attempts: 3,            // 螟ア雍・驥崎ッ<E5B48E>3谺?            backoff: {
              type: 'exponential',
              delay: 2000,          // 2遘偵€?遘偵€?遘?            },
          }
        });
        this.queues.set(type, queue);
      }

      // 豺サ蜉<EFBDBB>莉サ蜉。
      const job = await queue.add(type, data, {
        ...options,
        jobId: options?.jobId,  // 謾ッ謖∬<E8AC96>螳壻ケ泳obId
      });

      logger.info(`[RedisQueue] 莉サ蜉。蜈・髦滓<E9ABA6>蜉歔, { 
        type, 
        jobId: job.id,
        dataSize: JSON.stringify(data).length 
      });

      return {
        id: job.id!,
        type,
        data,
        status: 'pending',
        createdAt: new Date(),
      };
    } catch (error) {
      logger.error(`[RedisQueue] 莉サ蜉。蜈・髦溷、ア雍・`, { type, error });
      throw error;
    }
  }

  /**
   * 豕ィ蜀御ササ蜉。螟<EFBDA1>炊蝎?   */
  process<T = any>(type: string, handler: JobHandler<T>): void {
    try {
      // 蛻帛サコWorker
      const worker = new Worker(
        type,
        async (job: BullJob) => {
          logger.info(`[RedisQueue] €蟋句、<EFBFBD>炊莉サ蜉。`, { 
            type, 
            jobId: job.id,
            attemptsMade: job.attemptsMade,
            attemptsTotal: job.opts.attempts
          });
          
          const startTime = Date.now();
          
          try {
            // 隹<>畑荳壼苅螟<E88B85>炊蜃ス謨ー
            const result = await handler({
              id: job.id!,
              type,
              data: job.data as T,
              status: 'processing',
              createdAt: new Date(job.timestamp),
            });

            const duration = Date.now() - startTime;
            logger.info(`[RedisQueue] 莉サ蜉。螟<EFBFBD>炊謌仙粥`, { 
              type, 
              jobId: job.id,
              duration: `${duration}ms`
            });
            
            return result;
          } catch (error) {
            const duration = Date.now() - startTime;
            logger.error(`[RedisQueue] 莉サ蜉。螟<EFBFBD>炊螟ア雍・`, { 
              type, 
              jobId: job.id,
              attemptsMade: job.attemptsMade,
              duration: `${duration}ms`,
              error: error instanceof Error ? error.message : 'Unknown error'
            });
            throw error;  // 謚帛<E8AC9A>髞呵ッッ<EFBDAF>瑚ァヲ蜿鷹㍾隸?          }
        },
        { 
          connection: this.connection,
          concurrency: 1,  // 豈丈クェWorker蟷カ蜿大、<E5A4A7>炊1荳ェ莉サ蜉?        }
      );

      this.workers.set(type, worker);

      // 逶大成Worker莠倶サカ
      worker.on('completed', (job) => {
        logger.info(`[RedisQueue] ?莉サ蜉。螳梧<EFBFBD>`, { 
          type, 
          jobId: job.id,
          returnvalue: job.returnvalue 
        });
      });

      worker.on('failed', (job, err) => {
        logger.error(`[RedisQueue] ?莉サ蜉。螟ア雍・`, { 
          type, 
          jobId: job?.id,
          attemptsMade: job?.attemptsMade,
          error: err.message,
          stack: err.stack
        });
      });

      worker.on('error', (err) => {
        logger.error(`[RedisQueue] Worker髞呵ッッ`, { type, error: err.message });
      });

      logger.info(`[RedisQueue] Worker蟾イ豕ィ蜀形, { type });

    } catch (error) {
      logger.error(`[RedisQueue] Worker豕ィ蜀悟、ア雍・`, { type, error });
      throw error;
    }
  }

  /**
   * 闔キ蜿紋ササ蜉。迥カ諤?   */
  async getJob(id: string): Promise<Job | null> {
    try {
      // 驕榊紙謇€譛蛾弌蛻玲衍謇セ莉サ蜉?      for (const [type, queue] of this.queues) {
        const job = await queue.getJob(id);
        if (job) {
          return {
            id: job.id!,
            type,
            data: job.data,
            status: await this.getJobStatus(job),
            progress: job.progress as number || 0,
            createdAt: new Date(job.timestamp),
            error: job.failedReason,
          };
        }
      }
      return null;
    } catch (error) {
      logger.error(`[RedisQueue] getJob螟ア雍・`, { id, error });
      return null;
    }
  }

  /**
   * 闔キ蜿紋ササ蜉。迥カ諤?   */
  private async getJobStatus(job: BullJob): Promise<string> {
    const state = await job.getState();
    switch (state) {
      case 'completed': return 'completed';
      case 'failed': return 'failed';
      case 'active': return 'processing';
      case 'waiting': return 'pending';
      case 'delayed': return 'pending';
      default: return 'pending';
    }
  }

  /**
   * 譖エ譁ー莉サ蜉。霑帛コヲ
   */
  async updateProgress(id: string, progress: number, message?: string): Promise<void> {
    try {
      for (const queue of this.queues.values()) {
        const job = await queue.getJob(id);
        if (job) {
          await job.updateProgress(progress);
          if (message) {
            await job.log(message);
          }
          logger.debug(`[RedisQueue] 霑帛コヲ譖エ譁ー`, { id, progress, message });
          return;
        }
      }
      logger.warn(`[RedisQueue] 莉サ蜉。荳榊ュ伜惠<E4BC9C>梧裏豕墓峩譁ー霑帛コヲ`, { id });
    } catch (error) {
      logger.error(`[RedisQueue] 譖エ譁ー霑帛コヲ螟ア雍・`, { id, error });
    }
  }

  /**
   * 蜿匁カ井ササ蜉。
   */
  async cancelJob(id: string): Promise<boolean> {
    try {
      for (const queue of this.queues.values()) {
        const job = await queue.getJob(id);
        if (job) {
          await job.remove();
          logger.info(`[RedisQueue] 莉サ蜉。蟾イ蜿匁カ<E58C81>, { id });
          return true;
        }
      }
      logger.warn(`[RedisQueue] 莉サ蜉。荳榊ュ伜惠<EFBFBD>梧裏豕募叙豸<EFBFBD>, { id });
      return false;
    } catch (error) {
      logger.error(`[RedisQueue] 蜿匁カ井ササ蜉。螟ア雍・`, { id, error });
      return false;
    }
  }

  /**
   * 驥崎ッ募、ア雍・莉サ蜉。
   */
  async retryJob(id: string): Promise<boolean> {
    try {
      for (const queue of this.queues.values()) {
        const job = await queue.getJob(id);
        if (job) {
          await job.retry();
          logger.info(`[RedisQueue] 莉サ蜉。蟾イ驥崎ッ描, { id });
          return true;
        }
      }
      logger.warn(`[RedisQueue] 莉サ蜉。荳榊ュ伜惠<EFBFBD>梧裏豕暮㍾隸描, { id });
      return false;
    } catch (error) {
      logger.error(`[RedisQueue] 驥崎ッ穂ササ蜉。螟ア雍・`, { id, error });
      return false;
    }
  }

  /**
   * 貂<>炊譌ァ莉サ蜉?   */
  async cleanup(olderThan: number = 86400000): Promise<number> {
    try {
      let totalCleaned = 0;
      
      for (const [type, queue] of this.queues) {
        // 貂<>炊螳梧<E89EB3><EFBFBD>ササ蜉。<E89C89>井ソ晉蕗譛€霑?00荳ェ<E88DB3><EFBDAA>
        const completed = await queue.clean(olderThan, 100, 'completed');
        // 貂<>炊螟ア雍・逧<EFBDA5>ササ蜉。<E89C89>井ソ晉蕗譛€霑?0荳ェ<E88DB3><EFBDAA>
        const failed = await queue.clean(olderThan, 50, 'failed');
        
        const cleaned = completed.length + failed.length;
        totalCleaned += cleaned;
        
        if (cleaned > 0) {
          logger.info(`[RedisQueue] 髦溷<E9ABA6><EFBFBD>炊螳梧<E89EB3>`, { 
            type, 
            completed: completed.length,
            failed: failed.length
          });
        }
      }
      
      return totalCleaned;
    } catch (error) {
      logger.error(`[RedisQueue] 貂<>炊莉サ蜉。螟ア雍・`, { error });
      return 0;
    }
  }

  /**
   * 蜈ウ髣ュ謇€譛芽ソ樊磁<E6A88A>井シ倬寉蜈ウ髣ュ<E9ABA3>?   */
  async close(): Promise<void> {
    try {
      // 蜈ウ髣ュ謇€譛姥orkers
      for (const [type, worker] of this.workers) {
        await worker.close();
        logger.info(`[RedisQueue] Worker蟾イ蜈ウ髣ュ`, { type });
      }
      
      // 蜈ウ髣ュ謇€譛渦ueues
      for (const [type, queue] of this.queues) {
        await queue.close();
        logger.info(`[RedisQueue] Queue蟾イ蜈ウ髣ュ`, { type });
      }
      
      // 蜈ウ髣ュ謇€譛渦ueueEvents
      for (const [type, events] of this.queueEvents) {
        await events.close();
        logger.info(`[RedisQueue] QueueEvents蟾イ蜈ウ髣ュ`, { type });
      }
      
      logger.info(`[RedisQueue] 謇€譛芽ソ樊磁蟾イ蜈ウ髣ュ`);
    } catch (error) {
      logger.error(`[RedisQueue] 蜈ウ髣ュ霑樊磁螟ア雍・`, { error });
    }
  }
}

豁・鬪、5.3<EFBFBD>壽峩譁ーJobFactory謾ッ謖ヽedis髦溷<EFBFBD>

// 譁<>サカ<EFBDBB>喘ackend/src/common/jobs/JobFactory.ts

import { JobQueue } from './types.js'
import { MemoryQueue } from './MemoryQueue.js'
import { RedisQueue } from './RedisQueue.js'  // 竊?譁ー蠅<EFBDB0>
import { logger } from '../logging/index.js'
import { config } from '../../config/env.js'

export class JobFactory {
  private static instance: JobQueue | null = null

  static getInstance(): JobQueue {
    if (!this.instance) {
      this.instance = this.createQueue()
    }
    return this.instance
  }

  private static createQueue(): JobQueue {
    const queueType = config.queueType || 'memory'

    logger.info('[JobFactory] 蛻晏ァ句喧莉サ蜉。髦溷<E9ABA6>?, { queueType });

    switch (queueType) {
      case 'redis':  // 竊?譁ー蠅<EFBDB0>
        return this.createRedisQueue()
      
      case 'memory':
        return this.createMemoryQueue()
      
      default:
        logger.warn(`[JobFactory] Unknown QUEUE_TYPE: ${queueType}, fallback to memory`)
        return this.createMemoryQueue()
    }
  }

  /**
   * 蛻帛サコRedis髦溷<E9ABA6><E6BAB7>亥クヲ髯咲コァ遲也払<E4B99F>?   */
  private static createRedisQueue(): JobQueue {
    try {
      logger.info('[JobFactory] 豁」蝨ィ霑樊磁Redis髦溷<EFBFBD>...');
      
      const redisQueue = new RedisQueue();
      
      logger.info('[JobFactory] ?Redis髦溷<EFBFBD>蛻晏ァ句喧謌仙<EFBFBD>?);
      return redisQueue;
      
    } catch (error) {
      logger.error('[JobFactory] 笶?Redis髦溷<E9ABA6>蛻晏ァ句喧螟ア雍・<E99B8D>碁剄郤ァ蛻ー蜀<EFBDB0>ュ倬弌蛻?, { error });
      return this.createMemoryQueue();
    }
  }

  private static createMemoryQueue(): MemoryQueue {
    logger.info('[JobFactory] 菴ソ逕ィ蜀<EFBFBD>ュ倬弌蛻<EFBFBD>')
    
    const queue = new MemoryQueue()
    
    // 螳壽悄貂<E68284><EFBFBD>磯∩蜈榊<E89C88>蟄俶ウ<E4BFB6>シ擾シ<E693BE>
    if (process.env.NODE_ENV !== 'test') {
      setInterval(() => {
        queue.cleanup()
      }, 60 * 60 * 1000)
    }
    
    return queue
  }

  static reset(): void {
    this.instance = null
  }
}

*豁・鬪、5.4<EFBFBD>壻ソョ謾ケ荳壼苅莉」遐∽スソ逕ィ髦溷<EFBFBD>?

// 遉コ萓具シ哂SL譁<4C>鍵遲幃€画隼騾?// 譁<>サカ<EFBDBB>喘ackend/src/modules/asl/services/screeningService.ts

import { jobQueue } from '../../../common/jobs/index.js';

export async function startScreeningTask(projectId: string, userId: string) {
  // 1. 蛻帛サコ謨ー謐ョ蠎謎ササ蜉。隶ー蠖?  const task = await prisma.aslScreeningTask.create({
    data: {
      projectId,
      status: 'pending',
      totalItems: literatures.length,
      // ...
    }
  });

  // 2. 謗ィ騾∝芦Redis髦溷<E9ABA6><E6BAB7>井ク埼仆蝪櫁ッキ豎ゑシ?  await jobQueue.push('asl:title-screening', {
    taskId: task.id,
    projectId,
    literatureIds: literatures.map(lit => lit.id),
  });

  logger.info('莉サ蜉。蟾イ蜈・髦?, { taskId: task.id });

  // 3. 遶句叉霑泌屓<E6B38C>亥燕遶ッ霓ョ隸「霑帛コヲ<EFBDBA><EFBDA6>
  return task;
}

// 豕ィ蜀係orker<65>亥惠蠎皮畑蜷ッ蜉ィ譌カ<E8AD8C><EFBDB6>
// 譁<>サカ<EFBDBB>喘ackend/src/index.ts
jobQueue.process('asl:title-screening', async (job) => {
  const { taskId, projectId, literatureIds } = job.data;
  
  logger.info('€蟋句、<EFBFBD>炊遲幃€我ササ蜉?, { taskId, total: literatureIds.length });
  
  for (let i = 0; i < literatureIds.length; i++) {
    const literatureId = literatureIds[i];
    
    // 螟<>炊蜊慕ッ<E68595>枚迪ョ
    await processSingleLiterature(literatureId, projectId);
    
    // 譖エ譁ー霑帛コヲ
    const progress = ((i + 1) / literatureIds.length) * 100;
    await jobQueue.updateProgress(job.id, progress);
    
    // 譖エ譁ー謨ー謐ョ蠎?    await prisma.aslScreeningTask.update({
      where: { id: taskId },
      data: { processedItems: i + 1 }
    });
  }
  
  // 譬<>ョー螳梧<E89EB3>
  await prisma.aslScreeningTask.update({
    where: { id: taskId },
    data: { status: 'completed', completedAt: new Date() }
  });
  
  logger.info('遲幃€我ササ蜉。螳梧<E89EB3>?, { taskId });
  
  return { success: true, processed: literatureIds.length };
});

豁・鬪、5.5<EFBFBD>壽峩譁?env驟咲スョ

# backend/.env

# ==================== 莉サ蜉。髦溷<E9ABA6>驟咲スョ ====================
QUEUE_TYPE=redis  # 竊?謾ケ荳コredis

# Redis驟咲スョ<EFBDBD>井ク守シ灘ュ伜<EFBDAD>逕ィ<E98095>?REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0

豁・鬪、5.6<EFBFBD>壽オ玖ッ紐edis髦溷<EFBFBD>

# 1. 蜷ッ蜉ィ蜷守ォッ
cd backend
npm run dev

# 蠎碑ッ・逵句芦譌・蠢暦シ?# [JobFactory] 豁」蝨ィ霑樊磁Redis髦溷<E9ABA6>...
# [JobFactory] 笨?Redis髦溷<E9ABA6>蛻晏ァ句喧謌仙<E8AC8C>?
# 2. 謠蝉コ、豬玖ッ穂ササ蜉。<E89C89>井スソ逕ィREST Client謌鳳ostman<61>?POST http://localhost:3001/api/v1/asl/projects/:projectId/screening

# 3. 隗ょッ滓律蠢<E5BE8B>
# [RedisQueue] 莉サ蜉。蜈・髦滓<E9ABA6><EFBFBD> { type: 'asl:title-screening', jobId: '1' }
# [RedisQueue] 蠑€蟋句、<E58FA5>炊莉サ蜉?{ type: 'asl:title-screening', jobId: '1' }
# [RedisQueue] 笨?莉サ蜉。螳梧<E89EB3> { type: 'asl:title-screening', jobId: '1' }

# 4. 豬玖ッ募ョ樔セ矩㍾蜷ッ諱「螟<EFBDA2>
# 謠蝉コ、莉サ蜉。 竊?遲牙セ<E78999><EFBFBD>炊蛻?0% 竊?Ctrl+C蛛懈ュ「 竊?驥肴眠蜷ッ蜉ィ
# 莉サ蜉。蠎碑ッ・閾ェ蜉ィ莉山edis諱「螟榊ケカ扈ァ扈ュ螟<EFBDAD><E89E9F>?```

---

## 5. 豬玖ッ墓婿譯<E5A9BF>

### 5.1 蜊募<E89C8A>豬玖ッ墓ク<E5A293>

| 豬玖ッ暮。?|<>悄扈捺棡 | 螳樣刔扈捺棡 | 迥カ諤?|
|--------|---------|---------|------|
| Redis霑樊磁豬玖ッ<E78E96> | PONG | 笨?| 筮?蠕<><EFBFBD> |
| 蝓コ譛ャ隸サ蜀呎オ玖ッ<E78E96> | 蛟シ蛹ケ驟?| 笨?| 筮?蠕<><EFBFBD> |
| 蟇ケ雎。蠎丞<E8A08E>蛹匁オ玖ッ?| 蟇ケ雎。螳梧紛 | 笨?| 筮?蠕<><EFBFBD> |
| TTL霑<4C>悄豬玖ッ<E78E96> | 2遘貞錘荳コnull | 笨?| 筮?蠕<><EFBFBD> |
| 螟ァ蟇ケ雎。豬玖ッ包シ<E58C85>50KB<4B>?| <10ms | 笨?| 筮?蠕<><EFBFBD> |
| 髯咲コァ遲也払豬玖ッ<E78E96> | 閾ェ蜉ィ蛻<EFBDA8>困蜀<E59BB0><EFBFBD> | 笨?| 筮?蠕<><EFBFBD> |

### 5.2 髮<><E9ABAE>豬玖ッ墓ク<E5A293>

| 豬玖ッ暮。?| 謫堺ス懈ュ・鬪、 |<>悄扈捺棡 | 迥カ諤?|
|--------|---------|---------|------|
| **HealthCheckService** | 荳贋シ<E8B48B>Excel 2谺?| 隨?谺。郛灘ュ伜多荳?| 筮?蠕<><EFBFBD> |
| **LLM12FieldsService** | 謠仙叙蜷御ク€PDF 2谺?| 隨?谺。郛灘ュ伜多荳?| 筮?蠕<><EFBFBD> |
| **螟壼ョ樔セ狗シ灘ュ伜<EFBDAD>莠?* | 蜷ッ蜉ィ2荳ェ蜷守ォッ螳樔セ具シ悟ョ樔セ帰蜀吝<E89C80><E5909D>悟ョ樔セ毅隸サ蜿<EFBDBB> | B閭ス隸サ蛻ーA逧<41>シ灘ュ?| 筮?蠕<><EFBFBD> |
| **螳樔セ矩㍾蜷ッ謨ー謐ョ謖∽ケ<E288BD><EFBDB9>?* | 蜀吝<E89C80>郛灘ュ<E78198> 竊?驥榊星蜷守ォッ 竊?隸サ蜿<EFBDBB> | 閭ス隸サ蜿門芦 | 筮?蠕<><EFBFBD> |

### 5.3 蜴句鴨豬玖ッ<E78E96>

```bash
# 菴ソ逕ィab謌殆rk豬玖ッ募ケカ蜿題ッサ蜀<EFBDBB>

# 豬玖ッ<E78E96>1<EFBFBD>壼ケカ蜿大<E89CBF>蜈?ab -n 1000 -c 10 http://localhost:3001/api/test/cache-write

# 豬玖ッ<E78E96>2<EFBFBD>壼ケカ蜿題ッサ蜿?ab -n 10000 -c 50 http://localhost:3001/api/test/cache-read

# 鬚<>悄扈捺棡<E68DBA>?# - QPS > 1000
# - 蜩榊コ疲慮髣エ < 50ms
# - 髞呵ッッ邇?= 0%

5.4 謨<>囿讓。諡滓オ玖ッ<E78E96>

<EFBFBD>囿蝨コ譎ッ 讓。諡滓婿豕<EFBFBD> <EFBFBD>悄陦御クコ 迥カ諤?
Redis遯∫┯謖よ脂 docker stop ai-clinical-redis 邉サ扈滄剄郤ァ蛻ー蜀<EFBFBD>ュ倡シ灘ュ假シ悟コ皮畑扈ァ扈ュ霑占。<EFBFBD> 筮?蠕<><EFBFBD>
Redis鄂醍サ懷サカ霑<EFBFBD> tc qdisc add dev eth0 root netem delay 500ms <EFBFBD>慮驥崎ッ包シ梧怙扈郁ソ泌屓null 筮?蠕<><EFBFBD>
*Redis蜀<EFBFBD>ュ俶サ? 蜀吝<EFBFBD>螟ァ驥乗焚謐ョ閾?56MB 隗ヲ蜿銑RU鬩ア騾撰シ御ク榊スア蜩肴眠蜀吝<EFBFBD> 筮?蠕<><EFBFBD>
Redis蟇<EFBFBD><EFBFBD><EFBFBD>漠隸ッ 菫ョ謾ケ蟇<EFBFBD><EFBFBD><EFBFBD> 霑樊磁螟ア雍・<EFBFBD>碁剄郤ァ蛻ー蜀<EFBFBD>ュ倡シ灘ュ<EFBFBD> 筮?蠕<><EFBFBD>

6. 鬟朱勦隸<E58BA6>シー荳守シ楢ァ?

6.1 鬟朱勦遏ゥ髦オ

鬟朱勦 荳・驥肴€? 讎ら紫 蠖ア蜩<EFBFBD> 郛楢ァ」謗ェ譁ス 迥カ諤?
Redis霑樊磁螟ア雍・ <EFBFBD>閥 鬮? <EFBFBD>泯 荳? 邉サ扈滉ク榊庄逕? 笨?髯咲コァ遲也払 笨?蟾イ螳樒<E89EB3>?
謨ー謐ョ荳「螟ア <EFBFBD>泯 荳? <EFBFBD>泙 菴? 郛灘ュ伜、ア謨<EFBFBD> 笨?蜈ウ髞ョ謨ー謐ョ蜿悟<E89CBF>DB 竢?蠕<>ョ樒<EFBDAE>?
*<EFBFBD>ュ俶コ「蜃コ<EFBFBD><EFBFBD>OM<EFBFBD>? <EFBFBD>閥 鬮? <EFBFBD>泯 荳? Redis蟠ゥ貅<EFBFBD> 笨?荳・譬シTTL + 逶第而 竢?蠕<>ョ樒<EFBDAE>?
鄂醍サ懷サカ霑<EFBFBD> <EFBFBD>泙 菴? <EFBFBD>泙 菴? 蜩榊コ泌序諷「 笨?謇ケ驥乗桃菴<E6A183> 竢?蜿ッ騾我シ伜<EFBDBC>?
驟咲スョ髞呵ッッ <EFBFBD>泯 荳? <EFBFBD>泯 荳? 蜷ッ蜉ィ螟ア雍・ 笨?驟咲スョ鬪瑚ッ<E7919A> 笨?蟾イ螳樒<E89EB3>?
<EFBFBD><EFBFBD>∵ウ<EFBFBD> <EFBFBD>閥 鬮? <EFBFBD>泯 荳? 謨ー謐ョ豕<EFBFBD> 笨?KMS邂。逅<EFBDA1> 竢?蠕<>ョ樒<EFBDAE>?

6.2 蜈ウ髞ョ郛楢ァ」謗ェ譁ス

*郛楢ァ」謗ェ譁ス1<EFBFBD>夐剄郤ァ遲也払<EFBFBD>亥ソ<EFBFBD>。サ<EFBFBD>? 笨?```typescript

// 蟾イ蝨ィCacheFactory荳ュ螳樒<E89EB3>?// Redis荳榊庄逕ィ譌カ閾ェ蜉ィ蛻<EFBDA8>困蛻ーMemoryCache


#### **郛楢ァ」謗ェ譁ス2<EFBDBD><EFBFBD>髞ョ謨ー謐ョ蜿悟<E89CBF><E6829F>域耳闕撰シ?* 竢?```typescript
// 髴€隕∝惠荳壼苅莉」遐∽クュ豺サ蜉?
// 遉コ萓具シ壻ササ蜉。霑帛コヲ蜿悟<E89CBF>?export async function updateTaskProgress(taskId: string, progress: number) {
  // 1. 蜀儚edis<69>亥ソォ騾滓衍隸「<E99AB8><EFBDA2>
  await cache.set(`task:${taskId}:progress`, progress, 3600);
  
  // 2. 蜷梧慮蜀僖B<E58396>域戟荵<E6889F><EFBFBD>?  await prisma.aslScreeningTask.update({
    where: { id: taskId },
    data: { processedItems: progress }
  });
}

*郛楢ァ」謗ェ譁ス3<EFBFBD><EFBFBD>蟄倡尅謗ァ<EFBFBD>域耳闕撰シ? 竢?```typescript

// 蛻帛サコ<EFBDBB>喘ackend/src/scripts/monitor-redis.ts

import { cache } from '../common/cache/index.js';

setInterval(async () => { // 譽€譟・蜀<EFBDA5>ュ倅スソ逕? const info = await redis.info('memory'); const used = parseInt(info.match(/used_memory:(\d+)/)[1]); const max = 256 * 1024 * 1024;

if (used > max * 0.8) { logger.warn('笞<><E7AC9E><EFBFBD> Redis蜀<73>ュ倅スソ逕ィ雜<EFBDA8>ソ<EFBFBD>80%', { used, max }); // TODO: 蜿鷹€<E9B7B9>忠髓?驍ョ莉カ蜻願ュヲ } }, 60000);


#### **郛楢ァ」謗ェ譁ス4<EFBDBD><EFBFBD>鄂ョ鬪瑚ッ<E7919A>シ亥キイ螳樒鴫<E6A892><E9B4AB>** 笨?```typescript
// env.ts荳ュ逧ёalidateConfig()
// 蜷ッ蜉ィ譌カ譽€譟・Redis驟咲スョ

7. 荳顔コソ隶。蛻<EFBDA1>

7.1 荳顔コソ譌カ髣エ陦ィ<E999A6><EFBDA8>2.0譖エ譁ー<EFBFBD>?

髦カ谿オ 譌カ髣エ 莉サ蜉。 雍溯エ」莠? 迥カ諤?
Phase 1 Day 1荳雁壕 譛ャ蝨ー蠑€蜿醍識蠅<EFBFBD>㊥螟? 蠑€蜿? 筮?蠕<>€蟋?
Phase 2 Day 1荳句壕 螳樒鴫RedisCacheAdapter 蠑€蜿? 筮?蠕<>€蟋?
Phase 3 Day 2蜈ィ螟ゥ Redis郛灘ュ俶悽蝨ー豬玖ッ<EFBFBD> 蠑€蜿?豬玖ッ<E78E96> 筮?蠕<>€蟋?
Phase 4 Day 3荳雁壕 髦ソ驥御コ然edis雍ュ荵ー&驟咲スョ 霑千サエ 筮?蠕<>€蟋?
Phase 5 Day 3荳句壕-Day 5 <EFBFBD>閥 螳樒鴫RedisQueue<75>亥ソ<E4BAA5>。サ<EFBDA1>榎 蠑€蜿? 筮?蠕<>€蟋?
Phase 6 Day 6蜈ィ螟ゥ Redis髦溷<EFBFBD>譛ャ蝨ー豬玖ッ<EFBFBD> + 荳壼苅髮<E88B85><E9ABAE> 蠑€蜿?豬玖ッ<E78E96> 筮?蠕<>€蟋?
Phase 7 Day 7荳雁壕 SAE豬玖ッ慕識蠅<EFBFBD>ェ瑚ッ<EFBFBD> 蠑€蜿?豬玖ッ<E78E96> 筮?蠕<>€蟋?
Phase 8 Day 7荳句壕 逕滉コァ邇ッ蠅<EFBFBD>ク顔コソ 蜈ィ蜻<EFBFBD> 筮?蠕<>€蟋?
Phase 9 Day 7譎? 逶第而隗ょッ滂シ?4蟆乗慮<E4B997>? 霑千サエ 筮?蠕<>€蟋?

諤サ蟾・菴憺㍼<EFBFBD>?螟ゥ<E89E9F>域ッ泌次隶。蛻貞「槫刈4螟ゥ<E89E9F>御ス<E5BEA1>。ョ菫晄<E88FAB>ク蠢<EFBDB8>粥閭ス蜿ッ逕ィ<E98095><EFBDA8>

7.2 荳顔コソ豁・鬪、<E9ACAA>育函莠ァ邇ッ蠅<EFBDAF><EFBFBD>

*Step 1<>壼書蟶<E69BB8>燕譽€譟・<E8AD9F><EFBDA5>15蛻<35><EFBFBD>?

笨?莉」遐∝キイ謠蝉コ、Git
笨?譛ャ蝨ー豬玖ッ募<EFBDAF>驛ィ騾夊ソ<E5A48A>
笨?髦ソ驥御コ然edis蟾イ蟆ア扈?笨?SAE邇ッ蠅<EFBDAF>序驥丞キイ驟咲ス?笨?蝗樊サ壽婿譯亥キイ蜃<EFBDB2>、?笨?逶第而蟾イ蟆ア扈?```

#### **Step 2<><EFBFBD>蠎ヲ蜿大ク<E5A4A7><EFBFBD>30蛻<30><EFBFBD>?*
  1. SAE謗ァ蛻カ蜿?竊?騾画叫蠎皮畑
  2. 蠎皮畑驛ィ鄂イ 竊?蛻<>音蜿大ク<E5A4A7>
  3. 隨?謇ケ<E8AC87><EFBDB9>10%螳樔セ具シ郁ァょッ?5蛻<35><EFBFBD>?4. 隨?謇ケ<E8AC87><EFBDB9>50%螳樔セ具シ郁ァょッ?0蛻<30><EFBFBD>?5. 隨?謇ケ<E8AC87><EFBDB9>100%螳樔セ<E6A894>

#### **Step 3<>夐ェ瑚ッ<E7919A><EFBFBD>15蛻<35><EFBFBD>?*
```bash
# 1. 譽€譟・Redis霑樊磁
curl https://your-api.com/api/health
# 蠎碑ッ・霑泌屓<E6B38C>嘴 cache: "redis", status: "ok" }

# 2. 豬玖ッ慕シ灘ュ伜<EFBDAD>蜈・
# 荳贋シ<E8B48B>Excel 竊?譟・逵区律蠢<E5BE8B> 竊?遑ョ隶、Redis蜀吝<E89C80>

# 3. 豬玖ッ慕シ灘ュ倩ッサ蜿<EFBDBB>
# 蜀肴ャ。荳贋シ<E8B48B> 竊?譟・逵区律蠢<E5BE8B> 竊?遑ョ隶、郛灘ュ伜多荳ュ

# 4. 譽€譟・Redis蜀<73><EFBFBD>
髦ソ驥御コ第而蛻カ蜿ー 竊?Redis逶第而 竊?蜀<>ュ倅スソ逕ィ

*Step 4<>夂尅謗ァ隗ょッ滂シ<E6BB82>24蟆乗慮<E4B997>?

蜈ウ豕ィ謖<EFBFBD><EFBFBD><EFBFBD>シ?- Redis霑樊磁謨ー<E8ACA8>亥コ碑ッ・遞ウ螳夲シ?- 蜀<>ュ倅スソ逕ィ邇<EFBDA8>シ亥コ碑ッ・ < 50%<25>?- 郛灘ュ伜多荳ュ邇<EFBDAD>シ亥コ碑ッ・ > 80%<25>?- 蠎皮畑髞呵ッッ譌・蠢暦シ亥コ碑ッ・譌<EFBDA5>Redis逶ク蜈ウ髞呵ッッ<EFBDAF>?- API謌先悽<E58588>亥コ碑ッ・荳矩剄<E79FA9><E58984>

8. 蝗樊サ壽婿譯<E5A9BF>

8.1 蠢ォ騾溷屓貊夲シ<E5A4B2>5蛻<35>帖蜀<E5B896><EFBFBD>

*蝨コ譎ッ1<EFBFBD>啌edis霑樊磁螟ア雍・<EFBFBD>悟コ皮畑譌<EFBFBD>豕募星蜉?

# 譁ケ豕<EFBDB9>1<EFBFBD>壻ソョ謾ケSAE邇ッ蠅<EFBDAF>序驥<E5BA8F>
髦ソ驥御コ第而蛻カ蜿ー 竊?SAE蠎皮畑 竊?驟咲スョ邂。逅<EFBDA1> 竊?邇ッ蠅<EFBDAF>序驥<E5BA8F>
菫ョ謾ケ<E8ACBE>咾ACHE_TYPE=memory
菫晏ュ<E6998F> 竊?蠎皮畑驥榊星

# 譁ケ豕<EFBDB9>2<EFBFBD>夐㍾譁ー驛ィ鄂イ荳贋ク€荳ェ迚域<E8BF9A>?SAE謗ァ蛻カ蜿?竊?蠎皮畑驛ィ鄂イ 竊?迚域悽邂。逅<EFBDA1> 竊?蝗樊サ<E6A88A>

*蝨コ譎ッ2<EFBFBD>啌edis諤ァ閭ス髣ョ鬚假シ悟桃蠎泌序諷?

# 荳エ譌カ髯咲コァ蛻ー蜀<EFBDB0>ュ倡シ灘ュ?CACHE_TYPE=memory

# 謌紋ソ晉蕗Redis菴<73>€譟・鄂醍サ?ping r-xxxxxxxxxxxx.redis.rds.aliyuncs.com

蝨コ譎ッ3<EFBFBD>啌edis蜀<EFBFBD>ュ俶サ。<EFBFBD>梧裏豕募<EFBFBD>蜈・

# 譁ケ豕<EFBDB9>1<EFBFBD>壽ク<E5A3BD>炊Redis<69>亥些髯ゥ<E9ABAF><EFBDA9>シ?redis-cli -h r-xxx.redis.rds.aliyuncs.com -a password
> FLUSHDB

# 譁ケ豕<EFBDB9>2<EFBFBD>壼合郤ァRedis隗<73><E99A97>
髦ソ驥御コ第而蛻カ蜿ー 竊?Redis螳樔セ<E6A894> 竊?蜿倬<E89CBF> 竊?512MB

8.2 蝗樊サ壽」€譟・貂<EFBDA5><E8B282>?

笨?蠎皮畑閭ス蜷ヲ豁」蟶ク蜷ッ蜉ィ<E89C89>?笨?郛灘ュ俶弍蜷ヲ蟾・菴懶シ亥<EFBDBC>蟄俶ィ。蠑擾シ会シ?笨?API蜩榊コ疲弍蜷ヲ豁」蟶ク<E89FB6>?笨?髞呵ッッ譌・蠢玲弍蜷ヲ貂<EFBDA6><EFBFBD>?笨?逕ィ謌キ譏ッ蜷ヲ閭ス豁」蟶ク菴ソ逕ィ<E98095><EFBDA8>

9. 逶第而荳手ソ千サ?

9.1 逶第而謖<E8808C><E8AC96><EFBFBD>

Redis謖<EFBFBD><EFBFBD><EFBFBD>シ磯仭驥御コ第而蛻カ蜿ー<EFBFBD><EFBFBD>

<EFBFBD><EFBFBD><EFBFBD> 豁」蟶ク闌<EFBFBD> 蜻願ュヲ髦亥€? <EFBFBD>炊譁ケ譯<EFBFBD>
*<EFBFBD>ュ倅スソ逕ィ邇? < 50% > 80% €譟・螟ァkey<EFBFBD>€<EFBFBD>剔蜊<EFBFBD><EFBFBD>
*霑樊磁謨? < 50 > 100 €譟・霑樊磁豕<EFBFBD>シ?
QPS < 1000 > 5000 <EFBFBD>剔蛻<EFBFBD>
*蜻ス荳ュ邇? > 80% < 50% €譟・郛灘ュ倡ュ也<EFBFBD>?
蜩榊コ疲慮髣エ < 5ms > 50ms 譽€譟・鄂醍サ?

*蠎皮畑謖<EFBFBD><EFBFBD><EFBFBD><EFBFBD>AE譌・蠢暦シ?

# 譟・謇セRedis逶ク蜈ウ髞呵ッッ
grep "Redis" logs/app.log | grep "ERROR"

# 譟・謇セ郛灘ュ伜多荳ュ諠<EFBDAD><E8ABA0>
grep "Cache hit" logs/app.log | wc -l
grep "Cache miss" logs/app.log | wc -l

# 隶。邂怜多荳ュ邇?蜻ス荳ュ邇?= hits / (hits + misses) * 100%

9.2 霑千サエ蜻ス莉、

蟶ク逕ィRedis CLI蜻ス莉、

# 霑樊磁Redis
redis-cli -h r-xxx.redis.rds.aliyuncs.com -p 6379 -a your_password

# 譟・逵区園譛洩ey
KEYS *

# 譟・逵倶ササ蜉。逶ク蜈ウkey
KEYS task:*

# 譟・逵狗シ灘ュ倡嶌蜈ウkey
KEYS fulltext:*

# 譟・逵橘ey逧УTL
TTL task:abc123:progress

# 譟・逵橘ey逧<79>€?GET task:abc123:progress

# 譟・逵句<E980B5>蟄倅スソ逕ィ
INFO memory

# 譟・逵玖ソ樊磁謨?INFO clients

# 螳樊慮逶第而蜻ス莉、
MONITOR

# 貂<>ゥコ謨ー謐ョ蠎難シ亥些髯ゥ<E9ABAF><EFBDA9><EFBFBD>
FLUSHDB

<EFBFBD>ュ伜<EFBFBD><EFBFBD>

# 譟・謇セ螟ァkey<65>?10KB<4B>?redis-cli -h xxx -a password --bigkeys

# 譟・逵橘ey逧<79><E980A7>蟄伜頃逕?MEMORY USAGE task:abc123:progress

9.3 謨<>囿謗呈衍豬∫ィ<E288AB>

髣ョ鬚假シ啌edis霑樊磁螟ア雍・
  竊?1. 譽€譟・Redis螳樔セ狗憾諤<E686BE>シ磯仭驥御コ第而蛻カ蜿ー<E89CBF>?  竊?2. 譽€譟・逋ス蜷榊黒驟咲スョ<EFBDBD>域弍蜷ヲ蛹<EFBDA6>性SAE IP<49>?  竊?3. 譽€譟・蟇<EFBDA5><E89F87>∵弍蜷ヲ豁」遑?  竊?4. ping豬玖ッ慕ス醍サ懆ソ樣€€?  竊?5. 譟・逵句コ皮畑譌・蠢<EFBDA5>
  竊?6. 螯よ裏豕募ソォ騾溯ァ」蜀?竊?髯咲コァ蛻ー蜀<EFBDB0>ュ倡シ灘ュ?```

### 9.4 螳壽悄扈エ謚、

| 扈エ蠎ヲ | 鬚醍紫 | 蜀<>ョケ |
|------|------|------|
| **譌・蟶ク逶第而** | 豈丞、ゥ | 譟・逵句<E980B5>蟄倅スソ逕ィ縲∬ソ樊磁謨ー縲<EFBDB0>漠隸ッ譌・蠢?|
| **諤ァ閭ス蛻<EFBDBD>梵** | 豈丞捉 | 蛻<>梵郛灘ュ伜多荳ュ邇<EFBDAD>€∝桃蠎疲慮髣?|
| **螳ケ驥剰ッ<E589B0>シー** | 豈乗怦 | 隸<>シー256MB譏ッ蜷ヲ螟溽畑<E6BABD>梧弍蜷ヲ髴€隕∝合驟?|
| **螳牙<E89EB3>€譟?* | 豈乗怦 | 譽€譟・逋ス蜷榊黒縲∝ッ<E2889D><EFBDAF>∝シコ蠎ヲ縲∬ョソ髣ョ譌・蠢?|

---

## 10. 謌仙粥譬<E7B2A5>㊥

### 10.1 謚€譛ッ謖<EFBDAF><E8AC96><EFBFBD><EFBFBD>2.0譖エ譁ー<EFBFBD>?
| 謖<><E8AC96><EFBFBD> | 蠖灘燕蛟シ<E89B9F><EFBFBD>蟄假シ?| 逶ョ譬<EFBDAE>€<C280><EFBDBC>edis<69>?| 陦。驥乗婿豕<E5A9BF> |
|------|---------------|----------------|---------|
| **郛灘ュ俶戟荵<E6889F><E88DB5>?* | 笶?螳樔セ矩㍾蜷ッ荳「螟ア | 笨?謖∽ケ<E288BD>喧菫晏ュ?| 驥榊星蜷惹サ崎<EFBDBB>隸サ蜿?|
| **螟壼ョ樔セ句<EFBDBE>莠?* | 笶?蜷<>ョ樔セ狗峡遶?| 笨?蜈ィ螻€蜈ア莠ォ | 螳樔セ帰蜀呻シ悟ョ樔セ毅閭ス隸サ |
| **LLM API驥榊、崎ー<E5B48E>畑** | <20>閥 鬮?| <20>泙 菴?| 蜷御ク€PDF蜿ェ隹<EFBDAA><E99AB9>?谺?|
| **郛灘ュ伜多荳ュ邇?* | N/A | > 60% | 逶第而譌・蠢礼サ溯ョ。 |
| **莉サ蜉。謖∽ケ<E288BD><EFBDB9>?* | 笶?螳樔セ矩楳豈∽ク「螟?| 笨?莉サ蜉。扈ァ扈ュ | <20>閥 **譁ー蠅<EFBDB0>** |
| **髟ソ莉サ蜉。謌仙粥邇<E7B2A5>** | 10-30% | > 99% | <20>閥 **蜈ウ髞ョ謖<EFBDAE><E8AC96><EFBFBD>** |
| **邉サ扈溷庄逕ィ諤?* | 99% | 99.9% | 髯咲コァ遲也払菫晞囿 |

### 10.2 荳壼苅謖<E88B85><E8AC96><EFBFBD><EFBFBD>2.0譖エ譁ー<EFBFBD>?
| 謖<><E8AC96><EFBFBD> | 謾ケ騾<EFBDB9><EFBFBD> | 謾ケ騾<EFBDB9><EFBFBD> | 陦。驥乗婿豕<E5A9BF> |
|------|--------|--------|---------|
| **LLM API謌先悽** | ツ・X/譛?| 髯堺ス<E5A0BA>40-60% | 蟇ケ豈碑エヲ蜊<EFBDA6> |
| **莉サ蜉。荳「螟ア邇?* | <20>閥 70-95% | < 1% | <20>閥 **譛€驥崎ヲ<E5B48E>** |
| **莉サ蜉。螳梧<E89EB3>譌カ髣エ** | 荳咲。ョ螳?| 遞ウ螳<EFBDB3> | 逶第而譌・蠢<EFBDA5> |
| **逕ィ謌キ驥榊、肴署莠、谺。謨ー** | 蟷ウ蝮<EFBDB3>3谺?| 蜃<>荵惹ク? | 逕ィ謌キ陦御クコ蛻<EFBDBA>梵 |
| **逕ィ謌キ貊。諢丞コ?* | 蝓コ郤ソ | 譏セ闡玲署蜊<E7BDB2> | 髣ョ蜊キ隹<EFBDB7>衍 |

### 10.3 鬪梧噺譬<E599BA><EFBFBD><E38AA5>2.0譖エ譁ー<EFBFBD>?
#### **Redis郛灘ュ倬ェ梧噺**
```bash
笨?謇€譛臥シ灘ュ伜黒蜈<E9BB92>オ玖ッ暮€夊ソ<E5A48A>
笨?HealthCheckService郛灘ュ伜多荳ュ豬玖ッ暮€夊ソ<E5A48A>
笨?LLM12FieldsService郛灘ュ伜多荳ュ豬玖ッ暮€夊ソ<E5A48A>
笨?螳樔セ矩㍾蜷ッ蜷守シ灘ュ倅サ榊ュ伜惠
笨?郛灘ュ伜多荳ュ邇?> 60%
笨?LLM API隹<49>畑谺。謨ー荳矩剄 > 40%

Redis髦溷<EFBFBD>鬪梧噺 <20>閥 *譁ー蠅槫<EFBFBD>髞ョ鬘?

笨?Redis髦溷<E9ABA6>蜊募<E89C8A>豬玖ッ暮€夊ソ<E5A48A>
笨?髟ソ莉サ蜉。<E89C89><EFBDA1>2蟆乗慮<E4B997>画オ玖ッ暮€夊ソ<E5A48A>
笨?螳樔セ矩㍾蜷ッ蜷惹ササ蜉。閾ェ蜉ィ諱「螟?笨?莉サ蜉。螟ア雍・閾ェ蜉ィ驥崎ッ包シ?谺。<E8B0BA><EFBDA1>
笨?1000遽<30>枚迪ョ遲幃€<C280>蜉溽紫 > 99%
笨?譌<>逕ィ謌キ謚戊ッ我ササ蜉。荳「螟?笨?霑帛コヲ螳樊慮譖エ譁ー豁」蟶ク

<EFBFBD>囿諱「螟肴オ玖ッ<EFBFBD> <20>驥崎ヲ<EFBFBD>

笨?讓。諡溷ョ樔セ矩楳豈?竊?莉サ蜉。閾ェ蜉ィ諱「螟<EFBDA2>
笨?讓。諡欒edis螳墓惻 竊?邉サ扈滄剄郤ァ霑占。<E58DA0>
笨?讓。諡溽ス醍サ懷サカ霑<EFBDB6> 竊?莉サ蜉。豁」蟶ク螳梧<E89EB3>
笨?讓。諡溷ケカ蜿台ササ蜉。 竊?豁」遑ョ蛻<EFBDAE><E89BBB><EFBFBD>

逕滉コァ邇ッ蠅<EFBFBD>ェ梧噺

笨?逕滉コァ邇ッ蠅<EFBDAF>ソ占。<E58DA0>48蟆乗慮譌<E685AE>髞呵ッ?笨?2荳ェ螳梧紛逧<E7B49B>1000遽<30>枚迪ョ遲幃€我ササ蜉。謌仙<E8AC8C>?笨?逶第而謖<E8808C><E8AC96><EFBFBD>ュ」蟶ク<E89FB6><EFBFBD>蟄倥€∬ソ樊磁謨ー縲_PS<50>?笨?譌<>逕ィ謌キ謚戊ッ?```

---

## 11. FAQ

### Q1<51>壼ヲよ棡Redis蝨ィ蜊雁、懃ェ∫┯謖ゆコ<E38286>€惹ケ亥萱<E4BAA5><E890B1>
**A1**<2A>夂ウサ扈滉シ夊<EFBDBC>蜉ィ髯咲コァ蛻ー蜀<EFBDB0>ュ倡シ灘ュ假シ悟コ皮畑扈ァ扈ュ霑占。後€らャャ莠悟、ゥ霑千サエ譽€譟・蟷カ諱「螟抗edis縲?
### Q2<51>?56MB螟溽畑蜷暦シ滉サ€荵域慮蛟咎怙隕∝合驟搾シ<E690BE>
**A2**<2A>?- 蠖灘燕鬚<E78795>シー<EFBDBC>? 50% 菴ソ逕ィ邇?- 隗ヲ蜿大合驟堺ソ。蜿キ<E89CBF>?  - 蜀<>ュ倅スソ逕ィ > 80%
  - 鬚醍ケ∬ァヲ蜿銑RU鬩ア騾?  - 逶第而蜻願ュヲ
- 蜊<><E89C8A>譁ケ蠑擾シ夐仭驥御コ第而蛻カ蜿ー荳€髞ョ蜊<EFBDAE>コァ<EFBDBA>梧裏髴€驥榊星

### Q3<51>啌edis莨壼スア蜩咲ウサ扈滓€ァ閭ス蜷暦シ<E69AA6>
**A3**<2A>?- 譛ャ蝨ー蜀<EFBDB0>ュ假シ? 0.1ms
- 譛ャ蝨ーRedis<69>嘸1ms
- 髦ソ驥御コ然edis<69>亥酔蝨ー蝓滂シ会シ嘸2-5ms
- 蠖ア蜩榊庄蠢ス逡・<E980A1>御ク疲怏謇ケ驥乗桃菴應シ伜喧

### Q4<51>壼ヲよ棡蜿醍鴫Redis荳埼€ょ粋<E38287><EFBFBD>蝗樣€€蛻ー蜀<EFBDB0>ュ倡シ灘ュ伜雛<E4BC9C>?**A4**<2A>壼庄莉・<E88E89>∽ソョ謾ケ `CACHE_TYPE=memory` 蜊ウ蜿ッ<E89CBF>御サ」遐∵髪謖∫Ο<CE9F>困縲?
### Q5<51>夐怙隕∝ュヲ荵<EFBDA6>Redis蜻ス莉、蜷暦シ<E69AA6>
**A5**<2A>?- 蠑€蜿托シ壻ク埼怙隕<E68099>シ御サ」遐∝キイ蟆∬」?- 霑千サエ<EFBDBB>壼サコ隶ョ蟄ヲ荵?荳ェ蝓コ遑€蜻ス莉、<E88E89><EFBDA4>ET/SET/KEYS/TTL/INFO<46>?
---

## 12. 逶ク蜈ウ譁<EFBDB3>。」

- [莠大次逕溷シ€蜿題ァ<EFBFBD>](../04-蠑€蜿題ァ<E9A18C><EFBDA7>?08-莠大次逕溷シ€蜿題ァ<E9A18C><EFBDA7>?md)
- [SAE驛ィ鄂イ螳悟<EFBFBD><EFBFBD>](./02-SAE驛ィ鄂イ螳悟<E89EB3><EFBFBD>(莠ァ蜩∫サ冗炊迚?.md)
- [SAE邇ッ蠅<EFBFBD>序驥城<EFBFBD>鄂ョ謖<EFBFBD>](./03-SAE邇ッ蠅<EFBDAF>序驥城<E9A9A5>鄂ョ謖<EFBDAE>漉.md)
- [Redis螳俶婿譁<EFBFBD>。」](https://redis.io/docs/)
- [ioredis譁<EFBFBD>。」](https://github.com/redis/ioredis)
- [髦ソ驥御コ然edis譁<EFBFBD>。」](https://help.aliyun.com/product/26340.html)

---

## 13. 髯<><EFBFBD>

### 髯<>ス柊<EFBDBD>壼ョ梧紛逧<E7B49B>.env驟咲スョ讓。譚ソ

```env
# ==================== 謨ー謐ョ蠎?====================
DATABASE_URL=postgresql://postgres:password@localhost:5432/ai_clinical_research

# ==================== Redis驟咲スョ ====================
CACHE_TYPE=redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
# 謌紋スソ逕ィ霑樊磁蟄礼ャヲ荳イ
# REDIS_URL=redis://localhost:6379

# ==================== 髦溷<E9ABA6>驟咲スョ ====================
QUEUE_TYPE=memory  # 隨ャ荳€髦カ谿オ逕ィmemory<72>檎ャャ莠碁亳谿オ謾ケ荳コredis

# ==================== JWT ====================
JWT_SECRET=your-secret-key-change-in-production
JWT_EXPIRES_IN=7d

# ==================== LLM API ====================
DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxx
DASHSCOPE_API_KEY=sk-xxxxxxxxxxxxxx
CLOSEAI_API_KEY=sk-xxxxxxxxxxxxxx
CLOSEAI_OPENAI_BASE_URL=https://api.openai-proxy.org/v1
CLOSEAI_CLAUDE_BASE_URL=https://api.openai-proxy.org/anthropic

# ==================== Dify ====================
DIFY_API_URL=http://localhost/v1
DIFY_API_KEY=dataset-xxxxxxxxxxxxxx

# ==================== Server ====================
PORT=3001
NODE_ENV=development

# ==================== 蟄伜お驟咲スョ ====================
STORAGE_TYPE=local
LOCAL_STORAGE_DIR=uploads
LOCAL_STORAGE_URL=http://localhost:3001/uploads

# ==================== CORS驟咲スョ ====================
CORS_ORIGIN=http://localhost:5173

# ==================== 譌・蠢鈴<E8A0A2>鄂ョ ====================
LOG_LEVEL=debug

<EFBFBD>ス稗<EFBFBD>啌edis蜀<EFBFBD>ュ倩ョ。邂怜<EFBFBD>?

蜊穂クェLLM扈捺棡郛灘ュ假シ嘸50KB
蜊穂クェ蛛・蠎キ譽€譟・郛灘ュ假シ嘸5KB

鬚<>シー螳ケ驥擾シ?- 1000荳ェLLM扈捺棡 = 50MB
- 1000荳ェ蛛・蠎キ譽€譟?= 5MB
- 邉サ扈溷シ€髞€ = 20MB
-----------------------------
諤サ隶。 = 75MB / 256MB = 29% 菴ソ逕ィ邇?```

### 髯<>ス匹<EFBDBD>壽腐髫懈シ皮サ<E79AAE><EFBDBB>譛?
```bash
#!/bin/bash
# 譁<>サカ<EFBDBB>喘ackend/scripts/disaster-recovery-drill.sh

echo "<22>圷 Redis謨<73>囿貍皮サ<E79AAE>€蟋?.."

# 1. 蛛懈ュ「Redis
echo "1. 蛛懈ュ「Redis..."
docker stop ai-clinical-redis
sleep 2

# 2. 豬玖ッ募コ皮畑譏ッ蜷ヲ豁」蟶ク
echo "2. 豬玖ッ募コ皮畑蛛・蠎キ譽€譟?.."
response=$(curl -s http://localhost:3001/api/health)
echo "蜩榊コ<E6A68A>: $response"

if [[ $response == *"memory"* ]]; then
  echo "笨?髯咲コァ謌仙粥<E4BB99>御スソ逕ィ蜀<EFBDA8>ュ倡シ灘ュ?
else
  echo "笶?髯咲コァ螟ア雍・"
  exit 1
fi

# 3. 諱「螟抗edis
echo "3. 諱「螟抗edis..."
docker start ai-clinical-redis
sleep 5

# 4. 豬玖ッ紐edis諱「螟<EFBDA2>
echo "4. 豬玖ッ紐edis諱「螟<EFBDA2>..."
response=$(curl -s http://localhost:3001/api/health)
echo "蜩榊コ<E6A68A>: $response"

if [[ $response == *"redis"* ]]; then
  echo "笨?Redis諱「螟肴<E89E9F><EFBFBD>"
else
  echo "笞<><E7AC9E><EFBFBD> Redis譛ェ諱「螟搾シ御サ堺スソ逕ィ蜀<EFBDA8>ュ倡シ灘ュ?
fi

echo "<22>脂 謨<>囿貍皮サ<E79AAE>ョ梧<EFBDAE><E6A2A7>?

<EFBFBD>。」扈エ謚、閠<EFBFBD><EFBFBD> 謚€譛ッ蝗「髦? €蜷取峩譁ー<EFBFBD><EFBFBD> 2025-12-12
<EFBFBD>。」迥カ諤<EFBFBD><EFBFBD> 笨?蠕<>ョ。譬? *荳区ャ。譖エ譁ー<EFBFBD>? 謾ケ騾<EFBDB9>螳梧<E89EB3>蜷取€サ扈鍋サ城ェ梧蕗隶ュ


笨?謾ケ騾<EFBDB9>螳梧<E89EB3>€譟・貂<EFBDA5><E8B282>?

蝨ィ螳梧<EFBFBD>Redis謾ケ騾<EFBFBD>蜷趣シ瑚ッキ騾宣。ケ譽€譟・<EFBFBD><EFBFBD>

莉」遐∝アる擇

  • ioredis 蟾イ螳芽」?- [ ] RedisCacheAdapter 蟾イ螳樒<E89EB3>?- [ ] CacheFactory 蟾イ豺サ蜉<EFBDBB>髯咲コァ騾サ霎<EFBDBB>
  • .env 驟咲スョ蟾イ譖エ譁?- [ ] 謇€譛我スソ逕?cache.set()<>慍譁ケ驛ス隶セ鄂ョ莠<EFBDAE>TL

豬玖ッ募アる擇

  • 蜊募<EFBFBD>豬玖ッ募<EFBFBD>驛ィ騾夊ソ<EFBFBD>
  • <EFBFBD><EFBFBD>豬玖ッ募<EFBFBD>驛ィ騾夊ソ<EFBFBD>
  • 蜴句鴨豬玖ッ戊セセ譬<EFBFBD>
  • <EFBFBD>囿讓。諡滓オ玖ッ暮€夊ソ<EFBFBD>

驛ィ鄂イ螻る擇

  • 髦ソ驥御コ然edis蟾イ雍ュ荵?- [ ] 逋ス蜷榊黒蟾イ驟咲スョ
  • SAE邇ッ蠅<EFBFBD>序驥丞キイ驟咲ス?- [ ] 逕滉コァ邇ッ蠅<EFBDAF>キイ鬪瑚ッ?

<EFBFBD>。」螻る擇

  • 謾ケ騾<EFBFBD><EFBFBD>。」蟾イ譖エ譁ー
  • 霑千サエ譁<EFBFBD>。」蟾イ陦・蜈?- [ ] 逶第而謖<E8808C><E8AC96><EFBFBD>キイ隶ー蠖?- [ ] 扈城ェ梧蕗隶ュ蟾イ諤サ扈<EFBDBB>

*逾晄隼騾<EFBFBD>鬘コ蛻ゥ<EFBFBD>∝ヲよ怏髣ョ鬚假シ瑚ッキ蜿頑慮豐滄€€? <20>