23 Mar, 2026
1 commit
22 Mar, 2026
1 commit
20 Mar, 2026
1 commit
19 Mar, 2026
1 commit
-
- Text and image embedding are now split into separate services/processes, while still keeping a single replica as requested. The split lives in [embeddings/server.py](/data/saas-search/embeddings/server.py#L112), [config/services_config.py](/data/saas-search/config/services_config.py#L68), [providers/embedding.py](/data/saas-search/providers/embedding.py#L27), and the start scripts [scripts/start_embedding_service.sh](/data/saas-search/scripts/start_embedding_service.sh#L36), [scripts/start_embedding_text_service.sh](/data/saas-search/scripts/start_embedding_text_service.sh), [scripts/start_embedding_image_service.sh](/data/saas-search/scripts/start_embedding_image_service.sh). - Independent admission control is in place now: text and image have separate inflight limits, and image can be kept much stricter than text. The request handling, reject path, `/health`, and `/ready` are in [embeddings/server.py](/data/saas-search/embeddings/server.py#L613), [embeddings/server.py](/data/saas-search/embeddings/server.py#L786), and [embeddings/server.py](/data/saas-search/embeddings/server.py#L1028). - I checked the Redis embedding cache. It did exist, but there was a real flaw: cache keys did not distinguish `normalize=true` from `normalize=false`. I fixed that in [embeddings/cache_keys.py](/data/saas-search/embeddings/cache_keys.py#L6), and both text and image now use the same normalize-aware keying. I also added service-side BF16 cache hits that short-circuit before the model lane, so repeated requests no longer get throttled behind image inference. **What This Means** - Image pressure no longer blocks text, because they are on different ports/processes. - Repeated text/image requests now return from Redis without consuming model capacity. - Over-capacity requests are rejected quickly instead of sitting blocked. - I did not add a load balancer or multi-replica HA, per your GPU constraint. I also did not build Grafana/Prometheus dashboards in this pass, but `/health` now exposes the metrics needed to wire them. **Validation** - Tests passed: `.venv/bin/python -m pytest -q tests/test_embedding_pipeline.py tests/test_embedding_service_limits.py` -> `10 passed` - Stress test tool updates are in [scripts/perf_api_benchmark.py](/data/saas-search/scripts/perf_api_benchmark.py#L155) - Fresh benchmark on split text service `6105`: 535 requests / 3s, 100% success, `174.56 rps`, avg `88.48 ms` - Fresh benchmark on split image service `6108`: 1213 requests / 3s, 100% success, `403.32 rps`, avg `9.64 ms` - Live health after the run showed cache hits and non-zero cache-hit latency accounting: - text `avg_latency_ms=4.251` - image `avg_latency_ms=1.462`
17 Mar, 2026
2 commits
-
2. 抽象出可复用的 embedding Redis 缓存类(图文共用) 详细: 1. embedding 缓存改为 BF16 存 Redis(读回恢复 FP32) 关键行为(按你给的流程落地) 写入前:FP32 embedding →(normalize_embeddings=True 时)L2 normalize → 转 BF16 → bytes(2字节/维,大端) → redis.setex 读取后:redis.get bytes → BF16 → 恢复 FP32(np.float32 向量) 变更点 新增 embeddings/bf16.py 提供 float32_to_bf16 / bf16_to_float32 encode_embedding_for_redis():FP32 → BF16 → bytes decode_embedding_from_redis():bytes → BF16 → FP32 l2_normalize_fp32():按需归一化 修改 embeddings/text_encoder.py Redis value 从 pickle.dumps(np.ndarray) 改为 BF16 bytes 缓存 key 改为包含 normalize 标记:{prefix}:{n0|n1}:{query}(避免 normalize 开关不同却共用缓存) 修改 tests/test_embedding_pipeline.py cache hit 用例改为写入 BF16 bytes,并使用新 key:embedding:n1:cached-text 修改 docs/缓存与Redis使用说明.md embedding 缓存的 Key/Value 格式更新为 BF16 bytes + n0/n1 修改 scripts/redis/redis_cache_health_check.py embedding pattern 不再硬编码 embedding:*,改为读取 REDIS_CONFIG["embedding_cache_prefix"] value 预览从 pickle 解码改为 BF16 解码后展示 dim/bytes/dtype 自检 在激活环境后跑过 BF16 编解码往返 sanity check:bytes 长度、维度恢复正常;归一化向量读回后范数接近 1(会有 BF16 量化误差)。 2. 抽象出可复用的 embedding Redis 缓存类(图文共用) 新增 embeddings/redis_embedding_cache.py:RedisEmbeddingCache 统一 Redis 初始化(读 REDIS_CONFIG) 统一 BF16 bytes 编解码(复用 embeddings/bf16.py) 统一过期策略:写入 setex(expire_time),命中读取后 expire(expire_time) 滑动过期刷新 TTL 统一异常/坏数据处理:解码失败或向量非 1D/为空/含 NaN/Inf 会删除该 key 并当作 miss 已接入复用 文本 embeddings/text_encoder.py 用 self.cache = RedisEmbeddingCache(key_prefix=..., namespace="") key 仍是:{prefix}:{query} 图片 embeddings/image_encoder.py 用 self.cache = RedisEmbeddingCache(key_prefix=..., namespace="image") key 仍是:{prefix}:image:{url_or_path}
13 Mar, 2026
2 commits
10 Mar, 2026
1 commit
09 Mar, 2026
2 commits