文章总结: 本文分享安全运营Agent实践经验,作者从ClaudeCodeAgentSDK转向自建轻量级框架NanoBot,解决原方案结果不收敛、约束不住、黑盒不可控等问题。通过代码层面约束(工具白名单、执行限制、轮次控制、参数校验)实现可控性,Skill机制承载领域知识。核心结论:能用代码管住的事别指望Prompt,可控性比强大黑盒更重要。 综合评分: 89 文章分类: 安全运营,AI安全,安全建设,实战经验,安全工具
安全运营 Agent 实践:从”求它别乱来”到”它想乱也乱不了”
原创
Purpleroc Purpleroc
Purpleroc的札记
2026年3月8日 09:04 广东
#
上一篇文章《安全运营 Agent 落地:让 LLM 亲手把自己「炼」成规则》里,我基于 claude-code-agent-sdk 借助skill实现了 secops-analyzer,不过结果不收敛、约束不住。这篇主要受OpenClaw的启发,最后用 NanoBot 替换了 Claude Code Agent。
先回顾一下坑在哪
简单说下背景。我在做安全告警的自动化分析——业务上报的报错日志,经规则引擎筛选后被标记为”疑似 Web 攻击”。这些日志需要进一步分析:是真入侵导致还是扫描器瞎探测?是暴力破解还是正常用户输错密码?风险多高,要不要转人工?
之前用 claude-code-agent-sdk 做这个事。上篇文章里提了两个核心问题:
- 1. 结果不收敛。日志数据明明够判定了,它还要去查威胁情报、查历史告警,越查越散,结论反而更模糊。
- 2. 约束不住它。Skills 里写了「不许创建脚本」,加粗了、加 emoji 了,它还是自己创建脚本来”辅助分析”。
那之后我也不是直接放弃的,还挣扎过。
换掉之前,我试过优化
我想的是,Claude Code 处理起来慢,那我能不能减少它需要处理的量?
于是加了一层预处理逻辑:先用普通的 LLM 调用(不走 Agent,就是单轮对话)做一次快速分类——把日志丢给模型,让它判断是否有 Web 攻击。只有判定为”有攻击”的,才交给 Claude Code Agent 做深度分析。
逻辑上没问题,也确实过滤掉了一批。但剩下的丢给 Claude Code,还是慢。一个事件分析几分钟甚至十几分钟,中间不知道它在想什么、调了什么。偶尔还会卡在某个工具调用上半天不动。
而且加了预处理之后,整个系统变成了两层——先一次 LLM 分类,再一次 Agent 深度分析。代码复杂度上去了,但体验并没有本质改善。
慢不是唯一的问题。更本质的问题是不可控。
放弃的真正原因
翻来覆去想了想,不只是”慢”的问题,而是一连串的不舒服:
创建文件不可控。 它动不动就在工作目录下创建 Python 脚本、Shell 脚本、临时文件来辅助分析。跑一个事件还好,批量跑几百个之后,工作目录一片狼藉。关键是这些脚本跑了什么你也不太确定,副作用不可控。
完全黑盒,没法优化。 你看不到它每一步在想什么、为什么选择调这个工具。想优化某类事件的分析效果,得先搞清楚它在哪一步判断错了——但在黑盒模式下,你只能看到最终结果,中间过程全靠翻日志猜。日志量一大,这件事本身就变成了另一个需要”分析”的工作。
结果不稳定。 同一个事件跑两次,有时候给出不同结论。批量处理场景下不太能接受。
容易卡住。 不知道是工具调用卡住,还是LLM卡住,还是我没设置最大轮次,它什么时候结束全看心情。偶尔进入某种重试循环出不来,你只能等着或者手动 kill。
环境依赖重。 Node.js、Claude CLI、权限配置……对一个本质上”读数据 → 分析 → 写结论”的场景来说,太重了。
模型依赖。 不方便快速配置、使用可用的LLM API。
这就是上篇文章为啥写:能用代码管住的事,别指望 Prompt。 Claude Code 的工具调用、文件操作、执行路径,全靠 Prompt 约束。说白了就是在自然语言层面”拜托”它别乱来,它有时候听有时候不听。
怎么换的
决定换掉之后,需要一个替代品。我的要求其实不多:
- 1. 可控——可执行的命令列表我能定,每一步做了什么我能看到,
- 最大思考轮次我能设
- 2. 轻量——纯 Python,不依赖外部运行时
- 3. 支持 Skill——能加载领域知识来引导分析
- 4. 够用就好——不需要它能写代码、能上网
一细想,嗯,我是不是只要找个能支持 skill 的 Agent 就好了,比如 OpenClaw。恰好那天刷视频,作者吐槽 OpenClaw 太臃肿,夸 NanoBot 只用了几百行核心代码就实现了类似效果。出于好奇去看了它的实现,发现核心循环真的很简单,原理上来说:
LLM 调用 → 有 tool_calls?→ 执行工具 → 结果追加到上下文 → 继续调 LLM
没有 tool_calls → 返回最终结果
于是,让 Cursor直接帮我完成了整个 agent 的替换,顺带,我还给 tools 加上了urldecode、base64decode 等,方便做一些简单的编解码。从 NanoBot 抽离后,整个 agent 模块长这样:
agent/
├── loop.py # 核心循环,114 行
├── context.py # 上下文管理,53 行
├── provider.py # LLM 适配,68 行
├── skill_loader.py # Skill 加载,72 行
└── tools/
├── base.py # 工具基类 + 参数校验,94 行
├── registry.py # 工具注册表,60 行
├── filesystem.py# 文件读取,236 行
├── shell.py # 脚本执行(受限),155 行
└── decoders.py # 编解码工具,225 行
总共不到1000行。
代码层面的约束:上篇文章的教训
上篇文章里我说”约束不住 Claude Code”,这次吸取教训了。所有约束都在代码层面做,不靠 Prompt。
工具白名单。 Agent 能用的工具只有我显式注册的这几个:读文件(只能读 Skill 目录下的参考资料)、执行脚本(只能跑预先写好的 .py 脚本)、编解码(URL/Base64/HTML/Hex/Unicode)。它没有”写文件”的工具,没有”执行任意 Shell 命令”的工具。想创建辅助脚本?对不起,没这个能力。
脚本执行受限。exec 工具做了严格的路径校验——只允许运行 scripts/ 目录下的 .py 文件,用 subprocess_exec 而不是 shell 模式执行,路径解析后还会检查是否真的在允许目录内,防止 ../../ 之类的绕过。超时也有硬限制,120 秒到了直接 kill。
最大轮次。max_turns=15,15 轮之内必须给结论。不管你分析得怎么样,到了就停。不会再出现”跑了十几分钟还没完”的情况。
参数校验。 每个工具调用前都会验证参数格式,类型不对、缺必填字段直接报错返回,不会带着错误的参数去执行。
这些约束 LLM 绕不过去,因为它接触不到约束逻辑本身——它只能在我给的工具范围内行动。
这就是上篇文章说的”能用代码管住的事,别指望 Prompt”的具体落地。
切换后的效果
不会卡住了。 这是最直观的感受。以前偶尔一个事件分析十几分钟最后还没结果,现在有 max_turns 兜底,顶多打满。
每一步都清楚。 日志里打得明明白白——第几轮、调了什么工具、传了什么参数、拿到什么结果。想优化某个场景的分析效果,直接定位到具体的某一轮某个判断就行,不用在一大堆黑盒日志里翻来翻去。
[Agent Turn 1/15]
[Agent] 工具调用: read_file({"path": "references/web_attacks.md"})
[Agent Turn 2/15]
[Agent] 工具调用: exec({"command": "python scripts/query_waf_logs.py --mode attacker --ips 1.2.3.4 ..."})
[Agent Turn 3/15]
[Agent] 完成,最终响应 1842 字符
三轮结束,干净利落。
LLM 换了,成本降了。 先前想要切换,得找内部二开claude code的小伙伴,还不太好实现。现在随意切换,换到 DeepSeek,单次分析的 token 消耗也降了不少——没有那些自创脚本的上下文膨胀了。分析质量也没明显下降,因为真正驱动分析质量的是 Skill 里的领域知识,不是模型的通用能力。
预处理层也简化了。 之前在 orchestrator 里 hardcode 了大量分析逻辑——攻击模式检测、WAF 关联策略、响应安全信号检测——加起来好几百行。这些东西本质上是安全分析的领域知识,不应该写在数据预处理层。重构之后,orchestrator 只做数据搬运:取数据、精简格式、调 Agent、解析结果、存数据库。分析判断全部在 Skill 里,由 Agent 来做。
整个 orchestrator(analyzer.py)只有 250 行,干的事情一目了然。
Skill 才是灵魂
这个轻量 Agent 本身没什么技术含量,真的就是一个循环。它的”智慧”全在 Skill 里。
SKILL.md 大概 500 行,定义了分析的完整框架:
- • 分类流程:先看 payload 有没有 Web 攻击特征,再看行为统计有没有业务攻击模式,都没有就是正常报错
- • 判定标准:什么样的 payload 算通用探测(低危,AI 自动关闭)、什么算定向攻击(高危,转人工)
- • 知识库:各类攻击的特征模式放在
references/目录下,Agent 可以read_file按需读取 - • 工具使用指引:什么时候需要查 WAF 日志、怎么查、查完怎么判断
- • 输出规范:严格的 JSON 格式,每个字段的取值范围和含义
以前那些写在代码里的分析逻辑,现在全在 Markdown 里。调整分析策略?改 Markdown。新增一种攻击类型的识别模式?改 Markdown。不用动代码,不用发版。
随便聊聊:从黑盒到可控的一点思考
换完之后,我回头想了想这件事,觉得还挺有意思的。
前两年开始就写过一些关于 LLM 赋能安全运营的思考,随着 LLM 的发展,现在回看,一路走来大家应该都或多或少经过这么些阶段:最开始的时候,用 LLM 做事件解读,就是单轮对话;然后 MCP 火了,大家开始写各种工具服务一股脑丢给 Agent 让它自主决策;接着发现太不可控了,又开始在代码里 hardcode 流程、引导性 Prompt,从通用往专用收;ReAct 也火过一阵,给个目标让 Agent 自己 Think-Act-Observe 循环,理论很优雅但实际受限于上下文和指令遵从性;到现在 Skill 的概念起来了,核心思路是「需要什么知识就加载什么,需要什么工具就注册什么」。
我不是说哪个方案一定比哪个好。但至少在我这个场景里,Skill 模式确实比较合适——比完全开放的 Agent 可控,比 hardcode 所有流程灵活。你不需要在代码里穷举所有分析分支,只需要把领域知识描述清楚,Agent 在这个框架内灵活应对。
还有一个有意思的点。传统安全运营里有个 SOAR 的概念(Security Orchestration, Automation and Response),走的是 Workflow 路线——提前定义好所有流程和分支。我之前觉得 n8n 那种拖拽式的工具挺适合做 SOAR 的,而且还能结合LLM来大量的处理日志数据,做分类、场景定义等。但后来逐渐意识到,安全事件千变万化,你没法穷举所有场景。这也是我在选型时纠结的一个点:用 ReAct 吧,我定义不完所有可能的状态;用 Workflow 吧,覆盖不了所有变体。
Skill 的模式好像提供了一个折中——你不需要穷举所有分支,只需要把领域知识描述清楚,让 Agent 在这个知识框架内灵活应对。遇到新场景?写一个新 Skill 就行。复杂场景?大 Skill 里嵌套小 Skill。
当然这还是很粗糙的想法,纯粹随想。但方向上我觉得值得继续试。
总结
回到最初的问题,为什么换掉 Claude Code SDK?上篇文章里我自己总结过一句话:
能用代码管住的事,别指望 Prompt。
这次做的事情,就是把这句话落地了。
不是 Claude Code 不好,而是在我的场景里,”够用且可控”比”强大但黑盒”更实际。一千来行的轻量 Agent + 500 行的 Skill,替代了一整套 Claude Code Agent SDK。效率没降,可控性大幅提升,成本也降了。
上篇留了个坑,这篇算是填上了。
这是《安全运营 Agent 落地:让 LLM 亲手把自己「炼」成规则》的续篇。如果你也在做类似的事情,欢迎交流。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:Purpleroc的札记 Purpleroc Purpleroc《安全运营 Agent 实践:从”求它别乱来”到”它想乱也乱不了”》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论