Files
awoooi/apps/api/src/repositories/playbook_embedding_repository.py
Your Name b4055c5915
Some checks failed
Code Review / ai-code-review (push) Successful in 57s
run-migration / migrate (push) Failing after 44s
feat(embedding): ADR-110 升級 bge-m3:latest 1024 維向量
GCP-A (34.143.170.20) 無 nomic-embed-text,改用 bge-m3:latest(專用
多語言 embedding 模型),產生 1024 維向量。

變更:
- embedding_service.py: 加入 bge-m3:latest=1024 維到 MODEL_DIMENSIONS,
  預設模型改為 bge-m3:latest,更新文件說明
- playbook_embedding_repository.py + interfaces.py: 更新維度說明
- migrations/embedding_bge_m3_1024.sql: pgvector schema 遷移
  rag_chunks + playbook_embeddings vector(768) → vector(1024)
- scripts/reembed_bge_m3.py: 遷移後重新嵌入現有資料的 script

遷移步驟:
  1. 執行 embedding_bge_m3_1024.sql(清空現有 768 維向量,變更維度)
  2. 執行 python scripts/reembed_bge_m3.py 重新嵌入

2026-05-04 ogt + Claude Sonnet 4.6

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 11:18:20 +08:00

105 lines
3.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Playbook Embedding Repository — pgvector 持久化層
=================================================
C1 修正 (首席架構師審查 2026-04-10 Claude Sonnet 4.6 Asia/Taipei):
違規修復: playbook_embedding_service.py 直接 db.execute(text(...)) 繞過 Repository 層
解法: 移至此 RepositoryService 層透過此介面存取
職責: playbook_embeddings 表的 CRUD (pgvector)
對應遷移: migrations/flywheel_playbook_embeddings.sql
"""
from __future__ import annotations
import structlog
from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession
logger = structlog.get_logger(__name__)
class PlaybookEmbeddingRepository:
"""
Playbook Embedding Repository
職責: playbook_embeddings 表 CRUD
使用 pgvector 儲存 bge-m3:latest 1024 維向量ADR-110 2026-05-04 升級自 768 維)
Args:
db: SQLAlchemy AsyncSession (DI 注入)
"""
def __init__(self, db: AsyncSession) -> None:
self._db = db
async def upsert(
self,
playbook_id: str,
embedding: list[float],
alert_names: list[str],
keywords: list[str],
) -> bool:
"""
新增或更新 Playbook 向量 (UPSERT)。
pgvector 格式: '[x,y,z,...]' 字串
ON CONFLICT (playbook_id) → 更新向量快照與 updated_at
Args:
playbook_id: Playbook ID
embedding: 1024 維浮點向量 (list[float])bge-m3:latest
alert_names: 索引時的 alert_names 快照
keywords: 索引時的 keywords 快照
Returns:
True 表示成功
"""
try:
# 顯式格式化保證 pgvector 可解析,避免 str(list) 空格差異
vec_str = "[" + ",".join(str(float(x)) for x in embedding) + "]"
await self._db.execute(
text("""
INSERT INTO playbook_embeddings
(playbook_id, embedding, alert_names, keywords, indexed_at, updated_at)
VALUES
(:playbook_id, :embedding, :alert_names, :keywords, NOW(), NOW())
ON CONFLICT (playbook_id) DO UPDATE SET
embedding = EXCLUDED.embedding,
alert_names = EXCLUDED.alert_names,
keywords = EXCLUDED.keywords,
updated_at = NOW()
"""),
{
"playbook_id": playbook_id,
"embedding": vec_str,
"alert_names": alert_names,
"keywords": keywords,
},
)
return True
except Exception as e:
logger.warning(
"playbook_embedding_upsert_failed",
playbook_id=playbook_id,
error=str(e),
)
return False
async def delete(self, playbook_id: str) -> bool:
"""刪除 Playbook 向量記錄。"""
try:
await self._db.execute(
text("DELETE FROM playbook_embeddings WHERE playbook_id = :pid"),
{"pid": playbook_id},
)
return True
except Exception as e:
logger.warning(
"playbook_embedding_delete_failed",
playbook_id=playbook_id,
error=str(e),
)
return False