创见博客
Agent Capture:用 mitmproxy 给代码智能体做本地流量抓包
七崽爱吃小饼干2026/07/13阅读 5专栏 AI开发

Agent Capture:用 mitmproxy 给代码智能体做本地流量抓包

调试代码智能体时,最难定位的问题往往不是“模型有没有返回”,而是“请求到底发到了哪里、请求体是什么、响应是否被流式返回、前端或 TUI 为什么没有展示结果”。agent-capture 解决的就是这个问题:它把智能体的 HTTP/HTTPS 流量导入本地代理,用 mitmproxy 抓取请求和响应,再通过一个轻量的 Web 仪表盘展示调用明细。

这个项目的定位很清晰:不侵入被调试的 agent,不要求接 SDK,也不改业务代码。只要让目标进程的网络流量经过本地代理,就可以把请求记录落到本地 JSONL 文件里,并在页面上按 provider、状态、关键词查看。

项目由三部分组成

agent-capture 的核心结构很简单:

  1. capture/capture.py:mitmproxy 插件,负责真正抓包和落盘。
  2. server/index.ts:Node/TypeScript API,负责读取 JSONL、汇总统计、提供查询接口。
  3. src/main.tsx:React/Vite 仪表盘,负责展示请求列表、详情、统计卡片和 SSE 解析结果。

运行时还有三个默认地址:

text
mitmproxy 代理:127.0.0.1:8787
Web 仪表盘:http://127.0.0.1:5173
API 服务:http://127.0.0.1:5174

抓到的数据默认写入:

text
data/captures.jsonl

JSONL 的好处是追加写入成本低、出问题时容易保留历史,也方便后续用脚本二次分析。

抓包原理

整个链路可以理解成这样:

text
agent 进程 -> HTTP_PROXY/HTTPS_PROXY -> mitmproxy -> 目标模型服务
                                    |
                                    v
                            data/captures.jsonl
                                    |
                                    v
                            Node API + React dashboard

当 agent 发起模型请求时,请求先进入本地 mitmproxy。capture.py 在 mitmproxy 的 response 和 error 钩子里拿到完整 flow,然后组装成一条结构化记录,内容包括:

  1. 请求方法、URL、host、path、HTTP 版本。
  2. 请求头和响应头。
  3. 请求体和响应体。
  4. 状态码、耗时、捕获时间。
  5. 出错请求的错误信息。

项目会对敏感请求头做脱敏,例如 authorization、cookie、set-cookie、x-api-key、api-key、proxy-authorization。这能降低误把认证信息写入本地文件的风险,但仍然建议把 data/captures.jsonl 当作敏感调试数据处理,不要随意提交或分享。

响应体处理也做了区分:如果是 JSON、文本、XML、表单等文本类型,就直接保存文本;如果是二进制内容,就保存 base64。默认每个请求或响应最多保存 1MB,可以通过环境变量调整。

API 和仪表盘做了什么

Node API 不负责抓包,它只读 JSONL。启动后会提供几个接口:

text
GET  /api/health
GET  /api/requests
GET  /api/requests/:id
GET  /api/stats
POST /api/clear

其中 /api/requests 会把原始记录转换成列表摘要,包括 provider、model、tokens、状态和耗时。provider 的识别是基于 host 的简单规则,例如 OpenAI、Anthropic、Gemini、Azure OpenAI、本地服务等。model 的识别优先从请求体里的 model 字段读取,Gemini 和 Azure OpenAI 会从 path 中提取模型或 deployment 名称。

React 仪表盘每 2.5 秒轮询请求列表和统计数据。页面上可以看到:

  1. 总请求数、错误数、平均耗时、token 汇总。
  2. 请求列表:时间、方法、状态、host、path、provider、model、耗时。
  3. 请求详情:请求体、响应体、请求头、响应头。
  4. 对 SSE/NDJSON 响应的解析视图,用来直接看流式输出内容。

这对排查“接口 200 但 TUI 没显示”“模型实际返回了什么”“请求是否打到了错误 endpoint”“某次响应是否被截断”等问题很直接。

安装和启动

进入项目目录:

bash
cd /Users/bytedance/code/agent-capture

安装依赖:

bash
npm install

如果本机还没有 mitmproxy,需要单独安装:

bash
brew install mitmproxy

启动 API 和 Web 仪表盘:

bash
npm run dev

另开一个终端启动抓包代理:

bash
npm run capture

然后打开:

text
http://127.0.0.1:5173

让 agent 流量经过代理

最常见的方式是在启动 agent 时设置代理环境变量:

bash
HTTP_PROXY=http://127.0.0.1:8787 \
HTTPS_PROXY=http://127.0.0.1:8787 \
your-agent-command

如果 agent 是 Node.js 生态的程序,例如 opencode 或 ink,还需要注意 TLS 证书。mitmproxy 会用自己的 CA 证书解密 HTTPS,Node 默认会拒绝这种自签证书。调试时可以加上:

bash
NODE_TLS_REJECT_UNAUTHORIZED=0 \
HTTP_PROXY=http://127.0.0.1:8787 \
HTTPS_PROXY=http://127.0.0.1:8787 \
opencode

更完整的 HTTPS 方案是安装 mitmproxy CA。保持 npm run capture 运行,用走代理的浏览器打开:

text
http://mitm.it

然后按 macOS 的证书安装说明操作。

抓取 ink 或 opencode 的建议命令

交互式 ink 有一个容易踩坑的点:不要设置 ALL_PROXY。ink 会访问本地 HTTP endpoint 来驱动 TUI 或本地 opencode 服务。如果 ALL_PROXY 把本地请求也转进 mitmproxy,可能出现模型请求已经成功,但交互界面不显示回复的情况。

推荐命令:

bash
env -u ALL_PROXY -u all_proxy \
NODE_TLS_REJECT_UNAUTHORIZED=0 \
HTTP_PROXY=http://127.0.0.1:8787 \
HTTPS_PROXY=http://127.0.0.1:8787 \
NO_PROXY=127.0.0.1,localhost,::1 \
no_proxy=127.0.0.1,localhost,::1 \
ink

非交互式快速验证可以这样跑:

bash
NODE_TLS_REJECT_UNAUTHORIZED=0 \
HTTP_PROXY=http://127.0.0.1:8787 \
HTTPS_PROXY=http://127.0.0.1:8787 \
NO_PROXY=127.0.0.1,localhost,::1 \
no_proxy=127.0.0.1,localhost,::1 \
ink run "只回复 pong"

执行后到仪表盘看是否出现新的模型请求。

常用配置

默认会抓取所有经过 mitmproxy 的请求。如果只想减少落盘内容,可以配置 host 过滤:

bash
AGENT_CAPTURE_HOSTS=api.openai.com,anthropic.com npm run capture

如果只想抓 ink 的模型流量,可以使用:

bash
AGENT_CAPTURE_HOSTS=ink.bytedance.net npm run capture

调整保存的 body 大小:

bash
AGENT_CAPTURE_MAX_BODY_BYTES=2097152 npm run capture

指定落盘文件:

bash
AGENT_CAPTURE_FILE=/tmp/agent-captures.jsonl npm run capture
AGENT_CAPTURE_FILE=/tmp/agent-captures.jsonl npm run dev:server

注意抓包进程和 API 服务要读取同一个 AGENT_CAPTURE_FILE,否则仪表盘会显示为空。

如何排障

如果仪表盘没有请求,先确认代理是否启动在 8787,再确认 agent 启动命令里是否设置了 HTTP_PROXY 和 HTTPS_PROXY。

如果 HTTPS 请求失败,优先检查 mitmproxy CA 是否安装;Node.js agent 调试时可以临时使用 NODE_TLS_REJECT_UNAUTHORIZED=0。

如果模型接口返回 200,但交互式 ink 没显示内容,检查仪表盘里是否抓到了本地请求,例如:

text
http://127.0.0.1:<port>/command
http://127.0.0.1:<port>/mcp
http://127.0.0.1:<port>/session/.../message

如果这些本地请求也被代理了,说明代理环境变量过宽。去掉 ALL_PROXY,并设置 NO_PROXY=127.0.0.1,localhost,::1。

如果响应体看起来像一堆 data: 分片,可以看仪表盘里的 Parsed SSE Output。它会尝试从 OpenAI-like、Gemini-like 和通用 text 字段里提取流式文本,便于快速判断模型到底返回了什么。

适合什么场景

agent-capture 很适合以下场景:

  1. 调试代码智能体的模型请求。
  2. 比较不同 provider 或模型的请求格式。
  3. 排查流式响应、超时、状态码异常和 token 统计。
  4. 分析本地 CLI/TUI agent 的网络行为。
  5. 在不改 agent 源码的情况下保留可复盘的请求记录。

它不适合长期作为生产流量审计系统,也不应该无过滤地抓取包含大量敏感信息的全局系统代理流量。它更像一个本地调试显微镜:临时打开,定位问题,确认结果,然后及时关闭或清理数据。

总结

agent-capture 的价值在于把“黑盒 agent 调用”变成可观察的本地记录。mitmproxy 负责截获真实 HTTP/HTTPS 流量,Python 插件负责结构化落盘,Node API 负责查询聚合,React 仪表盘负责把请求和响应直观展示出来。

对代码智能体调试来说,这种方式足够轻量,也足够直接。遇到“请求没发出去”“接口返回了但界面没反应”“模型输出被流式切片包住”“不知道 token 或耗时在哪里花掉”这类问题时,先把 agent 通过 127.0.0.1:8787 代理跑一遍,通常很快就能看到答案。

评论
0/100