AI读代码前先清洗源文件:大规模代码库预处理实战指南

AI读代码前先清洗源文件:大规模代码库预处理实战指南 我们仓库里当时一共躺了九千多个源文件。任务听着也简单让 AI 帮我把这几万个文件的模块关系梳理清楚顺带给出重构建议。一开始我的想法很粗暴把整个目录拖进上下文让模型自己看。结果连试三轮都翻车不是文件解析到一半挂了就是模型开始分析 node_modules 里的压缩代码最离谱的是它把一份 SQL 备份当成了业务配置文件郑重其事写了一整页“配置说明”。后来我停下来想明白一件事把近万个源文件喂给 AI 之前真正该做的不是“投喂”而是先给这批文件做一次系统性的预处理和建档。这个动作谁都能做做不做得到位直接决定后面 AI 是帮你干活的工程师还是只会复制粘贴的聊天机器人。这篇文章就把我当时完整跑通的流程、脚本和踩过的坑拆开讲清楚适合所有准备把手头大规模代码/文档库交给 AI 分析、做 Agent 问答、或者整理数据喂给大模型的人。1. 为什么“全量塞给 AI”这个思路注定会失败1.1 上下文窗口和 token 成本先从数学上就过不去先别谈模型能力我们先算一笔账。我当时那个项目目录 9,341 个文件去掉明显无关的图片和二进制文件剩下文本类源文件大概也还有七千多个。假设每个源码文件平均 260 行、每行 40 个字符那就是大约 240 万行、9600 万字符。按常见 token 估算规则英文和代码大概 4 个字符一个 token这份语料总量得有两千多万 token。现在主流商用模型上下文窗口做到 128k 或者 200k 已经算大了你拿这些数字一除就明白全量内容连 1% 都放不进去。就算你强行用支持超长上下文的方案把所有文件压缩塞进去输入费用也会瞬间膨胀到没法看。这还没提一个更隐性的问题上下文中无关信息太多时模型对关键文件的注意力会被稀释回答质量肉眼可见地下降。所以不要跟上下文窗口硬刚。正确的做法是理解一个现实模型不需要一次读完所有文件它只需要知道“这个仓库里有什么、每个文件大概负责什么”然后按需去读真正相关的部分。这就是预处理要解决的第一件事让 AI 手里先有一张准确的地图。1.2 垃圾文件、构建产物和测试数据会带偏判断很多人以为喂给 AI 的是“源文件”实际目录里什么都有。我当时随手统计了一下有编译出来的 .class、.pyc有前端构建出来的 dist 目录有将近 2GB 的 .git 历史还有一堆 .DS_Store、Thumbs.db、日志文件、数据库导出 SQL甚至还有从旧机器上拷过来的整个 .idea 配置目录。这些文件对真正的源码分析没有贡献反而会制造大量噪声。模型不具备你脑子里的“这个目录不用管”的常识它只会老老实实把看到的所有内容都当作有效信息处理。于是它可能花大段文字分析一段压缩混淆后的 JS bundle或者把测试夹具数据当成业务逻辑去推导最后给你的结论自然跑偏。更隐蔽的是重复文件。同一个文件在多个备份目录里出现好几份或者旧版本、新版本、带后缀版本混在一起。模型一旦读到了过期版本就会拿旧逻辑回答新问题。这些问题都不是靠模型多聪明能解决的必须在投喂之前靠预处理把语料清洗干净。1.3 来源复杂的文件可能带病进门“源文件”这几个字容易让人放松警惕好像只要是文本就是安全的。但你如果接过那种从网盘、邮件附件、同事 U 盘、甚至数据库恢复工具里扒出来的目录就知道里面有多乱。有些文件扩展名是 .txt实际是个二进制文件有些文件带着 Windows 系统上的“来自互联网”标记本地解析器看一眼就拒绝打开还有些文件编码混乱GBK、UTF-8、UTF-16 混在一起直接读出来就是乱码。我特别提醒一点直接从网上下载的源码包、书源文件或者别人分享的补丁目录在喂给 AI 之前最好先做一次来源和内容体检。系统弹窗提示“该文件来自外部来源可能不安全”不是玄学它背后确实有一套安全机制在拦截可疑文件。你需要做的是甄别而不是无视确认文件来源可信、杀毒扫描通过、没有夹带私货再放进待处理队列。如果这一步不做轻则 AI 读到乱码开始胡编重则把你不希望泄露的信息比如藏在配置文件里的密码、密钥、内网地址一起发给云端模型。这是工程问题也是数据安全问题不能马虎。1.4 预处理的目标制造“有效语料子集 按需访问方案”我把整个思路理顺之后给自己定了三个预处理目标。第一个目标产出一份完整但精简的仓库索引内容包括目录结构、语言分布、每个文件的路径、大小和行数。第二个目标产出一份经过清洗的语料池只保留文本类源文件和可解析的文档统一编码、去除 BOM、修正换行符必要时过滤超大文件和重复文件。第三个目标设计一套按需读取机制让 AI 通过工具函数主动读取某个具体文件的内容而不是把几万个文件一次性堆给它。简单说就是先给 AI 地图再让它自己决定去哪栋楼、找哪个人。这套思路不仅适用于代码分析也适用于任何大规模文档投喂场景本质上是把“把数据库搬进对话”改成“在对话里装一个检索器”。预处理做完之后我自己实测的效果非常明显AI 不再满嘴跑火车开始能答到点子上。2. 我先做的这件事给整个源文件库做“体检与建档”2.1 第一步先盘清家底别凭感觉估数量动手预处理的第一步不是写清洗脚本而是先把仓库里到底有什么东西彻底摸清楚。我当时很多人会直接说“大概有一万多个文件吧”这种模糊认知就是后续所有问题的根源。你必须先回答几个精确的问题文件总数是多少总大小多大哪些目录占了大部分空间扩展名分布什么样哪些文件特别大我的处理方法很简单直接用一个 Python 脚本全局扫描一遍目录输出几个统计表。我不建议在这种摸底阶段就上特别复杂的工具先跑一段几十行的脚本拿到准确数字比任何 fancy 的工具都管用。from pathlib import Path from collections import Counter import os ROOT Path(./repo) total_files 0 total_size 0 ext_counter Counter() dir_counter Counter() large_files [] for p in ROOT.rglob(*): if not p.is_file(): continue # 主动跳过隐藏目录和版本控制目录 if any(part.startswith(.) for part in p.relative_to(ROOT).parts): continue total_files 1 size p.stat().st_size total_size size ext p.suffix.lower() if p.suffix else [noext] ext_counter[ext] 1 top_dir p.relative_to(ROOT).parts[0] if p.relative_to(ROOT).parts else ? dir_counter[top_dir] 1 if size 5 * 1024 * 1024: large_files.append((size, str(p))) print(f文件总数: {total_files}) print(f总大小: {total_size / 1024 / 1024:.1f} MB) print(扩展名分布 Top 20:) for ext, cnt in ext_counter.most_common(20): print(f {ext or [noext]}: {cnt}) print(顶层目录分布 Top 10:) for d, cnt in dir_counter.most_common(10): print(f {d}: {cnt})这个脚本跑一遍我对仓库就有了非常具体的认知。比如我那次就发现扩展名排第一的居然是 .json数量超过两千个里面有配置文件、测试夹具、翻译文件还有一堆不知道哪来的第三方数据.py 文件只有一千多个并没有我预想中那么多。顶层目录里光 test_data 就占了三千多个文件。这些数字直接影响了我后面的筛选策略。在执行这一步时有个小提醒用Path.rglob(*)会遍历所有子目录当仓库里有 node_modules、dist、.git 这类巨型目录时脚本会把它们也统计进去。我在上面的代码里通过跳过以点开头的目录来规避 .git但 node_modules 没有点前缀如果你不想统计它最好先手工把要排除的目录名写成一个大集合在遍历时直接跳过。2.2 用 magic bytes 识别真实类型别轻信扩展名盘完家底之后第二步是给文件做“真实身份”鉴别。扩展名太容易骗人了。我见过很多文件文件名结尾是 .txt用文本编辑器打开却是乱码因为它本来就是某个程序写出来的日志文件或者半二进制格式。真正可靠的做法是读取文件头部的 magic bytes也就是文件开头的几个字节判断真实格式。在命令行里file命令就是干这个的file suspicious.dat file --mime-type -b unknown.xyz这个命令几乎所有的 Linux 和 macOS 都自带Windows 下也可以用 Git Bash 或者 WSL 调用。它能在不看扩展名的情况下告诉你一个文件到底是 ASCII 文本、UTF-8 文本、PNG 图片、PDF 文档、SQLite 数据库还是某个程序的可执行文件。我用这个命令把仓库里“扩展名和真实类型不一致”的文件都筛了出来数量相当惊人。很多没有扩展名的文件其实是 SQLite 数据库很多 .txt 其实是老的 GB2312 编码文本还有几个 .dat 文件居然是 ZIP 压缩包。这些文件如果不管三七二十一全喂给 AI模型要么读到乱码要么把二进制内容当成指令去理解很可能产生幻觉。落到代码层面我建议直接读取文件开头一小段字节做判断。比如文本文件通常可以尝试用 UTF-8 解码二进制文件会在解码时直接抛异常。下面的思路可以作为筛选代码def looks_like_text(path, sample_size4096): try: with open(path, rb) as f: raw f.read(sample_size) # 空文件不算有效源文件 if not raw: return False # 尝试 UTF-8 解码允许 BOM raw.decode(utf-8) return True except UnicodeDecodeError: return False但是这里有个坑纯文本文件也可能是 GBK/GB2312 编码的UTF-8 解码会失败但这种文件依然是“人类可读的源文件”不能直接当成二进制排除。所以更稳妥的方案是“先尝试 UTF-8失败后再用编码探测库判断如果仍然不是常见编码再归类为二进制”。2.3 外部来源文件先检查“来源标记”和敏感信息这一步是我后来才补上的但是我认为经验价值很高。很多源文件不是你自己写的而是从网上下载、同事发来或者从压缩包里解压出来的。这些文件在 Windows 和 macOS 上可能会被系统打上一个叫做 Mark of the WebMOTW的标记表示它来自互联网。你在本地用某些解析工具打开时会遇到拦截KKFileView 这类预览服务经常会提示“文件来源不受信任拒绝访问”其实是这个标记在起作用。处理方式不是绕过安全机制而是先确认来源可信、再解除标记。Windows 下可以用 PowerShell 批量清除下载文件的标记Get-ChildItem -Path . -Recurse | Unblock-FilemacOS 下则可以用 xattr 查看并删除隔离属性xattr -l yourfile xattr -d com.apple.quarantine yourfile删除标记之后本地预览服务、解析器就能正常访问这些文件了。但我要强调解除标记不等于文件安全。如果这批文件是别人整理好发给你的你最好先跑一遍本地杀毒扫描再用脚本查一下里面是否包含密钥、密码、内网 IP、手机号之类的敏感信息。我就是这么发现仓库里有一个历史遗留的 config 文件里面写着一组数据库明文密码幸好提前筛出来了。2.4 统一编码和换行符文本文件要“洗”成标准形态编码问题听起来很基础但遇到老项目或者跨平台来源的文件时绝对能恶心到你。我那个仓库里一部分文件是 UTF-8一部分是 GBK还有几个是 UTF-16 编码的配置文件。直接在 Python 里用默认编码读非 UTF-8 的文件秒秒钟抛异常就算勉强读出来中文字符在模型眼里也是一堆乱码“锟斤拷”它根本没法理解。我后来在预处理流程里加了一个“文本标准化”环节大致逻辑是先尝试 UTF-8 解码失败后用 chardet 这类库去猜编码猜出来之后按对应编码解码最后统一转成 UTF-8 并去掉 BOM。换行符也统一转成\n避免 Windows 的 CRLF 混在里面引发不必要的解析问题。下面这段代码是我清洗阶段核心逻辑的简化版import chardet def normalize_text(raw: bytes) - str | None: # 去掉 UTF-8 BOM if raw.startswith(b\xef\xbb\xbf): raw raw[3:] return raw.decode(utf-8, errorsstrict).replace(\r\n, \n) try: return raw.decode(utf-8).replace(\r\n, \n) except UnicodeDecodeError: pass # 用 chardet 猜编码只作为 fallback guess chardet.detect(raw[:4096]) enc guess.get(encoding) if not enc: return None try: return raw.decode(enc).replace(\r\n, \n) except (UnicodeDecodeError, LookupError): return None这里有一个很关键的细节chardet 的结果只能当 fallback不能当首选。它的探测准确率在短文件和纯 ASCII 文件上并不稳定如果一上来就盲信 chardet很可能把一个本来好好的文件转错成乱码。我在实际踩坑中得出的经验是能用 UTF-8 解的绝不猜猜的时候只取前几 KB 做采样解码失败就放弃而不是强制替换。宁可少一个文件也不要让一个乱码文件混进语料里污染后续回答。清洗完的文本不要直接修改原始文件建议统一输出到一个独立的 staging 目录保持源目录的只读状态。这样你随时能回溯对比“清洗前”和“清洗后”的差异也方便在发现问题时重新跑流程。3. 从零复现一套完整的“扫描-清洗-索引-投喂”流程3.1 五类文件先分流源码、文档、数据、备份、垃圾预处理不能一刀切第一步应该做“分流”。我后来每次处理目录都会把文件分成五类。第一类是真正的源代码文件比如 .py、.java、.js、.ts、.go、.c、.cpp、.h 这些这是投喂的重点。第二类是可读文本文档比如 Markdown、纯文本、HTML、YAML、JSON 配置文件AI 可以读但优先级低于源码。第三类是二进制文档比如 PDF、Word、Excel模型不能直接读需要先转成纯文本再用。第四类是恢复数据/备份文件比如 SQL 导出、.ibd/.frm 这类 MySQL 数据文件、.bak、.zip 压缩包一般和源码分析无关默认排除。第五类是真正的垃圾文件包括临时文件、缓存文件、系统文件、构建产物直接过滤。这个分流逻辑必须写死在脚本里不然每次手工挑文件会累死人。我当时用的核心过滤规则大致是这样EXCLUDE_DIRS { .git, node_modules, dist, build, __pycache__, .idea, .vscode, target, .gradle, .cache } EXCLUDE_EXTS { .png, .jpg, .jpeg, .gif, .bmp, .ico, .webp, .class, .jar, .war, .pyc, .pyo, .zip, .tar, .gz, .bz2, .7z, .rar, .pdf, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .woff, .ttf, .eot, .mp4, .mp3, .avi, .exe, .dll, .so, .dylib, .o, .a, .ibd, .frm, .myd, .myi, .bak, .sql } MAX_FILE_BYTES 2 * 1024 * 1024 # 超过 2MB 的文本文件单独挑出来看排除 .sql 和 .ibd 这一条是我在处理一个混入数据库恢复文件的目录时总结出来的。很多人从 MySQL 数据恢复工具里扒出来一堆 .ibd、.frm 文件以为这也算源文件想一起喂给 AI。这些文件是 InnoDB 表空间和表结构定义文件本质是二进制格式喂给 AI 除了把上下文灌爆之外没有任何价值。真要做数据库恢复那是另一个专业话题别和代码分析混在一起。3.2 扫描、去重、清洗的主脚本照着改就能用把 3.1 的过滤规则和上一节的编码标准化合并起来就形成了一个比较完整的预处理脚本。流程是遍历目录跳过排除目录过滤扩展名按大小排除明显不该进语料的超大文件对文本文件做编码归一化同时计算行数和大小。最终生成一个 files.json里面记录每一个有效文件的路径、大小、行数、语言分类。我再补充一个去重逻辑。如果目录里存在大量内容完全一致的文件AI 会读到同样的信息好几遍。比较文件内容是否一致的快速方法是先按文件大小分组只有大小相同的文件才进一步计算哈希。import hashlib from pathlib import Path def file_hash(path: Path, chunk_size65536): h hashlib.md5() with open(path, rb) as f: while chunk : f.read(chunk_size): h.update(chunk) return h.hexdigest() def find_duplicates(file_paths): by_size {} for p in file_paths: size p.stat().st_size by_size.setdefault(size, []).append(p) dup_groups [] for size, paths in by_size.items(): if len(paths) 2: continue hashes {} for p in paths: h file_hash(p) hashes.setdefault(h, []).append(p) for group in hashes.values(): if len(group) 1: dup_groups.append(group) return dup_groups清洗输出到 staging 目录的操作我建议保留原文件路径结构不要把所有文件平铺到一个目录。否则后续 AI 按路径读取时看不到包名和模块路径就会丢失目录结构里蕴含的架构信息。比如 Java 的包名路径、Python 的包层级这些对模型理解项目结构很重要。清洗过程中导出的每一条记录最好都附带一行“来源路径”这样后续如果 AI 引用了某个文件里的内容你能反查到底来自哪个文件方便核对。3.3 索引文件不要做成“第二份全量文本”要做成地图和目录很多人有个误区觉得建索引就是把文件内容摘要一下然后全部塞进去。这也不行摘要也是文本几百上千个文件摘要加起来依然会爆上下文。我实际使用下来比较有效的做法是生成两级信息。第一级是给 AI 的首屏“仓库地图”控制在几百个 token 以内。它只讲整体结构比如有哪些顶级模块、每个模块大概多少文件、技术上是什么语言、明显的分层是 MVC 还是其他结构。第二级是一份更完整的 files.json所有文件的路径、大小、行数都在里面但这个文件不是一次性喂给模型而是“按需查”的资源。首屏地图我一般用 Markdown 手写或者脚本生成大致长这样# REPO_MAP ## 顶层模块 - backend/: Python FastAPI 服务约 1200 个源文件 - app/api/: 路由与接口层 - app/services/: 业务逻辑层 - app/models/: ORM 模型层 - frontend/: React TypeScript约 800 个源文件 - src/pages/: 页面组件 - src/components/: 通用组件 - scripts/: 运维脚本和数据处理脚本约 300 个 - tests/: 测试目录约 1800 个文件与主代码 1:1 对应模型看到这个地图基本上就明白这个项目哪里有东西。它如果想知道某个服务具体实现再根据问题去查询 files.json定位到具体路径后用 read_file 工具读取那一个文件的内容。整个交互模式从“我塞给你所有文件”变成“你先看目录再看你需要的文件”上下文占用直接降了几个数量级。3.4 让 AI 按需读文件工具函数怎么写我的做法是给模型暴露一个极简的读文件工具。如果你用的是 Agent 类框架可以直接注册一个函数给模型调用如果只是用 API 对话就用 function calling 功能把工具描述传过去。工具逻辑非常简单接收一个相对路径和可选的行范围返回目标文件里的内容并且自动加行号。def read_file(relative_path: str, start_line: int 1, end_line: int None) - str: base Path(./staging_repo) # 防目录穿越 safe_path (base / relative_path).resolve() if not str(safe_path).startswith(str(base.resolve())): return ERROR: invalid path if not safe_path.exists() or not safe_path.is_file(): return ERROR: file not found lines safe_path.read_text(encodingutf-8, errorsreplace).splitlines() total len(lines) if end_line is None: end_line total end_line min(end_line, total) selected lines[start_line - 1:end_line] numbered [f{i start_line}\t{line} for i, line in enumerate(selected)] return \n.join(numbered)注意这里有个安全细节一定不要直接拼接用户或者模型传来的路径不然容易发生目录穿越。模型本身不是恶意的但它可能在推理中拼出带../的路径导致读到 staging 目录之外的文件。用 resolve 之后做前缀校验虽然多写几行代码但能避免诡异问题。给模型的 prompt 里我会明确写清楚你需要分析代码时不要一次性读取整个仓库。请先查看 REPO_MAP.md 了解项目结构。如果要定位具体实现使用 read_file 工具读取目标文件。优先从入口文件和路由文件入手。每次最多读取 1000 行根据需要分多次读取。这种限制看起来有点啰嗦但实测能显著减少模型“试图一口气读完全部文件”导致的上下文浪费。3.5 可疑文件先预览再放行KKFileView 这类工具能帮上忙有些文件通过了扩展名检查也通过了文本检测但我仍然不放心比如从网上下载的文档、同事发来的压缩包里的文件。这时候我会用 KKFileView 这类开源的在线预览服务在本地先打开看一眼确认内容确实正常再放行进语料池。KKFileView 的部署很简单在已经装好 Docker 的机器上一条命令就能启动docker run -d -p 8012:8012 keking/kkfileview启动后浏览器访问http://localhost:8012上传文件就能在线预览很多格式不需要在本地装 Office 全家桶。这个工具本身不是给 AI 用的它的价值在于让你能快速抽样检查预处理结果里的可疑文件而不是把几百个文件挨个用本地软件打开浪费一下午。如果你处理的目录里没有可疑文件这一步可以跳过。但每次从外部拿到的代码包和数据包我都会抽 5% 到 10% 的文件人工快速浏览确认没有夹带私货。这个习惯帮我躲掉过至少三次被塞进来历不明脚本的坑。4. 预处理过程中的高频问题和排查技巧实录4.1 问题test 文件数量太多把主代码淹没了普通开发者写测试当然是好事但喂给 AI 分析业务逻辑时几千个测试文件的噪声会干扰模型判断。我那个仓库里有接近一半文件带 test 字样模型看到这么多文件会以为测试逻辑就是主业务回答里经常出现莫名其妙的断言和 mock 建议。我在索引里专门划了一个“test: true”字段并且把测试目录和主代码目录分开展示。如果分析任务只关心业务实现那我会在 prompt 里直接加一句“忽略 tests 目录除非问题明确涉及测试”。如果你处理的任务和测试相关再单独把测试文件拿出来喂给 AI效果会好很多。4.2 问题文件明明存在AI 却说什么都找不到这里通常不是模型能力问题而是我给它的地图没有更新到最新状态。清洗之后我把文件复制到了新的 staging 目录但 REPO_MAP 里某些路径还是用的源目录相对路径导致模型按照地图去读文件结果跑到了不存在的路径上。解决方式很朴素在生成索引和地图之后加一个自动校验环节把所有索引里记录的路径都检查一遍是否存在。如果不存在就标记删除或者修正路径。这一步脚本半小时就能好但能省掉后续跟模型反复拉扯确认文件的巨大麻烦。4.3 问题编码统一后还是有个别文件变成乱码我在第 2 节说过 chardet 不能作为首选就是因为这个。有个文件原本是 GB2312 编码我第一版脚本先用 UTF-8 解码失败后chardet 居然把它识别成了 ISO-8859-1结果转换出来全是乱码。这种错误不会报异常因为 ISO-8859-1 可以解码任何字节序列但它会静默地产生错误内容。修复思路是给文本检测加一个“中文编码偏好”逻辑。如果文件里包含非 ASCII 字符且原始编码是 ISO-8859-1 或 Windows-1252而文件明显有中文特征就要强制尝试 GBK/GB2312/Big5 等编码。另外还要对转换结果做一次质量校验看是否包含大量替换字符\ufffd或者极其常见的乱码模式有的话就打回重新走编码探测流程。4.4 问题大文件混进来索引没爆模型先卡了仓库里常常有一个几十 MB 的日志文件、JSON 导出文件或者 SQL dump。这些文件虽然扩展名合理也能当作文本处理但你要真让模型去读它上下文分分钟被撑爆。我当时索引里最大的文本文件有 40 多 MB接近一千万字符足够把常见模型的窗口全部灌满。处理方式是对超大文本文件单独设置阈值。超过 2MB 的文件不直接进入可投喂列表而是单独生成一个“超大文件清单”里面记录路径、大小、前 100 行的预览内容。这样模型起码知道它存在如果问题确实跟这个文件相关再决定是否要按片段读取而不是让这个庞然大物从一开始就待在上下文里。4.5 问题模型在找不到答案时开始编造文件路径模型有一个很讨厌的倾向就是它会“脑补”不存在的文件路径。比如我问某个功能怎么实现如果索引里没有直接对应的文件它会编一个src/utils/helper.py这样的路径出来然后一本正经地解释里面的代码。这种事在缺少文件级工具约束时特别容易发生。我的对策是双重的。一方面在 prompt 里明确要求“所有引用的文件路径必须来自 files.json不存在就不要引用可以回答找不到”。另一方面在工具层做路径校验任何一个读文件请求如果指向了不存在的路径就返回明确的错误信息而不是抛一个让模型自行猜测的模糊异常。经过这两层约束编造路径的问题基本被压住了。4.6 常见问题速查表下面这张表是我自己后来做同类任务时经常翻的排查清单列几个最高频的症状和处理方案省得每次重新踩坑。症状常见原因处理建议模型读文件时出现大量乱码编码未统一GBK/UTF-16 混入预处理阶段加编码归一化统一转 UTF-8AI 分析时老提 node_modules/构建产物排除规则没生效检查 EXCLUDE_DIRS确认脚本真的跳过目标目录回答内容前后矛盾像看了两个版本仓库里有重复文件或旧版本备份用哈希做去重只保留一份同一个文件被重复读取上下文很快耗尽没有按需读取机制改用 read_file 工具按行范围按需加载模型引用的文件路径根本不存在模型幻觉路径没有校验索引路径校验 工具层禁止读不存在文件某些文档文件本地预览正常工具里打不开文件带外部来源标记先清理 MOTW 属性再让解析器访问几千个测试文件干扰主业务分析test 文件没有单独隔离索引中标记测试文件prompt 里按需排除这些问题的共性是它们没有一个是靠“换个更聪明的模型”能解决的全部需要在投喂之前从数据侧堵住漏洞。处理过几轮之后我养成了一个机械习惯每次拿到一批新文件先跑体检脚本再看统计报告再决定后面怎么喂。数据侧干净了AI 侧的效果会立刻体现出来。最后分享一个我自己操作上的小偏好。正式投喂之前我会把“REPO_MAP files.json 的访问说明 一到两个明确的业务问题”拼成一小段开篇先让 AI 读这一段再开始对话。不要一上来就甩问题也不要一上来就让它读文件。给它一点“熟悉仓库”的时间它后续给出的回答会更稳。这个顺序看似不起眼但实际对分析质量的影响比想象中大得多。