Community图像github.com

claudejaune/gemma-embed-search

使用本地多模态嵌入模型对图像、音频或文本文件夹建立索引,并通过自然语言进行检索。构建一个可通过任何兼容 OpenAI 的 /v1/embeddings 服务器恢复的向量索引(如运行 EmbeddingGemma 2 的 llama.cpp 的 llama-server),然后支持颜色过滤、多条件 AND 查询、JSON 输出以及 HTML 缩略图页等方式按语义相似度对文件进行排序。

gemma-embed-search 是什么?

Gemma Embed Search 是一个 AI 辅助搜索工具,允许用户通过自然语言查询在本地文件系统中检索图像、音频和文本文件。它使用一个在用户本地硬件上运行的多模态嵌入模型(如 Llama.cpp 的 EmbeddingGemma 2),所有处理均在本地完成,无需将数据上传到外部服务器。该工具通过构建一个可增量更新的向量索引来实现搜索,支持色彩过滤、复合条件(AND/OR)查询、JSON 输出以及生成包含 Base64 缩略图的独立 HTML 联系人页面。它特别适用于需要根据概念和内容而非文件名或元数据来查找文件的场景。支持的平台包括 Linux、macOS 和 Windows,主要通过一个 Python 脚本(embed_search.py)实现,该脚本提供 doctor、init、index、search 和 stats 等命令。

兼容平台✓Claude Code✓Codex CLI~Cursor
npx skills add https://github.com/claudejaune/gemma-embed-search/tree/HEAD

Installed? Explore more 图像 skills: steipete/songsee, affaan-m/frontend-design, mattpocock/improve-codebase-architecture · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

Gemma 嵌入搜索

根据含义检索本地文件。一个嵌入空间容纳图像、音频和文本,因此一个打字查询会返回与之匹配的图片、剪辑或文档。模型通过本地服务器在用户硬件上运行;没有任何内容上传到任何地方。

scripts/embed_search.py 执行四项操作:doctor 检查服务器,init 保存连接,index 将文件嵌入到缓存中,search 根据查询对它们进行排名。

本地配置

设置保存在 config.json 中,绝不保存到此文件中:

  • Linux 和 macOS:~/.config/gemma-embed-search/config.json
  • Windows:%USERPROFILE%\.config\gemma-embed-search\config.json

首先读取该文件。如果其中有 url 和 model,则后续所有命令都无需连接标志即可工作,并且可以跳过步骤 2。

如果该文件缺失或不完整,请询问用户服务器的 URL 和要索引的文件夹,运行 doctor 列出服务器实际提供的模型,然后运行 init 写入文件。init 是合并的,因此一次补充一个设置是安全的。切勿猜测模型名称。

将 config.example.json 从此文件夹复制到该路径并编辑它,与 init 的作用相同,适用于希望直接编辑文件的用户。仅当服务器需要时才在其中添加 "api_key"。

切勿将设置保存在技能文件夹本身内部。更新技能会替换其中的每个文件,并且写入那里的任何内容都会丢失。

设置清单

进度:

  • 步骤 1:确认服务器并选择一个模型名称 (doctor)
  • 步骤 2:保存连接 (init)
  • 步骤 3:索引一个包含 20 个文件的试点项目并快速查看一个查询
  • 步骤 4:索引整个文件夹
  • 步骤 5:回答用户的真实查询,并使用 --html 以便他们可以看到结果

如果 config.json 已经具有 url 和 model,请跳过步骤 2。

使用 uv run scripts/embed_search.py(从脚本的内联元数据安装 Pillow)或 python3 scripts/embed_search.py(如果 Pillow 已可导入)运行脚本。

步骤 1 — 查找服务器和模型名称。 切勿猜测模型名称;从服务器读取它。

uv run scripts/embed_search.py --url http://127.0.0.1:1235 doctor

doctor 打印服务器提供的模型并探测一个嵌入。如果用户尚未启动服务器,请阅读 references/setup.md 并向他们提供其模型的命令。

步骤 2 — 保存它,以便后续调用无需标志。仅当服务器需要时才传递 --api-key;它将以模式 0600 写入配置文件。在任何后续命令中传递 --root 以处理不同的文件夹,而无需更改已保存的内容。

uv run scripts/embed_search.py --url http://127.0.0.1:1235 --model <name-from-doctor> init --root <folder>
uv run scripts/embed_search.py index --limit 20

步骤 3 — 在长时间运行之前进行试点。 索引是昂贵的步骤:在桌面 GPU 上大约每张图像 0.25 秒,因此 10,000 个文件大约需要 40 分钟。索引 20 个文件,运行一个查询,并在提交到完整处理之前确认结果看起来正确。

步骤 4 — 完整索引。 重新运行 index 仅嵌入新的或更改的文件,因此中断的运行会自动恢复。

uv run scripts/embed_search.py index

步骤 5 — 搜索。

uv run scripts/embed_search.py search "海滩上的黑色拉布拉多犬" --top 20 --html hits.html

查询备忘单

意图命令
纯语义搜索search "拥挤的夜市"
两个条件都必须满足search "一个女人" --must "黄色"
同义词,以免一种措辞占主导search "一个女人" --also "一个女孩" --also "女性肖像"
查找包含 X 的每个图像search "红发角色" --top 60 --html hits.html
主题加上真实颜色约束search "一个女人" --color yellow --min-color 0.05
主要是单色或暗色调画面search "任何图像" --max-color blue --modality image
只有一种文件类型search "警笛声" --modality audio
机器可读,用于进一步处理search "..." --json --top 50
更快,分数准确性略低search "..." --dims 256

--also 将释义向量平均为一个查询。--must 保留最弱方面的分数,这就是表达 AND 的方式。对同义词使用 --also,对独立条件使用 --must。

读取输出

纯文本输出是 score [modality] path,最好的排在第一。颜色运行会在分数后插入像素比例。--json 在标准输出上发出 {"query", "index", "total", "filtered_out", "results": [{"rank", "score", "color", "modality", "path"}]};所有进度信息都发送到标准错误,因此标准输出始终可以干净地通过管道传输到 jq。

来自此模型系列的余弦分数介于大约 0.3 到 0.8 之间。绝对数值意义不大;命中与未命中之间的差距才重要。如果列表顶部是错误的,则查询是错误的——重新措辞,而不是信任排名。切勿将一个长列表呈现为同样好:指出可靠结果在何处停止,猜测从何处开始。

短列表不是完整的答案。 每个索引文件都会获得一个分数,因此输出中的 total 会计算整个语料库,但绝不会告诉您有多少结果是合理的。相反,找到悬崖——即分数停止急剧下降的点。如果您显示的列表在该点之前结束,请说明结果已被截断,并提供更长的运行:更大的 --top 只需要多生成一个联系人页面,而不是另一个索引。尊重用户指定的数量,并且仍然陈述您应用的截止点。切勿在未使用扩展列表(至少 --top 100 或更多)并且有人实际查看过的情况下声称“没有结果”。

对于视觉审查,始终提供 --html:它会写入一个包含分数和文件名的缩略图联系人页面,用户可以在浏览器中打开。裸 --html 会在工作目录中写入 hits.html;--html FILE 设置路径。缩略图以 Base64 形式嵌入,因此该页面是自包含的,可以独立传输。该页面嵌入了匹配文件的真实缩略图,因此将其视为该库的一部分副本:将其保留在版本控制和任何您转发的内容之外。默认宽度为 512 像素;要提高 --thumb-width 以进行细节的近距离检查。

注意事项

  • 查询文本中的颜色词是软提示,而不是过滤器。"一个具有黄色主题的女人" 会将穿着橙色夹克的女人排在第一位。使用 --color yellow --min-color 0.05 进行实际约束;它是根据像素计算的,而不是根据模型。
  • 风格词也是一个软提示,而且更弱。"一个红发的逼真女人" 会将红发女人的墨水插画排在第一位,因为主题名词的权重超过了风格。将风格移入 --must,以便将其作为自己的方面进行评分,并使用 --color 固定视觉属性。两者都不是真正的过滤器:--must 取各方面的最小值,因此在一个方面用力过猛会牺牲另一个方面。
  • 一个画面中的多个主题会融合成一个单一向量,因此该画面在单主题查询中的得分低于专门针对该主题的画面:一个红发女人与一个男人并排的肖像在 "红发女人" 搜索中可能会排在很后面,而在 "红发角色" 搜索中则可能位居前列。同一个单一想法的同义词无济于事;复数或集体形式才有帮助。当问题是“查找包含 X 的所有内容”时,扩大 --top 的范围。
  • --color 需要索引保存颜色统计信息,这只能由 index 计算。使用旧版本构建的索引会为所有内容报告 0.0:如果颜色过滤悄悄地排除了每个文件,请使用 --force 重新索引。
  • 索引和查询必须使用相同的模型。切换 --model 会悄悄地混合不兼容的向量;在模型更改后使用 --force 重新索引,并在 stats 中注明模型。
  • 服务器必须运行具有均值池化并加载了其视觉投影仪(--mmproj)的嵌入模型。没有投影仪,图像请求会失败或返回一个忽略图片的向量。doctor 证明文本有效;运行一个图像查询以证明视觉功能。
  • 图像在发送前会以最长边 1536 重新编码为 JPEG。重新编码是必需的,而不是装饰性的:服务器通常会拒绝 WebP、16 位或标签错误的文件,而 Pillow 会读取这些文件。模型没有像素限制——服务器会将每张图像重新采样为固定的视觉令牌预算——因此 --resize 是用上传大小来换取保真度,而不是正确性。测量数据以及详细信息在 references/setup.md 中。
  • 除非 Pillow 是使用插件构建的,否则不会读取 HEIC 和原始相机格式。脚本会记录 skip <file>: cannot identify image file 并继续;检查索引摘要中的 skipped_errors,而不要假设完全覆盖。
  • 在从未索引过的文件夹上运行 search 会退出并显示消息,返回码为 4。在 stats 中的索引计数看起来正确之前,不要将空结果列表视为“无匹配”。
  • 音频受服务器上下文限制:每秒 25 个令牌,每个剪辑大约 5.5 分钟。较长的文件会因请求错误而失败,而不是作为错误匹配。
  • 分数不能跨语料库或跨量化进行比较。切勿承诺阈值;根据用户看到的情况调整 --min-color 和 --top。

脚本参考

doctor                       检查服务器,列出模型,探测一个嵌入
init [--root DIR]            将 url、model、key 和默认根目录保存到配置文件中
index   --root DIR           嵌入新的和更改的文件(增量式,可恢复)
        [--modality auto|image|audio|text] [--include '*.png,sub/*.jpg']
        [--exclude-dir node_modules,.git] [--limit N] [--batch 16] [--resize 1536]
        [--force] [--compact]
search  "query"              对索引文件进行排名;打印分数、模态和路径
        [--also P]... [--must P]... [--color NAME --min-color F | --max-color F]
        [--modality image] [--dims 512|256|128] [--top 20] [--offset 0]
        [--json] [--html [FILE]] [--thumb-width 512]
stats                        条目数、模态混合、模型、索引大小

退出码:0 成功,2 用法错误,3 服务器不可达或身份验证被拒绝,4 索引缺失,5 无操作,6 服务器运行中错误。运行 scripts/embed_search.py <command> --help 获取完整的标志文档。

配置解析顺序为:标志 > 环境变量(EMBED_SEARCH_URL、EMBED_SEARCH_MODEL、EMBED_SEARCH_API_KEY、EMBED_SEARCH_ROOT)> 配置文件 ~/.config/gemma-embed-search/config.json。索引默认为 ~/.cache/gemma-embed-search/<folder>-<hash>.jsonl,每个文件一个 JSON 记录,因此可以安全地为不同文件夹保留多个索引。

更多详情

相关技能