一个多月前,我最常用的命令是cat,而不是deepseek-harness。每次想让模型看某份代码或文档,我都得先手动把文件内容拼进提示词里,拼完还要担心长度超限、截断位置不对、贴错了文件。直到我把 deepseek-harness 升级到最新版,发现它支持@文件补全之后,这个工作流才算真正顺了。
所谓@文件补全,就是在提示词里用@路径的方式引用本地文件,harness 会在发送请求前自动读取这些文件内容并拼到上下文中。听起来是个很小的改动,但实际用下来,它对"拿大模型处理真实项目"这件事的影响远比想象中大。这篇文章把功能原理、使用场景、落地细节和坑都梳理一遍,给正在折腾 DeepSeek 相关工具链的朋友一个参考。
1. 起因:被手动拼上下文折磨三个月之后
先说为什么我会对这个功能这么敏感。我平时用 deepseek-harness 的场景很固定:写代码审阅、生成单元测试、整理接口文档、分析线上报错日志。这些任务有个共同点——都需要把"项目里已有的文件内容"交给模型,而不是只靠一句干巴巴的提问。
1.1 手拼上下文最常见的三种翻车场景
第一种是贴代码贴到一半,终端滚动缓冲区把内容截断了。尤其在 macOS 的 Terminal 里,一次性cat一个几百行的文件再手动复制,经常丢头去尾。你以为模型看到的是完整实现,实际上它只看到了文件中间那一段,给出的建议当然会跑偏。
第二种是文件太多,提示词顺序乱了。我经常要同时参考接口定义、数据库模型和一个工具函数。手动拼接时,顺序全靠复制粘贴的先后决定,粘贴的时候脑子一乱,把模型定义放在了业务代码前面,模型理解起来的难度就大了很多。
第三种最坑——cat命令把文件内容打到终端之后,特殊字符会被终端解释掉一部分。比如代码里的反引号、$符号、甚至某些中文引号,在复制粘贴过程中都可能被吃掉或者被转义。我有一回排查一个诡异的语法错误,最后发现是提示词里的反引号丢了,模型根本没看到那段内联代码。
这些事单独拎出来都不算大问题,但组合在一起,会让"准备一次高质量提问"变成一个比写代码本身还累的活。所以 deepseek-harness 把@文件补全做成原生功能,对我来说是从"手工搬运工"变成"真正用工具"的分水岭。
1.2 为什么用 @ 而不是 /read 或 #include
可能有人会问,市面上不少工具用的指令格式是斜杠命令或者#注释式语法,为什么 deepseek-harness 选了@。
从实际使用角度讲,@在 shell 和文本编辑场景里几乎没有歧义。提示词里出现@docs/api.md,人类一眼能看出"这是引用一个文件",模型在解析 prompt 时也不会把它误解成注释或者特殊指令。而#在很多代码语境里是注释符号,如果引用语句混在代码片段里,解析器容易误伤。/开头更是危险,因为路径本身就以/开头,/read /src/core.py这种写法在解析时会有额外的边界判断。
@还能和自然语言混在一起使用。我可以写"请参考 @src/core.py 和 @docs/api.md 来完成这次重构",读起来不像在敲命令,更像在正常说话。这个体验上的差异,长期使用下来感受非常明显。
2. 从贴路径到自动拉取:@文件补全到底怎么工作
@文件补全的原理不算复杂,但要把细节做对也不容易。它的核心流程是:从提示词中识别所有@路径标记,校验路径合法性后读取文件内容,再把内容以结构化的方式插入到提示词的合适位置,最后才发送给模型。
2.1 先看一个完整示例
假设项目结构是这样的:
myapp/ ├── src/ │ └── core.py ├── docs/ │ └── api.md └── tests/ └── test_core.py以前我要让模型审查core.py,得这样操作:
cat src/core.py # 手动复制输出,粘贴到提示词里 deepseek-harness run "请审查下面这段代码,重点看并发安全:"现在直接这样写:
deepseek-harness run "请审查 @src/core.py,重点关注并发安全,并给出改进建议"harness 会把@src/core.py替换成一份标记清晰的上下文块,类似:
<reference path="src/core.py"> (文件完整内容) </reference>模型收到的不再是孤零零的一句"审查代码",而是"审查代码 + 完整源码"的组合体。这一步省掉的不只是复制粘贴的时间,更重要的是上下文完整性有了保障。
2.2 解析器的三个关键步骤
我在本地翻了下源码,把它的解析逻辑概括成三步。
第一步是词法扫描。解析器会扫描整个提示词,找出所有以@开头、并且能匹配到文件系统中真实路径的字符串。这个步骤需要注意的点是@后面可以跟绝对路径,也可以跟相对于当前工作目录的路径;我习惯用相对路径,这样整个指令可以写成一行,换台机器执行也不会因为目录不一致而失效。
第二步是访问校验。在读取文件之前,解析器会检查路径是否真实存在、是否是目录、是否超过了配置的允许读取范围。这一步很重要,如果没有任何限制,模型上下文就可能被一个不小心写出来的@/拖垮——直接把整个根目录读进来了。
第三步是内容折叠和插入。解析器读取文件后,不是简单地把原文替换掉@路径,而是在外面包一层引用标记,并记录文件路径和行数。这样做的好处是模型能够区分"哪段是引用文件的内容",后续回答时可以精确说"根据core.py第 42 行的逻辑,我建议……"。
2.3 路径歧义和通配符处理
实际使用中,路径歧义是个绕不开的问题。比如我写@core.py,而项目里存在src/core.py和tests/core.py两个同名文件。deepseek-harness 的做法是:先精确匹配,匹配不到再尝试模糊匹配;如果模糊匹配命中多个文件,会把这些文件全部列出来,让你在交互模式里二次确认。
通配符则更直接。我偶尔会写@tests/*.py来引用整个测试目录,所有匹配到的文件都会以多个引用块的形式插入上下文。这个功能别滥用,一旦目录里文件数量太多,上下文长度会迅速爆炸,后面的实测部分会再细说。
3. 把 @文件补全塞进日常流程的三个高性价比用法
功能落地的关键永远是找到高频率、高收益的使用场景。我这段时间用下来,有三类用法最顺手,比传统的方式效率提升不少。
3.1 代码审查:把 diff 和引用文件一起喂进去
代码审查是我用得最频繁的场景。过去我审查一个分支,需要先把git diff的结果复制进提示词,再找出变更涉及的源文件,把关键部分也复制进去。这个流程在文件变更少时还能应付,一旦一次改动涉及五六个文件,准备提示词的时间比审代码的时间还长。
现在我会这样组合:
git diff HEAD~1 --stat deepseek-harness run "以下是分支改动涉及的代码上下文:@src/core.py @src/utils.py @tests/test_core.py,请结合这些文件审查我的 git diff,注意改动是否破坏了原有接口兼容性"实际执行时还可以用管道直接接入:
git diff HEAD~1 | deepseek-harness run "针对这次 diff 做代码审查,涉及文件:@src/core.py @src/utils.py"这里有个小技巧:git diff的结果和引用文件内容同时存在时,模型能分辨"新改动的部分"和"项目原有的部分",给出的审查意见会更具体。它不会只泛泛地说"注意空指针问题",而是能指出来"你在这个 diff 里删掉了core.py中的锁,但utils.py里仍然有并发访问,这里需要再想想"。
3.2 生成测试用例:把被测模块和样例测试一起带上
让模型写单元测试,最大的痛点是它不了解被测模块的接口细节。直接丢一句"给core.py写测试",模型只能靠猜,生成出来的测试大概率连 import 都过不了。
用@文件补全之后,我会把被测模块和一个已有的测试样例都引进去:
deepseek-harness run "参考 @src/core.py 的实现和 @tests/test_core.py 的现有写法,给 UserService 类补充 5 个边界测试用例,覆盖空输入和重复提交的情况"模型能直接看到真实函数签名、返回类型、异常抛出方式,生成的测试代码结构和项目现有风格高度一致。我试过一次之后,测试用例的"一次通过率"明显提高,不需要反复回去改断言。
3.3 文档与技术方案:让模型基于真实代码说话
写技术方案文档是我的另一个高频场景。这个场景里,手拼上下文的痛点是"上下文数量大"——一份设计文档可能涉及接口定义、数据库表结构、现状代码、历史决策记录四五类素材。
我的习惯是把所有素材都用@引进去,然后给一个结构化指令:
deepseek-harness run "基于 @docs/current-api.md 的现状、@src/models.py 的数据结构、@src/repository.py 的存取实现,设计一个缓存优化方案,要求兼容现有接口,并评估对数据一致性的影响"这个用法和直接粘贴全文最大的区别是——模型知道哪个信息来自哪个文件,写作时会更自然地交叉引用。"基于models.py的Order模型和repository.py里的get_order方法,我建议在缓存层增加版本号字段"这种回答质量,是单靠一段拼凑文本很难达到的。
4. 循环引用、路径穿越与超长输入:实现时踩过的几个坑
真实项目中能用好一个功能的前提,是知道它的边界在哪。下面这几个问题,是我在升级后实测过程中真实撞到的,也是插件和脚本类工具最常出问题的位置。
4.1 循环引用:当文件 A 引用了文件 B,文件 B 又引用了文件 A
deepseek-harness 对引用内容的处理有一个细节:如果被引用的文件里,本身又写了@另一个文件这种语法,默认是不展开的。也就是说,它只做一层解析,不会递归读取被引用文件里的引用标记。
这个设计我认为很合理。如果做成递归展开,一旦出现循环引用,进程就会陷入无限循环;即使不循环,一层套一层的引用也会让上下文内容完全失控。我见过一个朋友自制的脚本因为递归展开,把整个项目的.py文件全读进一次请求里,直接撞到上下文上限。所以现在我在文件内容里写@符号时,会主动避开行首引用语法;如果是注释里需要描述某个文件路径,我会写成@(路径)或者干脆用普通文字描述。
4.2 路径穿越:不是所有路径都可以被读取
由于@文件补全能读取本地文件,路径校验必须严格。我实测过几个情况:
- 使用
@../越权读取工作目录之外的敏感文件,会被拦截并报错。 - 使用绝对路径引用
/etc/passwd这类系统文件,同样会被默认策略拒绝。 - 指向目录的
@src/也会被拒绝,因为目录不是文件,无法直接读取内容。
这个安全边界很有必要。尤其是当 harness 被集成到团队共享脚本或者 CI 流程里时,如果有人构造了一个恶意提示词尝试读取服务器上的敏感信息,没有这层校验会非常危险。我自己在二次开发时也发现,校验必须放在路径解析之后、文件读取之前,而且要同时检查规范化之后的真实路径,不能只看用户输入的原始字符串。
4.3 大文件和超长输入:模型不是无限容量的垃圾桶
这是我最想提醒大家的一点——@文件补全虽然方便,但绝不意味着可以把整个项目塞进去。
DeepSeek 模型的上下文窗口虽然有几十万 token,但当你把多个大文件全部展开之后,模型对关键信息的注意力会被稀释。我做过一次对比实验:同样的审查任务,只引用core.py(约 400 行)时,模型能给出非常具体的建议;把整个src/目录里的 30 个文件全部引用之后,模型的回答开始变得泛泛而谈,只挑最表层的问题说,深层问题反而被忽略了。
这里的经验是:引用文件时先想清楚"这次任务只需要哪几个文件"。如果需要引用的文件超过 3 个,我会先用wc -l看下总行数,超过 2000 行的内容就得考虑拆分成多轮对话,或者让模型先归纳每个文件的核心逻辑,再进入下一轮分析。
4.4 中文文件名和空格路径的处理
另一个容易踩的坑是路径中包含空格或中文。deepseek-harness 的解析器默认只把@后直到空白字符为止的内容当作路径,因此路径里有空格时需要加引号:
deepseek-harness run "参考 @'src/my module/core.py' 完成重构"如果不想记这些规则,最简单的做法是把路径里的空格全部换成下划线,或者把所有工作文件都放到src/、docs/这种无空格目录下。中文文件名的情况类似,大部分系统下都能正常读取,但我在 Windows + Git Bash 混用环境下遇到过几次编码问题,后面会专门提。
5. 什么时候别用 @:几条我总结出来的边界判断
工具是拿来提高效率的,不是拿来炫技的。@文件补全并不适合所有文件读取场景,我根据自己的实践总结了几个"不该用"的情况。
5.1 超大文件:日志和构建产物直接排除
我最早犯过的错就是拿@去引用日志文件。日志动辄几十 MB,读取之后不仅浪费 token,而且日志里的重复噪声会把模型带偏。现在我的规则很明确:超过 1MB 的文件,或者行数超过 3000 行的文件,不用@文件补全直接引用,而是先做预处理。
处理方式有两种。一种是用grep先过滤出关键行,再把过滤结果放进提示词;另一种是用 harness 的管道能力,先让模型读取文件摘要:
head -200 app.log | deepseek-harness run "先帮我总结这个日志片段里的报错模式"同样道理,node_modules、dist、build这类生成目录里的文件,几乎永远不会成为良好的上下文素材,引用来只会浪费资源。
5.2 多文件并列比较时的顺序问题
当你想让模型对比几个文件的差异时,@文件补全也能做到,但效果取决于引用顺序。解析器是按照提示词里出现的顺序展开文件内容的,所以哪个文件放在前面,模型就会天然把它当作基准。
比如对比两个版本:
deepseek-harness run "对比 @src/core.py 和 @src/core_old.py,列出行为差异"模型会把core.py当作新的、主要的对象,core_old.py当作参照物。如果你的本意是"以旧版本为基准",那就应该把core_old.py放在前面。这个顺序规则虽然算不上坑,但是用错时对比结果会绕一些,容易让人误解模型的理解能力有问题。
5.3 编码和跨平台路径差异
最后提一下编码问题。我在 Windows 上遇到过UTF-8编码文件被用GBK读取导致乱码的情况,后来统一把所有项目文件转成了UTF-8无 BOM 格式才彻底解决。如果你手头有老项目,文件编码比较混杂,建议先做一次编码统一再启用@文件补全。
Linux 和 macOS 下基本没有这些问题,但它们对绝对路径的处理逻辑和 Windows 不完全一致。Windows 的盘符路径C:\Users\...在解析时需要注意转义,我个人的经验是尽量用相对路径,避免在提示词里出现反斜杠。相对路径一旦用习惯,整个工作流就非常顺,换目录执行也不用改提示词。
6. 这个改动给我的工作流带来什么变化
最后说点个人体会。deepseek-harness增加@文件补全之后,我最大的感受是:我终于愿意把复杂的真实项目问题交给模型去处理了。
以前我要花大量时间筛选、拼接上下文,所以只能挑一些简单的、单文件的问法去问。现在我可以很自然地写出"结合@src/core.py的实现、@docs/api.md的约定、@tests/里的现有测试风格"这种复杂指令,模型给出的答案质量完全是另一个层级。
如果你也在折腾类似的工作流,我的建议是先从小场景切入:挑一个你每周都会重复做的任务,把原来手动cat的步骤替换成@引用,用一周时间感受差异。过程中注意我上面提到的边界问题——大文件控制、路径校验、引用顺序。这套用法熟练之后,你会慢慢找到自己的节奏,甚至可以根据项目特点扩展出更适合自己的提示词模板。
现在我的习惯是:拿到一个任务,先写一个带@引用的提示词草稿,再根据输出质量微调引用范围和顺序。这个流程跑顺之后,deepseek-harness 从一个"命令行玩具"彻底变成了我日常开发的基础设施。如果你试出好用的组合玩法,也欢迎交流。