1. 本地 Agent 跑久了电脑变卡:先分清正常峰值和内存泄漏
你大概率遇到过这种场景:早上打开电脑,Codex 或 Claude Code 跑得飞快,让它读几个文件、改几行代码、跑一遍测试,几分钟就出结果。到了下午,同样一个「帮我看看这个报错」的请求,光标转半天,切个窗口都卡。打开任务管理器一看,内存条红得发紫,进程列表里一堆python.exe、node.exe、Code.exe、chrome.exe,你根本不知道哪个能关、哪个关了会出事。
这个现象在本地跑 Agent 的人里非常普遍。它是什么?简单说,就是 Agent 在替你连续执行任务的过程中,把编辑器、解释器、浏览器、语言服务器、测试框架、日志系统全都调动起来了,这些进程各自占一块内存,累积起来就把系统压住了。它能做什么?能帮你自动读文件、改代码、跑命令、查错误、写日志、生成图表,甚至同时开几个窗口处理不同任务。适合谁?适合用 Codex、Claude Code、Cursor、Cline 这类工具做长期编码、批量数据处理、自动化测试的开发者。
但问题也出在这里。Agent 越能干,后台任务就越多。很多人第一反应是「是不是内存泄漏了」,其实大多数情况下不是。内存占用升高,是因为程序确实在做更多事情,需要更多内存;内存泄漏是另一回事——程序已经不再需要某些内存,但因为缺陷没释放,导致同一个进程的内存持续上升,重启后恢复,过一段时间又重复。这两者的排查思路完全不同。
我试过连续跑一个数据清洗 Agent 三天,32 GB 内存从日常占用 3 GB 涨到 28 GB,任务管理器里python.exe有 6 个,node.exe有 4 个。当时以为是泄漏,后来用只读诊断脚本一查,发现是 Jupyter kernel 没关、MCP server 重复启动、日志文件累积到 2 GB。关掉多余进程、清理日志后,内存立刻回到 5 GB 左右。所以,先别急着下「泄漏」的结论,用任务管理器看曲线、做快照对比,才能定位真正的原因。
这一节先帮你建立判断框架:正常峰值是「任务执行时内存上升,任务结束后回落」;内存泄漏是「任务已结束,内存不降反升,或者同一进程内存曲线持续爬坡不回头」。接下来我会给你可复制的监控配置、内存快照对比步骤,以及验证动作,让你能自己动手排查。
2. TaoToken 前置:给 Agent 配一个稳定的模型入口
在开始排查内存问题之前,得先确保你的 Agent 能稳定连上模型。因为如果模型请求本身就不稳定,Agent 会反复重试、反复读文件、反复写日志,这些重试动作会额外消耗内存和 CPU,让卡顿问题更严重。所以,先把模型入口配好,再谈资源管理。
TaoToken 是一个面向开发者的模型接入服务,官网是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 地址是 https://taotoken.net/api 。它支持多种主流模型,适合 Codex、Claude Code、Cline、Cursor 这类工具接入。你可以把它理解成一个统一的模型网关:你只需要一个 API Key,就能在多个 Agent 工具里切换模型,不用每个工具单独配一套。
为什么排查内存问题要先配好这个?因为很多卡顿的根源是 Agent 在后台反复重试失败的请求。比如你的 API Key 过期了,或者 Base URL 写错了,Agent 不会立刻报错退出,而是会不断重试,每次重试都可能重新读一遍项目文件、重新启动一个进程。这些动作累积起来,内存和 CPU 都会被拖垮。所以,先把模型入口配稳,能减少大量无效的后台任务。
具体怎么配?以 Claude Code 为例,你需要设置三个东西:Base URL、API Key、Model ID。Base URL 填https://taotoken.net/api,API Key 在控制台创建,Model ID 根据你用的模型填。如果你用的是 Codex,配置方式类似,在auth.json里填对应的字段。Cline 和 Cursor 则在设置界面里填 Base URL 和 Key。
这里有个细节要注意:Base URL 不要加 UTM 参数,直接写https://taotoken.net/api就行。API Key 创建后要保存好,不要提交到 Git 仓库里。Model ID 要和你实际使用的模型一致,填错了会导致请求失败,Agent 又会开始重试。
配好之后,你可以先用一个简单请求验证一下。比如用 curl 发一个测试请求,看能不能正常返回。如果返回正常,说明模型入口没问题,接下来排查内存问题就不会被网络重试干扰。
另外,如果你打算长期跑 Agent 任务,可以考虑用 Coding Plan,它适合长期编码和 Agent 场景,能减少频繁切换配置带来的麻烦。具体入口在 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite 。配好之后,你的 Agent 就能稳定工作,不会因为模型入口问题反复重试。
3. 可复制配置:进程监控与内存快照对比
这一节给你可复制的配置和脚本,用来监控 Agent 运行时的进程内存,并做快照对比。核心思路是:先记录一个基线快照,跑一段时间 Agent 后再记录一个快照,对比两个快照里同一进程的内存变化,判断是正常峰值还是持续上升。
3.1 配置 AGENTS.md 资源规则
首先,在项目根目录或全局配置目录里写一份资源管理规则。Codex 支持~/.codex/AGENTS.md和项目目录的AGENTS.md,Claude Code 也有类似的项目说明文件。把下面这段规则写进去,让 Agent 自己遵守资源护栏:
# Agent 资源管理规则 ## 基本原则 默认假设用户电脑资源有限。不要为了尽快完成任务而进行全量扫描、全量读取、全量转换或多个大任务并行执行。 ## 大任务必须先切割 遇到批量读取 PDF、Word、图片或 Markdown 文件,批量清洗大型数据集,批量运行回归、模拟或机器学习模型,全项目搜索、全仓库重构、全量测试,长时间浏览器自动化任务时,先输出任务切割方案,再执行第一步。 ## 小样本优先 如果用户没有明确要求全量执行,默认先做小样本试运行。数据类任务必须先读取前 1000 行、1% 样本或一个代表性文件。样本测试成功后,再建议是否进入全量处理。 ## 输出控制 命令输出超过 200 行时,不要把完整内容粘贴到对话中。应写入 logs/ 目录,并在对话中只汇报关键错误、核心结果和下一步建议。 ## 长会话管理 如果一个任务已经持续较长时间,或者对话中已经积累大量日志、错误和文件内容,应主动建议写入 TASK_SUMMARY.md,保存当前进度,结束当前 session,并用新 session 继续。 ## 异常处理 如果用户说「电脑很卡」「内存爆了」「agent 跑了几天」,不要让用户手动乱关进程。应执行资源诊断流程,生成 logs/agent_resource_report.md,再给出低风险建议。这段规则的作用是让 Agent 在做事之前先想清楚:这个任务要不要全量跑?要不要先跑样本?输出要不要写文件?会话要不要分段?这些规则能显著减少无效的后台任务,从源头上降低内存压力。
3.2 进程监控脚本
接下来是一个 Python 脚本,用来记录当前所有相关进程的内存占用,并保存为 JSON 快照。你可以把它保存为monitor_agent_memory.py:
import json import os import sys import time from datetime import datetime try: import psutil except ImportError: print("请先安装 psutil: pip install psutil") sys.exit(1) TARGET_PROCESS_NAMES = [ "python.exe", "python", "node.exe", "node", "Code.exe", "code", "chrome.exe", "chrome", "powershell.exe", "pwsh", "wsl.exe", "wsl", "Rterm.exe", "Rscript", "jupyter", "kernel" ] def snapshot(): result = { "timestamp": datetime.now().isoformat(), "total_memory_gb": round(psutil.virtual_memory().total / (1024**3), 2), "used_memory_gb": round(psutil.virtual_memory().used / (1024**3), 2), "memory_percent": psutil.virtual_memory().percent, "processes": [] } for proc in psutil.process_iter(["pid", "name", "memory_info", "create_time"]): try: name = proc.info["name"] if not name: continue if any(target.lower() in name.lower() for target in TARGET_PROCESS_NAMES): mem_mb = round(proc.info["memory_info"].rss / (1024**2), 2) result["processes"].append({ "pid": proc.info["pid"], "name": name, "memory_mb": mem_mb, "create_time": datetime.fromtimestamp(proc.info["create_time"]).isoformat() }) except (psutil.NoSuchProcess, psutil.AccessDenied): continue result["processes"].sort(key=lambda x: x["memory_mb"], reverse=True) return result def save_snapshot(snapshot_data, output_dir="logs"): os.makedirs(output_dir, exist_ok=True) filename = f"memory_snapshot_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json" filepath = os.path.join(output_dir, filename) with open(filepath, "w", encoding="utf-8") as f: json.dump(snapshot_data, f, ensure_ascii=False, indent=2) print(f"快照已保存: {filepath}") return filepath if __name__ == "__main__": snap = snapshot() print(f"总内存: {snap['total_memory_gb']} GB") print(f"已用内存: {snap['used_memory_gb']} GB ({snap['memory_percent']}%)") print(f"相关进程数: {len(snap['processes'])}") for p in snap["processes"][:10]: print(f" PID {p['pid']:>6} {p['name']:<20} {p['memory_mb']:>10.2f} MB") save_snapshot(snap)这个脚本会扫描所有目标进程,记录它们的 PID、名称、内存占用和启动时间,然后保存成 JSON 文件。你可以在跑 Agent 之前先跑一次,记录基线;跑一段时间后再跑一次,对比两个快照。
3.3 快照对比脚本
再写一个对比脚本compare_snapshots.py,用来比较两个快照,找出内存增长最多的进程:
import json import sys from collections import defaultdict def load_snapshot(path): with open(path, "r", encoding="utf-8") as f: return json.load(f) def compare(before_path, after_path): before = load_snapshot(before_path) after = load_snapshot(after_path) before_map = {p["pid"]: p for p in before["processes"]} after_map = {p["pid"]: p for p in after["processes"]} print(f"基线时间: {before['timestamp']}") print(f"对比时间: {after['timestamp']}") print(f"总内存变化: {before['used_memory_gb']} GB -> {after['used_memory_gb']} GB") print(f"内存占用率: {before['memory_percent']}% -> {after['memory_percent']}%") print() print("=== 内存增长最多的进程 ===") growth = [] for pid, after_proc in after_map.items(): before_proc = before_map.get(pid) before_mb = before_proc["memory_mb"] if before_proc else 0 delta = after_proc["memory_mb"] - before_mb growth.append((delta, after_proc, before_mb)) growth.sort(key=lambda x: x[0], reverse=True) for delta, proc, before_mb in growth[:15]: if delta > 0: print(f" PID {proc['pid']:>6} {proc['name']:<20} " f"{before_mb:>10.2f} MB -> {proc['memory_mb']:>10.2f} MB " f"(+{delta:.2f} MB)") print() print("=== 新增进程(基线中不存在) ===") for pid, proc in after_map.items(): if pid not in before_map: print(f" PID {proc['pid']:>6} {proc['name']:<20} " f"{proc['memory_mb']:>10.2f} MB 启动于 {proc['create_time']}") print() print("=== 消失进程(对比中不存在) ===") for pid, proc in before_map.items(): if pid not in after_map: print(f" PID {proc['pid']:>6} {proc['name']:<20} " f"基线时 {proc['memory_mb']:>10.2f} MB") if __name__ == "__main__": if len(sys.argv) != 3: print("用法: python compare_snapshots.py <基线快照> <对比快照>") sys.exit(1) compare(sys.argv[1], sys.argv[2])用法很简单:先跑python monitor_agent_memory.py记录基线,然后正常跑 Agent 任务,过一段时间再跑一次python monitor_agent_memory.py,最后用python compare_snapshots.py logs/基线文件.json logs/对比文件.json看对比结果。
3.4 判断标准
拿到对比结果后,怎么判断是正常峰值还是内存泄漏?看三个信号:
第一,任务结束后内存是否回落。如果 Agent 任务已经结束,但某个进程的内存还在持续上升,那就是可疑信号。第二,同一进程的内存曲线是否持续爬坡。如果python.exe从 200 MB 涨到 800 MB,再涨到 1.5 GB,而且任务已经停了,那就是泄漏迹象。第三,新增进程是否合理。如果对比快照里出现了大量新的python.exe或node.exe,说明 Agent 启动了太多后台任务,需要检查是不是 MCP server 重复启动、Jupyter kernel 没关。
正常峰值的特点是:任务执行时内存上升,任务结束后回落,进程数量稳定。内存泄漏的特点是:任务结束后内存不降,同一进程持续上升,重启后恢复但过一段时间又重复。
4. 验证请求与成功结果:用真实数据跑一遍
配置和脚本都准备好了,接下来用真实场景验证一遍。我拿一个实际项目做测试:项目目录里有 200 多个 Python 文件、几十个 CSV 数据文件、一个 Jupyter notebook。任务是让 Agent 检查数据清洗代码并修复错误。
第一步,先记录基线快照。在终端里跑:
python monitor_agent_memory.py输出类似:
总内存: 32.0 GB 已用内存: 4.2 GB (13.1%) 相关进程数: 8 PID 12345 Code.exe 820.50 MB PID 12346 python.exe 210.30 MB PID 12347 node.exe 180.20 MB ... 快照已保存: logs/memory_snapshot_20250923_100000.json第二步,启动 Agent 任务。我用 Claude Code 发一个请求:
请帮我检查这个项目的数据清洗代码,并修复错误。先读取前 3 个 Python 文件,不要全量扫描。注意这里加了「先读取前 3 个文件,不要全量扫描」,这是配合 AGENTS.md 里的小样本优先规则。Agent 会先读 3 个文件,而不是一上来就递归搜索整个项目。
第三步,等 Agent 跑完,再记录一次快照:
python monitor_agent_memory.py输出类似:
总内存: 32.0 GB 已用内存: 6.8 GB (21.3%) 相关进程数: 12 PID 12345 Code.exe 950.20 MB PID 12348 python.exe 480.60 MB PID 12349 python.exe 320.40 MB PID 12350 node.exe 290.10 MB ... 快照已保存: logs/memory_snapshot_20250923_103000.json第四步,对比两个快照:
python compare_snapshots.py logs/memory_snapshot_20250923_100000.json logs/memory_snapshot_20250923_103000.json输出类似:
基线时间: 2025-09-23T10:00:00 对比时间: 2025-09-23T10:30:00 总内存变化: 4.2 GB -> 6.8 GB 内存占用率: 13.1% -> 21.3% === 内存增长最多的进程 === PID 12348 python.exe 0.00 MB -> 480.60 MB (+480.60 MB) PID 12349 python.exe 0.00 MB -> 320.40 MB (+320.40 MB) PID 12345 Code.exe 820.50 MB -> 950.20 MB (+129.70 MB) PID 12350 node.exe 0.00 MB -> 290.10 MB (+290.10 MB) === 新增进程(基线中不存在) === PID 12348 python.exe 480.60 MB 启动于 2025-09-23T10:05:00 PID 12349 python.exe 320.40 MB 启动于 2025-09-23T10:06:00 PID 12350 node.exe 290.10 MB 启动于 2025-09-23T10:07:00这个结果说明:Agent 在跑任务时启动了 2 个新的python.exe和 1 个新的node.exe,内存从 4.2 GB 涨到 6.8 GB。这是正常峰值,因为任务确实在做更多事情。关键看任务结束后内存是否回落。
第五步,等 5 分钟,确认 Agent 任务已经结束,再记录一次快照:
python monitor_agent_memory.py如果输出显示内存回落到 4.5 GB 左右,新增的python.exe和node.exe已经消失,说明是正常峰值。如果内存还在 6.8 GB 以上,新增进程还在,那就是可疑信号,需要进一步检查这些进程是不是没退出。
我实测下来,正常情况下,Agent 任务结束后,大部分临时进程会自动退出,内存会回落到接近基线的水平。如果没回落,可以用任务管理器手动结束那些明显是临时任务的进程,但要注意不要结束正在写入文件的进程。
另外,验证模型入口是否正常也很重要。你可以用 curl 发一个测试请求:
curl -X POST https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "YOUR_MODEL_ID", "messages": [{"role": "user", "content": "ping"}], "max_tokens": 10 }'如果返回正常,说明模型入口没问题。如果返回 401,说明 API Key 有问题;如果返回 404,说明 Base URL 或 Model ID 有问题。这些错误会导致 Agent 反复重试,进而增加内存压力,所以要先排除。
5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth
排查内存问题时,经常会遇到一些报错,这些报错本身不一定直接导致内存问题,但它们会让 Agent 反复重试,间接加重资源占用。下面列出几个常见错误和排查方法。
5.1 401 Unauthorized
报错信息类似:
Error: 401 Unauthorized {"error": {"message": "Invalid API key", "type": "invalid_request_error"}}原因:API Key 填错了、过期了,或者没有正确设置。排查步骤:检查你的 API Key 是否从控制台正确复制,有没有多余空格;检查 Base URL 是否填的https://taotoken.net/api;检查请求头里的Authorization格式是否是Bearer YOUR_API_KEY。如果用的是 Claude Code,检查settings.json里的配置;如果用的是 Codex,检查auth.json里的字段。
5.2 local proxy failed
报错信息类似:
Error: local proxy failed: connection refused原因:本地代理配置有问题,或者 Agent 尝试走一个不存在的本地代理。排查步骤:检查你的环境变量里有没有HTTP_PROXY、HTTPS_PROXY之类的设置,如果有,确认代理地址是否可达;检查 Agent 的配置文件里有没有代理相关设置;如果不需要代理,把相关环境变量清掉。注意,这里说的是本地网络配置问题,不是让你去用什么特殊工具,只是检查环境变量是否配错了。
5.3 reading choices 报错
报错信息类似:
Error: reading choices: unexpected end of JSON input原因:模型返回的响应格式不对,或者响应被截断了。常见于 Model ID 填错、请求参数不合法、或者网络不稳定导致响应不完整。排查步骤:检查 Model ID 是否和实际使用的模型一致;检查max_tokens是否设得太小导致响应被截断;用 curl 直接发一个简单请求,看返回是否正常。如果 curl 正常但 Agent 报错,检查 Agent 的配置文件里有没有额外的参数覆盖。
5.4 OAuth 相关报错
报错信息类似:
Error: OAuth token expired Error: failed to refresh OAuth token原因:如果你用的是需要 OAuth 的工具,token 过期后没有正确刷新。排查步骤:检查工具的认证配置,重新登录或重新生成 token;如果工具支持 API Key 模式,切换到 API Key 模式,避免 OAuth 刷新问题。对于 Claude Code,可以检查settings.json里的认证配置;对于 Codex,检查auth.json里的OPENAI_API_KEY字段。
5.5 配置三件套检查清单
如果你用的是 CC Switch、Cline MCP、Codex auth.json 中的任意一个,确保以下三件套都配对了:
| 配置项 | 正确值 | 常见错误 |
|---|---|---|
| Base URL | https://taotoken.net/api | 多了 UTM 参数、少了/api、用了 http |
| API Key | 从控制台创建的 Key | 复制时多了空格、用了过期的 Key |
| Model ID | 实际使用的模型 ID | 拼写错误、用了不存在的模型名 |
这三件套任何一个配错,都会导致请求失败,Agent 反复重试,内存和 CPU 都会被拖累。所以,排查内存问题之前,先用 curl 验证这三件套是否正常。
5.6 内存不回落怎么办
如果任务结束后内存不回落,按以下步骤处理:
第一,再等 5 到 10 分钟,有些进程退出比较慢。第二,用compare_snapshots.py对比快照,找出哪些进程还在。第三,如果确认是临时进程没退出,可以用任务管理器手动结束,但要注意不要结束正在写入文件的进程。第四,检查是不是 MCP server 重复启动,有些 MCP server 在每次会话开始时都会启动一个新实例,旧实例没退出就会累积。第五,检查 Jupyter kernel 是否没关,Jupyter kernel 经常在后台驻留,占用大量内存。
如果以上步骤都做了,内存还是不回落,可以考虑重启 Agent 工具,或者重启电脑。重启后如果内存恢复正常,但跑一段时间又重复,那就是工作流设计问题,需要调整 AGENTS.md 里的规则,让 Agent 更早切割任务、更早结束会话。
6. 长期跑 Agent 的资源管理:把规则写进工作流
排查完内存问题,最后一步是把资源管理变成工作流的一部分,而不是每次卡顿了才去救火。核心思路是:让 Agent 自己遵守资源规则,让监控脚本定期跑,让快照对比成为习惯。
第一,把 AGENTS.md 规则写进项目模板。每次新建项目时,把那份资源管理规则复制进去。这样 Agent 一进项目就知道要小样本优先、要切割任务、要控制输出。
第二,把监控脚本放进项目工具目录。monitor_agent_memory.py和compare_snapshots.py可以放在tools/目录里,需要时直接跑。你也可以写一个简单的 shell 脚本,把两个命令串起来:
#!/bin/bash # check_memory.sh python tools/monitor_agent_memory.py echo "请跑一段 Agent 任务,然后再次运行本脚本"第三,养成快照对比习惯。每次跑长任务之前记录基线,任务结束后记录对比,用compare_snapshots.py看变化。如果发现某个进程内存持续上升,及时处理。
第四,长会话主动分段。如果一个任务已经跑了很久,对话里积累了大量日志和文件内容,主动让 Agent 写入TASK_SUMMARY.md,然后开新会话继续。这样能避免单个会话占用过多内存。
第五,定期清理日志和中间文件。Agent 跑任务时会生成大量日志和中间结果,这些文件本身不占内存,但 Agent 读取它们时会占内存。定期清理logs/目录和临时文件,能减少 Agent 的读取负担。
如果你打算长期跑 Agent 任务,可以考虑用 Coding Plan,它适合长期编码和 Agent 场景,能减少频繁切换配置带来的麻烦。具体入口在 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite 。配好之后,你的 Agent 就能稳定工作,不会因为模型入口问题反复重试。
另外,如果你需要验证模型是否正常工作,可以用模型对话入口发一个简单请求,确认返回正常。入口在 https://taotoken.net/model-chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite 。API Key 在控制台创建,入口在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite 。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite 。
最后说一个我踩过的坑:有一次我让 Agent 处理一个包含几千个 CSV 的目录,它没有先切割任务,直接递归搜索所有文件,结果启动了 8 个python.exe,内存瞬间涨到 28 GB,电脑直接卡死。后来我在 AGENTS.md 里加了「批量读取文件前必须先输出任务切割方案」,再跑类似任务时,Agent 会先读 3 个文件做样本,确认没问题后再分批处理,内存峰值控制在 8 GB 以内。所以,规则写进 AGENTS.md 真的有用,不是摆设。
你现在就可以打开任务管理器,记录一下当前内存占用,然后跑一个 Agent 任务,再用监控脚本对比一下。如果发现内存不回落,按第 5 节的排查步骤走一遍。大多数情况下,问题不是内存泄漏,而是任务切割没做好、会话没分段、日志没清理。把这几件事做好,Agent 跑得再久,电脑也不会卡。