跳到主要内容

排查前端部署后线上未更新的问题

· 阅读需 4 分钟

TL;DR

前端代码已推送到 Git,但线上看不到新功能?根因通常是服务器构建产物未更新。通过对比本地和服务器的 dist/ 目录时间戳即可确认,解决方案是在服务器上重新运行 npm run build

问题现象

新开发的按钮/功能在本地 (npm run dev) 正常显示,但部署后线上看不到:

  • 浏览器刷新无效
  • 清除浏览器缓存无效
  • 检查代码逻辑没问题
  • Git 确认代码已推送

根因

前端静态文件通常由 Nginx 直接服务,流程如下:

Git Push → 服务器 git pull → 服务器 npm run build → Nginx 服务 dist/

问题出在第二步到第三步之间:代码已 git pull,但 npm run build 未执行或执行失败。

典型场景:

  1. 自动部署脚本只同步代码,未触发构建
  2. 手动部署时忘记运行构建命令
  3. 构建命令执行了但输出到错误目录

解决方案

步骤 1:对比构建产物时间戳

# 本地
ls -la dist/assets/ | head -5

# 服务器
ssh user@server "ls -la /path/to/project/dist/assets/ | head -5"

输出对比:

# 本地(最新构建)
Mar 7 22:55 index-28dFGXhX.js ← 包含新功能

# 服务器(旧构建)
Mar 7 20:18 index-DsFdnylh.js ← 不包含新功能

文件名不同(hash 变化)说明内容有变化,时间戳不同说明构建未同步。

步骤 2:在服务器上重新构建

ssh user@server "cd /path/to/project && npm run build"

步骤 3:验证构建产物已更新

ssh user@server "ls -la /path/to/project/dist/assets/"

确认时间戳和文件名与本地一致。

步骤 4:刷新页面

由于 Vite/Webpack 构建会生成带 hash 的新文件名(如 index-28dFGXhX.js),index.html 会引用新文件,用户只需正常刷新即可,无需强制清除缓存。

完整排查命令

#!/bin/bash
# 保存为 check-deploy.sh

SERVER="user@server"
PROJECT_PATH="/path/to/project"

echo "=== 本地最新提交 ==="
git log --oneline -1

echo -e "\n=== 服务器最新提交 ==="
ssh $SERVER "cd $PROJECT_PATH && git log --oneline -1"

echo -e "\n=== 本地构建时间 ==="
ls -la dist/assets/ | head -3

echo -e "\n=== 服务器构建时间 ==="
ssh $SERVER "ls -la $PROJECT_PATH/dist/assets/ | head -3"

echo -e "\n=== 服务器与远程差异 ==="
ssh $SERVER "cd $PROJECT_PATH && git fetch origin && git log HEAD..origin/main --oneline"

FAQ

Q: 为什么 Git 已推送但线上还是旧代码?

A: Git 推送只更新了源代码,前端需要 npm run build 生成静态文件。如果部署流程没有自动触发构建,服务器上的 dist/ 目录仍是旧版本。Nginx 直接服务静态文件,不会自动执行构建。

Q: 清除浏览器缓存为什么无效?

A: 问题不在浏览器缓存,而在服务器上的静态文件本身是旧的。即使强制刷新,Nginx 返回的仍是旧的 JS/CSS 文件。正确做法是更新服务器上的构建产物。

Q: 如何避免忘记重新构建?

A: 两种方案:1) 配置 CI/CD 自动构建(如 GitHub Actions);2) 在服务器上使用 git hooks,git pull 后自动执行 npm run build

Q: Vite 构建为什么文件名带 hash?

A: Vite 默认给打包文件添加 content hash(如 index-28dFGXhX.js),文件内容变化时 hash 变化。这是缓存破坏策略,确保用户总能获取最新版本,同时保留长期缓存能力。

修复 Milvus 混合搜索的四个常见坑

· 阅读需 3 分钟

在 RAG 知识库项目中调试混合检索评分问题,以下是完整排查过程。

TL;DR

Milvus 混合搜索(Dense + Sparse)有四个常见坑:空稀疏向量报错、Collection 未加载、sparse 格式错误、阈值过高。本文给出每个问题的最小修复代码。

问题现象

坑 1:空稀疏向量插入失败

MilvusException: (code=65535, message=empty sparse float vector row)

坑 2:Collection 未加载

MilvusException: (code=101, message=failed to search: collection not loaded[collection=xxx])

坑 3:Sparse 向量格式错误

ParamError: (code=1, message=`search_data` value [{0: {81705: 1.3486}}] is illegal)

坑 4:搜索无结果(分数被过滤)

{"answer": "抱歉,知识库中没有相关内容", "similarity": 0.0}

根因

坑 1:Milvus 的 SPARSE_FLOAT_VECTOR 类型不接受空字典 {},必须有至少一个键值对。

坑 2:Milvus 2.4+ 要求搜索前显式调用 load_collection(),否则报 collection not loaded。

坑 3:DashScope API 返回的 sparse 格式是 {text_index: sparse_vec},搜索时需要提取 sparse_vec 本身,而非整个嵌套结构。

坑 4:混合搜索的分数是加权组合(如 0.7 * dense_score + 0.3 * sparse_score),通常在 0.3-0.5 之间。如果阈值设为 0.7,所有结果都会被过滤。

解决方案

坑 1:为空稀疏向量添加占位符

# 获取稀疏向量,如果为空则使用最小占位符
sparse_vec = sparse_vectors.get(chunk_idx, {})
if not sparse_vec:
sparse_vec = {0: 0.0} # Milvus 不接受空 sparse vector

data = {
"dense_vector": dense_embeddings[chunk_idx],
"sparse_vector": sparse_vec, # 保证非空
"text": chunk,
"doc_id": doc_id,
"metadata": metadata
}

坑 2:搜索前加载 Collection

async def hybrid_search(self, collection_name: str, ...):
self.get_or_create_collection(collection_name)

# Milvus 2.4+ 要求:搜索前必须加载
self.client.load_collection(collection_name=collection_name)

dense_results = self.client.search(...)
sparse_results = self.client.search(...)

坑 3:正确提取 Sparse 向量

async def embed_query(self, text: str) -> dict:
result = await self._embed_batch([text], text_type="query", use_instruct=True)
# _embed_batch 返回 {"sparse": {0: sparse_vec}}
# 需要提取 index 0 的向量本身
return {
"dense": result["dense"][0],
"sparse": result["sparse"].get(0, {}) # 提取 sparse_vec
}

坑 4:调整混合搜索阈值

# config.py 或环境变量
rag_min_similarity: float = 0.3 # 过滤阈值(原 0.7 过高)
rag_refuse_similarity: float = 0.3 # 拒答阈值(原 0.5 过高)

混合搜索分数计算公式:

# 典型分数范围:0.3 - 0.5
score = dense_similarity * 0.7 + sparse_similarity * 0.3

FAQ

Q: Milvus 为什么不接受空的稀疏向量?

A: Milvus 的 SPARSE_FLOAT_VECTOR 类型要求每行至少有一个非零元素。空字典 {} 无法确定向量维度,会触发 empty sparse float vector row 错误。使用 {0: 0.0} 作为占位符即可绕过。

Q: Milvus 2.4 搜索前必须调用 load_collection 吗?

A: 是的。Milvus 2.4+ 默认不自动加载 Collection 到内存,必须显式调用 client.load_collection(collection_name) 后才能搜索。这是性能优化设计,避免不用的 Collection 占用内存。

Q: 混合搜索的分数为什么通常只有 0.3-0.5?

A: 混合搜索分数是加权组合(如 0.7 * dense + 0.3 * sparse)。即使两个检索都完美匹配(1.0),加权后最高也只有 1.0。实际场景中 dense 和 sparse 分数很少同时为 1.0,所以典型分数在 0.3-0.5。阈值应设为 0.3 左右,而非 0.7。

Q: DashScope sparse embedding 返回什么格式?

A: DashScope 返回 {"embeddings": [{"sparse_embedding": [{"index": 123, "value": 0.5}, ...]}]}。批量调用时,转换后格式为 {text_index: {dim_index: value}}。搜索时需要用 .get(0, {}) 提取第一条的 sparse 向量。