当前位置: 首页 > news >正文

AI 看不懂老项目?先用“项目说明书 + 目录树”把上下文交给它

摘要

接手老项目时,AI 最容易给出“看起来很合理、实际上没法落地”的答案,根源通常不是模型不够强,而是项目上下文不完整。本文给出一套可直接复制的流程:先扫描目录树、过滤敏感内容、生成PROJECT_CONTEXT.md,再让 AI 分模块分析依赖、入口和排错路径。


正文

接手一个老项目,最让人头疼的往往不是某一行代码看不懂。

真正难的是:你不知道这个项目从哪里启动、核心逻辑藏在哪、某个接口经过了几层调用、数据库字段是谁写进去的、一个报错到底是配置问题还是业务问题。

很多人这时候会直接把几段代码、一个报错截图,或者整个目录结构扔给 AI,然后问:

这个项目怎么跑?
这个接口为什么 500?
这段代码能不能重构?
你帮我看一下核心业务逻辑。

AI 往往也会给出一大段看似完整的回答。

但你真正去验证时,常常会发现:入口文件判断错了、模块关系猜错了、依赖方向搞反了,甚至建议修改了根本不该动的文件。

这不是 AI “不会写代码”,而是它拿到的上下文不够完整。

Anthropic 当前的提示词最佳实践文档已将 Claude Sonnet 5 纳入覆盖模型范围,但模型能力再强,也不能替代开发者把项目边界、目录结构、运行方式和业务约束交代清楚。对老项目来说,先建立一份结构化上下文,比一上来让 AI 改代码重要得多。

这篇文章分享一套我更建议在真实项目里使用的办法:

先让 AI 看懂“项目地图”,再让它分析局部问题。
不要先让它猜。


一、老项目最容易犯的错:把“碎片代码”当成“项目上下文”

假设你接手了一个典型的旧项目:

legacy-api/ ├── src/ │ ├── controllers/ │ ├── services/ │ ├── repositories/ │ ├── middlewares/ │ ├── jobs/ │ └── utils/ ├── config/ ├── scripts/ ├── migrations/ ├── package.json ├── docker-compose.yml └── README.md

你随手复制一个UserController、一个报错日志,再加一句:

帮我分析用户注册为什么失败。

问题在于,AI 不知道:

  • 用户注册入口是 HTTP 接口、消息队列消费者,还是定时任务;
  • UserController有没有经过鉴权、中间件、参数校验;
  • 数据库操作是在Repository、ORM 模型,还是远程 RPC 服务;
  • 项目是本地启动、Docker 启动,还是依赖某个内部配置;
  • 这次报错是测试环境、开发环境,还是生产环境;
  • 你是否允许它修改数据库结构、接口返回值或历史兼容逻辑。

没有这些信息,AI 只能根据常见项目结构猜。

猜得顺的时候,会让人觉得它很聪明;猜错的时候,返工比自己读代码还慢。

所以第一步不是问 AI:

“这个项目是干什么的?”

而是先给它一份可验证的事实清单。


二、我会先准备两份东西:目录树和项目说明书

第一份叫做:

PROJECT_CONTEXT.md

它不是完整设计文档,也不是为了写得漂亮。

它的目标只有一个:

让 AI 第一次接触项目时,先知道“这个项目有哪些模块、哪些地方不能碰、当前问题发生在哪”。

这份文件至少要包含六类信息:

信息类别作用示例
项目结构告诉 AI 文件和模块在哪里src/controllerssrc/services
技术栈避免 AI 给错框架方案Node.js + TypeScript + MySQL
启动方式帮助定位入口与运行链路pnpm devdocker compose up
核心模块明确各目录责任services负责业务编排
当前问题让 AI 聚焦真实任务注册接口偶发 500
约束边界防止它随意改动不改表结构、不改鉴权逻辑

第二份是自动生成的目录树。

为什么不建议手写?

因为老项目文件多、目录深,手写一遍容易漏,也容易过期。更现实的方式是用一个脚本生成基础结构,再由人补业务说明。

下面这段 Python 脚本只使用标准库,可以扫描项目目录、过滤常见依赖目录和敏感文件,并输出一份可作为 AI 上下文的 Markdown 文档。

三、可直接运行:生成PROJECT_CONTEXT.md的 Python 脚本

新建文件:

generate_project_context.py

粘贴下面代码:

#!/usr/bin/env python3from__future__importannotationsimportargparsefromdatetimeimportdatetimefrompathlibimportPathfromtypingimportIterable IGNORED_DIRS={".git",".hg",".svn",".idea",".vscode",".vs",".venv","venv","env","node_modules","dist","build","coverage","__pycache__",".next",".nuxt","target",".turbo",".cache",}SENSITIVE_PREFIXES=(".env",)SENSITIVE_TOKENS=("secret","credential","private_key","id_rsa","keystore","apikey","api_key",)IMPORTANT_FILENAMES={"package.json","pnpm-lock.yaml","yarn.lock","package-lock.json","pyproject.toml","requirements.txt","poetry.lock","pipfile","pom.xml","build.gradle","settings.gradle","go.mod","cargo.toml","dockerfile","docker-compose.yml","docker-compose.yaml","readme.md","makefile","vite.config.ts","next.config.js",}defis_sensitive(name:str)->bool:lowered=name.lower()return(lowered.startswith(SENSITIVE_PREFIXES)orany(tokeninloweredfortokeninSENSITIVE_TOKENS))defdisplay_name(path:Path)->str:returnpath.name+("/"ifpath.is_dir()else"")defwalk_tree(root:Path,max_depth:int,max_files:int,)->tuple[list[str],list[Path],int]:lines=[f"{root.name}/"]files:list[Path]=[]skipped_sensitive=0stopped=Falsedefvisit(current:Path,prefix:str,depth:int)->None:nonlocalskipped_sensitive,stoppedifstoppedordepth>max_depth:returntry:children=sorted(current.iterdir(),key=lambdaitem:(notitem.is_dir(),item.name.lower()),)exceptPermissionError:lines.append(prefix+"└── [permission denied]")returnvisible_children:list[Path]=[]forchildinchildren:ifchild.is_dir()andchild.nameinIGNORED_DIRS:continueifis_sensitive(child.name):skipped_sensitive+=1continuevisible_children.append(child)forindex,childinenumerate(visible_children):iflen(files)>=max_files:lines.append(prefix+"└── ... [file limit reached]")stopped=Truereturnis_last=index==len(visible_children)-1branch="└── "ifis_lastelse"├── "lines.append(prefix+branch+display_name(child))ifchild.is_dir():next_prefix=prefix+(" "ifis_lastelse"│ ")ifdepth<max_depth:visit(child,next_prefix,depth+1)else:lines.append(next_prefix+"└── ...")else:files.append(child)visit(root,"",1)returnlines,files,skipped_sensitivedefimportant_files(files:Iterable[Path],root:Path)->list[str]:result:list[str]=[]forfile_pathinfiles:name=file_path.name.lower()ifnameinIMPORTANT_FILENAMESorname.endswith((".csproj",".sln")):result.append(file_path.relative_to(root).as_posix())returnresult[:30]defmain()->None:parser=argparse.ArgumentParser(description=("Generate a safe project-structure context file ""for AI-assisted code reading."))parser.add_argument("project_root",type=Path)parser.add_argument("--max-depth",type=int,default=4)parser.add_argument("--max-files",type=int,default=400)args=parser.parse_args()root=args.project_root.resolve()ifnotroot.is_dir():raiseSystemExit(f"Not a directory:{root}")tree_lines,files,skipped_sensitive=walk_tree(root=root,max_depth=args.max_depth,max_files=args.max_files,)key_files=important_files(files,root)print("# PROJECT_CONTEXT")print()print("## 1. Scan metadata")print(f"- Project root: `{root.name}`")print(f"- Generated at: "f"`{datetime.now().isoformat(timespec='seconds')}`")print(f"- Max depth: `{args.max_depth}`")print(f"- Visible files: `{len(files)}`")print(f"- Sensitive paths skipped: `{skipped_sensitive}`")print("- Ignored generated/dependency directories: "+", ".join(sorted(IGNORED_DIRS)))print()print("## 2. Directory tree")print("```text")print("\n".join(tree_lines))print("```")print()print("## 3. Important build and configuration files")ifkey_files:foriteminkey_files:print(f"- `{item}`")else:print("- No common build or configuration files were detected automatically.")print()print("## 4. Fill in before asking AI to analyze the project")print("- Business purpose:")print("- Runtime and dependency commands:")print("- Entry point / startup path:")print("- Core modules and their responsibilities:")print("- External dependencies (database, message queue, third-party APIs):")print("- Current problem and reproduction steps:")print("- Constraints: files AI must not modify, ""compatibility requirements, test expectations:")if__name__=="__main__":main()

我已经按一个包含src/controllerssrc/services.envnode_modules的示例项目验证过这段脚本:它会跳过.envnode_modules,并正常输出目录树、关键配置文件与待补充说明。

运行方式:

python generate_project_context.py /你的项目路径 --max-depth4>PROJECT_CONTEXT.md

例如:

python generate_project_context.py ./legacy-api --max-depth4>PROJECT_CONTEXT.md

如果项目特别大,可以再限制文件数量:

python generate_project_context.py ./legacy-api\--max-depth4\--max-files300\>PROJECT_CONTEXT.md

四、脚本输出大概长什么样

假设你的项目是:

legacy-api/ ├── src/ │ ├── controllers/ │ │ └── user.ts │ └── services/ │ └── user_service.ts ├── package.json ├── README.md ├── .env └── node_modules/

生成结果会类似这样:

# PROJECT_CONTEXT ## 1. Scan metadata - Project root: `legacy-api` - Max depth: `4` - Visible files: `4` - Sensitive paths skipped: `1` ## 2. Directory tree ```text legacy-api/ ├── src/ │ ├── controllers/ │ │ └── user.ts │ └── services/ │ └── user_service.ts ├── package.json └── README.md

3. Important build and configuration files

  • package.json
  • README.md

4. Fill in before asking AI to analyze the project

  • Business purpose:
  • Runtime and dependency commands:
  • Entry point / startup path:
  • Core modules and their responsibilities:
  • External dependencies:
  • Current problem and reproduction steps:
  • Constraints:
注意,这只是“机器生成的结构层”。 它告诉 AI:项目有什么。 但还没有告诉 AI:项目为什么这样设计。 所以脚本跑完后,我建议你至少补充下面这段内容。 --- ## 五、真正决定 AI 输出质量的,是这段人工补充说明 在 `PROJECT_CONTEXT.md` 最后加上: ```markdown ## 5. Manual project brief ### Business purpose 这是一个面向后台运营人员的用户管理系统。 主要能力包括:用户注册、用户登录、角色分配、订单查询。 ### Runtime - Node.js 20 - TypeScript - PostgreSQL - Redis - Docker Compose ### Startup - 本地开发:`pnpm dev` - 数据库启动:`docker compose up -d postgres redis` - 测试:`pnpm test` ### Core modules - `src/controllers`:HTTP 请求入口,负责参数接收与响应返回。 - `src/services`:业务编排层,处理注册、登录、权限判断。 - `src/repositories`:数据库访问层。 - `src/middlewares`:鉴权、请求日志、统一异常处理。 - `src/jobs`:定时任务和异步任务。 ### Current issue 用户注册接口偶发返回 500。 已知现象: 1. 只有部分邮箱地址会触发。 2. 数据库中没有生成用户记录。 3. 日志中出现 `duplicate key value`。 4. 不能修改现有数据库表结构。 ### Constraints - 不要读取 `.env`、生产配置、密钥文件。 - 不要修改登录和鉴权中间件。 - 优先给出排查路径,再给出修改建议。 - 所有修改建议必须附带测试点。

这段内容比“把更多代码贴进去”更重要。

因为它给的是判断边界。

AI 最怕的不是代码多,而是信息没有优先级。


六、把项目上下文交给 AI 后,不要直接让它改代码

很多人生成完PROJECT_CONTEXT.md,下一步就会说:

现在帮我修复所有问题。

不建议。

更稳的步骤是分四轮。

第 1 轮:让 AI 复述项目结构

可以直接这样问:

下面是一个项目上下文文件。 请先不要修改代码,也不要给重构方案。 请只完成四件事: 1. 用不超过 300 字说明项目职责; 2. 列出主要启动入口和请求链路; 3. 按“控制层、业务层、数据层、基础设施层”归类目录; 4. 标出你无法根据现有信息确定的部分。 如果存在猜测,请明确标记为“待确认”。

这一步的目的不是拿答案。

而是先检查 AI 有没有理解错方向。

如果它连项目启动方式、核心模块、依赖层次都判断错了,后面的改代码建议就没有必要看。


第 2 轮:让 AI 画出问题相关链路

接着再问:

当前问题是: 用户注册接口偶发返回 500。 日志中出现 duplicate key value。 数据库中没有生成用户记录。 不能修改现有数据库表结构。 请基于项目上下文,输出: 1. 从 HTTP 请求进入到数据库写入的可能调用链; 2. 最可能相关的文件路径; 3. 需要补充的日志位置; 4. 需要验证的数据库约束; 5. 不建议直接修改的文件或模块; 6. 一个按优先级排序的排查计划。 不要直接生成修复代码。

这里最关键的一句是:

不要直接生成修复代码。

因为老项目排错最怕“先改再说”。

AI 给出的价值,应该先体现在缩小排查范围,而不是先替你做风险操作。


第 3 轮:让 AI 给“最小改动方案”

当你已经确认调用链、日志和数据约束后,再进入代码建议阶段。

目前已确认: 1. 注册请求会进入 `UserController.register`; 2. `UserService.createUser` 会先查询邮箱,再执行插入; 3. 数据库 email 字段存在唯一索引; 4. 并发请求可能同时通过“邮箱不存在”的检查; 5. 不允许修改表结构。 请给出最小改动方案。 要求: - 不改接口返回结构; - 不删除现有校验逻辑; - 优先通过事务、唯一约束异常捕获或幂等处理解决; - 给出修改涉及的文件; - 给出每个修改点的风险; - 给出对应的测试用例。

这样提问后,AI 输出会更像技术方案,而不是一段“看上去能跑”的补丁。


第 4 轮:让 AI 写测试点,而不是只写代码

最后一步很容易被跳过。

但老项目最需要的,恰恰是测试点。

可以继续要求:

请基于上面的最小改动方案,补充测试清单: 1. 单用户正常注册; 2. 同邮箱连续注册; 3. 两个并发请求同时注册同邮箱; 4. 数据库插入异常; 5. Redis 不可用; 6. 邮件服务超时; 7. 旧接口调用方兼容性。 每个测试点请包含: - 前置条件; - 输入; - 预期结果; - 需要观察的日志; - 是否适合自动化测试。

AI 很适合补全边界条件、帮助你想测试场景。

但上线前的人工 Code Review、自动化测试、日志验证和灰度策略,还是不能省。


七、一个可复用的老项目分析提示词模板

下面这段可以直接保存成:

legacy-project-analysis-prompt.md

以后接手任何陌生项目都能复用。

你是一名协助理解老项目的开发助手。 下面我会提供项目上下文文件、问题描述和部分代码。 你的目标不是直接修改代码,而是先帮助我建立可验证的理解。 请严格遵守: 1. 不根据缺失信息补充业务事实; 2. 不读取或要求提供 .env、密钥、生产凭证、用户隐私数据; 3. 不建议直接修改数据库结构,除非我明确允许; 4. 所有推断都要标记“已确认”或“待确认”; 5. 先输出排查路径,再输出最小改动建议; 6. 修改建议必须包含风险点和测试点。 请按以下格式输出: ## 项目理解 - 项目职责: - 启动入口: - 核心模块: - 关键依赖: ## 当前问题链路 - 请求入口: - 涉及模块: - 可能的数据流: - 待确认信息: ## 排查计划 1. 2. 3. ## 最小改动建议 - 修改位置: - 修改原因: - 风险: - 回滚方案: ## 测试清单 - 正常路径: - 异常路径: - 并发场景: - 兼容性:

这类模板看起来普通,但它能明显减少 AI “自由发挥”的空间。


八、不要把这些文件直接交给 AI

项目上下文越完整越好,不等于项目文件越多越好。

下面这些内容,默认不建议直接放进 AI 对话或外部工具:

文件或信息原因
.env可能包含密钥、数据库账号、第三方 Token
云服务配置可能暴露访问地址和权限信息
生产数据库导出可能涉及用户隐私和业务数据
支付、身份认证配置可能涉及敏感凭证
内部接口密钥可能导致权限泄露
完整生产日志可能包含手机号、邮箱、订单号、用户标识

Claude Code 的官方安全建议也明确提到:处理敏感代码时,应审查所有建议修改、为敏感仓库设置项目级权限,并考虑用开发容器做额外隔离。其设置文档还给出了通过permissions.deny阻止读取.envsecrets、凭证文件和构建目录的方式。

如果你使用 Claude Code,也可以在项目里加一份最基础的限制配置:

{"permissions":{"deny":["Read(./.env)","Read(./.env.*)","Read(./secrets/**)","Read(./config/credentials.json)","Read(./build)"]}}

这不是万能防护,但至少能让“默认不该读的文件”先被挡在外面。


九、这套流程适合什么项目,不适合什么项目

适合:

  • 新人接手老系统;
  • 接手外包项目;
  • 重构前做代码摸底;
  • 排查跨模块 Bug;
  • 给 AI 准备稳定上下文;
  • 写项目 README、接口文档、模块说明;
  • 做代码 Review 前的第一轮分析。

不适合直接拿来做:

  • 直接把 AI 输出当作生产修改;
  • 未脱敏的生产日志分析;
  • 涉及支付、身份认证、权限系统的无审查改动;
  • 大规模数据库迁移;
  • 核心安全逻辑重构;
  • 高并发、分布式一致性问题的最终决策。

AI 可以帮你缩小范围、梳理链路、补测试点。

但它不能替你承担线上风险。


十、最后总结

老项目不是“代码多”才难读。

而是它的上下文散在目录、配置、历史约定、业务流程和人的记忆里。

所以我更推荐这条顺序:

先扫描目录 → 再过滤敏感内容 → 再生成项目上下文 → 再补人工业务说明 → 再让 AI 复述项目理解 → 再分析调用链 → 最后才讨论修改方案

当你把 AI 当成“先帮我建立项目地图的搭档”,而不是“直接帮我改代码的人”,它给出的建议通常会更稳定,也更容易验证。

http://www.cnnetsun.cn/news/3211542.html

相关文章:

  • [特殊字符]《京东宙斯迁移公告解读:老接口 360buy.ware.get→ jingdong.ware.read.get平滑切换(附Python源码)》
  • 从零搭建工业物联网监控大屏:Node-RED + 有人云 零代码方案
  • HRD进化论04讲:战略解码
  • PostgreSQL高级特性应用
  • PyTorch DDP 多机多卡配置:2节点8卡集群部署与通信瓶颈分析
  • Models-学习指南_小白版
  • Mermaid终极指南:用代码快速创建专业图表,彻底告别文档滞后问题
  • Windows 证书管理:3种存储位置(个人/根/中间)的导入策略与安全影响
  • AD7175-8与PIC18F4610高精度信号采集方案解析
  • 2026年AI写作辅助平台实测认证:5款神器从文献到降重一站式避坑指南
  • 15个实用的代码审查工具,自动化检查
  • 如何快速精通猫抓资源嗅探:进阶用户终极配置指南
  • 15个实用的代码版本控制工具,Git替代品
  • 《B4071 [GESP202412 五级] 武器强化》
  • GHelper终极指南:轻松掌控华硕笔记本性能的轻量级神器
  • 兰州兰银官话语义角色标注数据采集公司
  • FFmpeg 入门:命令行的 20 个常用场景
  • OpenCV 相机标定与3D可视化:8张棋盘格图像标定,内参误差低于0.5像素
  • 内存化在对账系统中的应用实践
  • YOLO实战第四篇:训练曲线全方位解读教程(看懂收敛、过拟合、精度好坏,告别盲目训练)
  • 数据分析师常用的数据分析方法有哪些?
  • 耶鲁大学与谷歌联手:让AI学会“自知之明“,不再自信地胡说八道
  • 破局 PCIe 4.0 交换瓶颈@ACP#IX8024 / ASM58024参数及应用对比
  • 深度解析 GitHub Copilot Modernization:老旧项目的“返老还童”术与换模避坑指南
  • 2026江苏GEO优化公司厂家分析:B2B行业经验评价维度——拒绝广告干扰
  • 揭秘Anime4K:实时动漫超分技术的革命性突破与深度解析
  • DVWA 靶场 10 大漏洞实战:从 Low 到 High 的 3 种安全级别攻防解析
  • Stable Diffusion 3 与 Midjourney V6 对比评测:5大商业场景出图质量与成本分析
  • QRazyBox终极指南:5分钟学会修复损坏二维码的免费神器
  • SM2258XT 开卡工具 Q1225A 版实战:3步定位闪存ID与B17A制程匹配