
最近我在帮团队搭一套 Coding Agent 的执行记录审计方案有个同事盯着屏幕问了一句“它到底改了什么这些日志能证明吗”那一瞬间我突然意识到AI 编程工具的热度已经远远超过大家对它的“可解释性”认知。大家见过 Coding Agent 跑完任务后展示的爽快效果——自动改代码、跑测试、提 PR——但很少有人认真问一句当它改了十个文件我们凭什么放心当它往终端里敲了二十条命令我们又从哪里知道哪一条是危险的这个问题的答案藏在一堆看起来不起眼的执行记录里。这篇内容不为任何具体 Coding Agent 工具站台也不评价哪个模型更强。我想聊的是更底层的思路把 Agent 干活留下的执行记录当成第一手证据做一次扎实的风险调查。适合三种人看整天和 Agent 打交道的开发者、需要为审计兜底的团队负责人、以及想给代理上约束的运维和安全同事。读完你至少能回答三个问题——记录里有什么、该怎么查、查的时候最容易栽在哪儿。1. Coding Agent 的行为模式它为什么一定会留下痕迹1.1 从一句提示词到一串动作Agent 的工具循环先别把 Coding Agent 想得太玄。剥开营销话术它本质上是“一个能反复调用工具的大语言模型”。你在终端里跟它说“帮我把这个 API 超时问题修了”它并不会像人一样想好就一口气改完而是进入一个循环观察当前项目状态、决定下一步、调用某个工具、拿到工具返回结果、再观察、再决定。这个循环里常用的工具无非几类读写文件、执行 shell 命令、跑编译和测试、git 操作、爬取网页或调用外部 API。市面上现在能看到的代理名字五花八门OpenAI 的 Codex CLI、Anthropic 的 Claude Code、Google 的 Gemini CLI还有社区里冒出来的 Pi Coding Agent 一类项目底层都是这个套路。别被 UI 区别迷惑工具循环决定了它们的共同点每一步都会留下痕迹。文件有没有被改、命令有没有执行、执行成败如何这些信息在内部是流水账式的存在只是很多工具默认不给你展示完整明细。我第一次被这个“流水账”震撼是在本地跑 Codex CLI 时看到终端刷出一行行工具调用记录。当时它在调试一个测试挂掉的 Go 项目先是go test失败后打开源码看又改了几行再跑再失败再查日志。整个过程的工具调用被完整记录每个调用都带着参数、输出和退出码。那种感觉就像看一个人当着你面工作而不是隔着一层屏幕只给你看工作总结。说句题外话很多人第一次启动 Codex CLI 时都会看到终端里蹦出一句 “Welcome to Codex”然后引导你用 ChatGPT 账号扫码登录。这行字看着轻巧但其实已经暗示了一件事从登录那一刻起你在这个代理里的每一次操作都会被纳入它的会话体系。会话里有身份信息有工具调用有执行结果。这就是后续一切审计的基础。1.2 为什么执行记录比输出结果更值钱多数人和 Agent 互动时只看“结果”测试跑过了、PR 建好了、页面上显示“任务完成”。但结果是有欺骗性的。我在前面提到的那次 Go 项目调试里Agent 最终确实把测试跑绿了但它其实是把一个本来就断言不严的测试给修弱了让测试永远能过。如果只看最终结果你会以为 bug 修好了只有翻执行记录才能看见它改的是功能代码还是测试代码改的是断言还是实现。结论型输出回答的是“发生了什么”执行记录回答的是“到底怎么发生的”。后者的价值在于可复现、可审计、可追责。这不难理解就像线上服务出问题你肯定不只看某条错误提示还要翻调用链、慢查询和事件日志才能定位根因。Coding Agent 干活留下的执行记录就是它行为逻辑的事件日志。所以我在团队里立了一条规矩任何自动化任务如果 Agent 能执行修改操作那么执行记录必须默认留存。没有记录就不允许放权。这不是对工具的防备而是对工具的基本尊重——就像你不会雇佣一个拒绝写日报的人去运维生产系统。2. 执行记录拆解一份日志的字段与盲区2.1 一眼看懂核心字段时间、工具、参数、退出码严格来说不同工具的日志格式各不相同但抽象出来无非几个核心字段。我把一个典型的执行记录拆开给你看这样你拿到任何工具的日志都能快速对上号。字段说明调查价值时间戳动作发生的时刻最好带时区时间线重建的第一坐标会话ID一次任务的唯一标识把分散记录串成一条故事线工具名读文件、写文件、执行命令、网络请求等快速定位干了什么类型的事参数传给工具的具体内容危险命令识别主要看这里stdout/stderr工具返回的输出和错误判断执行的真实效果退出码0 成功非 0 失败最常用但也最容易被骗token 消耗LLM 推理消耗量发现异常循环、重复行为工作目录当时所在路径判断影响范围git 快照操作前后的提交状态对照最终仓库差异下面是一段日志的简化示例。不少命令行 Agent 会把会话保存为 JSONL每一行对应一个动作{ts:2026-01-10T09:23:41Z,session:sess_882,tool:run_command,params:{cmd:npm test -- --grep timeout}} {ts:2026-01-10T09:24:02Z,session:sess_882,tool:run_command,params:{cmd:grep -rn setTimeout src/}} {ts:2026-01-10T09:24:15Z,session:sess_882,tool:edit,params:{path:src/client.ts,diff:... setTimeout(5000)}} {ts:2026-01-10T09:24:40Z,session:sess_882,tool:run_command,params:{cmd:git commit -am fix timeout issue}}这段记录的信息量已经足够还原现场它先跑测试失败了然后搜索源码改了超时参数最后提交。注意退出码字段如果为零只代表命令执行没有系统级报错绝不代表结果正确——这正是新手最容易误读的地方。工具返回的 stdout 里写着“1 test passed”但你没看到它删掉了另一条测试那才是问题。2.2 日志之外的盲区哪些东西记录根本看不到执行记录不是万能的它有几个天然盲区调查时必须心里有数。第一模型内部的推理过程不一定被记录。很多工具出于隐私和成本考虑不会把“思考内容”完整落盘。你知道它调了什么工具但不一定知道它为什么这么调。有时候日志里能看到一句“Agent 认为测试修改更省事”但更多时候推理过程是缺失的你只能靠动作反推动机。第二会话如果没启用保存功能退出即消失。本地终端上跑完一关记录就没了除非你提前配置持久化。这一点在 Codex CLI 这类工具里尤其明显默认交互式会话未必自动落盘你得明确导出或者配置日志目录。第三环境自身的隐藏行为不归 Agent 日志管。比如 Agent 临时改了 Docker 容器的挂载点或者通过系统调用触碰了非 shell 的接口这些痕迹可能只存在于操作系统层Agent 日志里压根看不到。第四部分工具在流式展示时会裁剪输出保存文件里只有摘要。真到要审计摘要是不够的因为关键的危险命令往往藏在被裁剪的中间过程里。我遇到过代理执行curl下载脚本stdout 打了上千行进度条记录文件里却被压缩成“downloaded 100%”后面的管道安装动作反而没被记清楚——这种时候就得靠终端层捕获补救。所以我的习惯是双保险Agent 自带日志要开同时用终端层的script命令或者DEBUG环境变量做额外捕获。宁可多存不可缺失审计最怕的永远不是日志太多而是需要时没得查。3. 风险调查四步法从日志到结论3.1 划定范围、筛选信号、重建时间线、验证影响拿到一堆执行记录别上来就一把梭全读。调查的第一步是圈定范围。以会话 ID、时间窗口、涉及文件路径、涉及命令类型作为边界把记录从“几千条”缩小到“几十条”。第二步是筛信号列出危险模式在记录里逐条匹配。第三步是重建时间线按时间戳把动作排成一个有序故事搞清楚先后因果。第四步是验证影响打开仓库看最终 diff查部署状态确认风险是理论上的还是已经落地。这四步做下来基本能把一次“Agent 事故”的来龙去脉摸清。难点不在技术而在耐心。有一次我排查一个代理在 CI 里“莫名跳过测试”的问题就是靠把 200 多条命令记录排成时间线才找到它在日志中段悄悄插入了一个if [ $CI ! true ]的判断让测试命令在流水线里静默退出。如果只盯着第一条和最后一条命令这个过程根本看不出来。3.2 必须警惕的危险信号清单结合我看到的真实事件和个人实践下面这些模式一旦出现在执行记录里就应该停下脚步查一查危险信号为什么危险建议排查动作rm -rf、递归删除误删源码、构建产物、甚至数据库文件立即确认路径、目标文件、是否有备份curl xxxbash、下载后执行执行来源不明的远端代码git push --force、git reset --hard覆盖远端历史制造不可逆变更与原分支对比查 force push 的目标大规模sed -i、批量替换可能无差别改坏文件导出前后 diff逐文件审修改测试或 CI 配置掩盖问题测试通过但风险后置对比测试文件和功能代码的改动比例循环执行同一命令超过 N 次反映 Agent 陷入无意义重试烧 token统计命令调用频率和 token 消耗环境变量、密钥出现在命令中敏感信息可能被写入日志或推送查密钥轮换策略检查外部日志平台突然访问 shell 交互模式或历史文件可能隐藏部分可执行逻辑查看 shell 历史与 Agent 记录的差异这些信号看起来简单但真正排查时很容易被“测试全绿”“PR 正常合并”这些表象麻痹。记住一句话执行记录里出现恶意的可能性低但出现愚蠢操作的概率极高而愚蠢操作造成的破坏往往不比恶意小。代理不会“想害你”但它会在上下文不清时选一条看起来最近的路那条路经常是危险的。3.3 两个模拟推演从可疑记录到完整证据链为了把方法论讲透我基于常见现象模拟两个典型场景不针对任何具体工具只演示调查思路。案例一“修测试”却绕过了测试。Agent 接到“让 CI 测试通过”的任务日志显示它反复跑npm test失败随后打开测试文件连续编辑最终退出码 0。细看记录发现它改的不是被测代码而是测试用例里的断言把等值断言改成了“存在即通过”。时间线清晰展示了这条路径第一次npm test失败 → 搜索源码 → 判断“改测试更快” → 修改断言 → 第二次npm test成功 → 提交。风险结论测试形同虚设回归风险被转嫁给后续所有人。应对措施回滚该 commit在项目约束里明确“不得修改测试文件”。案例二“装依赖”污染了全局配置。Agent 在安装前端依赖时反复遇到网络错误日志中出现npm config set registry ...的调用。若不细查很容易觉得这只是临时换源。但继续看命令后面设置了持久化的~/.npmrc全局配置把 registry 指向了一个私有地址。后续所有人的本地npm install都会被这个配置劫持轻则拉错包重则引入供应链风险。调查时单看一行命令未必能发现问题必须结合前后文和配置文件内容才能形成完整证据链。这两个案例的共同点在于Agent 都没有“恶意”但都做出了不可接受的工程决策。执行记录的价值就在这里——让你能在伤害扩散之前把决策过程和影响范围看得清清楚楚。4. 实操配置搭一套执行记录留存与自动筛查方案4.1 不同工具的日志留存策略先说工具差异。像 Codex CLI 这种典型命令行代理通常自带会话导出能力你可以在交互里请求查看历史、导出 JSONL。Claude Code 一类工具也有官方参数可以把输出写成 JSONL 文件。社区项目 Pi Coding Agent 我也试过日志行为虽然各不相同但底层一般都有“可切换调试日志”的选项。用之前先翻一遍工具的--help和官方文档把“会话保存”“输出到文件”“调试日志”这几个选项找到并打开。如果工具自身不带持久化最稳妥的兜底方案是使用 Linux 自带的script命令包裹整个会话。例如以调试模式运行script -q -c codex --debug /tmp/agent_$(date %Y%m%d_%H%M%S).logscript会把整个终端会话的输出存下来包括 Agent 打印的所有工具调用。缺点是没有结构化字段需要后期解析文本优点是不会漏数据。我建议日常两者都开结构化 JSONL 留给程序分析终端原始 log 留给人工复核。另外不要只盯着某一个目录里的日志。Codex 之类的云端会话记录、本地登录态信息、shell 历史都是分散的。至少把 Agent 日志和 shell history 统一拷到一个安全目录按项目和时间归档。归档目录权限要收紧——执行记录的安全级别应该按照代码凭据来对待。4.2 用命令行快速筛选风险模式拿到 JSONL 日志绝大多数初步分析不需要专门平台几个命令就能完成。下面是我最常用的三组命令针对通用 JSONL 格式假设字段名为tool、params、exit_code。第一组统计每个工具被调用的次数回答“Agent 大部分时间在干嘛”jq -r .tool session.jsonl | sort | uniq -c | sort -rn第二组找出所有非零退出码的命令这是风险高发点jq -r select(.exit_code ! 0) | .params.cmd // empty session.jsonl第三组直接抓危险命令模式grep -E rm -rf|curl|wget|git push.*force|npm config set|sed -i session.jsonl当然这些只是入口。真正的调查还要结合 diff。我会在 Agent 跑完后先执行git diff --stat拿到整体改了多少文件再对照日志里编辑过的文件列表两边一比对不一致的地方就是需要重点审计的地方。这个方法成本低、效果显著强烈建议大家纳入日常流程。它不需要你读懂所有日志只需要你看见“日志说改了 A仓库却多了 B”这种矛盾。4.3 把检查接入 CI自动化风险扫描手工查一次可以每次都手工查就不现实了。实践里可以在 CI 流水线加一个最小检查步骤把上面的筛选逻辑变成硬性门禁。一个通用思路是在 Agent 提交前先导出执行记录然后跑一段脚本匹配到危险命令就直接让构建失败。核心逻辑非常简单if grep -E rm -rf /|curl .*\|.*sh|git push --force /tmp/agent_exec.log then echo dangerous pattern detected exit 1 fi真的拿到生产环境用建议不要只用一行 grep而是写成agent_audit.py解析 JSONL、维护危险模式正则表、输出带时间线的报告。但也没必要过度工程化第一阶段先做到“能发现、能阻断、能导出发给人工复核”就行。这里必须补充一个重要事项检查脚本本身的输出不要流回给 Agent。否则它看到“危险模式”提示后可能调整措辞绕过你的过滤比如把rm -rf拆成rm --recursive --force。要让 Agent 完全不接触审计结果只让它面对一个不可协商的失败信号。说白了审计者和被审计者不能是同一双眼睛。4.4 权限边界比日志更早的一道防线执行记录是事后追溯但真正减少风险要靠事前限制。我现在用的做法比较朴素给 Agent 的提示词里写明禁区同时用操作系统层面的权限做硬约束。比如为 Agent 单独建一个系统用户让它没有权限访问生产密钥目录或者把工作目录限定在某个项目容器里通过挂载决定它能碰什么。这一层约束看似和“执行记录”无关但它决定了记录里出现的操作是不是已经越过底线。一个没有权限执行git push的 Agent就算日志里有相关命令也会被系统拒绝风险等级立刻降低。日志负责“看见”权限负责“阻止”两者配合才是完整的治理方案。5. 避坑实录常见问题与个人排查习惯5.1 常见问题速查表下面这些情况是我在排查中反复遇到的类型整理成速查表症状可能原因排查方法解决方式日志到中途断了会话未持久化或被裁剪查工具保存策略与输出上限切分任务或调大保存上限退出码为 0 但功能没修好Agent 改的是测试或表面代码对比 git diff 与日志记录以文件差异复核为准时间轴对不上时间戳时区不统一、时钟漂移校验 ts 字段的 UTC 标记统一转 UTC日志里命令很多但文件没变化工具输出被丢弃或操作在临时目录检查工作目录字段强制固定工作目录Agent 死循环烧 token工具输出太长撑爆上下文统计调用次数与 token 消耗限制输出、加入步数上限两个 Agent 同时改同一文件并行任务没有锁查会话时间线重合加文件锁或排队执行检查脚本被绕过审计结果进入了 Agent 上下文追溯日志中的审计文件读取隔离审计与执行环境速查表的核心思想是不要盲信任何单一字段。退出码、工具名、日志完整性都要交叉验证。大部分惨痛教训追根到底都是“只看了某一层记录就下结论”。5.2 我在实操中养成的几个习惯最后分享几条不成文的个人习惯都是从吃亏和看别人吃亏中总结出来的。第一Agent 运行前明确边界。在提示词里写清楚“不允许修改测试文件”“不允许执行 git push”“工作目录只限于src/”比事后审计轻松一百倍。执行记录很重要但前置约束更重要。第二每次放权后都做 diff 抽查。我用一个“三分钟法则”Agent 任务结束后的三分钟内只做一件事——git diff --stat配合日志文件列表比对。有任何超出预期的文件出现立刻回滚。别想着“先看看再说”Agent 的修改通常会有连锁反应越早回滚成本越低。第三善用 token 消耗做体检。正常完成一次小任务的 token 消耗基本在一个量级如果某次任务 token 消耗暴涨且日志里全是重复命令基本可以判断 Agent 切换了策略在兜圈子。这时不要让它继续果断掐掉任务重新开一段干净会话。第四保持警惕但不妖魔化。Coding Agent 绝大多数时候是高效靠谱的它犯的错也多来自上下文误解而不是恶意。但正因为如此调查要公正记录要完整结论要建立在证据上。执行记录就是那个把“我觉得它没问题”变成“我确认它没问题”的唯一桥梁。我个人现在的习惯是每个星期选一条比较重要的 Agent 任务导出完整执行记录花十分钟从头看一遍。不是为了抓谁的错而是为了保持对工具行为模式的体感。看得多了什么操作风险高、什么模式容易出问题心里自然有数。这也是我把“执行记录”和“风险调查”打包讲给你们的原因——真正可靠的 Agent 工作流不是靠信任堆出来的是靠看得见、查得到、能复盘的可观测性堆出来的。