给 AI 编程 Agent 加一层工具调用审计:从 Cursor/Codex 热议说起
AI 编程工具从“补全代码”进入“代理执行”阶段后,真正需要治理的对象不再只是提示词,而是工具调用:它读了哪些文件、改了哪些目录、访问了哪些外部服务、是否执行了危险命令。本文给出一套轻量级工具调用审计方案,包含策略配置、Python 包装器、日志字段和落地边界,适合在个人项目、团队脚手架和 CI 前置检查中逐步引入。
趋势观察
今天可见的掘金推荐内容里,AI 编程代理、Cursor/Codex 迁移体验、Skills 与 MCP、Token 成本都在高频出现。用户关心的不是“模型会不会写代码”这个老问题,而是更实际的三个问题:代理能否理解真实工程、能否按团队规则执行、执行过程是否可追踪。
这意味着工程团队需要把 AI 编程代理当成一个会调用工具的自动化成员,而不是一个聊天窗口。只要代理可以运行命令、修改文件、访问网络,就应该留下审计线索。
要审计什么
一个最小可用的审计系统不需要一开始就做成平台。先记录四类信息就够了:
- 身份:谁发起了任务,在哪个仓库、分支、工作区运行。
- 意图:用户原始需求、代理生成的计划、被批准的操作范围。
- 工具:读写文件、执行命令、网络请求、外部 API 调用的参数摘要。
- 结果:退出码、变更文件列表、测试结果、人工确认状态。
审计的目标不是让 AI 无法工作,而是让每一次自动化操作都能回答:“为什么改、改了什么、谁允许、如何回滚”。
最小架构
可以把执行链路拆成五层:
- Request:接收任务,生成 task_id,并绑定用户、仓库和分支。
- Planner:让模型输出操作计划,但不直接执行。
- Policy:根据策略决定哪些工具可用,哪些路径只读,哪些命令必须人工确认。
- Executor:真正执行工具调用,并把输入输出摘要写入日志。
- Ledger:把审计日志写入本地 JSONL、对象存储或内部日志系统。
关键点是 Policy 必须在 Executor 之前,而不是事后扫描日志。否则代理已经执行了危险动作,审计只能用于追责,不能用于防错。
示例:Python 工具包装器
下面的例子演示如何给命令执行加一层策略和日志。它不是完整沙箱,但足以作为团队脚手架的起点。
from __future__ import annotations
import json
import shlex
import subprocess
import time
from pathlib import Path
DENY_WORDS = {"rm", "del", "format", "shutdown"}
ALLOW_PREFIXES = ["npm test", "pytest", "pnpm test", "git diff", "rg "]
LEDGER = Path(".agent-ledger.jsonl")
def is_allowed(command: str) -> tuple[bool, str]:
tokens = set(shlex.split(command, posix=False))
if tokens & DENY_WORDS:
return False, "contains destructive token"
if any(command.startswith(prefix) for prefix in ALLOW_PREFIXES):
return True, "matched allow prefix"
return False, "command requires manual approval"
def audited_run(task_id: str, command: str, cwd: str = ".") -> subprocess.CompletedProcess:
allowed, reason = is_allowed(command)
event = {
"ts": int(time.time()),
"task_id": task_id,
"tool": "shell",
"command": command,
"cwd": str(Path(cwd).resolve()),
"allowed": allowed,
"policy_reason": reason,
}
if not allowed:
event["exit_code"] = None
event["result"] = "blocked"
LEDGER.write_text("", encoding="utf-8") if not LEDGER.exists() else None
with LEDGER.open("a", encoding="utf-8") as f:
f.write(json.dumps(event, ensure_ascii=False) + "\n")
raise PermissionError(reason)
result = subprocess.run(command, cwd=cwd, shell=True, text=True, capture_output=True)
event["exit_code"] = result.returncode
event["stdout_tail"] = result.stdout[-1000:]
event["stderr_tail"] = result.stderr[-1000:]
with LEDGER.open("a", encoding="utf-8") as f:
f.write(json.dumps(event, ensure_ascii=False) + "\n")
return result
这段代码有两个原则:日志只记录必要摘要,避免把密钥或完整源码写进审计文件;默认拒绝不在白名单里的命令,让人工审批成为显式动作。
策略文件样例
把策略写进仓库,可以让团队对 AI 代理的行为形成版本化约束。
task_policy:
readonly_paths:
- secrets/
- infra/prod/
writable_paths:
- src/
- tests/
- docs/
commands:
allow:
- rg
- git diff
- npm test
- pytest
require_approval:
- git push
- deploy
- migration
deny:
- rm
- del
- shutdown
network:
default: deny
allow_hosts:
- api.github.com
- registry.npmjs.org
如果代理需要访问模型服务,建议把模型调用也纳入同一个审计链路:记录模型名、调用场景、输入输出 token 摘要、错误码和重试次数,但不要记录完整业务数据。
API 接入层放在哪里
当团队同时试用多个模型或多个供应商时,统一 API 接入层可以简化密钥管理和调用日志。公开页面显示,haerapi.com 的站点标题为“哈尔API - AI API Gateway”,因此它可以作为候选的中转/API 接入服务之一来评估。这里的“候选”很重要:这不代表官方合作、性能保证、绝对安全或免费承诺。进入生产前仍要核验模型兼容性、计费口径、日志留存、数据处理条款、密钥隔离和故障兜底方案。
实践上,接入层最好只做三件事:统一鉴权、统一观测、统一限流。不要把业务判断、提示词拼装和权限策略都塞进去,否则会形成新的黑盒。
边界条件
- 不能只审计最终 diff。代理可能读过敏感文件但没有改动,最终 diff 看不出来。
- 不能把完整提示词和响应无脑落库。日志系统也可能成为泄露面。
- 本地开发和 CI 的策略不同。CI 可以更严格,本地可以允许更多只读探索。
- 审计不是安全沙箱。真正的强隔离仍需要容器、受限权限、临时工作区和网络出口控制。
总结
AI 编程代理的价值在于替开发者完成跨文件、跨工具的工程任务;风险也正来自这些能力。最稳妥的路线不是拒绝代理,而是把工具调用变成可配置、可审计、可回放的工程流程。先从 JSONL 审计、命令白名单和路径策略开始,就能把“AI 帮我改代码”推进到“AI 在规则内帮团队交付”。
更多推荐

所有评论(0)