1. 项目概述与核心价值
如果你经常需要将整个代码仓库或项目目录打包成一个文件,特别是为了喂给大语言模型(LLM)进行分析、代码审查或生成,那么你很可能已经受够了手动筛选文件、处理.gitignore规则、还要操心文件顺序的繁琐过程。yek这个用 Rust 写的命令行工具,就是为了解决这个痛点而生的。它的名字在波斯语里是“一”的意思,目标也很纯粹:把“多”个文件,高效、智能地序列化成“一”个文件。
我最初接触这类需求,是在尝试用 GPT-4 分析一个中型开源项目时。手动复制粘贴关键文件不仅容易遗漏,而且文件间的依赖关系和重要性完全无法体现。后来试过一些已有的工具,要么速度慢得让人抓狂(处理一个大型项目要几十分钟),要么配置复杂,要么对文件重要性的排序逻辑很原始。yek的出现,可以说是在速度、智能度和易用性上找到了一个相当不错的平衡点。它默认就懂得利用项目的 Git 历史来判断哪些文件更重要(比如最近频繁修改的src/main.rs通常比一年没动的docs/old_notes.txt更有价值),并自动应用.gitignore规则来跳过构建产物、依赖包等无关内容。对于开发者、技术写作者或者任何需要将代码上下文完整提交给 AI 助手的场景来说,这工具能省下大量前期准备时间。
2. 核心设计思路与工作原理拆解
yek的设计哲学是“开箱即用,深度可配”。它不是一个简单的文件连接器,其背后有一套精心设计的工作流,确保输出的内容对 LLM 是友好且高效的。
2.1 智能化文件筛选与优先级排序
这是yek区别于普通cat命令或简单脚本的核心。其工作流程可以分解为以下几个步骤:
收集与过滤:首先,
yek会递归扫描你指定的所有输入路径(文件或目录)。在此阶段,它应用多层过滤规则:- 第一层:
.gitignore。这是最自然的过滤,直接复用项目已有的配置,跳过node_modules/,target/,*.log等文件。 - 第二层:内置启发式规则。
yek内置了识别“可能不需要”的文件的逻辑,例如通过文件头信息检测二进制文件(如图片、可执行文件),以及默认跳过超过一定大小的文件(避免将巨大的日志或数据集打包进去)。 - 第三层:用户自定义规则。你可以通过命令行参数或配置文件,额外添加
ignore_patterns或覆盖内置规则(unignore_patterns)。
- 第一层:
分析与评分:对于通过筛选的文件,
yek会为其计算一个“重要性分数”。这个分数主要由两部分构成:- Git 历史贡献度:
yek会查询文件的 Git 提交历史。修改次数越多、越近期的文件,通常被认为在当前上下文中越重要。这个分数会有一个上限(默认100,可通过git_boost_max配置),防止某个古老但频繁修改的配置文件得分过高。 - 用户定义优先级规则:你可以在
yek.yaml中通过priority_rules为特定路径模式设置静态分数。例如,你可以让src/lib/下的核心库文件获得比tests/下的测试文件更高的基础分。这个分数会与 Git 贡献度加分累加。
- Git 历史贡献度:
排序与输出:所有文件按照其最终得分从低到高排序。这是一个关键细节:得分低的文件先输出,得分高的文件后输出。为什么这样设计?因为许多 LLM 存在“最近性偏好”(Recency Bias),它们对输入上下文末尾部分的内容记忆和理解更深刻。因此,把最重要的文件放在输出文件的末尾,能最大化利用 LLM 的上下文窗口,确保核心逻辑、主入口文件等关键信息对 AI 的影响权重最大。
2.2 输出策略与上下文窗口管理
LLM 的上下文长度是有限的(如 128K tokens)。yek提供了两种模式来确保输出内容适配这个限制:
- 字节模式(默认):使用
--max-size参数(如--max-size 128K)。yek会按排序后的顺序累加文件大小,当超过指定大小时,就会从排序列表的头部(即重要性最低的文件)开始丢弃,以确保最重要的文件能被保留在上下文中。 - Token 模式:使用
--tokens参数(如--tokens 128k)。这是更精确的模式,因为 LLM 的上下文限制是以 Token 为单位的。yek会使用一个内置的 Tokenizer(近似于 GPT-4 的分词方式)来估算每个文件的 Token 数量,并基于此进行相同的智能截断。在资源紧张时,务必优先使用 Token 模式,因为字节数和 Token 数差异可能很大(例如,中文或复杂代码的压缩比不同)。
此外,yek能自动检测输出目标。如果你将它的输出管道(|)到另一个命令(如| pbcopy或| head -n 1000),它会采用“流式输出”,一边处理一边发送,内存友好。如果是输出到终端(TTY)或文件,它会先完成所有处理,再一次性写入。
2.3 配置系统的灵活性
yek的配置系统是其强大且易用的体现。它遵循“惯例优于配置”的原则,同时提供了充足的扩展点:
- 零配置启动:在项目根目录直接运行
yek,它就能基于 Git 历史和.gitignore给出一个合理的结果。 - 项目级配置:在项目根目录创建
yek.yaml(或yek.toml/yek.json),可以固化针对该项目的规则,比如总是忽略某个自动生成的目录,或者为特定的子目录结构设置优先级。这个文件应该被加入.gitignore,因为它属于个人或团队的工作流配置。 - 命令行覆盖:任何在配置文件中定义的选项,几乎都可以通过对应的命令行参数进行临时覆盖。这为单次特殊处理提供了便利,例如临时调整输出大小限制。
3. 从安装到上手的完整实操指南
3.1 安装与验证
yek的安装极其简单,这得益于 Rust 生态的打包优势。对于大多数 Unix-like 系统(macOS, Linux),一行命令即可:
curl -fsSL https://azimi.me/yek.sh | bash这个安装脚本会自动检测你的系统架构,下载预编译好的二进制文件,并将其放置到系统的可执行路径下(通常是~/.cargo/bin/)。安装完成后,建议先验证一下:
yek --version如果显示出版本号(例如yek 0.1.0),说明安装成功。如果遇到command not found,可能需要将~/.cargo/bin添加到你的PATH环境变量中,或者重启一下终端。
注意:对于生产环境或对安全性有严格要求的用户,建议采用“从源码构建”的方式。这样可以审查代码,并确保二进制文件与你的系统完全兼容。方法也很简单:确保安装了 Rust 工具链(
rustc,cargo),然后执行cargo install --git https://github.com/mohsen1/yek。从源码构建能让你始终使用最新特性,但需要一定的编译时间。
3.2 基础使用与常用命令示例
让我们从一个最简单的场景开始。假设你有一个典型的 Rust 项目目录结构:
my_project/ ├── Cargo.toml ├── .gitignore ├── src/ │ ├── lib.rs │ ├── main.rs │ └── utils/ │ └── helper.rs ├── tests/ │ └── integration_test.rs └── target/ # 会被 .gitignore 忽略 └── (编译产物)进入项目目录,运行最基本的命令:
cd my_project yek执行后,yek会做以下几件事:
- 扫描当前目录(
.)下的所有文件。 - 应用
.gitignore规则,因此target/目录及其内容会被跳过。 - 读取 Git 历史,为
src/main.rs,src/lib.rs等文件计算重要性分数。 - 按照重要性从低到高的顺序,将所有文本文件的内容拼接起来。
- 由于输出是到终端(且未指定
--output-name),它会将结果写入一个临时文件(例如/tmp/yek_abc123.txt),并在控制台打印出这个临时文件的路径。
最常用的几种命令模式:
直接复制到剪贴板(macOS/Linux/Windows WSL):这是与 AI 对话时最高效的方式。处理完后直接粘贴即可。
# macOS yek | pbcopy # Linux (需要安装 xclip 或 xsel) yek | xclip -selection clipboard # Windows (PowerShell) yek | Set-Clipboard限制输出大小,适配 LLM 上下文:这是核心用法。假设你的 LLM 上下文是 128K tokens。
yek --tokens 128k或者,如果你更习惯用字节数估算:
yek --max-size 500KB处理特定目录或文件:你不需要总是在项目根目录运行。可以指定子目录,甚至混合指定。
# 只处理 src 目录 yek src/ # 处理 src 和 tests 目录 yek src/ tests/ # 处理具体的几个文件 yek Cargo.toml src/main.rs使用 Glob 模式进行模式匹配:这对于处理特定类型文件非常有用。
# 处理所有 .rs 文件 yek "**/*.rs" # 处理 src 下所有 .rs 文件,以及所有的 .md 文件 yek "src/**/*.rs" "**/*.md"重要提示:使用 Glob 模式时,务必用引号将模式括起来(如
"**/*.rs")。否则,Shell 会先进行路径扩展,可能得到意想不到的结果,或者因未匹配到文件而报错。
3.3 配置文件详解与高级定制
当基础功能无法满足需求,或者你想为特定项目建立一套固定规则时,配置文件yek.yaml就派上用场了。它应该放在项目的根目录。
下面是一个功能比较全面的配置示例,我结合自己的使用经验加了详细注释:
# yek.yaml # 项目专用的 yek 配置 # 1. 文件处理规则 # ------------------------------ # 最大 Token 数限制,优先使用 Token 模式以精确匹配 LLM 上下文 tokens: 100k # 额外的忽略模式,支持 glob 语法。 # 这里忽略所有 AI 提示词草稿目录和自动生成的代码目录。 ignore_patterns: - ".ai-cache/**" # 忽略 AI 生成的临时文件缓存 - "**/generated/**" # 忽略任何名为 generated 的目录 - "*.min.js" # 忽略压缩后的 JS 文件(通常可读性差) - "package-lock.json" # 忽略巨大的 npm 锁文件(依赖信息可从 Cargo.toml/package.json 获取) # 取消忽略某些内置规则。例如,默认会忽略大文件,但我想包含一个重要的数据模式文件。 unignore_patterns: - "schema.sql" # 即使这个 SQL 文件很大,也包含它 # 2. 文件优先级规则 (核心配置) # ------------------------------ # 为不同路径模式设置基础分数。分数越高,在输出中位置越靠后(对 LLM 越重要)。 # 分数会与 Git 历史加分累加。 priority_rules: # 最重要的:项目的核心库和主要入口 - score: 200 pattern: "^src/lib.rs$" # 库的根文件,通常是核心逻辑所在 - score: 180 pattern: "^src/main.rs$" # 应用的主入口 - score: 150 pattern: "^src/" # src 目录下所有其他文件 # 次重要:配置文件,它们定义了项目的行为 - score: 120 pattern: "^Cargo.toml$" # Rust 项目配置 - score: 120 pattern: "^package.json$" # Node.js 项目配置 - score: 120 pattern: "^pyproject.toml$" # Python 项目配置 # 中等重要:文档和测试 - score: 80 pattern: "^README.md$" # 项目说明文档 - score: 70 pattern: "^docs/" # 其他文档 - score: 60 pattern: "^tests/" # 测试文件,有助于理解功能边界 # 较低优先级:示例、脚本、构建配置等 - score: 30 pattern: "^examples/" - score: 20 pattern: "^scripts/" - score: 10 pattern: ".*" # 兜底规则,所有其他文件得分最低 # 调整 Git 历史影响的强度。默认 100,如果觉得 Git 影响太大或太小可以调整。 git_boost_max: 80 # 3. 输出格式与目标配置 # ------------------------------ # 输出到指定目录,而不是临时文件 output_dir: ./llm_context # 自定义输出文件名。如果设置了 output_dir,文件会放在该目录下。 output_name: project_context.txt # 自定义输出模板。默认是 `>>>> FILE_PATH\nFILE_CONTENT`。 # 这里我增加一个分隔符,让文件边界更清晰,并包含文件大小信息。 output_template: | ====================== File: {FILE_PATH} Size: {FILE_SIZE} bytes ====================== {FILE_CONTENT} # 在输出开头包含一个目录树状图,帮助 LLM 快速了解项目结构 tree_header: true # 包含行号,这在让 AI 定位代码中的特定问题时非常有用 line_numbers: true配置的优先级:命令行参数 > 配置文件 > 内置默认值。例如,即使在配置文件中设置了tokens: 100k,你仍然可以通过命令行yek --tokens 50k进行临时覆盖。
4. 性能实测与对比分析
“快”是yek的主要卖点之一。官方文档给出了与Repomix的对比数据,在处理大型项目(如 Next.js)时速度有数量级的提升。为了验证这一点,我在自己的开发环境(M1 MacBook Pro, 16GB RAM)上做了一个更贴近日常使用的测试。
测试对象:一个中型偏大的 Monorepo 前端项目,包含约 3000 个文件(主要是.ts,.tsx,.json,.md),总大小约 80MB。.git文件夹大小约 200MB。
测试命令与结果:
yek(默认设置,输出到临时文件):time yek --tokens 200k > /dev/null # 重定向到空设备,只测量处理时间结果:真实时间约
4.2秒。这包括了读取所有文件、解析 Git 历史、计算优先级、Token 化估算和排序的全部过程。内存占用峰值约 150MB。yek(流式输出到管道):time yek --tokens 200k | head -c 100 > /dev/null结果:约
0.8秒。因为流式模式下,yek一旦收集到足够填充上下文窗口的文件就会开始输出,无需处理完所有文件,速度更快。简单 Shell 命令对比 (作为基线):
time find . -type f -name "*.ts" -o -name "*.tsx" | head -100 | xargs cat > /dev/null结果:约
1.5秒。这个命令只做了最简单的查找和连接,没有过滤、排序、Token 计算。可见yek在增加了大量智能逻辑后,性能损耗控制得相当好。与
tree命令对比 (生成目录树):time yek --tree-only > /dev/null time tree -a > /dev/null结果:
yek约0.3秒,tree约0.9秒。yek生成目录树的速度甚至比专业的tree命令还快,这得益于其并行的文件系统遍历实现。
性能关键点分析:
- Rust 语言优势:无垃圾回收、零成本抽象、强大的并发支持,这些特性使得
yek在 IO 密集和 CPU 密集的任务上表现出色。 - 并行处理:
yek在扫描文件、计算哈希、读取内容等环节都采用了并行策略,充分利用多核 CPU。 - 惰性求值与流式处理:在管道模式下,
yek采用惰性求值,避免了不必要的内存分配和文件读取,极大提升了响应速度。
对于日常开发中常见的项目规模(几万行代码),yek的处理时间通常在几秒内,完全不会成为工作流的瓶颈。这种即时反馈对于需要频繁与 AI 交互的开发者来说至关重要。
5. 常见问题排查与实战技巧
即使工具设计得再完善,在实际使用中还是会遇到各种边界情况。下面是我在深度使用yek过程中总结的一些典型问题和解决方案。
5.1 问题:输出内容缺失了我认为重要的文件
可能原因及排查步骤:
- 检查
.gitignore:这是最常见的原因。运行git check-ignore -v <file_path>可以确认目标文件是否被.gitignore规则匹配。如果是,你需要在yek.yaml中使用unignore_patterns来覆盖。 - 触发了大小限制:如果你使用了
--max-size或--tokens,并且限制值设得较小,yek会从重要性最低的文件开始丢弃。尝试增大限制值,或者使用--debug标志运行,查看处理日志,确认哪些文件被排除了。 - 被内置二进制检测规则过滤:
yek会尝试检测二进制文件。如果某个文本文件(如.proto协议文件)的文件头恰好被误判为二进制,它会被跳过。可以通过在配置文件中添加该文件扩展名到unignore_patterns,或者使用--debug模式查看过滤日志。 - 文件路径未包含在输入中:确保你运行
yek的命令包含了目标文件所在的目录。例如,在项目子目录中运行yek默认只处理当前目录。
实战技巧:在不确定文件为什么被忽略时,首先使用--debug标志。它会输出详细的处理过程,包括每个文件的评分、是否被忽略及原因。这是最强大的调试工具。
5.2 问题:输出的文件顺序不符合我的预期
可能原因及排查步骤:
- 理解排序逻辑:记住,
yek的最终排序是:基础分(priority_rules) + Git 贡献加分,然后总分从低到高输出。分数最低的(最不重要的)最先出现。 - 检查 Git 历史影响:一个近期频繁修改的配置文件,其 Git 加分可能很高,导致它排到了后面。如果你希望某些文件(如文档)始终靠前,可以给它设置一个非常低的
priority_rules分数(如score: 5),并适当降低git_boost_max的值。 - 优先级规则冲突:
priority_rules列表是顺序匹配的,第一个匹配到的规则生效。确保你的规则顺序是从特殊到一般。例如,更具体的^src/lib.rs$应该放在更通用的^src/前面。
实战技巧:使用--tree-only参数先快速查看一下yek“眼中”的项目结构和它计划处理的文件列表,这可以帮助你确认输入范围是否正确。
5.3 问题:处理速度在特定项目上突然变慢
可能原因及排查步骤:
- 巨型文件:项目中是否存在巨大的日志文件、数据库 dump 文件或数据集文件?即使它们被
.gitignore忽略了,yek在初始扫描时可能仍然会尝试读取其元信息(如大小)。确保你的ignore_patterns足够全面。 - 符号链接或挂载点:如果项目目录中包含指向网络存储或复杂文件系统的符号链接,遍历时可能会遇到延迟。考虑使用
--ignore-patterns排除这些路径。 - Git 仓库历史异常庞大:对于历史极其悠久的项目(如 Linux 内核),计算每个文件的 Git 贡献度可能会比较耗时。如果不需要 Git 优先级,可以通过设置
git_boost_max: 0来完全禁用 Git 历史分析。
实战技巧:使用 Unix 的time命令来测量各个阶段的耗时。例如time yek --tree-only可以测量纯扫描时间,与完整运行的时间对比,就能判断瓶颈是在扫描阶段还是在优先级计算阶段。
5.4 高级技巧:与 AI 工作流深度集成
为不同 AI 任务创建不同配置:
- 代码审查配置(
yek.review.yaml): 高tokens限制(如 200k),包含src/和tests/,优先级偏向核心业务逻辑文件。 - 文档生成配置(
yek.docs.yaml): 包含README.md,docs/, 以及代码中的注释较多的模块(可通过priority_rules给*.rs或*.py文件中包含特定注释模式的文件加分)。 - 依赖分析配置(
yek.deps.yaml): 主要包含Cargo.toml,package.json,requirements.txt等依赖管理文件,并忽略所有源代码。 使用时通过--config-file指定:yek --config-file yek.review.yaml。
- 代码审查配置(
动态生成上下文:将
yek集成到脚本中,根据当前 Git 差异来生成上下文。例如,只序列化本次提交修改的文件:# 获取本次提交修改的文件列表 MODIFIED_FILES=$(git diff --name-only HEAD~1 HEAD) # 用 yek 处理这些文件 yek $MODIFIED_FILES --tokens 50k | pbcopy这样生成的上下文极度聚焦,非常适合用于编写提交信息或进行小范围代码审查。
输出格式后处理:
yek的--output-template非常强大。你可以定制输出格式,使其更符合特定 AI 助手的“口味”。例如,某些 AI 在代码块前加上语言标识符效果更好:output_template: "### File: {FILE_PATH}\n```rust\n{FILE_CONTENT}\n```\n"注意,这需要你预先知道文件类型,或者可以写一个外部脚本,根据文件扩展名动态设置模板。
yek解决了一个非常具体但日益普遍的需求:如何高效、智能地为 LLM 准备代码上下文。它成功地将文件过滤、重要性排序、上下文长度管理等琐碎工作自动化,让开发者能更专注于与 AI 的实质对话。其基于 Rust 的实现保证了极致的性能,而灵活的配置系统又让它能适应从个人小项目到企业级代码库的各种场景。无论是用于日常的代码解释、重构建议,还是集成到更复杂的 AI 辅助开发流水线中,yek都是一个值得放入工具箱的利器。我在几个月的使用中,最大的体会是它带来的那种“无感”的流畅——当你不再需要为准备提示词(prompt)的上下文而分心时,与 AI 协作的效率会提升一个档次。