Skip to content

vector search#388

Open
yutin1987 wants to merge 2 commits into
masterfrom
vector-search
Open

vector search#388
yutin1987 wants to merge 2 commits into
masterfrom
vector-search

Conversation

@yutin1987

@yutin1987 yutin1987 commented May 5, 2026

Copy link
Copy Markdown
--- Gemini Developer API Vertex AI
認證 API key(GEMINI_API_KEY GCP 專案 + ADC / service account、IAM
端點 全域 generativelanguage.googleapis.com 綁專案 + 區域(location
檔案 Files API(暫存 ~48h)、GCS gs://、HTTPS URL、inlineData GCS gs://、inlineData
學習成本 低(貼 key 即用) 高(專案、IAM、憑證)

混合搜尋(kNN 檢索 + BM25 排序)使用說明

如何啟用

ListArticles / ListRepliesfilter 加上 embedding 欄位:

  • 不傳 embedding → 純 BM25(預設,完全向後相容)。
  • 傳一個數字(最小 cosine 相似度,0~1)→ kNN 檢索 + BM25 排序。
    • 數字越大越嚴格;建議從 0.7 起調。

embedding 是一個 Float,直接給門檻值即可,不是物件。

範例

文字查詢

# 純 BM25(原本行為)
query {
  ListArticles(filter: { moreLikeThis: { like: "雞蛋 缺蛋 漲價" } }) {
    edges { node { id text } }
  }
}

# kNN 檢索 + BM25 排序,相似度門檻 0.7
query {
  ListArticles(filter: {
    moreLikeThis: { like: "雞蛋 缺蛋 漲價" }
    embedding: 0.7
  }) {
    edges { node { id text } }
  }
}

ListReplies 用法相同(文字查詢):

query {
  ListReplies(filter: {
    moreLikeThis: { like: "疫苗 副作用" }
    embedding: 0.7
  }) {
    edges { node { id text } }
  }
}

媒體查詢(僅 ListArticles)

傳入媒體 URL + embedding,就會對圖片/音訊/影片做語意 kNNquery {
  ListArticles(filter: {
    mediaUrl: "https://example.com/suspicious.jpg"
    embedding: 0.7
  }) {
    edges { node { id articleType attachmentHash } }
  }
}

- 若該媒體系統裡已有(相同內容雜湊),直接重用既有向量,不重算。
- 沒有的話,伺服器會抓該 URL 的內容即時產生向量(不落地 GCS)。

行為與注意事項

- Opt-in:沒傳 embedding 時一律純 BM25,行為不變。
- 永不弄壞搜尋:若向量產生失敗(例如外部服務異常),會自動退回純 BM25。
- 相似度門檻:embedding 的值是每個查詢片段對文件向量的最小 cosine 相似度;低於門檻的候選會被丟掉。
- 音訊/影片:文件向量只涵蓋前 80 秒(模型單次上限);超過的內容仍由逐字稿走 BM25 涵蓋。

伺服器端前提

- 必須設定 GEMINI_API_KEY(查詢向量與文件向量都靠它產生)。
- 文件要先有向量才搜得到:
  - 新文章/回覆建立時會自動 embed。
  - 舊資料需跑 backfillnode build/scripts/migrations/backfillVertexEmbeddings.js --index both
(建議先 --dry-run 或 --limit 小量試跑)。

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces AI embedding generation and caching capabilities. It adds a new EMBEDDING type to the GraphQL schema, implements a createEmbedding utility using Google Vertex AI with support for media chunking, and includes comprehensive tests. Feedback focuses on a breaking change in the createAIResponse utility that affects existing callers, the lack of handling for concurrent 'LOADING' states which could lead to redundant API calls, and opportunities to improve performance by parallelizing media chunk processing and reusing AI clients. Additionally, the AIEmbedding GraphQL type should include the text field to expose error messages.

Comment thread src/graphql/util.js
Comment thread src/util/embedding.ts
Comment on lines +86 to +93
if (
cached &&
cached.status === 'SUCCESS' &&
Array.isArray(cached.embeddings) &&
cached.embeddings.length > 0
) {
return cached.embeddings as EmbeddingChunk[];
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current logic only returns cached embeddings if the status is SUCCESS. If an embedding is currently being generated (status: 'LOADING'), this code will proceed to call createAIResponse, which likely creates a duplicate loading record and starts a redundant generation process. It should instead wait for the existing process to complete.

Comment thread src/util/embedding.ts Outdated
Comment thread src/util/embedding.ts Outdated
Comment on lines +105 to +107
fields: {
...commonAiResponseFields,
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The AIEmbedding type is missing the text field. Since createEmbedding stores error messages in the text field when the status is ERROR, this field should be added to the GraphQL model to allow clients to retrieve error details.

  fields: {
    ...commonAiResponseFields,
    text: { type: GraphQLString },
  },

@yutin1987
yutin1987 force-pushed the vector-search branch 2 times, most recently from 770f044 to cbfc471 Compare May 5, 2026 03:43
@yutin1987
yutin1987 force-pushed the vector-search branch 2 times, most recently from b88be5e to 0e01635 Compare June 27, 2026 16:26
@coveralls

coveralls commented Jun 27, 2026

Copy link
Copy Markdown

Coverage Status

coverage: 82.958% (+0.7%) from 82.22% — vector-search into master

@yutin1987
yutin1987 force-pushed the vector-search branch 2 times, most recently from 366b8d2 to 299be47 Compare June 28, 2026 05:52
@yutin1987 yutin1987 changed the title [draft] Vector search vector search Jul 12, 2026
@yutin1987
yutin1987 force-pushed the vector-search branch 3 times, most recently from db134bc to a42a369 Compare July 13, 2026 03:12
Add dense-vector (kNN) retrieval to ListArticles/ListReplies, opt-in via
the `embedding` similarity filter. kNN narrows the candidate set and the
existing BM25 / perceptual-hash scoring ranks the results.

- Embed articles and replies on create; backfill script for old docs.
- Audio/video embed as a single vector capped at 80s (no duration probe).
- Media queries reuse the doc-side embedding by content hash, else fetch
  bytes from the query URL and embed — search never persists to GCS, so
  no orphan files.
- All Gemini calls (embeddings + transcription) use the Developer API and
  feed media via the Files API; Vertex AI is removed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants