在外链建设中,一个核心难题是把有限的投放预算与精力放在最相关且高价值的站点上。人工逐条筛选不仅耗时,而且难以保持一致性与规模。当目标关键词、行业、语言和页面类型多时,传统方法无法快速给出可解释、可重复的"候选站点优先级"列表。Agent SEO(https://www.aiseoauto.com/)就是为了解决这个场景:自动从海量候选站点中找出与目标页面/关键词最相关、同时考虑站点质量的优先投放清单。
下面聚焦并深入讲解"Python + 嵌入向量相似度"这段代码与工程实践,包括如何把语义相似度与站点质量(如 DA/流量)合并为最终排序分数、怎样做规模化与性能优化,以及调参建议。
核心思路
将目标页面或关键词转为文本向量(embedding),把候选站点的描述(或站点抓取到的语义摘要)也转为向量;用余弦相似度计算语义相关度,再与站点质量分按权重合并,得到最终排序分数。
代码(精炼、可直接运行)
"""
智能选站:基于 sentence-transformers 嵌入 + 余弦相似度 + 质量加权的候选站点排序
依赖: pip install -U sentence-transformers numpy
"""
from sentence_transformers import SentenceTransformer
import numpy as np
from typing import List, Dict, Tuple
# --- 配置 ---
EMBED_MODEL = 'all-MiniLM-L6-v2' # 轻量且效果均衡。可按需替换
SIMILARITY_WEIGHT = 0.7 # 最终分数中语义相似度权重
QUALITY_WEIGHT = 0.3 # 站点质量(DA/流量/可信度)权重(两者和应为1)
MIN_SIM_THRESHOLD = 0.35 # 可选:低于此阈值可视为"不相关"
# --- 工具函数 ---
_model = None
def get_model():
global _model
if _model is None:
_model = SentenceTransformer(EMBED_MODEL)
return _model
def embed_texts(texts: List[str]) -> np.ndarray:
"""
批量编码文本为向量。返回 shape = (len(texts), dim)
"""
model = get_model()
return model.encode(texts, convert_to_numpy=True, show_progress_bar=False)
def cosine_similarity_matrix(query_vec: np.ndarray, candidate_vecs: np.ndarray) -> np.ndarray:
"""
query_vec: (D,), candidate_vecs: (N, D) -> 返回 (N,) 的相似度数组
"""
# 防止0向量: 加 eps
dot = np.dot(candidate_vecs, query_vec)
q_norm = np.linalg.norm(query_vec) + 1e-9
c_norm = np.linalg.norm(candidate_vecs, axis=1) + 1e-9
return dot / (c_norm * q_norm)
def normalize_quality_scores(scores: List[float]) -> np.ndarray:
"""
将质量分(例如 DA)归一化到 0-1。输入可为 None 模式 -> 返回 zeros
"""
arr = np.array([s if s is not None else 0.0 for s in scores], dtype=float)
if arr.max() - arr.min() < 1e-9:
return np.zeros_like(arr)
return (arr - arr.min()) / (arr.max() - arr.min())
# --- 主要排序函数 ---
def rank_sites_by_relevance(target_text: str, candidates: List[Dict], top_k: int = 20) -> List[Tuple[Dict, float, float, float]]:
"""
candidates: list of dict, required fields:
- 'site_id', 'site_desc' (或 'site_snapshot_text'), 可选 'quality'(数字,如 DA/流量)
- 可包含 'language' 等元数据,调用方应事先过滤
返回 list of tuples: (candidate_dict, sim_score, quality_norm, final_score), 按 final_score 降序
"""
# 1) 预过滤:如果需要,可在外部按语言/地域/类型先过滤 candidates
texts = [c.get('site_desc', '') for c in candidates]
all_vecs = embed_texts(texts) # (N, D)
target_vec = embed_texts([target_text])[0] # (D,)
sims = cosine_similarity_matrix(target_vec, all_vecs) # (N,)
qualities = [c.get('quality', None) for c in candidates]
q_norm = normalize_quality_scores(qualities) # (N,)
# 最终分数(简单线性组合)
final_scores = SIMILARITY_WEIGHT * sims + QUALITY_WEIGHT * q_norm
# 可选:把低于 MIN_SIM_THRESHOLD 的项标记或排除
result = []
for idx, c in enumerate(candidates):
result.append((c, float(sims[idx]), float(q_norm[idx]), float(final_scores[idx])))
# 按最终分数降序,取 top_k
result_sorted = sorted(result, key=lambda x: x[3], reverse=True)[:top_k]
return result_sorted
# --- 使用示例 ---
if __name__ == "__main__":
target = "AI SEO 自动化 外链 提交 自动表单 填充"
candidates = [
{'site_id': 1, 'site_desc': '技术博客,后端、DevOps 话题', 'quality': 45},
{'site_id': 2, 'site_desc': 'SEO 外链教程与投放经验分享', 'quality': 30},
{'site_id': 3, 'site_desc': '数码评测与购物导购', 'quality': 20},
{'site_id': 4, 'site_desc': 'AI 研发与机器学习研究', 'quality': 50},
]
ranked = rank_sites_by_relevance(target, candidates, top_k=3)
for c, sim, qn, fs in ranked:
print(f"site_id={c['site_id']}, sim={sim:.4f}, quality_norm={qn:.4f}, final={fs:.4f}")
代码
-
模型选择 :
all-MiniLM-L6-v2为轻量级通用句向量模型,适合工程化部署。对质量要求高的场景可换为更大的模型(例如paraphrase-multilingual-MiniLM-L12-v2或更大 transformer),但会牺牲速度与内存。 -
批量编码 :
embed_texts将文本一次性编码,避免逐条调用模型造成的性能瓶颈。部署时对大库应使用分批(batch)与并行化策略。 -
余弦相似度 :实现为向量内积除以范数乘积,注意数值稳定性(加入
1e-9),返回范围理论上为 [-1,1]。实际文本嵌入通常为正相关区间 >0。 -
质量归一化 :
normalize_quality_scores将不同尺度的质量指标(DA、流量、权威分)统一到 0-1。若质量数据缺失,视作 0(可按业务改为平均值或中位数填补)。 -
线性加权合并 :
final_score = alpha * semantic + (1-alpha) * quality。这是可解释、易调参的组合方法;实际可改为乘法或更复杂的模型(例如学习到的加权模型)。 -
低相似度排除 :可使用
MIN_SIM_THRESHOLD排除明显不相关的站点(例如仿冒流量站、垂直领域不相关站点)。
工程与部署
-
预计算与缓存:对候选站点库提前批量生成并缓存向量,每次查询只对目标文本编码并计算相似度(O(N·D)),大幅降低实时延迟。
-
向量索引(向量 DB):当站点规模到数万/数十万时,使用 FAISS、Milvus、Pinecone 等近似最近邻(ANN)索引以实现子线性查询时间。
-
分层过滤:先按语言、地域、站点类型做粗过滤,让向量检索只在小子集上进行,减小误差并提高速度。
-
在线学习/打分微调:把人工审核/历史投放成功率作为标签,用轻量模型(例如 logistic regression)来学习语义分与质量分的最优组合权重。
-
指标监控:跟踪"相似度分到实际提交成功率"的映射,用以调整阈值与权重。
调参与阈值
-
语义相似度阈值:
0.3--0.6区间常见,具体取决于模型与语料。对all-MiniLM-L6-v2:若要保证相关性可把MIN_SIM_THRESHOLD设为~0.45。 -
权重选择:若更看重相关性(推荐语义优先),
SIMILARITY_WEIGHT=0.6-0.8;若更看重权威站(例如 DA 高但语义稍弱),可把质量权重上调到0.4。 -
Top-K:给业务端展示
top_k=20作候选池,人工审核或自动化后再细化为最终投放列表。
常见问题与应对
-
问:候选站点文本从哪来?
答:可以用站点描述(站点 About)、首页段落、最近文章的集合、以及爬取到的"可投稿页面摘要"。融合多段文本通常比单一字段更稳健。
-
问:如何处理语言差异?
答:先用
language元数据过滤;对于多语言站点,分别生成不同语言向量并按语言匹配查询。 -
问:模型部署成本高?
答:在 CPU 环境可选轻量模型并启用 batching;对于高并发建议部署 GPU(或使用向量数据库 + 离线索引)以降低实时成本。