会改本地文件的助手,第一次用就可能删错东西:五类高频翻车
【免费下载链接】gemini-cliAn open-source AI agent that brings the power of Gemini directly into your terminal.项目地址: https://gitcode.com/GitHub_Trending/gemi/gemini-cli
Gemini CLI 的定位很直白:把模型放进终端,允许它读你的仓库、写你的文件、跑你的命令。这种能力的另一面是——它第一次启动时,你对它的默认值一无所知,而它对你要保护什么也一无所知。
社区对它的评价也印证了这种"能力先行、可靠性待磨合"的阶段特征:开源、免费额度大、上手极快,但同时"接近早期预览版,距稳定可靠还有距离"。这篇文章不谈跑分,只谈一件事:一个会落盘修改的 agent,在头几次使用时最容易从哪些地方翻车,以及仓库里已经写好了哪些刹车。
以下所有结论都能在当前仓库源码与文档中找到对应实现,涉及的具体文件会以仓库相对路径标注。
一、为什么"第一次用"最危险:默认值清单
在动手之前,先把几个关键默认值摆出来,它们共同决定了你第一次运行时的风险基线。
| 行为 | 默认状态 | 依据 |
|---|---|---|
| 工具执行确认 | default,逐个询问 | 命令行参数 |
| 自动批准编辑工具 | 需显式auto_edit或yolo | 同上 |
| Checkpoint(修改前快照) | 关闭 | Checkpointing |
| 受信任目录(Trusted Folders) | 关闭 | Trusted Folders |
| 沙箱 | 需显式开启 | Sandboxing |
| Plan Mode | 默认可用,可设为默认模式 | Plan Mode |
注意这两项的对照关系:"询问"是开的,但"快照"和"信任校验"是关的。
这意味着默认状态下,工具调用会停下来问你;一旦你因为"它在跑测试、它在整理目录"而连续按下确认,剩下的唯一安全网就是你自己对 diff 的阅读速度。而 Chekpointing 默认关闭,意味着那一步点击没有任何可回退的落点。
二、五类高频翻车
第一类:陌生仓库的项目级配置,先于你的判断生效
这是最容易被忽略、也最"第一次"的一类。克隆一个仓库、cd进去、直接gemini,是所有人的标准起手式。
问题在于,项目级配置是随仓库一起进来的。Trusted Folders 的文档把机制写得很清楚:开启后,首次进入某个目录会弹出信任对话框,并在**发现阶段(discovery phase)**列出该项目里的 Commands、MCP Servers、Hooks、Skills,以及Setting overrides,也就是项目级settings.json对你的全局配置的覆盖;如果发现"自动批准某些工具"或"关闭安全沙箱"这类危险设置,CLI 会显式告警。
但这项特性默认是关闭的。默认关闭时,你不会看到这个对话框,项目配置会按正常工作流加载。
一旦文件夹被判定为不受信任,防线反而是完整的:Trusted Folders 列出了七条限制——不加载.gemini/settings.json、不加载项目.env、禁止扩展装卸、工具自动接受被强制关闭(即使你全局开了自动批准)、不自动加载内存文件、不连接 MCP、不加载自定义命令。换句话说,那个对话框不是"多问一句",而是这七条限制的总开关。
怎么防:在跑任何陌生仓库之前,先做只读侦察——cat .gemini/settings.json、看有没有.gemini/commands、.gemini/hooks、.gemini/skills。想长期解决就把信任机制打开:
{ "security": { "folderTrust": { "enabled": true } } }CI/无头场景下没有交互界面,未受信任的目录会直接抛FatalUntrustedWorkspaceError退出,此时才使用--skip-trust或GEMINI_CLI_TRUST_WORKSPACE=true临时放行——而不是把它们当成日常启动参数。
第二类:write_file是全量覆盖,"省略的部分"真的会落盘
write_file的语义在 文件系统工具参考 里只有一句话:写入内容到指定文件,存在则覆盖。
覆盖本身不是 bug,问题在于模型输出天然倾向于"节省":改一个 800 行的文件,它可能只写出改动附近的内容,然后在中间留一行:
// rest of methods ...如果这行被当成文件内容写进去,你的文件就在语法上被截断了。仓库对这个场景是有专门防御的——write-file.ts 在参数校验阶段调用 omissionPlaceholderDetector.ts,识别rest of code ...、unchanged methods ...、// rest of method ...这类占位符,并直接拒绝本次调用:
const omissionPlaceholders = detectOmissionPlaceholders(params.content); if (omissionPlaceholders.length > 0) { return "`content` contains an omission placeholder (for example 'rest of methods ...'). Provide complete file content."; }这恰恰说明它曾经是真实痛点:检测器是"事后补的补丁",而不是"从来不会发生"。
还有一层更隐蔽的行为:写入前会走ensureCorrectFileContent,对内容做一次模型侧修正(转义、缩进等),再由 diff 展示。好处是确认框里你看到的就是最终要落盘的文本;坏处是——如果你不看清 diff 就按y,你批准的并不是模型原始输出,而是修正后的版本。
怎么防:
- 在提示词里明确"只允许用
replace做局部修改,不要重写整文件"; - 大文件改动一律走
replace,让old_string的精确匹配成为你的锚点; - 确认框里的 diff 是最后的关卡,看到大面积删除(整段
-行)就按n。
第三类:replace的匹配歧义,改对了字符串、改错了位置
replace被设计成"精确、局部"的工具:默认要求old_string在文件中恰好出现一次。edit.ts 里的校验逻辑非常明确:
if (!params.allow_multiple && exactOccurrences > 1) { // Failed to edit, expected 1 occurrence but found N }失败信息也写得很到位:0 次匹配时提示你"如果之前的编辑改动了文件,或你要改的行不在最近读取窗口内,请先用 ReadFile 检查目标行再重试"。
真正的风险出现在你主动放开的时候。allow_multiple: true会把所有匹配全部替换——如果你给的是一个足够短、足够常见的片段(return null;、if (!user)、一个空行组合),它会在多个函数里同时命中。这类修改在事后 review 时极难发现,因为每一处单独看都"像是改对了"。
另一类失败来自上下文不足:模型凭记忆写old_string,与磁盘上的实际文本(缩进、行尾、注释)存在细微差异,触发修正路径;修正仍失败后,它可能退回到"那我用write_file重写整个文件"——第二类翻车就此接上第一类。
怎么防:
- 提示词里要求"
old_string必须包含至少 5 行上下文"; - 除非明确要全局替换,否则永远不要建议模型开
allow_multiple; - 对关键文件,先
@引用进上下文,再让它改。上下文越完整,old_string越准,回退到整文件重写的概率越低。
第四类:shell 的破坏半径不受工作区约束
文件工具是有边界意识的。config.ts 的validatePathAccess会拒绝工作区与项目临时目录之外的路径:
Path not in workspace: Attempted path "..." resolves outside the allowed workspace directories: ...paths.ts 里还专门写了resolveDefensiveToolPath,处理模型给出的@src/foo.ts这类引用前缀——如果字面路径不存在,会剥掉@,避免真的创建一个名为@src的目录。这些都是"文件工具层"的防线。
但run_shell_command是另一回事。它执行的是你系统上的真实 shell 命令(Windows 用powershell.exe -NoProfile -Command,其他平台用bash -c),工作目录只是起点,不是边界。rm -rf ~/xxx、> ../../important.txt、git checkout -- .都可以跨出项目目录。
仓库对 shell 做的是"识别 + 提示",而不是"拦截":
- shell.ts 会检测命令替换语法(
detectCommandSubstitution),命中后直接拒绝执行,这是防注入的; - 确认弹窗会解析根命令、列出
untrustedFlags(未经审查的参数),并把"构建/测试命令 + 构建文件被改过"标为安全警告; write_file改了package.json这类构建文件时,会通过recordModifiedBuildFile记账,随后跑npm install时弹出提示。
最后这条设计很值得注意:它防的不是"删错文件",而是**"先改package.json,再触发依赖安装"这条供应链路径**。这也是为什么"只读计划、集中执行"的模式在安全上更有意义。
怎么防:
- 不要对 agent 说"帮我清理一下构建产物"这种开放式命令,改成枚举式:"删除
dist/和.turbo/两个目录"; - 首次使用一律
default确认模式,逐条看命令; - 需要放开时用沙箱,而不是用
yolo。Sandboxing 明确:容器内工作目录以相同绝对路径挂载,系统其余部分不可见;GEMINI_SANDBOX=docker一行即可。
第五类:自动批准 + 默认关闭的 checkpoint,等于没有后悔药
auto_edit会自动批准replace与write_file,只对其他工具(如 shell)保留询问;yolo则批准一切。这两者的定位在 参数表 里写得很清楚,--yolo已经标记为 deprecated,推荐统一走--approval-mode。
它们的共同点是:跳过的正是那个能看到 diff 的确认框。而默认状态下,write_file/replace执行前的快照(checkpoint)是关闭的。
这两件事叠在一起,构成最坏的组合:没有 diff 可看,也没有快照可退。
更细节的一层:使用--yolo时沙箱会被默认启用,这是仓库里少见的"负负得正"——放开确认的同时收紧了执行边界。但这个默认只在 yolo 下生效,auto_edit并不会自动给你套沙箱。
怎么防:
{ "general": { "checkpointing": { "enabled": true } } }注意文档里的提醒:--checkpointing命令行开关在 0.11.0 已被移除,只能用settings.json开启。很多人"以为开了",其实是旧文章里的用法。
三、动手前的快照与回滚准备
回滚能力分为两套机制,边界完全不同,混用会出事。
Checkpoint:文件级的时间点
开启后,每次你批准一个会修改文件系统的工具(write_file、replace),CLI 会自动创建 checkpoint,包含三样东西:
- Git 快照:提交到
~/.gemini/history/<project_hash>下的影子仓库,不影响你自己的.git; - 会话历史;
- 即将执行的那次工具调用本身。
恢复走/restore:
/restore /restore 2025-06-22T10-00-00_000Z-my-file.txt-write_file检查点文件名由时间戳、被改文件名、工具名拼成,恢复后文件与会话都回到那一刻,并且原始工具调用会重新出现,让你决定重跑、修改还是放弃。这个"重放"设计比单纯的文件回滚更有价值:你可以改一句提示词,然后立刻重试同一步。
有一个必须知道的副作用:恢复的语义是"把项目里所有文件还原到快照状态"。如果你在快照之后手动改了别的文件,那些改动也会被一起覆盖。所以在/restore之前,先把手上的未提交改动处理掉。
Rewind:会话级的回退
/rewind(或连按两次Esc)是另一套:选一个交互点,然后选择"回退会话和代码"、"只回退会话"、"只回退代码"。
它的边界在 Rewind 里写得很清楚,而且是本文最重要的一条警示:
Rewinding only affects file changes made by the AI's edit tools. It doesnotundo manual edits you've made or changes triggered by the shell tool (
!).
也就是说,rm -rf删掉的东西,/rewind救不回来;只有编辑类工具造成的改动在它的管辖范围内。第四类翻车恰恰落在它的盲区里。
所以正确的准备顺序是:
- 进仓库先
git status确认工作区干净,或者至少知道哪些改动没提交; - 打开 checkpointing;
- 对"要跑破坏性命令"的场景,改用沙箱而不是依赖回滚;
- 区分清楚:编辑失误找
/rewind或/restore,命令失误只能靠备份或沙箱。
四、把风险压到最低:配置与提示词
1. 用 Plan Mode 做第一次接触
Plan Mode 是只读环境,默认可用:
gemini --approval-mode=plan它会先研究代码、和你确认策略,再写一份 Markdown 计划到 plans 目录,按Ctrl+X可以用外部编辑器直接改计划、写批注。对"第一次进这个仓库"的场景,这比任何提示词都有效——你可以在零写入的前提下,看清它打算怎么动手。
一个细节值得记住:enter_plan_mode工具在 YOLO 模式下不可用。也就是说,一旦进了 yolo,你就失去了"让模型自己退一步做规划"的路径。
2. 用策略引擎做硬性拒绝
策略引擎 支持allow/deny/ask_user三种决策,按优先级取最高。放在用户级目录~/.gemini/policies/*.toml:
[[rule]] toolName = "run_shell_command" commandPrefix = "rm -rf" decision = "deny" priority = 100[[rule]] toolName = "run_shell_command" commandRegex = "git (commit|push)" decision = "ask_user" priority = 100deny比"每次都点拒绝"更彻底:对全局规则(不带argsPattern),被拒绝的工具会被完全从模型的工具列表里移除,模型根本看不到它,既安全又省上下文。
两个坑需要提前说明:
commandPrefix和commandRegex不能写在同一条规则里;- Workspace 层级的策略目前不生效,写在项目
.gemini/policies下不会起作用,请使用 User 或 Admin 策略。这一点在文档里以警告形式明确标注,很容易被忽略。
3. 用.geminiignore缩小可见面
.geminiignore语法与.gitignore基本一致,支持!取反:
.env local-db-dump.sql /archive/ *.md !README.md但要说清它的性质:它约束的是尊重该文件的工具(文件发现、搜索、@引用等)的可见范围,文档也明确"这些文件对其他服务(如 Git)仍然可见"。不要把它当成权限边界——shell 工具可以直接cat任何文件。它的价值在于减少误读、减少上下文污染,以及降低敏感内容被模型顺手带进日志的概率。
4. 提示词里写清楚"不许做什么"
配置管的是能力边界,提示词管的是意图边界。第一次使用时,把这几句固定加在任务前面,收益远大于任何调优:
- "只修改
src/foo.ts,其他文件保持不动。" - "使用
replace做局部修改,不要用write_file重写整个文件。" - "不要执行任何安装、删除、
git push或修改 lockfile 的命令。" - "先给出将要修改的位置和 diff 摘要,等我确认后再动手。"
这四条分别对应本文的四类翻车:越界修改、全量覆盖、破坏性命令、无确认执行。
5. 自动批准只给"可重建的工作区"
auto_edit并非不能用,它的适用条件很具体:这个仓库可以被一条命令完全重建(容器、临时目录、干净的 worktree),而且你不介意丢掉未提交的改动。在真实业务仓库里,它省下的几次回车,抵不上一次误覆盖的排查成本。
结语:把"信任"拆成可验证的几步
这个类别的工具真正的转折点,不是模型变多强,而是默认值是否站在"不丢数据"这一侧。从代码看,这个仓库已经准备了相当完整的刹车系统:路径校验、占位符检测、匹配次数校验、命令替换检测、构建文件记账、checkpoint、策略引擎、多层沙箱。
问题在于,这些刹车大多默认不踩:checkpoint 关着,文件夹信任关着,沙箱要显式开。它们更像是"给知道自己在做什么的人准备的开关",而不是"给第一次使用的人准备的护栏"。
所以第一次使用的正确姿势不是"先试试看它能做什么",而是先花三分钟做三件事:看清仓库里的项目级配置、把工作区变成可回滚的状态、把第一次交互限制在只读模式。能力是它给的,边界得你自己划。
【免费下载链接】gemini-cliAn open-source AI agent that brings the power of Gemini directly into your terminal.项目地址: https://gitcode.com/GitHub_Trending/gemi/gemini-cli
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考