正文
一、先上结论
把完整项目代码一次性喂给 AI,是目前对抗“AI 幻觉”最有效、最靠谱的方法,没有之一。
DeepSeek + “完整项目喂入法” = 几乎不翻车的 AI 编程助手。
二、一个扎心的真实场景
你有没有遇到过这种情况:
你:帮我把 userInfo 字段改成 profile
AI:好的,已修改 ✅
(实际结果:AI 把 userInfo、userinfo、user_info 全改了,还顺便重命名了你没提到的变量)
或者更崩溃的:
你:在这个页面加个搜索功能
AI:好的,已添加 🔍
(实际结果:样式崩了、接口调错了、分页逻辑也改坏了)
这就是 AI 幻觉——它太“聪明”了,聪明到会自己脑补不存在的逻辑。
三、我找到的解法:完整上下文法
核心思路:在提需求之前,先让 AI 完整“看”一遍你的整个项目。
具体操作(超级简单)
| 步骤 | 操作 |
|---|---|
| 1 | 运行脚本,自动生成 project_documentation.md |
| 2 | 把这个文件上传给 DeepSeek |
| 3 | 在同一个会话里提出你的具体需求 |
| 4 | 如果对话太长,直接新开话题重新上传文档 |
为什么新开话题很重要?
当对话轮次过多(比如超过 10-15 轮),AI 的注意力会分散,容易出现“前面说过的东西后面忘了”的情况。这时候果断新开话题、重新上传文档,效果反而更好。
一句话总结:每次重要修改,都让 AI 重新“熟悉”一遍项目。
四、配套脚本:一键生成项目快照
我写了一个 Python 脚本,能在几秒内把整个项目转成 AI 能消化的 Markdown 文档。
脚本核心能力:
- ✅ 自动生成目录树
- ✅ 收集 15+ 种代码文件(.cpp/.vue/.js/.py/.ts 等)
- ✅ 按文件类型分组,语法高亮
- ✅ 自动忽略 .git、node_modules 等无用目录
- ✅ 多编码自动检测(UTF-8/GBK/Latin-1)
五、为什么这个方法能让 AI 幻觉“归零”?
| AI 看到的内容 | 可能的行为 |
|---|---|
| 只有零散的几段代码 | “用户可能想改这个?我猜一下” → 乱改一通 ❌ |
| 完整的目录结构 + 所有代码 | “我看到了完整的调用链和项目规范” → 精准修改 ✅ |
说白了:AI 不是神,它是靠信息做判断的。你给的信息越完整,它的判断就越准。
六、我的“最佳实践”总结(建议收藏)
- 每次重要任务前,都重新生成文档喂给 AI(不要依赖短期记忆)
- 对话超过 10-15 轮,果断新开话题(避免上下文污染)
- 先上传文档,再提需求(顺序别反了)
- 让 AI 输出修改方案后你再确认(不要直接让它改)
- 敏感项目注意脱敏(密钥、密码记得删掉)
七、一些心里话
我试过很多方法,最后沉淀下来的就是这套流程。
它不 fancy,但有效。
可能你会觉得“每次都要重新上传文档好麻烦”,但比起 AI 乱改代码后你花半小时 debug,这 10 秒钟的“麻烦”简直不值一提。
如果你也在用 AI 辅助编程,强烈建议试试这个方法。
真的,试一次你就回不去了。
八、互动
- 你被 AI 幻觉坑过最惨的一次是什么?
- 你有没有其他好用的“防幻觉”技巧?
欢迎评论区分享 👇
附录:完整脚本代码
# -*- coding: utf-8 -*-
"""
将当前目录下的文件结构及代码整合到Markdown文件
支持文件类型: .h, .cpp, .pro, .vue, .js, .ts, .jsx, .tsx, .yml, .yaml, .xml, .html, .py
"""
import os
import sys
from pathlib import Path
import fnmatch
def get_directory_tree(start_path, ignore_patterns=None):
"""
获取目录树结构
"""
if ignore_patterns is None:
ignore_patterns = ['.git', '__pycache__', '*.pyc', '.idea', 'build', 'dist', 'node_modules']
tree_lines = []
def _walk(dir_path, prefix=""):
# 获取当前目录下的所有项目
try:
items = sorted(os.listdir(dir_path))
except PermissionError:
return
# 过滤掉忽略的项目
items = [item for item in items
if not any(fnmatch.fnmatch(item, pattern)
for pattern in ignore_patterns)]
for i, item in enumerate(items):
item_path = os.path.join(dir_path, item)
is_last = (i == len(items) - 1)
# 选择适当的前缀符号
if is_last:
tree_lines.append(f"{prefix}└── {item}")
new_prefix = prefix + " "
else:
tree_lines.append(f"{prefix}├── {item}")
new_prefix = prefix + "│ "
# 如果是目录,递归遍历
if os.path.isdir(item_path):
_walk(item_path, new_prefix)
# 添加根目录名称
root_name = os.path.basename(os.path.abspath(start_path))
tree_lines.append(f"{root_name}/")
_walk(start_path)
return "\n".join(tree_lines)
def should_process_file(filename):
"""
判断文件是否需要处理
支持的扩展名: .h, .cpp, .pro, .vue, .js, .ts, .jsx, .tsx, .yml, .yaml, .xml, .html, .py
"""
valid_extensions = [
'.h', '.cpp', '.pro', '.vue', '.js', '.ts', '.jsx', '.tsx',
'.yml', '.yaml', '.xml', '.html', '.htm', '.py'
]
return any(filename.endswith(ext) for ext in valid_extensions)
def read_file_content(filepath):
"""
读取文件内容
"""
try:
with open(filepath, 'r', encoding='utf-8') as f:
return f.read()
except UnicodeDecodeError:
# 如果UTF-8解码失败,尝试使用其他编码
try:
with open(filepath, 'r', encoding='gbk') as f:
return f.read()
except:
try:
with open(filepath, 'r', encoding='latin-1') as f:
return f.read()
except:
return f"[无法读取文件: 编码不支持]"
except Exception as e:
return f"[读取文件时出错: {str(e)}]"
def generate_markdown(output_file="project_documentation.md"):
"""
生成Markdown文档
"""
current_dir = os.getcwd()
dir_name = os.path.basename(current_dir)
with open(output_file, 'w', encoding='utf-8') as md_file:
# 写入标题
md_file.write(f"# {dir_name} 项目文档\n\n")
# 写入生成时间
from datetime import datetime
md_file.write(f"*生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*\n\n")
# 写入目录树
md_file.write("## 目录结构\n\n")
md_file.write("```\n")
md_file.write(get_directory_tree(current_dir))
md_file.write("\n```\n\n")
# 收集所有需要处理的文件
files_to_process = []
for root, dirs, files in os.walk(current_dir):
# 忽略一些目录
dirs[:] = [d for d in dirs if not d.startswith('.')
and d not in ['__pycache__', 'build', 'dist', 'node_modules']]
for file in files:
if should_process_file(file):
full_path = os.path.join(root, file)
rel_path = os.path.relpath(full_path, current_dir)
files_to_process.append((rel_path, full_path))
# 按文件类型和路径排序
files_to_process.sort(key=lambda x: (os.path.splitext(x[0])[1], x[0]))
# 写入文件内容
md_file.write("## 源代码\n\n")
if not files_to_process:
md_file.write("*没有找到支持的文件类型*\n")
md_file.write("*支持的文件类型: .h, .cpp, .pro, .vue, .js, .ts, .jsx, .tsx, .yml, .yaml, .xml, .html, .py*\n")
else:
# 按文件类型分组
file_types = {
'.pro': 'Qt项目文件',
'.h': '头文件',
'.cpp': 'C++源文件',
'.vue': 'Vue组件',
'.js': 'JavaScript文件',
'.ts': 'TypeScript文件',
'.jsx': 'React JSX文件',
'.tsx': 'React TSX文件',
'.yml': 'YAML配置文件',
'.yaml': 'YAML配置文件',
'.xml': 'XML文件',
'.html': 'HTML文件',
'.htm': 'HTML文件',
'.py': 'Python文件'
}
# 定义分组顺序
extensions_order = [
'.pro', '.h', '.cpp',
'.vue', '.ts', '.tsx', '.js', '.jsx',
'.yml', '.yaml', '.xml', '.html', '.htm',
'.py'
]
for ext in extensions_order:
type_files = [(rel, full) for rel, full in files_to_process if rel.endswith(ext)]
if type_files:
md_file.write(f"### {file_types.get(ext, ext)}文件\n\n")
for rel_path, full_path in type_files:
# 写入文件名作为子标题
md_file.write(f"#### {rel_path}\n\n")
# 写入文件内容
content = read_file_content(full_path)
# 根据文件扩展名设置代码块语言
lang_map = {
'.h': 'cpp',
'.cpp': 'cpp',
'.pro': 'makefile',
'.vue': 'vue',
'.js': 'javascript',
'.ts': 'typescript',
'.jsx': 'jsx',
'.tsx': 'tsx',
'.yml': 'yaml',
'.yaml': 'yaml',
'.xml': 'xml',
'.html': 'html',
'.htm': 'html',
'.py': 'python'
}
lang = lang_map.get(ext, '')
md_file.write(f"```{lang}\n")
md_file.write(content)
if content and not content.endswith('\n'):
md_file.write('\n')
md_file.write("```\n\n")
# 写入统计信息
md_file.write("## 统计信息\n\n")
md_file.write(f"- 总文件数: {len(files_to_process)}\n")
# 按扩展名统计
ext_counts = {}
for rel_path, _ in files_to_process:
ext = os.path.splitext(rel_path)[1]
ext_counts[ext] = ext_counts.get(ext, 0) + 1
# 显示统计信息
ext_names = {
'.pro': 'Qt项目',
'.h': '头文件',
'.cpp': 'C++源文件',
'.vue': 'Vue组件',
'.js': 'JavaScript',
'.ts': 'TypeScript',
'.jsx': 'React JSX',
'.tsx': 'React TSX',
'.yml': 'YAML',
'.yaml': 'YAML',
'.xml': 'XML',
'.html': 'HTML',
'.htm': 'HTML',
'.py': 'Python'
}
for ext, count in ext_counts.items():
name = ext_names.get(ext, ext)
md_file.write(f"- {name}文件 ({ext}): {count}个\n")
print(f"文档已生成: {output_file}")
print(f"共处理了 {len(files_to_process)} 个文件")
print(f"支持的文件类型: .h, .cpp, .pro, .vue, .js, .ts, .jsx, .tsx, .yml, .yaml, .xml, .html, .py")
def main():
"""
主函数
"""
print("项目文档生成器 (支持C/C++/Qt/Vue/JavaScript/TypeScript/YAML/XML/HTML/Python)")
print("=" * 80)
# 可以自定义输出文件名
output_filename = "project_documentation.md"
if len(sys.argv) > 1:
output_filename = sys.argv[1]
generate_markdown(output_filename)
if __name__ == "__main__":
main()