AI Agent 常误读工作目录导致文件写入 /tmp,提供了三个命令验证 git root/pwd/python cwd 是否一致的检查流程。
你的 agent 真的在仓库根目录启动了吗?
我在聊天记录里不断看到这种说法。然后一个相对路径就写到了 /tmp 里。痛苦,对吧?
Agent 循环故意隐藏工作目录。模型在 narrate,shell 保持沉默。
我想要一份可以反复执行的检查清单。不是又一次信心喊话。
声明:本文是 MonkeyCode 产品推广的一部分。
我在另一台机器上运行这份检查清单。那里用的是 MonkeyCode 的免费模型访问。那台免费服务器就是第二台机器。把它当作一台陌生的笔记本电脑,而不是你自己的克隆。
明天把产品名删掉。这些路径迷思依然会咬人。
关于路径的一句话是 claim。pwd 是 measurement。
我先写一个指纹文件,再 diff 那个文件,最后才相信聊天记录。
这个工作流是 propose 的。我不是在报告未公开的 timing。我不是在编造配额或硬件规格。
为什么这个说法还能存活?因为措辞听起来像是操作性的。
它不是操作性的。它是穿着硬壳的 prose。
编辑文件之前,先测量三个字符串:
git rev-parse --show-toplevel
pwd
python -c "import os; print(os.getcwd())"
如果这三个字符串不一致,停下来。不要"直接打补丁"。
修正后的心智模型:transcript 是证词。shell 是法庭。
Home 目录让人觉得可以移植。其实不行。
macOS 路径在 Linux 上会死。驱动器盘符死得更快。你的 IDE 的绝对路径是一个陷阱。
问一个直白的问题。/Users/you/src/app 在一台借来的主机上存在吗?
Proposed one-liner,标注为 unexecuted 直到你运行它:
# proposed fingerprint snippet, not a production SLA
import os, json, pathlib, socket, time, sys
payload = {
"cwd": os.getcwd(),
"home": str(pathlib.Path.home()),
"user": os.environ.get("USER") or os.environ.get("USERNAME"),
"host": socket.gethostname(),
"platform": sys.platform,
"sep": os.sep,
"pid": os.getpid(),
"utc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
}
print(json.dumps(payload, indent=2))
在你的笔记本上运行它。在免费服务器上运行它。diff 这两个 JSON 文件。
那个 delta 才是真正的架构图。聊天永远不会画出它。
你的循环复用了 shell 吗?诚实一点。
复用的 shell 记得 cd。记得 export。记得一个坏掉的 venv。
残留文件就是状态。状态是 bug 的温床。
Proposed test plan。No pass-rate theater attached:
为 agent 打开一个长命的 shell。
运行 cd /tmp && echo leak > marker.txt。
发送一个新的 prompt:只列出项目文件。
检查 marker.txt 是否可见。
在 prompt 之前打印 pwd,之后也打印。
如果 marker.txt 出现了,cwd 泄漏了。你的"新任务"并不新。
修正后的心智模型:把 cwd 当作数据库事务。要么 commit 它,要么 reset 它。
Relative paths 在你 pin 住根目录之后才能用。在那之前,它们是赌博。
我想要一个磁盘上的文件。我不想要一个段落。
把指纹放在补丁旁边。在同一视野里审查两者。
# proposed layout
mkdir -p .agent
python env_fingerprint.py > .agent/env_fingerprint.json
git rev-parse HEAD > .agent/head.txt
diff -u .agent/env_fingerprint.local.json .agent/env_fingerprint.remote.json
No fingerprint, no merge。那条规则很便宜。Path bugs 不便宜。
Free models 不修复 POSIX。Free servers 不克隆你的 $PATH。
两台机器意味着两个时钟。两个路径分隔符。两个残留的工作空间。
循环越便宜,你越要频繁地重新运行它。重新运行会放大路径错误。
修正后的心智模型:cost 是预算选择。Fingerprints 是正确性选择。
这里有一个提议的小脚本。在你信任任何相对编辑之前运行它。
#!/usr/bin/env python3
"""Write a tiny environment fingerprint for agent loops."""
from __future__ import annotations
import json
import os
import pathlib
import platform
import socket
import subprocess
import sys
import time
def git(cmd: list[str]) -> str | None:
try:
out = subprocess.check_output(
["git", *cmd],
stderr=subprocess.DEVNULL,
text=True,
)
return out.strip() or None
except (OSError, subprocess.CalledProcessError):
return None
def main() -> None:
repo = git(["rev-parse", "--show-toplevel"])
head = git(["rev-parse", "HEAD"])
cwd = pathlib.Path(os.getcwd()).resolve()
repo_path = pathlib.Path(repo).resolve() if repo else None
payload = {
"cwd": str(cwd),
"repo_toplevel": repo,
"head": head,
"cwd_is_repo": bool(repo_path and cwd == repo_path),
"home": str(pathlib.Path.home()),
"user": os.environ.get("USER") or os.environ.get("USERNAME"),
"host": socket.gethostname(),
"platform": platform.platform(),
"python": sys.version.split()[0],
"sep": os.sep,
"path_entries": os.environ.get("PATH", "").split(os.pathsep)[:8],
"tz": list(time.tzname),
"utc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
"pid": os.getpid(),
"ppid": os.getppid() if hasattr(os, "getppid") else None,
}
out_dir = pathlib.Path(".agent")
out_dir.mkdir(exist_ok=True)
out = out_dir / "env_fingerprint.json"
out.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8")
print(out.read_text(encoding="utf-8"))
if __name__ == "__main__":
main()
How I wire it into a loop:
python env_fingerprint.py
# later, on the other host
python env_fingerprint.py > /tmp/remote.json
diff -u .agent/env_fingerprint.json /tmp/remote.json
把 diff 大声读出来。然后再决定相对路径是否合理。
把表格打在 PR 里。Reviewers 比模型更需要它。
# test_env_fingerprint.py — proposed local guard
import json
from pathlib import Path
def test_fingerprint_cwd_matches_repo():
data = json.loads(Path(".agent/env_fingerprint.json").read_text())
assert data["repo_toplevel"], "git toplevel missing"
assert data["cwd_is_repo"] is True
这个测试不能证明 agent 很聪明。它只能证明 agent 站在了仓库里。
这是一个很低的门槛吗?是的。但仍然有人会失败,然后补丁就落到了错误的树上。
这个指纹是浅层的。它忽略容器挂载。它忽略 NFS root squashing。它忽略 os.sep 之外的 Windows 驱动器映射。
它不会冻结包版本。用 lockfile 干那个活。它不能证明模型对测试说了真话。
时钟可能偏移。主机名可能冲突。PATH 被故意截断到 8 个条目。
如果你的 agent 在 Docker 里运行,给容器做指纹。只给 hypervisor 做指纹仍然会骗你。
如果你已经有 hermetic builders,跳过这个。Bazel 用户、Nix 用户,你们已经领先了。
跳过生产部署。JSON 文件不是 signed provenance record。
如果 agent 不能执行本地命令,跳过。那是一个更大的问题。
不要把 secrets 放在免费远程服务器上。指纹可能泄露 $HOME 和用户名。粘贴之前先 redact。
我在每个 agent patch 旁边保留三个文件:
.agent/env_fingerprint.json —— 本地指纹
HEAD —— 打补丁时的 commit
.agent/env_fingerprint.remote.json —— 远程指纹(diff 后保留)
如果 diff 太 noisy,我拒绝这个补丁。苛刻吗?也许。但 path bugs 更苛刻。
还是觉得聊天记录就是 pwd?跑一次这个脚本。然后告诉我哪个迷思还活着。