rafal-qa/slopo
GitHub: rafal-qa/slopo
基于 embedding 的代码重复检测 CLI 工具,专门识别传统工具难以捕捉的非精确相似代码。
Stars: 230 | Forks: 7

# Slopo
一款轻量级 CLI 工具,通过 embedding 模型检测非精确代码重复。
它专注于那些最难检测且危害最大的相似代码:写法相似的代码片段、在代码库中相距甚远的代码,通常分布在不同的模块中,或者在一个大文件中分隔开来。精确的复制粘贴很容易被其他工具发现,而距离相近的重复代码也很容易被人类或 AI 发现。
有关该问题的更多高层级描述及 LLM prompt 示例,请参阅 [slopo.dev](https://slopo.dev/)。
### 支持的语言
Python、TypeScript、JavaScript、Java、Kotlin、C#、Go、Rust、PHP、Elixir
## 工作原理
它采用了与典型重复检测不同的方法。对于每个代码单元,它会计算一个 embedding,然后寻找 embedding 距离相近的配对。相似的代码不一定是重复的,因此每个配对都是需要确认的潜在重复项。功能相同但实现方式完全不同的代码会产生相距甚远的 embedding,因此不会被检测到。
最终结果是相似代码单元的集群,按相似度和在代码库中的距离进行排名。这些集群旨在作为你的 AI 编码代理的输入,由其检查集群是否为真正的重复项。经过审查的集群可以标记为忽略,或交由后续进行重构。
### 示例报告
请参阅基于 Slopo 代码的 `src` 目录、git tag `v0.2.0` 生成的 [doc/example-report](doc/example-report)。
这个示例证实了每种语言的代码解析器都存在大量重复,有些是完全相同的副本,有些是相似的变体。这需要进行重构。
## 快速开始
### 安装
```
uv tool install slopo
```
或者升级到最新版本
```
uv tool upgrade slopo
```
此命令使用 `uv`([安装 uv](https://docs.astral.sh/uv/getting-started/installation/)),这是一个 Python 包管理器,在隔离的虚拟环境中从 [PyPI](https://pypi.org/project/slopo/) 安装/升级 Slopo。无需单独获取 Python。
### 设置
运行 `slopo init` 以创建包含进一步说明的配置文件模板。只需要配置包含待分析代码的目录和 embedding 模型即可。
### Embedding 模型
Embedding 通过外部提供商进行计算。为了获得最佳效果,请考虑使用专门针对代码的模型,例如 [Voyage AI](https://docs.voyageai.com/docs/embeddings)(它在 `512` 这样的低维度下也能很好地工作)。
你可以使用任何兼容 LiteLLM 的模型提供商,[在此处查看详情](https://docs.litellm.ai/docs/embedding/supported_embedding)。
出于安全考虑,建议将提供商的 API key 设置为环境变量。
### 分析
运行 `slopo show-config` 以验证你的配置并显示所有可配置参数,其中大部分具有合理的默认值,属于可选项。
现在你可以准备索引代码、计算 embedding 并生成报告了:
```
slopo index
slopo embed
slopo analyze
```
## 实际工作流
本节演示如何在实际的开发工作流中使用 Slopo。
它利用增量重新索引(仅使用更改过的文件更新索引)以及 `slopo.ignore.txt` 来丢弃已经审查过的集群。
1. 创建你的首次分析并检查结果。你会注意到 `index.md` 包含所有集群的列表以及每个文件的集群详情。
2. 你可能希望排除某些目录或文件模式,通常排除测试是一个好主意。如果结果太多或太少,你也可以调整阈值。
3. 对分析结果满意后,要求你的 AI 编码代理过滤掉非真正重复的集群。这是一种常见情况,因为并非所有相似的代码都需要处理。要求 AI 代理将丢弃的集群哈希值添加到 `slopo.ignore.txt` 中。
4. 重新运行分析以生成不包含已审查集群的报告。这是重构的基础,可以由 AI 代理完成。
5. `ignore` 文件可以提交到你的 Git 仓库并在整个团队中复用。新增和修改过的集群将重新出现在报告中。不含 API key 的配置文件也可以提交。不要提交 `slopo.db`,这是你的本地数据。
## 配置
你可以随时运行 `slopo --help` 和 `slopo show-config` 自行探索配置。
大多数配置是通过配置文件完成的,但有两个例外:
1. 配置文件的位置可以使用 `--config` 选项覆盖。
2. API key 可以通过 `SLOPO_EMBEDDING_API_KEY` 环境变量设置,也可以从当前目录的 `.env` 文件中读取。
**请注意,有些参数在首次索引后无法更改。** 你需要删除 `slopo.db` 并从头开始索引/计算 embedding:`source_dir`、`embedding_model`、`embedding_dimensions`、`body_node_count_threshold`。
### 所有可配置参数
- `source_dir`:包含待索引代码的源目录,可为绝对或相对路径。
- `source_dir_exclude`:用于在索引时排除的 .gitignore 风格的模式。
- `db_file`:包含工具数据的 SQLite 数据库文件。
- `report_dir`:分析报告的输出目录。
- `ignore_file`:包含被忽略集群的文本文件。
- `embedding_model`:LiteLLM 格式的 embedding 模型名称。
- `embedding_dimensions`:与所用模型兼容的 embedding 维度。
- `embedding_api_key`:embedding 提供商的 API key。如果已通过环境变量配置,则此项为可选。
- `embedding_batch_size` 和 `embedding_batch_chars`:为了提高性能,对 embedding API 的请求会进行批量处理。默认值适用于大多数情况。
- `similarity_threshold`:控制 embedding 之间的最小余弦相似度。
- `rerank_threshold`:控制应用反映代码库距离的权重提升后的最小相似度。
- `body_node_count_threshold`:函数体内部(不包括签名和注解)的 AST 节点数量。此值反映了所包含代码单元的最小代码复杂度,比文本长度更精确。如果在报告中看到不想要的、过小的代码单元,请增加此值。
## 详情
### 排名阈值
相似代码单元通过两次过程进行过滤,每次过程都有各自可配置的阈值。pipeline 如下:
1. `similarity_threshold` 过滤掉 embedding 不够相似的代码单元对。计算出的值为余弦相似度,范围从 `-1` 到 `1`,其中 `1` 表示完全相同。
2. 相似的配对被分组成集群。
3. 集群中的单元在应用权重提升后会重新排名。权重提升的计算基于到达配对中另一个文件所需的目录跳转次数(最高 15%)。如果它们在同一个文件中,则权重提升基于行数距离计算(最高 10%)。`rerank_threshold` 会过滤掉得分最高的配对分数不够高的集群。
### 精确副本重复
此工具的主要目标是检测非精确代码重复,但也会报告精确副本(多个路径下的完全相同代码),只是处理方式与仅仅是相似的代码略有不同:
- 报告会显示一次该代码,并列出其出现的所有路径,而不是重复显示相同的代码片段。
- `analyze` 命令报告的“相似比率”(被标记为相似的代码单元比例)包含两个版本:包含和不包含精确副本。
标签:AI辅助, SOC Prime, 代码重复检测, 嵌入模型, 开发工具, 逆向工具