假设和现实
注释比代码更像自然语言,所以向量模型理解起来应该更好。大多数工程师设计代码 Embedding 时都从这个假设出发。
实验数据给出了不同的答案。
实验设计
数据集: 27 个 Python 函数,跨 5 个业务模块(认证、数据库、缓存、支付、通知)
三种策略,使用同一个 Embedding 模型(BAAI/bge-large-zh-v1.5):
python
Strategy A:原始代码
嵌入完整的函数体------变量名、库调用、控制流全部保留
Strategy B:签名 + 注释(注释增强)
只嵌入 def 行 + docstring,去掉所有实现代码
例:
def validate_jwt_token(token: str)
"""Decode and validate a JWT token. Returns payload if valid."""
Strategy C:混合(注释 + 代码)
把注释作为前缀,附加完整代码体
例:
# Decode and validate a JWT token. Returns payload if valid.
def validate_jwt_token(token: str):
payload = jwt.decode(token, SECRET_KEY, ...)
...
三个策略用同一个模型,差异仅来自输入格式,排除了模型质量的干扰。
评估指标: Recall@3 和 Recall@5(12 个自然语言查询 × ground truth 相关函数)
运行结果
kotlin
Strategy Recall@3 Recall@5 vs A
──────────────────────────── ────────── ────────── ──────
A_raw_code 0.889 0.958 base
B_comment_enhanced 0.847 0.917 -0.041
C_hybrid 0.847 0.917 -0.041
Strategy A 最好。 B 和 C 打平,都比 A 低 0.041。
逐查询分析(Recall@5)
sql
Query A B C
────────────────────────────────────────────────── ────── ────── ──────
verify user identity and check JWT token validity 1.00 0.50 0.50 ←
encrypt and store user password securely 1.00 1.00 1.00
generate JWT access token for authenticated user 1.00 1.00 1.00
check if user has permission to perform an action 1.00 1.00 1.00
store and retrieve data from Redis cache 1.00 1.00 1.00
limit how many times a user can call an API 1.00 1.00 1.00
execute SQL query safely against the database 1.00 1.00 1.00
process payment and create Stripe charge 0.50 0.50 0.50
issue refund to customer 1.00 1.00 1.00
send email notification to user 1.00 1.00 1.00
send mobile push notification 1.00 1.00 1.00
delete a record without removing it from database 1.00 1.00 1.00
分化只发生在两道题上:
Q1(JWT 验证):A=1.0,B/C=0.5
Strategy B 只嵌入了:
python
def validate_jwt_token(token: str)
"""Decode and validate a JWT token. Returns payload if valid."""
Strategy A 还嵌入了:
python
payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
except jwt.ExpiredSignatureError:
except jwt.InvalidTokenError:
jwt.decode、ExpiredSignatureError、InvalidTokenError 是高质量的技术语义信号------这些词汇让向量模型知道"这段代码和 JWT 深度相关"。注释只说了"decode and validate",没有透露具体的技术实现路径。
Q8(Stripe 支付):A/B/C 全部 0.5
三个策略都找到了 create_payment_intent,但没有找到 calculate_order_total。原因:查询是"process payment and create Stripe charge",而 calculate_order_total 的注释是"Sum item prices, apply discount, compute tax"------两者之间的语义距离在向量空间里确实很远,换任何策略都无法弥补。
为什么原始代码更好
代码本身包含的技术词汇是高质量语义信号:
go
函数名和变量名:
validate_jwt_token → "JWT" "token" "validate"
hash_password → "hash" "password"
rate_limit_check → "rate" "limit"
库和包名:
import bcrypt → 密码哈希领域
jwt.decode() → JWT 认证领域
stripe.PaymentIntent → Stripe 支付领域
redis.Redis → 缓存/Redis 领域
异常类名:
jwt.ExpiredSignatureError → JWT 过期场景
stripe.error.SignatureVerificationError → 支付验签场景
这些技术词汇在预训练语料里有丰富的上下文,Embedding 模型能把它们映射到正确的语义空间。注释版虽然"更像人话",但往往用了更模糊的动词("process"、"handle"、"manage"),反而丢失了这些精确的技术信号。
类比: 在医学文献里,"发烧、咳嗽、咽痛"比"身体不舒服"给医生更多信息,即使前者不是"更自然的语言"。
什么时候注释增强有效
策略 B/C 不是没有价值,它们在以下场景可能反而更好:
场景 1:代码非常"内部语言"
python
def do_proc_v2(x, y, flag=False):
"""Process user authentication with fallback to legacy mode."""
result = _run_auth_pipeline(x, y)
if flag:
return _legacy_compat(result)
return result
函数名 do_proc_v2、参数名 x, y 完全没有语义,docstring 才是唯一的有效信息。这种情况下,Strategy B 会比 A 好很多。
场景 2:代码注释覆盖了实现没有体现的业务上下文
python
def calculate_fee(amount: float) -> float:
"""
Platform fee calculation per the 2024 pricing agreement with Partner XYZ.
Fee = 2.5% for amounts < 1000, 1.8% for amounts >= 1000.
"""
return amount * (0.025 if amount < 1000 else 0.018)
"Partner XYZ"和"2024 pricing agreement"是业务上下文,纯代码里看不出来,只有注释包含了这个信息。
场景 3:代码库规范良好,docstring 覆盖率高
如果代码库有严格的文档规范,所有函数都有高质量 docstring,Strategy B/C 的优势会更明显。
实际工程建议
对大多数代码库: 用 Strategy A(原始代码)作为基线------简单、效果好、不需要额外处理。
有以下情况时,考虑 Strategy C:
- 代码库里有大量"语义不透明"的函数(命名不规范、参数名无意义)
- 代码库有高质量 docstring 覆盖(> 60% 的函数有实质性注释)
- 查询类型以业务语言为主("用户注册流程"而非"JWT token validation")
函数级 Chunk 附加元数据:
不管用哪种 Embedding 策略,函数级 Chunk 都应该附加结构化元数据:
python
{
"content": func_body, # Embedding 的文本
"metadata": {
"name": "validate_jwt_token",
"module": "auth",
"file": "services/auth.py",
"line_start": 12,
"line_end": 20,
"has_docstring": True,
"called_by": ["login_handler", "middleware"],
}
}
元数据在检索后过滤时很有价值("只搜索 auth 模块"、"找到函数后跳转到定义位置")。
总结
- 原始代码 Embedding 效果最好(Recall@5=0.958):代码里的技术词汇(库名、异常类、函数名)是高质量语义信号,Embedding 模型能正确理解
- Q1 的分化揭示了根因 :
jwt.decode、ExpiredSignatureError这些词汇是 Strategy A 优于 B 的具体原因,注释删掉了精确的技术信号,换成了模糊的动词 - Q8 说明向量检索有语义鸿沟 :
calculate_order_total和"create Stripe charge"的语义距离,用任何 Embedding 策略都无法弥合------这类情况需要用其他手段(关键词搜索、调用图)补充
参考资料
- 本系列完整 Demo 代码:codebase-kb-03-embedding
欢迎访问 PrimeSkills ------ 一个精心策划的 AI Agent 与技能市场,所有内容均经过真实企业级工作流验证。没有噱头,只有真正有效的东西。
更多实用知识和有趣产品,欢迎访问我的个人主页