[{"content":"","date":null,"permalink":"https://tttjhgan.top/categories/","section":"Categories","summary":"","title":"Categories"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/congo/","section":"Tags","summary":"","title":"Congo"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/hugo/","section":"Tags","summary":"","title":"Hugo"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/seo/","section":"Tags","summary":"","title":"SEO"},{"content":"","date":null,"permalink":"https://tttjhgan.top/series/","section":"Series","summary":"","title":"Series"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/","section":"Tags","summary":"","title":"Tags"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/tailwind-css/","section":"Tags","summary":"","title":"Tailwind CSS"},{"content":"Hello world.\n","date":"2026-07-31","permalink":"https://tttjhgan.top/posts/test-post/","section":"技术文章","summary":"","title":"Test Post"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E6%A0%87%E7%AD%BE%E4%BA%91/","section":"Tags","summary":"","title":"标签云"},{"content":"这个系列记录了个人博客的整个迭代过程：\n视觉风格的统一与优化 分类体系与信息架构设计 SEO 与性能优化 功能迭代与实践心得 本系列文章：\n","date":null,"permalink":"https://tttjhgan.top/series/%E5%8D%9A%E5%AE%A2%E6%8C%81%E7%BB%AD%E4%BC%98%E5%8C%96%E7%B3%BB%E5%88%97/","section":"Series","summary":"","title":"博客持续优化系列"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E5%8D%9A%E5%AE%A2%E4%BC%98%E5%8C%96/","section":"Tags","summary":"","title":"博客优化"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E5%88%86%E7%B1%BB%E4%BD%93%E7%B3%BB/","section":"Tags","summary":"","title":"分类体系"},{"content":"这两天对个人博客做了一次完整的全站优化，从 首页视觉风格到 分类体系，踩了一些坑，也想清楚了很多事情。记录下来，给自己做个备忘，也给同样用 Hugo 搭博客的朋友做个参考。\n为什么要做这次优化 #之前博客的状态其实是能用的，但总觉得少了点什么：\n首页几个区块的交互风格不统一，有的是卡片有的是纯链接，视觉上有点乱 文章分类几乎没有，读者看完一篇找不到相关的内容 没有标签体系，内容沉淀不下来 这次的目标很简单：让首页风格彻底统一，搭好完整的内容分类架构。\n关于设计的思考：少即是多 #一开始我走了弯路，把首页做成了很重的卡片风格，每个链接都加边框、加阴影、加 hover 动效。结果看起来特别拥挤，失去了 Congo 主题原本那种清爽极简的感觉。\n后来全部回滚，做了一个决定：全站只保留一种链接 hover 效果——纯文字变色。\n\u0026lt;a class=\u0026#34;group\u0026#34; href=\u0026#34;...\u0026#34;\u0026gt; \u0026lt;strong class=\u0026#34;group-hover:text-primary-600 dark:group-hover:text-primary-400\u0026#34;\u0026gt;标题\u0026lt;/strong\u0026gt; \u0026lt;span class=\u0026#34;text-neutral-600 dark:text-neutral-400\u0026#34;\u0026gt;描述\u0026lt;/span\u0026gt; \u0026lt;/a\u0026gt; 没有背景、没有边框、没有阴影、没有位移，就这么简单。\n效果反而出奇的好。整个页面干净了很多，阅读时注意力不会被多余的动效分散。\n设计最重要的不是加什么，而是敢于删什么。\n三层分类体系搭建 #这是这次优化最核心的部分。用 Hugo 原生的 Taxonomy 系统，搭了三层分类：\n层级 名称 粒度 示例 L1 categories（分类） 粗 技术研究 / 量化研究 / 随笔 L2 tags（标签） 细 Hugo / 安全 / Agent / 量化 L3 series（系列） 系列化 SecurityClaw 学习系列 / 博客优化系列 为什么要分三层？因为人的浏览习惯是不一样的：\n有人喜欢按分类逛：想看看你都做过哪些方向的研究 有人喜欢按标签找：就想看所有跟\u0026quot;安全\u0026quot;相关的文章 有人喜欢追系列：想把 SecurityClaw 整个系列按顺序看完 三层覆盖了所有浏览场景。\n标签云 页面 #标签云不是随便列出来就完事了，做了一个很重要的细节：标签字体大小随文章数量动态变化。\n{{ $count := .Count }} {{ $size := add 0.8 (mul (div (float $count) 10) 0.8) }} {{ if gt $size 2.0 }}{{ $size = 2.0 }}{{ end }} 这样写得多的标签自然会更大更醒目，读者一眼就能看到你主要在写什么，相当于自动生成的\u0026quot;技术画像\u0026quot;。\n这次调整的文件清单 #做了这些改动，都是无侵入式的：\nconfig/_default/hugo.toml # 新增三层 taxonomy 配置 config/_default/menus.zh-CN.toml # 导航栏加标签云和分类 layouts/_default/terms.html # 标签云/分类列表模板 layouts/_default/term.html # 单个标签/分类的文章列表页面 核心原则：尽量不修改主题原有文件，全部用 Hugo 的模板覆盖机制来做。以后 Congo 主题升级时不会冲突。\n几个重要的原则总结 #这次折腾下来，有几个感受特别深：\n1. 最小改动原则 #不要一上来就大改主题的核心文件。先想清楚是不是真的需要改，能不能用更轻的方式实现。\n一开始我想改 Congo 的 list.html 来显示标签，后来发现主题本身就内置了 list.showTaxonomies 配置。一行配置就能解决的事，差点写了一百行模板。\n2. 视觉一致性比\u0026quot;好看\u0026quot;更重要 #一个到处都是不同 hover 效果的\u0026quot;好看\u0026quot;页面，远不如所有交互完全统一的普通页面体验好。\n用户不需要学习三种不同的点击反馈，一种就够了。\n3. 分类体系要提前搭 #博客刚起步时文章少，分类的价值看不出来。等到写了几十上百篇，内容就会开始乱。\n提前把三层分类搭好，写文章时顺手加几个标签，积累半年回头看就是一笔巨大的财富。\n接下来的计划 #这个博客的优化还没完，接下来想做的几件事：\n文章底部加相关文章推荐，基于标签匹配 系列文章的前后导航（上一篇 / 下一篇） 更清晰的专题落地页 全站搜索 博客是个慢工程，不用追求一次做完美。先把骨架搭好，慢慢迭代。\n写技术博客这件事本身，就是最好的持续学习。\n","date":"2026-07-31","permalink":"https://tttjhgan.top/posts/blog-optimization-2026/","section":"技术文章","summary":"\u003cp\u003e这两天对个人博客做了一次完整的全站优化，从\n      \n    \u003ca href=\"https://tttjhgan.top/\"\u003e首页\u003c/a\u003e视觉风格到\n      \n    \u003ca href=\"https://tttjhgan.top/categories/\"\u003e分类体系\u003c/a\u003e，踩了一些坑，也想清楚了很多事情。记录下来，给自己做个备忘，也给同样用 Hugo 搭博客的朋友做个参考。\u003c/p\u003e","title":"个人博客全站优化记：从视觉风格到分类体系的完整升级"},{"content":"这两天对个人博客做了一次完整的全站优化，从 首页视觉风格到 分类体系，踩了一些坑，也想清楚了很多事情。记录下来，给自己做个备忘，也给同样用 Hugo 搭博客的朋友做个参考。\n为什么要做这次优化 #之前博客的状态其实是能用的，但总觉得少了点什么：\n首页几个区块的交互风格不统一，有的是卡片有的是纯链接，视觉上有点乱 文章分类几乎没有，读者看完一篇找不到相关的内容 没有标签体系，内容沉淀不下来 这次的目标很简单：让首页风格彻底统一，搭好完整的内容分类架构。\n关于设计的思考：少即是多 #一开始我走了弯路，把首页做成了很重的卡片风格，每个链接都加边框、加阴影、加 hover 动效。结果看起来特别拥挤，失去了 Congo 主题原本那种清爽极简的感觉。\n后来全部回滚，做了一个决定：全站只保留一种链接 hover 效果——纯文字变色。\n\u0026lt;a class=\u0026#34;group\u0026#34; href=\u0026#34;...\u0026#34;\u0026gt; \u0026lt;strong class=\u0026#34;group-hover:text-primary-600 dark:group-hover:text-primary-400\u0026#34;\u0026gt;标题\u0026lt;/strong\u0026gt; \u0026lt;span class=\u0026#34;text-neutral-600 dark:text-neutral-400\u0026#34;\u0026gt;描述\u0026lt;/span\u0026gt; \u0026lt;/a\u0026gt; 没有背景、没有边框、没有阴影、没有位移，就这么简单。\n效果反而出奇的好。整个页面干净了很多，阅读时注意力不会被多余的动效分散。\n设计最重要的不是加什么，而是敢于删什么。\n三层分类体系搭建 #这是这次优化最核心的部分。用 Hugo 原生的 Taxonomy 系统，搭了三层分类：\n层级 名称 粒度 示例 L1 categories（分类） 粗 技术研究 / 量化研究 / 随笔 L2 tags（标签） 细 Hugo / 安全 / Agent / 量化 L3 series（系列） 系列化 SecurityClaw 学习系列 / 博客优化系列 为什么要分三层？因为人的浏览习惯是不一样的：\n有人喜欢按分类逛：想看看你都做过哪些方向的研究 有人喜欢按标签找：就想看所有跟\u0026quot;安全\u0026quot;相关的文章 有人喜欢追系列：想把 SecurityClaw 整个系列按顺序看完 三层覆盖了所有浏览场景。\n标签云 页面 #标签云不是随便列出来就完事了，做了一个很重要的细节：标签字体大小随文章数量动态变化。\n{{ $count := .Count }} {{ $size := add 0.8 (mul (div (float $count) 10) 0.8) }} {{ if gt $size 2.0 }}{{ $size = 2.0 }}{{ end }} 这样写得多的标签自然会更大更醒目，读者一眼就能看到你主要在写什么，相当于自动生成的\u0026quot;技术画像\u0026quot;。\n这次调整的文件清单 #做了这些改动，都是无侵入式的：\nconfig/_default/hugo.toml # 新增三层 taxonomy 配置 config/_default/menus.zh-CN.toml # 导航栏加标签云和分类 layouts/_default/terms.html # 标签云/分类列表模板 layouts/_default/term.html # 单个标签/分类的文章列表页面 核心原则：尽量不修改主题原有文件，全部用 Hugo 的模板覆盖机制来做。以后 Congo 主题升级时不会冲突。\n几个重要的原则总结 #这次折腾下来，有几个感受特别深：\n1. 最小改动原则 #不要一上来就大改主题的核心文件。先想清楚是不是真的需要改，能不能用更轻的方式实现。\n一开始我想改 Congo 的 list.html 来显示标签，后来发现主题本身就内置了 list.showTaxonomies 配置。一行配置就能解决的事，差点写了一百行模板。\n2. 视觉一致性比\u0026quot;好看\u0026quot;更重要 #一个到处都是不同 hover 效果的\u0026quot;好看\u0026quot;页面，远不如所有交互完全统一的普通页面体验好。\n用户不需要学习三种不同的点击反馈，一种就够了。\n3. 分类体系要提前搭 #博客刚起步时文章少，分类的价值看不出来。等到写了几十上百篇，内容就会开始乱。\n提前把三层分类搭好，写文章时顺手加几个标签，积累半年回头看就是一笔巨大的财富。\n接下来的计划 #这个博客的优化还没完，接下来想做的几件事：\n文章底部加相关文章推荐，基于标签匹配 系列文章的前后导航（上一篇 / 下一篇） 更清晰的专题落地页 全站搜索 博客是个慢工程，不用追求一次做完美。先把骨架搭好，慢慢迭代。\n写技术博客这件事本身，就是最好的持续学习。\n","date":"2026-07-31","permalink":"https://tttjhgan.top/writing/blog-optimization-2026/","section":"随笔","summary":"\u003cp\u003e这两天对个人博客做了一次完整的全站优化，从\n      \n    \u003ca href=\"https://tttjhgan.top/\"\u003e首页\u003c/a\u003e视觉风格到\n      \n    \u003ca href=\"https://tttjhgan.top/categories/\"\u003e分类体系\u003c/a\u003e，踩了一些坑，也想清楚了很多事情。记录下来，给自己做个备忘，也给同样用 Hugo 搭博客的朋友做个参考。\u003c/p\u003e","title":"个人博客全站优化记：从视觉风格到分类体系的完整升级"},{"content":"","date":null,"permalink":"https://tttjhgan.top/posts/","section":"技术文章","summary":"","title":"技术文章"},{"content":"","date":null,"permalink":"https://tttjhgan.top/categories/%E7%BD%91%E7%AB%99%E5%BB%BA%E8%AE%BE/","section":"Categories","summary":"","title":"随笔"},{"content":" 一些零散的观察、思考和阶段性记录，不适合塞进技术栏目，但值得在未来回看。 ","date":null,"permalink":"https://tttjhgan.top/writing/","section":"随笔","summary":"","title":"随笔"},{"content":" 在 AI 安全、量化研究与持续学习之间建立连接，记录值得反复检验的工程判断。 项目 # 🛡️ SecurityClaw 学习笔记 18 篇系列文章\n从零拆解开源 SOC Agent 框架：RAG 引擎、LangGraph 监督循环、模块化技能系统。\n📊 量化交易系统 模拟盘 · 双策略 · 每日自动\n18 只科技/AI 股，趋势 MA + 超跌反弹 Bounce 双策略，¥100,000 模拟盘每日自动交易与报告。\n🧩 OpenSkills 仓库 ↗ Codex / Hermes 技能集合\nhermesagent-publish-notes 等自动化技能，打通本地 AI Agent 与线上博客的发布流水线。\n📚 持续学习 Rust · AI 工程 · 安全\nRust + AI 工程的系统学习路径，包括学习计划、资源清单与阶段性复盘。\n研究主线 # AI 安全 Agent 安全、LangGraph 工作流、SOC 自动化、RAG 向量记忆。\n量化研究 策略开发、回测系统、模拟盘基础设施。18 只科技/AI 股每日自动运行双策略。\n持续学习 Rust、AI 工程、安全研究。围绕源码和实验的系统性学习，而非教程消费。\n","date":null,"permalink":"https://tttjhgan.top/","section":"田稼禾的个人网站","summary":"","title":"田稼禾的个人网站"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E7%BD%91%E7%AB%99%E6%9E%B6%E6%9E%84/","section":"Tags","summary":"","title":"网站架构"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/caddy/","section":"Tags","summary":"","title":"Caddy"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E5%8D%9A%E5%AE%A2/","section":"Tags","summary":"","title":"博客"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E6%9C%8D%E5%8A%A1%E5%99%A8/","section":"Tags","summary":"","title":"服务器"},{"content":"系列总览 #这是一个关于个人网站从 0 到 1，再从 1 到持续优化的完整记录。从腾讯云服务器选型开始，到 Hugo 博客搭建、Caddy 反向代理、HTTPS 证书、自动化发布流水线、安全加固，再到视觉风格和分类体系的全站升级——每一步都是真实踩坑后的产出。\n第一部分：搭建与上线（01-03） #这一部分回答的是：一台空服务器，怎么变成一个能访问的博客？\n核心问题 # 服务器、域名、HTTPS 怎么配 Hugo + Caddy 怎么搭 AI 助手能帮到什么程度 自动发布流水线怎么跑 章节目录 # # 章节 核心问题 01 从零搭建个人网站：一台服务器 + AI助手 = 全栈搞定 完全零基础，怎么从买到服务器到上线？ 02 个人网站搭建全记录：从零到HTTPS上线 每一步的技术细节和坑点是什么？ 03 从 Codex 到线上博客：一个只有 240 行的发布流水线是怎么跑起来的 笔记写完怎么自动变博客？ 第二部分：优化与加固（04-06） #这一部分回答的是：博客上线之后，怎么让它更好看、更安全、更易维护？\n核心问题 # 安全漏洞怎么发现和修复 视觉风格怎么统一升级 分类体系怎么组织更合理 章节目录 # # 章节 核心问题 04 我的个人网站安全加固与优化全记录 用 Claude Code 爬自己的站，发现了什么问题？ 05 个人博客全站优化记：从视觉风格到分类体系的完整升级 Hugo 主题升级 + 分类重构怎么做？ 06 你好，我是田稼禾 这个网站的起点：我是谁，我会分享什么。 这个系列不是教程，是真实实践记录。从完全不懂服务器，到有 HTTPS、有自动发布、有安全加固的线上博客——每篇都是当时真实的决策和踩坑。\n","date":null,"permalink":"https://tttjhgan.top/series/site-building/","section":"Series","summary":"","title":"个人网站建设系列"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E5%BB%BA%E7%AB%99/","section":"Tags","summary":"","title":"建站"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E5%AE%89%E5%85%A8/","section":"Tags","summary":"","title":"安全"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E7%BD%91%E7%AB%99%E8%BF%90%E7%BB%B4/","section":"Tags","summary":"","title":"网站运维"},{"content":"背景 #前几天把博客从 Congo v1 升级到了 v2，页面是好看了，但也埋下了不少坑。正好想试试 Claude Code 的「在线网站分析」能力，于是就有了这次完整的整改闭环：\nClaude Code 爬取线上网站 → 生成问题清单 + 整改建议 → 逐项修复验证 整个过程非常丝滑，从发现问题到全部上线，花了不到 2 小时。\n第一轮：发现问题 #用 Claude Code 打开我的博客首页，不到 1 分钟就输出了一份非常精准的问题清单，甚至带上了修复优先级。\n🚨 High Priority # 首页 meta description 为空\nHugo 模板逻辑有问题，首页 front matter 的 description 没被正确读取 影响 SEO，搜索引擎抓取不到正确描述 家教页推荐徽章位置错误\n「推荐」徽章在高中 ¥120 卡片上，而不是高三 ¥150 价格卡片 CSS 渲染错乱\n课程设计模块颜色、排版都有问题 ⚠️ Medium Priority # About 页图片占位位置错误\n顺序是「标题 → 图片占位 → 介绍语」，应该是「标题 → 介绍语 → 图片占位」 首页「现在在做」卡片不可点击\n三个卡片分别应该跳转到安全研究、SecurityClaw 专题、量化页面 缺少自定义 404 页面\n访问不存在的路径时返回默认 404，体验很差 🔍 优化建议 # 博客单篇文章加右侧浮动 TOC 长文没有目录，阅读体验不好 补充安全响应头 HSTS / CSP / Permissions-Policy / 隐藏 Server 头 检查 sitemap 空条目 Hugo 生成的 sitemap 可能包含空 \u0026lt;url\u0026gt; 标签 第二轮：逐项修复 #1. meta description 修复 #问题定位：Congo 模板的 head.html 里 description 绑定逻辑有多层 fallback，但首页 content/_index.md 的 front matter 虽然写了 description，但实际渲染出来是空的。\n解决：直接在 languages.zh-CN.toml 里加全局 params.description 作为最终兜底。\n[params] description = \u0026#34;网络空间安全学生，专注 Web 安全、代码审计、AI Agent 安全研究，持续更新技术博客。\u0026#34; 2. 家教页完全重构 #原页面用的是 Markdown 内嵌 HTML 写法，导致 CSS 类经常被转义或错乱。\n改成纯 Hugo 布局模板 layouts/_default/tutoring.html：\n价格卡片用 Tailwind 的 group + hover 状态 高三卡片加琥珀色边框 + 右上角推荐徽章 教学理念三卡片独立区块 真实案例区块结构化 联系方式与二维码占位 还顺手把所有 bg-white/50 这种带透明度的背景改成了实色，暗色模式下不再有发白割裂感。\n3. 首页卡片可点击 + hover 上浮 #原来三个卡片只是静态文本，用户想点却点不了。\n\u0026lt;a href=\u0026#34;{{ \u0026#34;/securityclaw-learning/\u0026#34; | relURL }}\u0026#34; class=\u0026#34;p-4 rounded-lg bg-neutral-100 dark:bg-neutral-800/50 hover:ring-2 hover:ring-primary-400/30 hover:-translate-y-0.5 transition-all cursor-pointer\u0026#34;\u0026gt; \u0026lt;!-- 卡片内容 --\u0026gt; \u0026lt;/a\u0026gt; 加了 hover:-translate-y-0.5 轻微上浮效果，交互感立刻就有了。\n4. 自定义 404 页面 #layouts/404.html：\n{{ define \u0026#34;main\u0026#34; }} \u0026lt;section class=\u0026#34;mx-auto max-w-2xl py-20 text-center\u0026#34;\u0026gt; \u0026lt;h1 class=\u0026#34;text-6xl font-bold text-primary-500\u0026#34;\u0026gt;404\u0026lt;/h1\u0026gt; \u0026lt;p class=\u0026#34;mt-6 text-xl\u0026#34;\u0026gt;🛡️ 请求的资源不存在，或已被安全策略拦截。\u0026lt;/p\u0026gt; \u0026lt;p class=\u0026#34;mt-3 text-neutral-500\u0026#34;\u0026gt;Attack pattern detected? 别慌，只是页面走丢了。\u0026lt;/p\u0026gt; \u0026lt;a href=\u0026#34;/\u0026#34; class=\u0026#34;mt-8 inline-block px-6 py-3 rounded-lg bg-primary-500 text-white hover:bg-primary-600\u0026#34;\u0026gt;返回首页\u0026lt;/a\u0026gt; \u0026lt;/section\u0026gt; {{ end }} Caddy 配置配合：\nhandle_errors { @404 expression {http.error.status_code} == 404 rewrite @404 /404.html file_server } 5. 安全响应头加固 #直接在 Caddyfile 里加完整 header 块：\nheader { # HSTS 一年 Strict-Transport-Security \u0026#34;max-age=31536000; includeSubDomains\u0026#34; # CSP - 静态博客够用的宽松策略 Content-Security-Policy \u0026#34;default-src \u0026#39;self\u0026#39;; script-src \u0026#39;self\u0026#39; \u0026#39;unsafe-inline\u0026#39;; style-src \u0026#39;self\u0026#39; \u0026#39;unsafe-inline\u0026#39;; img-src \u0026#39;self\u0026#39; data: https:; font-src \u0026#39;self\u0026#39; data:; connect-src \u0026#39;self\u0026#39;; frame-ancestors \u0026#39;none\u0026#39;; base-uri \u0026#39;self\u0026#39;; form-action \u0026#39;self\u0026#39;\u0026#34; # 禁用不需要的浏览器 API Permissions-Policy \u0026#34;camera=(), microphone=(), geolocation=(), payment=(), usb=()\u0026#34; X-Content-Type-Options nosniff X-Frame-Options DENY Referrer-Policy strict-origin-when-cross-origin # 隐藏 Caddy 版本 -Server } 第三轮：验证与验收 #✅ 功能验证清单 # 检查项 结果 meta description 正确渲染 推荐徽章在高三卡片 ✅ 价格卡片 hover 效果 ✅ 首页卡片可点击跳转 ✅ 404 页面正常返回 404 ✅ CSP 不影响 Mermaid 渲染 ✅ CSP 不影响 fuse.js 搜索 ✅ CSP 不影响主题切换 ✅ Sitemap 空条目 0 个 Server 头隐藏 ✅ 📝 最终修改文件 #/etc/caddy/Caddyfile # 安全响应头 + handle_errors layouts/_default/tutoring.html # 家教页纯模板重构 layouts/_default/404.html # 自定义 404 layouts/partials/home/custom.html # 卡片 hover 上浮 content/_index.md # description 修正 感想 #这次体验让我对 AI + 本地开发代理 的工作流有了更深的感受：\nClaude Code 当自动化测试：它能像真实用户一样浏览网站，发现各种视觉 bug 和功能缺陷，比我自己一页页翻高效太多\nHermes 当执行代理：拿到整改清单后，改 Caddyfile、改 Hugo 模板、构建部署、curl 验证，全程不用我动手\n验证闭环很重要：每改完一项立刻用 curl 验证，避免「以为修好了实际上没生效」的尴尬\n整个过程就像有个自动化测试团队 + 运维团队，我只需要看报告就行。这种开发体验真的会上瘾。\n🌐 最终成果：https://tttjhgan.top\n","date":"2026-07-28","permalink":"https://tttjhgan.top/posts/website-security-upgrade-2026/","section":"技术文章","summary":"","title":"我的个人网站安全加固与优化全记录"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/ai%E5%B7%A5%E5%85%B7/","section":"Tags","summary":"","title":"AI工具"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/gpt/","section":"Tags","summary":"","title":"GPT"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/hermes-agent/","section":"Tags","summary":"","title":"Hermes Agent"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/obsidian/","section":"Tags","summary":"","title":"Obsidian"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E6%8C%81%E7%BB%AD%E5%AD%A6%E4%B9%A0/","section":"Tags","summary":"","title":"持续学习"},{"content":"先说结论 #我现在越来越确认一件事：学习效率高不高，不取决于你装了多少 AI 工具，而取决于你有没有一个稳定的知识流。\n工具可以很多，但信息最好只有一条主线。\n如果今天用桌面版 GPT 想东西，明天在浏览器里用 Sider 看网页，后天在云主机上让 Agent 跑任务，再加上本地开发、知识库、零碎笔记、思维导图全都散着放，短期会很爽，长期就很容易出现一个问题：\n你看了很多，做了很多，但最后沉淀下来的东西很少。\n所以这篇文章不是单纯列工具，而是把我现在这套学习/工作流拆开，看看每个工具该放在哪个位置，它们之间怎么配合，以及如何避免工具之间互相内耗。\n一、什么叫工具互相内耗 #AI 工具多了以后，真正的问题通常不是“不够强”，而是都能干一点，于是谁都在抢活，最后谁都没把事情做完整。\n你会很容易遇到这种场景：\n想总结一个概念，桌面 GPT 能做 正在看网页，Sider 也能做 想自动整理一下，Agent 似乎也能做 顺手想写笔记，Obsidian 里也能记 想边做边试，Trae 还能直接生成原型 看起来每个工具都很能打，但真正的问题是：\n如果没有分工，这些工具不会叠加效率，只会叠加切换成本。\n我理解的内耗，不是工具卡顿，而是下面这些情况：\n同一个问题被你在 3 个 AI 里重复问一遍 同一种总结被你写在 2 个地方，最后哪个是最新版都不清楚 某个任务本来该交给 Agent 持续跑，你却反复手动做 某个想法本来只是临时实验，却被你当成正式沉淀 某个工具本来只是辅助理解，你却把它当长期知识库 这些情况最大的坏处不是浪费几分钟，而是让你的认知链条断掉。\nflowchart LR A[同一个问题多处重复处理] --\u0026gt; B[上下文被切碎] B --\u0026gt; C[版本混乱] C --\u0026gt; D[沉淀变少] D --\u0026gt; E[越学越忙 但收获不成体系] 所以“防内耗”的本质，不是限制工具，而是建立边界。\n二、分工的原则，不是按品牌分，而是按任务分 #很多人分工具是这么分的：\n这个是 OpenAI 的 这个是浏览器插件 这个是本地 IDE 这个是云端 Agent 这种分法不够用。真正有用的分法应该是：\n谁负责即时思考 谁负责长期执行 谁负责网页上下文 谁负责实验 谁负责沉淀 也就是说，工具应该按任务类型分层，而不是按产品名字分层。\n三、6 个工具的明确分工方案 #1. 桌面版 GPT：负责“即时思考” #桌面 GPT 最适合干的事，是高频、短反馈、需要连续对话的任务。\n比如：\n拆概念 问为什么 帮你理一个思路 草拟提纲 对比两个方案 快速复盘一件事 它不适合做什么？\n长期自动执行 多步骤持续运行任务 需要稳定文件环境的流程 当最终知识库 一句话概括：\n桌面 GPT 是你的思考界面，不是你的后台系统，也不是你的档案馆。\n2. Hermes Agent（云主机）：负责“持续执行” #云主机上的 Hermes Agent 更像一个后台大脑。它适合干那些要跑、要连、要持续的任务。\n比如：\n自动化脚本 cron 定时任务 博客发布 模拟盘报告 笔记加工 多步文件处理 工作流串联 它不该替代什么？\n日常所有零碎提问 纯粹为了想一想的闲聊 明明一句话能解决却非要上完整流程的任务 一句话概括：\nHermes Agent 是执行系统，不是随手聊天窗口。\n3. Sider：负责“网页就地辅助” #Sider 这种浏览器侧 AI，本质上解决的是一个很具体的问题：\n你正在看网页，但你不想复制来复制去。\n它适合的场景包括：\n摘要长文 翻译英文页面 解释网页里的陌生词 快速比较多段网页内容 但它不适合：\n做最终沉淀 代替系统学习 代替知识库管理 一句话概括：\nSider 是输入辅助器，不是沉淀中心。\n4. Trae / vibe coding：负责“实验和原型” #Trae 这类环境最适合的是探索阶段，因为快。\n它负责：\n快速搭 demo 测试一个小想法 写最小可运行原型 验证某个方案值不值得继续投入 但它有一个明显风险：很容易产出很多临时结果，却没有被系统整理。\n所以它该承担的角色是：\n实验层，不是归档层。\n实验可以在本地快速长出来，但实验结论不能只留在聊天记录里，最后还是要回 Obsidian。\n5. 腾讯 ima：负责“轻收集和补充仓” #腾讯 ima 这种产品，如果你觉得“貌似还不错”，那它比较适合放在一个中间层。\n也就是说：\n临时资料收集 某个专题的快速归档 面向移动端或轻输入场景的知识堆叠 作为外部知识容器补充 但我不建议把它当最终总库。\n原因不是它不好，而是你的长期知识资产最好掌握在自己手里。ima 可以用，但更适合当“中转站”或“补充仓”。\n一句话概括：\nima 可以当临时仓，但别当总仓。\n6. Obsidian：负责“唯一长期沉淀” #如果前面所有工具都在处理输入和中间过程，那 Obsidian 应该只承担一件事：\n把你真正想留住的东西留下来。\n原因很简单：\n它是你自己的 格式长期可控 迁移成本低 不依赖某个 AI 产品继续活着 它最不该被稀释成什么？\n一个临时聊天缓存区 一个杂乱收藏夹 什么都塞进去但没有结构的仓库 你应该明确一点：\n临时理解可以散 长期知识不能散 一句话概括：\nObsidian 是唯一总库。\n四、把这些工具连起来，真正有用的是这条知识流 #如果我要把这套系统压缩成一句话，那就是：\n输入可以分散，理解要收束，资产必须归一。\n更具体一点，就是下面这条链路：\nflowchart LR A[网页/文章/视频/对话/项目] --\u0026gt; B[Sider / GPT / ima 辅助理解] B --\u0026gt; C[临时笔记与概念拆解] C --\u0026gt; D[Obsidian 本地知识库] D --\u0026gt; E[Hermes Agent 读取/加工/总结] E --\u0026gt; F[博客发布 / 项目沉淀 / 复盘输出] 这条链路里，真正最重要的是两个动作：\n把临时理解写进 Obsidian 把成熟理解输出成文章 只要这两步一直在发生，你的学习就不是“看过”，而是在积累。\n五、这套工作流已经对的地方 #1. 你已经有“主知识库意识”了 #很多人最大的问题不是不会学，而是没有知识主仓。你这里已经很明确：\n学习知识文档最终汇合到本地 Obsidian。\n这一步是对的，而且非常重要。\n2. 你已经开始区分“执行工具”和“思考工具” #云主机上的 Agent 负责跑，桌面 GPT 负责日常想，浏览器 AI 负责网页辅助，这个分工本身是健康的。\n一旦所有工具都想同时承担“思考 + 执行 + 存档”三件事，就会混乱。\n3. 你知道新概念要用思维导图 #这说明你不是只想记答案，而是想整理结构。\n思维导图特别适合你现在这个阶段，因为你补的是“股市基本逻辑”“AI 工具协同”“知识工作流”这种有层次关系的东西。它比平铺笔记更适合看全局。\n六、最值得优化的三个地方 #1. 工具角色还可以再明确一点 #现在你的工具很多，但边界感还不够强。\n建议直接定死：\n工具 核心职责 Hermes Agent 持续执行、自动化、发布、后台任务 桌面版 GPT 日常问答、思考、拆概念、写初稿 Sider 网页阅读时的就地辅助 Trae 实验开发、快速原型 ima 临时收集、移动补充 Obsidian 唯一长期知识库 只要角色不乱，信息流就不会乱。\n2. 临时结论要有“回库动作” #你现在最大的潜在损耗，不是工具不够强，而是临时理解可能没有系统回流。\n最容易丢的东西通常不是正式文章，而是这些：\n聊天里突然想明白的一句话 网页里看到的一个新概念 开发时踩出来的一个坑 某个策略为什么失效的判断 这些东西如果不回写，很快就蒸发了。\n所以你需要一个固定动作：\n每次学完一个主题，只留下三种东西进 Obsidian：\n一句话结论 一张结构图 一个可复用案例 3. 思维导图不要单独漂着 #很多人会画图，但图和正文是分离的，结果过一阵只剩图，忘了当时为什么这么连。\n所以思维导图最好直接嵌在笔记正文里，下面跟着当时的思考过程。\n图是结构，文是解释，两者一起才是完整沉淀。\n七、为什么很多人明明有分工，还是会内耗 #这块很有意思。很多人的问题不是不知道工具不同，而是明明知道不同，最后还是混着用。\n通常有三个原因。\n1. 贪快 #“这个工具现在就在眼前，我顺手就在这里做了。”\n这在当下是省事的，但如果没有后续回流动作，长期一定乱。\n2. 不敢删工具 #总觉得“万一以后要用呢”，于是每个工具都留一条入口。\n结果就是每个工具都在用一点，但每个工具都没深度用起来。\n3. 没有统一出口 #如果最后没有一个“必须回到这里”的地方，那每个工具都会试图当最终存档。\n这就是为什么 Obsidian 必须是唯一总库。\n它不是因为功能最强，而是因为只有它能给你提供一个“不管在哪学，最后都回到这里”的终点。\n八、最后总结 #你这套工作流方向是对的，而且已经比大多数人清楚了。\n你现在缺的不是再加新工具，而是把下面这三件事卡紧：\n每个工具只做自己那一层的事 临时理解必须回写回 Obsidian 成熟理解必须输出成文章或项目沉淀 只要这三件事持续发生，你就不会陷入“学了很多但没沉淀”的状态。\n这也是为什么我一直说：\n学习不是收集工具，而是建立一条能持续把临时理解变成长期资产的流水线。\n","date":"2026-07-27","permalink":"https://tttjhgan.top/posts/learning-workflow-2/","section":"技术文章","summary":"","title":"持续学习工作流 2.0：6 个 AI 工具的分工、边界与知识汇流"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E5%B7%A5%E4%BD%9C%E6%B5%81/","section":"Tags","summary":"","title":"工作流"},{"content":"","date":null,"permalink":"https://tttjhgan.top/categories/%E6%8A%80%E6%9C%AF%E7%A0%94%E7%A9%B6/","section":"Categories","summary":"","title":"技术研究"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E7%9F%A5%E8%AF%86%E7%AE%A1%E7%90%86/","section":"Tags","summary":"","title":"知识管理"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/ai-development/","section":"Tags","summary":"","title":"Ai-Development"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/automation/","section":"Tags","summary":"","title":"Automation"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/claude-code/","section":"Tags","summary":"","title":"Claude-Code"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/codex/","section":"Tags","summary":"","title":"Codex"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E8%82%A1%E7%A5%A8/","section":"Tags","summary":"","title":"股票"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E9%87%8F%E5%8C%96/","section":"Tags","summary":"","title":"量化"},{"content":"","date":null,"permalink":"https://tttjhgan.top/categories/%E9%87%8F%E5%8C%96%E4%BA%A4%E6%98%93/","section":"Categories","summary":"","title":"量化交易"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E4%B9%B0%E7%82%B9/","section":"Tags","summary":"","title":"买点"},{"content":"先说结论 #很多人学股票，最先问的是：买点在哪，卖点在哪？\n这个问题本身没错，但如果你脑子里想的是“一个神奇价格，买了就涨；一个神奇价格，卖了就不回头”，那方向就偏了。\n真实交易里，买点和卖点不是两个孤立按钮，而是一整套计划：\n为什么现在可以买 如果买错了，跌到哪必须认错 如果买对了，涨到哪先兑现一部分 剩下的仓位，是继续拿还是保护利润 说得直接一点：\n会买不算本事，会带着计划买，才算刚入门。\n一、买点不是抄最低，卖点不是猜最高 #市场里最容易把人带偏的，就是“最低点执念”和“最高点执念”。\n总有人想做到：\n买在最低点 卖在最高点 但这套想法最大的副作用，是你会一直犹豫。\n因为你总会觉得：\n再等等，会不会更低 再拿拿，会不会更高 结果就是：\n买的时候不敢下手 卖的时候不舍得走 所以更实用的思路应该是：\n买在确认开始强的时候 卖在逻辑被破坏的时候 赚到预设收益时先落袋一部分 这听起来不性感，但这是可以长期执行的。\n二、最常见的三类买点 #1. 趋势买点 #趋势买点适合强票，尤其适合你现在关注的科技、AI、半导体这类高弹性标的。\n典型信号：\n均线多头排列 股价站上短期均线 放量上涨 突破前高 flowchart LR A[均线开始多头] --\u0026gt; B[价格站稳短期均线] B --\u0026gt; C[放量确认] C --\u0026gt; D[突破或回踩不破] D --\u0026gt; E[买入] 这种买点的核心不是“便宜”，而是“强势得到确认”。\n优点：\n容易顺势拿到一段主升浪 看对以后空间往往比较大 缺点：\n看起来像追高 假突破会很难受 所以趋势买点一定要配止损，不然一旦是假的，回撤会很丑。\n2. 超跌反弹买点 #这类买点你现在的模拟盘已经在用，而且很符合高波动科技股的节奏。\n典型信号：\n连跌几天 跌幅够大 RSI 很低 有止跌迹象 简单理解就是：\n不是因为跌了就买，而是因为跌多了、有人开始接了，才试着买。\n优点：\n买得相对低 盈亏比通常不差 缺点：\n最容易抄在半山腰 如果不是反弹，而是继续杀跌，会很被动 所以超跌反弹买点，最重要的不是“信号多好看”，而是止损够不够硬。\n3. 回踩确认买点 #这类买点比纯追涨更舒服，也更适合波段。\n典型场景：\n原来就在上涨趋势里 短期回调到 MA5 / MA10 / MA20 缩量调整 再次转强 这类买点的本质是：\n不是在猜底，而是在强趋势里等一次低风险上车。\n三、卖点为什么比买点更重要 #很多散户一聊交易，90% 精力放在买点上，剩下 10% 才想到卖。\n这其实是反的。\n因为买错以后，只要你卖得够快，小亏还能活；但卖错了，或者根本不卖，亏损会迅速失控。\n所以卖点比买点更重要，不是因为它更复杂，而是因为它决定你能不能活得久。\n四、三类最实用的卖点 #1. 止损卖点 #止损不是悲观，止损是承认自己会看错。\n常见的止损方式：\n固定比例止损：-3%、-5%、-8% 跌破关键均线止损 跌破前低止损 如果你做的是高波动科技股，固定比例止损通常会更简单直接。\n关键不是具体用 -4% 还是 -5%，关键是：\n买之前就定好，到了就走，不临时改规则。\n2. 固定止盈卖点 #止盈的意义不是卖飞，而是落袋。\n最简单的做法：\n+3% 先兑现一点 +6% 再兑现一点 +10% 看情况决定要不要继续留 这套分步卖法特别适合波动大的票。\n因为你会同时得到两件事：\n一部分利润已经锁住 一部分仓位还能继续吃趋势 3. 趋势破坏卖点 #如果你原本是按趋势买的，那卖点最好也按趋势来。\n比如：\n跌破 MA10 跌破 MA20 放量长阴 冲高回落后结构走坏 这类卖点不是在数利润，而是在盯结构。\n说白了：\n买入靠逻辑，卖出靠纪律。\n五、最稳的做法：买入时就把卖出写好 #这其实就是交易计划。\n你在下单之前，最好把这几个数字同时定下来：\n入场价 止损价 第一止盈价 第二止盈价 最终目标价 flowchart TD A[准备买入] --\u0026gt; B[定义入场逻辑] B --\u0026gt; C[定义止损价] C --\u0026gt; D[定义第一止盈] D --\u0026gt; E[定义第二止盈] E --\u0026gt; F[定义最终目标] F --\u0026gt; G[下单] 这一步一旦做了，后面很多临盘情绪都会少很多。\n因为你不是在盘中现编，而是在执行之前已经定好的计划。\n六、分步止盈，为什么比一次性全卖更适合你 #你现在想做的是“波段 + 短线”混合，这种风格最怕两件事：\n赚一点就全卖飞 死拿回吐全部利润 所以分步止盈特别适合你。\n比如一笔超跌反弹单，可以这样设计：\nTP1：+3% TP2：+6% TP3：+10% 如果仓位更大，就分三档；仓位很小，就一档先走。\n它的好处很直接：\n第一档解决“先赚到钱” 第二档解决“别太早下车” 第三档解决“如果真走成趋势，还能继续吃” 分步止盈之后，止损也要跟着抬 #这点非常重要。\n例如：\n第一档止盈后，把止损抬到成本价 第二档止盈后，把止损继续抬高 这样做的目的不是为了完美，而是为了防止盈利回吐。\n很多人明明一度盈利很多，最后却又回到亏损，核心问题就在这里：\n利润没有保护。\n七、仓位决定你能不能执行下去 #买点卖点之外，还有一个常被低估的东西：仓位。\n如果你一把全上，哪怕买点和卖点逻辑没问题，情绪也会把你搞乱。\n所以更稳的思路通常是：\n单笔不要压太满 给自己留现金 允许试仓 看对以后再逐步放大 这也是为什么成熟交易系统，通常不只写“买什么”，还会写“买多少”。\n八、你现在这套系统，最适合怎么定买卖点 #如果按你的风格，我更建议你固定成两套模板。\n模板 1：超跌反弹 #适用：高波动科技票、回调后的短线修复\n入场看：\n3日跌幅明显 5日跌幅明显 RSI 低 有止跌或放量迹象 出场定法：\n止损：-5% TP1：+3% TP2：+6% TP3：+10% 模板 2：趋势跟随 #适用：强势股、波段上升趋势\n入场看：\n均线多头排列 放量确认 回踩不破再转强 出场定法：\n止损：-4% TP1：+5% TP2：+10% TP3：+15% 这两套模板已经足够你先把交易系统跑起来，不需要一开始就把自己搞得很复杂。\n结尾 #买点和卖点，说到底不是为了预测未来，而是为了让你在不确定的市场里，始终知道自己下一步该干什么。\n这件事真正值钱的地方，不是你猜得有多神，而是你有没有把自己的错误限制住，把自己的正确放大一点。\n所以别再执着于“最神买点”和“最神卖点”了。你真正该建立的是：\n入场逻辑 止损纪律 分步止盈 仓位控制 只要这四件事慢慢稳定下来，你的交易就会从“凭感觉”进入“有系统”。\n","date":"2026-07-22","permalink":"https://tttjhgan.top/posts/how-to-set-entry-and-exit/","section":"技术文章","summary":"","title":"买点和卖点，到底该怎么定"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E5%8D%96%E7%82%B9/","section":"Tags","summary":"","title":"卖点"},{"content":"让 Claude Code 主控 Codex：完整配置指南 #背景 #希望让 Claude Code 负责规划、编写和修复代码，同时调用 Codex 进行分析、审查和风险检查，可以通过 OpenAI 官方的 codex-plugin-cc 插件完成。两者共享当前项目目录和本机 Git 工作区，但职责可以明确分开：Claude Code 负责推进任务，Codex 负责质量门禁。\n配置关系 #这套配置包含四个部分：\nClaude Desktop：提供桌面端 Claude 应用，但它不等于 Claude Code。 Claude Code：命令行开发入口，负责主控开发流程。 Codex CLI：由 Claude Code 插件调用的本地 Codex 命令行程序。 codex-plugin-cc：为 Claude Code 增加审查、委派、状态和结果查询命令。 安装 Claude Code #Claude Code 需要 Node.js 18.18 或更高版本。没有管理员权限时，可以把 Node.js 和包管理器安装到用户目录，然后执行：\npnpm add --global @anthropic-ai/claude-code 确认命令可用：\nclaude --version 如果系统提示找不到命令，把用户级包目录加入当前用户的 PATH，不需要修改系统 PATH 或申请管理员权限。\n安装 Codex 插件 #在 Claude Code 中添加官方 marketplace，并安装插件：\n/plugin marketplace add openai/codex-plugin-cc /plugin install codex@openai-codex /reload-plugins 确认插件状态：\n/plugins 插件安装成功后，可以使用以下命令：\n/codex:setup /codex:review --background /codex:adversarial-review --background /codex:rescue investigate why the build is failing /codex:status /codex:result /codex:cancel Codex CLI 和认证 #插件使用本机的 Codex CLI 和 Codex 配置，不会创建单独的 Codex 运行环境。确认 CLI 可用：\ncodex --version codex login Codex 应使用 ChatGPT 登录或 OpenAI API Key。Anthropic 的 sk-ant-... Key 不能直接作为 OpenAI API Key 使用。Claude Code 的 Anthropic 认证和 Codex 的 OpenAI 认证是两套独立配置。\n如果 Claude Code 使用了第三方 API 路由或代理，还需要确认该路由有可用额度；否则插件命令可能还没有开始调用 Codex，就先因 Claude Code API 返回 402 而失败。\n推荐协作流程 #1. Claude Code 先规划 #先阅读项目结构和现有代码，只输出实现计划，不要修改文件。 确认计划后，再让 Claude Code 实施并运行测试：\n按计划实施。完成后运行相关测试，并总结修改内容、风险和未覆盖的测试。 2. Codex 审查 #/codex:review --background 重点关注功能逻辑、边界条件、安全问题、错误处理、性能、兼容性和测试覆盖。审查阶段默认只读，不让两个模型同时修改同一批文件。\n3. Claude Code 修复 #将 Codex 的审查结果交给 Claude Code：\n请根据下面的代码审查意见逐项修复。修复后重新运行测试，并说明哪些问题已解决。 4. Codex 复审 #/codex:review --background 确认上一轮问题已解决，并检查修复是否引入新的回归。\n项目规则建议 #在项目根目录维护 CLAUDE.md，约束 Claude Code 先规划、再修改、最后测试；维护 AGENTS.md，告诉 Codex 默认只审查、按严重程度报告问题，并提供文件路径和行号。\n常见问题 #Claude Desktop 安装成功不代表 Claude Code 已安装；桌面应用和命令行工具是两个独立组件。\nWindows 上 Cowork 需要现代 MSIX 安装方式和管理员权限。如果当前账号没有管理员权限，普通 Claude Desktop 仍可使用，但 Cowork 的现代安装和虚拟化依赖可能无法启用。\n最终的职责划分可以概括为：Claude Code 负责规划、编写和修复，Codex 负责分析、审查和风险检查。通过串行协作和 Git 检查点，可以避免两个模型同时修改文件造成覆盖。\n","date":"2026-07-22","permalink":"https://tttjhgan.top/posts/20260722-claude-code-codex-collaboration/","section":"技术文章","summary":"","title":"让 Claude Code 主控 Codex：完整配置指南"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E6%AD%A2%E6%8D%9F/","section":"Tags","summary":"","title":"止损"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E6%AD%A2%E7%9B%88/","section":"Tags","summary":"","title":"止盈"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E5%9F%BA%E7%A1%80%E9%80%BB%E8%BE%91/","section":"Tags","summary":"","title":"基础逻辑"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E4%BA%A4%E6%98%93/","section":"Tags","summary":"","title":"交易"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E6%8A%95%E8%B5%84/","section":"Tags","summary":"","title":"投资"},{"content":"你先别急着学指标 #很多人一上来就盯着 MA、RSI、MACD，像是在学一套神秘咒语。问题是，咒语背后到底在解决什么，他们并不清楚。\n我觉得股市最先要补的，不是公式，而是这几个底层问题：\n价格为什么会涨跌 谁在推动价格变化 为什么同一个策略有时赚钱，有时失效 为什么回测看起来很好，实盘却很难看 如果这四件事没想通，后面的技术指标、量化模型、仓位管理，都会变成一堆散装知识。\n一、价格不是“算出来”的，是“撮合出来”的 #股票价格本质上不是公司价值的即时答案，它是买卖双方在交易所里不断撮合出来的结果。\n你可以把市场想成一个持续竞价的拍卖场。\nflowchart LR A[买方想更便宜] --\u0026gt; C[交易所撮合] B[卖方想更贵] --\u0026gt; C C --\u0026gt; D[成交价] D --\u0026gt; E[新的市场预期] E --\u0026gt; A E --\u0026gt; B 这件事很重要，因为它意味着：\n价格不是静态真理 价格是预期的折中 预期变化，比“事实本身”更快反映到盘面里 所以你会看到，财报刚出来，股价可能先跌；利好刚落地，股价反而可能回落。市场交易的不是昨天发生了什么，而是大家现在怎么理解这件事。\n二、股价涨跌，核心还是供需 #你可以把涨跌理解成最朴素的一句话：\n买的人比卖的人更急，价格就上去；卖的人比买的人更急，价格就下来。\n这背后当然有很多变量，比如：\n资金量 预期差 行业景气度 风险偏好 政策环境 流动性 但最后都要落回到供需。\n很多散户最容易犯的错，是把“我觉得便宜”当成“市场也会觉得便宜”。这两者不是一回事。你觉得便宜，不代表有人愿意马上接你的筹码。\n三、股票市场里，不同角色看的东西不一样 #同一只股票，不同人看的不是同一个东西。\n角色 关注点 散户 题材、涨跌、消息、感觉 短线资金 盘口、量能、情绪、节奏 机构资金 业绩、估值、行业趋势、容量 公司自己 融资、回购、股权激励、经营 这也是为什么一个票涨起来以后，很多人会觉得“逻辑变了”。其实不是逻辑变了，是参与者变了。\n短线看的是情绪和流动性，长线看的是价值和兑现能力。你如果拿长线逻辑去做短线，或者拿短线逻辑去解释长线，基本都会出问题。\n四、股市里最重要的，不是赚得多，而是活得久 #很多人学投资，只盯收益率。这个视角太窄了。\n更完整的看法应该是：\n收益率：赚了多少 回撤：中途亏了多少 胜率：大概几次能对一次 盈亏比：赚的时候和亏的时候差多少 仓位控制：一把能不能把自己打死 一个策略如果年化高，但回撤极大，那并不是真正可用的策略。因为人不是机器，连续亏损几次就容易乱。\n所以你会发现，真正能长期执行的系统，不是“最猛的”，而是“最稳的”。\n五、为什么策略会失效 #这块是关键。\n策略失效通常不是因为“指标不准”，而是因为它只在某种市场状态下有效。\n比如：\n趋势策略适合趋势行情，不适合横盘震荡 反弹策略适合下跌后的修复，不适合单边下杀 突破策略适合放量启动，不适合假突破 高波动策略适合高弹性赛道，不适合低波动蓝筹 也就是说，策略不是万能钥匙，它是一个“场景工具”。\nflowchart TD A[市场状态] --\u0026gt; B{趋势/震荡/恐慌/亢奋} B --\u0026gt;|趋势| C[趋势跟随] B --\u0026gt;|震荡| D[均值回归] B --\u0026gt;|恐慌| E[观察等待或分批试仓] B --\u0026gt;|亢奋| F[控仓/止盈/防回撤] 很多人会误以为“某个策略赚钱了”就是这个策略厉害。其实更准确的说法是：这个策略刚好适配了那段市场环境。\n六、量化到底在干什么 #量化不是把投资变成冷冰冰的数字游戏，它本质上是在做三件事：\n把主观判断变成可重复规则 把情绪影响压低 把策略的成败边界说清楚 也就是说，量化不是替你“预测未来”，而是帮你回答：\n什么条件下买 什么条件下卖 亏多少必须停 哪些市场状态不做 这其实很适合做交易，因为人最难控制的就是自己。\n七、A股还有几个必须接受的现实 #A股不是教科书市场，它有自己的规则。\nT+1：今天买，明天才能卖 100股一手：最小交易单位固定 涨跌停：价格不是想走多快就走多快 流动性差异大：有些票能进能出，有些票进去容易出来难 题材轮动快：今天强的，明天未必强 这些规则决定了，A股量化不能只抄海外策略。你得接受它的节奏、制度和流动性结构。\n八、你现在最该建立的认知 #如果让我把股市基础逻辑压缩成一句话，我会说：\n市场价格是预期和资金共同作用的结果，策略只是适配某种市场状态的工具。\n这句话背后再展开，就是你现在最该补齐的知识框架：\n先懂价格怎么形成 再懂资金怎么推动价格 再懂市场状态怎么切换 最后才懂策略为什么有用、为什么失效 你现在做量化，不需要先把所有理论都学完，但至少要知道自己在和什么东西打交道。\n结尾 #我更愿意把这件事看成一套“市场语言”。\n你不需要一开始就说得很专业，但你得先听懂市场在说什么。等你能分清趋势、情绪、流动性、回撤、仓位这些词到底在说什么，后面再看指标和策略，就不是背公式了，而是在读盘。\n下一篇我准备把“趋势、反转、均值回归到底是什么”单独拆开讲，接着把你现在的模拟盘逻辑串起来。\n","date":"2026-07-21","permalink":"https://tttjhgan.top/posts/stock-market-logic/","section":"技术文章","summary":"","title":"先把股市最底层的逻辑搞明白"},{"content":"结论先说：安全护栏不是 prompt 附件，而是运行时控制系统 #一个 Agent 系统里，最危险的误解就是：\n只要在 prompt 里告诉模型“不要做危险操作”，系统就安全了。\n这当然不够。\n真正的安全护栏必须在运行时落地，至少包括：\n权限 人工确认 幂等性 预算 注入防护 一、为什么“能规划”不等于“能执行” #LLM 可以很擅长做这些事：\n理解意图 提出动作 组合多步计划 但它不该拥有这些权力：\n决定自己有没有权限 决定高风险动作是否可以直接落地 决定同一操作失败后能否无限重试 决定哪段用户输入可以直接进入系统调用 所以最核心的原则是：\n规划权可以给模型，执行权必须收回到系统。\n二、权限护栏：动作白名单而不是善意假设 #class PermissionRule(BaseModel): action: Literal[\u0026#34;read\u0026#34;, \u0026#34;write\u0026#34;, \u0026#34;external\u0026#34;, \u0026#34;modify_file\u0026#34;] entities: list[str] def validate_permissions(plan, manifest): for step in plan.steps: if step.action not in manifest.allowed_actions: return False if step.entity not in manifest.allowed_entities: return False return True 这一步不是“提醒”，而是拒绝条件。\n为什么这层必须在运行时 #因为 prompt 规则无法保证：\n模型不幻觉 工具名不漂移 用户输入不诱导模型越权 只有运行时白名单，才是硬边界。\n三、人工确认：高风险动作为什么必须暂停 #有些动作根本不该自动通过：\n删除数据 改权限 发外部邮件 写敏感路径文件 对外联网请求高风险地址 if manifest.risk_level == \u0026#34;high\u0026#34;: request = HumanConfirmRequest(plan=step, timeout=30) approved = wait_for_confirmation(request) if not approved: return Denied(\u0026#34;confirmation timeout\u0026#34;) 这一步的重要性在于：\n把组织责任留在人 把不可逆动作从模型自动化里摘出来 给系统留一个最后的刹车点 四、幂等性：恢复不能放大事故 #恢复机制如果没有幂等护栏，会非常危险。\nif execution_records.exists(idempotency_key): return execution_records.get(idempotency_key) 这层防的是什么 # 重试导致重复写库 重试导致重复发信 重试导致重复外呼 同一失败路径被反复放大 所以幂等性不是优化，而是安全护栏的一部分。\n五、预算护栏：安全不只是权限，也是成本边界 #if token_budget.remaining \u0026lt;= 0: return Stop(\u0026#34;budget exhausted\u0026#34;) if tool_cost[tool_name] \u0026gt; remaining_cost: return Denied(\u0026#34;tool budget exceeded\u0026#34;) 很多人把预算理解成成本控制，其实它同时是防滥用边界：\n防无限循环 防外部 API 被打爆 防单个用户问题拖垮系统资源 从系统安全角度看，它和权限一样重要。\n六、注入防护：为什么不能让用户输入直接影响执行层 #在 Agent 场景里，注入不只是 prompt injection，也包括：\n用户输入污染工具参数 外部文档内容反向影响控制流 检索结果把系统带偏 所以防护至少应该包括：\n6.1 参数白名单 #只允许结构化参数进入工具\n6.2 用户输入与系统约束分离 #用户能影响目标，不能影响执行边界\n6.3 检索证据不直接等同于命令 #RAG 结果是证据，不是执行指令\n这也是为什么“自然语言拼系统命令”是危险设计。\n七、为什么安全护栏不能全放在 manifest 里 #manifest 可以声明：\nallowed actions allowed fields risk level idempotent 但它不能成为最终安全边界，因为：\nmanifest 本身也可能出错 manifest 只是框架层声明 真正执行动作的权限最终还在基础设施层 所以更准确的分层是：\nmanifest: 声明策略 runtime: 执行策略 infra: 最终兜底 少任何一层，护栏都会变脆。\n八、这一篇真正要记住的框架 #安全护栏可以压成这五个问题：\n这个动作允许吗？ 这个动作需要人工确认吗？ 这个动作失败后能安全重试吗？ 这个动作值得继续消耗预算吗？ 用户输入会不会污染执行层？ 如果系统不能系统性回答这五个问题，那它只是“带工具的 LLM”，还不是可上线 Agent。\n九、验证命令 #pytest tests/ -k \u0026#34;permission or guardrail or idempotent or confirm or budget\u0026#34; -v ","date":"2026-07-16","permalink":"https://tttjhgan.top/securityclaw-learning/ch16-agent-guardrails-permissions/","section":"SecurityClaw 学习笔记","summary":"","title":"16 安全护栏与权限：Agent 能规划不等于能执行"},{"content":"结论先说：看最终答案，只能判断“像不像对”；看 trace，才能判断“系统到底怎么到这一步的” #Agent 评测最容易犯的错，就是只看最后一句输出。\n但真实系统里，真正值得评测的是整条运行链：\n计划是什么 调了哪些工具 哪一步失败 是否进入恢复 为什么停止 是否浪费了预算 也就是说：\nAgent 评测的对象不只是 final answer，而是运行证据本身。\n一、可观测性和评测为什么不能分开看 #可观测性回答：\n系统做了什么 花了多久 失败在哪 评测回答：\n这样做是否合理 是否达成目标 是否值得复用 所以这两层在 Agent 系统里天然耦合。\n如果没有 trace、metrics、replay，你的评测只能停留在“看答案像不像对”。\n二、trace 应该记录什么 #一个最小可用的 Agent trace，至少应当包括：\nTraceEntry = { \u0026#34;step\u0026#34;: 3, \u0026#34;plan_summary\u0026#34;: \u0026#34;query_asset_db(ip=10.0.0.4)\u0026#34;, \u0026#34;tool_name\u0026#34;: \u0026#34;query_asset_db\u0026#34;, \u0026#34;duration_ms\u0026#34;: 182, \u0026#34;status\u0026#34;: \u0026#34;success\u0026#34;, \u0026#34;evaluation_reason\u0026#34;: \u0026#34;evidence insufficient\u0026#34;, } 真正重要的不是“记录很多”，而是记录这些决策级字段：\n这一步试图做什么 实际调用了什么 花了多少时间 成功还是失败 评估层怎么解释这个结果 这几项合在一起，才构成一条可审计证据链。\n三、metrics 不是为了仪表盘好看，而是为了发现系统退化 #至少要有这几类指标：\n3.1 成本指标 # 平均 token 消耗 单任务平均步数 平均工具调用次数 3.2 质量指标 # 成功率 恢复率 空结果率 提前停止率 3.3 稳定性指标 # timeout 比例 permission denied 比例 schema mismatch 比例 如果你只看最终答案是否“对”，是看不到这些系统退化信号的。\n四、为什么 replay 是 Agent 系统里的高价值能力 #replay 的本质不是“回放一遍很酷”，而是：\n复现 bug 比较模型升级前后行为 评估恢复路径是否合理 验证新 guardrail 是否改变结果 # 伪代码：replay 依赖结构化 trace 和 checkpoint replay_session = { \u0026#34;initial_input\u0026#34;: question, \u0026#34;state_snapshots\u0026#34;: checkpoints, \u0026#34;trace\u0026#34;: trace_entries, \u0026#34;observations\u0026#34;: tool_results, } 如果系统没有结构化 trace，replay 基本做不起来。因为你根本不知道上一轮到底是：\n计划变了 工具变了 评估逻辑变了 还是 budget 护栏提前触发了 五、Agent eval 为什么不能只看最终答案 #一个答案看起来像对，不代表路径正确。\n例如：\n模型猜对了 工具结果其实为空，但模型用背景知识补了答案 系统走了错误恢复路径，但阴差阳错给出合理文本 这种情况如果只用“最终答案正确率”评测，会误把偶然命中当成系统质量。\n所以 Agent eval 至少应该增加这些维度：\n证据充分性：答案有没有足够依据 工具使用合理性：是否调用了适合的能力 恢复路径合理性：失败后是否做了有意义的转向 停止条件合理性：系统何时停止是否合适 六、Mock 为什么比 demo 更可信 #你前面提过一个关键点：\nMock、trace 和测试为何比最终文案更可靠\n这点在评测层尤其成立。\n为什么 demo 不够 # demo 只展示成功样本 demo 无法稳定复现 demo 看不到系统边界 为什么 Mock 更值钱 # 可以构造极端失败路径 可以稳定复现某种输入输出 可以判断系统在坏路径是否仍然守约 def test_timeout_path_records_recovery_trace(): ... def test_zero_result_marks_plan_exhausted(): ... 这些测试的价值不在“让 CI 绿”，而在于它们定义了系统的行为契约。\n七、这一篇真正要记住的框架 #可观测性和评测可以压成这条链：\ntrace 记录发生了什么 -\u0026gt; metrics 统计系统趋势 -\u0026gt; replay 复现具体路径 -\u0026gt; eval 判断路径是否合理 所以真正成熟的 Agent 系统，评测的对象不只是 answer，而是：\nanswer path evidence cost recovery 八、验证命令 #pytest tests/ -k \u0026#34;trace or replay or eval or observability\u0026#34; -v ","date":"2026-07-16","permalink":"https://tttjhgan.top/securityclaw-learning/ch17-agent-observability-evals/","section":"SecurityClaw 学习笔记","summary":"","title":"17 可观测性与 Agent 评测：从最终答案到运行证据"},{"content":"结论先说：不是所有问题都配得上 Agent，更不是所有 Agent 都该长成多 Agent #做完前 17 篇，最后真正该问的问题不是“还能加什么能力”，而是：\n什么问题根本不该用 Agent？\n这是架构判断里最重要的一步。因为很多系统不是不够强，而是从一开始就选错了形态。\n一、先把四种形态分清 #1.1 普通函数 #输入明确，步骤固定，输出规则化。\n1.2 工作流（Workflow） #步骤有限，但包含条件分支和状态推进。\n1.3 单 Agent #需要基于当前状态做开放式规划，但仍有统一控制面。\n1.4 多 Agent #不同子任务需要不同角色、不同上下文、不同局部目标协作。\n很多时候，问题只配得上前两种。\n二、什么时候该用普通函数 #如果任务满足这三个条件：\n输入结构固定 步骤路径稳定 输出判断明确 那最好的架构就是函数。\ndef daily_report(): rows = query_db(...) summary = aggregate(rows) send_email(summary) 这类任务强行上 Agent，只会引入：\n不必要 token 消耗 不稳定规划 更多失败路径 所以一个成熟的架构师，首先要有勇气说：\n这个地方不要上 Agent。\n三、什么时候工作流比 Agent 更合适 #工作流适合：\n状态多步推进 分支清楚 每一步动作基本已知 恢复路径主要靠规则而不是开放式推理 flowchart TD Start --\u0026gt; Parse Parse --\u0026gt; Validate Validate --\u0026gt;|ok| Query Validate --\u0026gt;|bad| Reject Query --\u0026gt; Aggregate Aggregate --\u0026gt; Report 工作流的优势 # 可预测 易审计 易测试 成本低 工作流的局限 # 对开放问题适应差 不擅长动态选择能力 对模糊用户意图不够灵活 所以工作流不是低级方案，而是在确定性问题上更高级的选择。\n四、单 Agent 什么时候值得上 #当任务开始具备这些特征时，单 Agent 才真正有价值：\n用户目标不完全结构化 系统需要在多个能力之间动态选择 执行中会根据结果修正策略 恢复路径不能完全靠固定规则写死 这时单 Agent 的优势就出来了：\n统一状态 统一控制面 统一恢复与停止逻辑 成本仍可控 所以大多数真实项目，最合理的上限其实是：\n工作流不够用时，先上单 Agent，而不是直接拆成多 Agent。\n五、为什么多 Agent 不是默认答案 #多 Agent 看起来很高级，但它会立刻带来新的复杂度：\nAgent 之间如何共享状态 谁负责冲突协调 token 成本如何累加 恢复路径如何跨角色传播 trace 如何解释清楚 如果这些问题你还没想明白，多 Agent 很可能只是把一个系统问题拆成多个更难 debug 的系统问题。\n多 Agent 真正适合的场景 # 子任务目标差异很大 每个角色需要不同工具和上下文 角色分工天然存在 单 Agent 的上下文已经明显过载 如果只是“一个 Agent 有点复杂”，通常还不到上多 Agent 的时候。\n六、真正的架构判断顺序 #这一步很重要。架构不是看潮流，而是按问题约束往上升级。\n普通函数 -\u0026gt; 工作流 -\u0026gt; 单 Agent -\u0026gt; 多 Agent 正确判断顺序应该是：\n6.1 先问：能不能用普通函数 #如果能，就别用 Agent\n6.2 再问：能不能用工作流 #如果步骤已知、分支有限，优先工作流\n6.3 再问：是否真的需要开放式规划 #如果需要，再上单 Agent\n6.4 最后问：单 Agent 是否已经明显过载 #只有这时，多 Agent 才开始有意义\n七、SecurityClaw 给出的真正启发 #SecurityClaw 值得学习的，不是“用了 Agent”，而是它没有把所有问题都扔给 Agent。\n它的整体设计说明了一件事：\n技能层保持能力独立 控制面保持系统约束统一 知识层和状态层分离 Agent 主要负责开放式规划与证据迭代 这意味着它已经在架构上默认承认：\n不是所有事都应该交给模型自由决定。\n这其实比“会不会做多 Agent”更难得。\n八、这一篇真正要记住的框架 #架构取舍可以压成这张表：\n形态 适合什么问题 优势 代价 普通函数 确定性、固定流程 简单、便宜、稳定 不灵活 工作流 多步但规则清楚 可预测、可审计 开放性差 单 Agent 需要动态规划 灵活、统一控制面 成本更高 多 Agent 角色和上下文明显分化 可拆复杂任务 协调最复杂 所以真正成熟的结论不是“多 Agent 更强”，而是：\n先选最低复杂度、仍能覆盖问题约束的那种形态。\n九、验证命令 #pytest tests/ -k \u0026#34;workflow or agent or orchestration\u0026#34; -v ","date":"2026-07-16","permalink":"https://tttjhgan.top/securityclaw-learning/ch18-agent-architecture-tradeoffs/","section":"SecurityClaw 学习笔记","summary":"","title":"18 架构取舍：工作流、单 Agent 与多 Agent"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/agent/","section":"Tags","summary":"","title":"Agent"},{"content":"如果你要从零开始理解 Agent 开发，这篇文章是索引 #过去三周，我通过 SecurityClaw 这个开源 SOC Agent 框架，把 Agent 开发的 18 个核心主题走了一遍。这篇文章不是教程——它是一个知识地图，告诉你每个概念在哪个文件里，以及它们之间的调用关系。\n如果你想按章节系统读完整个专题，直接看这里：SecurityClaw 学习笔记总纲。\n第一层：LLM 是引擎，不是大脑 #很多人入门 Agent 的第一个误区是把 LLM 当成决策中枢。实际上，LLM 只负责一件事：给定状态，输出下一步动作建议。所有\u0026quot;能不能执行\u0026quot;、\u0026ldquo;执行到一半怎么办\u0026rdquo;、\u0026ldquo;状态怎么恢复\u0026rdquo;——这些是代码的责任。\n# Agent 的核心不是 LLM，是这个循环体 class AgentLoop: def run(self, state: AgentState) -\u0026gt; FinalOutput: while not self.should_stop(state): action = self.llm.plan(state) # LLM 建议下一步 result = self.execute(action) # 代码验证 + 执行 state = self.reflect(state, result) # 评估结果，更新状态 return state.final_output 这个模式在 SecurityClaw 的多个文件中反复出现：core/runner.py（编排层）、core/chat_router/logic.py（LangGraph 循环）、skills/ 下的各个技能执行器。\n第二层：六个必须理解的概念 #1. 状态管理（State） #Agent 的\u0026quot;记忆\u0026quot;是一个显式的数据结构，不是隐式的对话历史：\nclass AgentState(TypedDict): messages: list[Message] # LLM 对话记录 task_plan: Optional[Plan] # 当前执行计划 execution_history: list[Step] # 已完成步骤（含成功/失败） context: dict[str, Any] # 注入的环境（DB、LLM、配置） error_count: int # 连续失败计数（防止死循环） final_output: Optional[str] # 最终输出 为什么用 TypedDict 而不是 Pydantic？ 在 LangGraph 的节点间传递状态时，不可变字典操作（{**state, key: val}）比 Pydantic 模型的 .copy(update=...) 更高效，且与 LangGraph 的 checkpoint 机制兼容。\n2. 工具调用（Tool Calling） #def dispatch(tool_name: str, args: dict, runtime: Runtime) -\u0026gt; Observation: # 模型只建议 tool_name + args，代码负责五步验证 if tool_name not in runtime.whitelist: return error(\u0026#34;not allowed\u0026#34;) if not runtime.validator.check(args): return error(\u0026#34;invalid args\u0026#34;) if not runtime.authorizer.check(tool_name): return error(\u0026#34;permission denied\u0026#34;) if runtime.budget.remaining \u0026lt; cost: return error(\u0026#34;budget exceeded\u0026#34;) return runtime.execute(tool_name, args) # 终于执行 关键设计：工具调用的每一步失败都返回一个结构化的 Observation 对象（含 status 和 error），而不是抛异常。这让 LLM 能在下一轮规划时看到\u0026quot;上次为什么失败\u0026quot;，决定是否重试。\n3. 规划与循环（Planning \u0026amp; Loop） #flowchart TD Input[用户输入] --\u0026gt; Plan[LLM 规划\u0026lt;br/\u0026gt;生成 Action 列表] Plan --\u0026gt; Execute[执行 Action] Execute --\u0026gt; Eval{评估结果} Eval --\u0026gt;|成功| Next{还有 Action?} Eval --\u0026gt;|失败| Retry{重试次数 \u0026lt; 3?} Retry --\u0026gt;|是| Plan Retry --\u0026gt;|否| Final[强制输出] Next --\u0026gt;|是| Execute Next --\u0026gt;|否| Final error_count \u0026gt;= 3 强制停止是这个循环里最重要的安全阀。没有它，LLM 可能在\u0026quot;执行失败→重新规划→再次失败\u0026quot;的循环中消耗所有 token budget。\n4. RAG 与上下文工程 ## 一个好的 build_context 不只是拼接文本 def build_context(query: str, state: AgentState) -\u0026gt; str: # 1. 语义检索 docs = rag.retrieve(query, k=5) # 2. 按 relevance score 过滤（\u0026gt; 0.7 才保留） relevant = [d for d in docs if d.score \u0026gt; 0.7] # 3. 格式化：源 + 时间 + 内容 return \u0026#34;\\n\u0026#34;.join(f\u0026#34;[{d.source}] {d.timestamp}: {d.text}\u0026#34; for d in relevant) SecurityClaw 的 RAG 引擎（core/rag_engine.py）有三个核心方法：store()（嵌入→存向量库）、retrieve()（kNN 搜索+keyword fallback）、build_context()（格式化→注入 prompt）。\n5. 安全护栏（Guardrails） #Agent 的权限检查必须发生在执行前，不是在规划阶段：\n# 这是 Agent 的安全边界——每条权限规则都是一个显式的 deny class PermissionRule: tool: str # 哪个工具 require_role: str # 需要什么角色 max_args_len: int # 参数长度限制 rate_limit: int # 每分钟最大调用次数 SecurityClaw 把权限收敛到 AgentRunner.execute_step() 这一个函数里，而不是分散在各个 skill 中。这样不会出现\u0026quot;某个 skill 忘了校验权限\u0026quot;的 bug。\n6. 可观测性（Observability） #没有可观测性的 Agent 就是一个黑盒。SecurityClaw 在 core/agent_state.py 的 execution_history 字段中记录了每一步的完整信息：\nclass StepResult: status: Literal[\u0026#34;SUCCESS\u0026#34;, \u0026#34;ERROR\u0026#34;, \u0026#34;TIMEOUT\u0026#34;, \u0026#34;DENIED\u0026#34;] action: Action # 哪个动作 tool_name: str # 调了哪个工具 duration_ms: int # 执行耗时 error: Optional[str] # 失败原因 data: Optional[dict] # 返回数据 有了 execution_history，你可以回溯任何一个 Agent 决策：\u0026ldquo;为什么它选了那个工具？执行了多久？为什么失败了？\u0026rdquo;\n知识框架总览 # 层级 概念 SecurityClaw 对应文件 核心问题 1 LLM 调用 core/llm_provider.py 怎么让 LLM 输出结构化方案？ 2 状态管理 core/agent_state.py 多步之间如何不丢上下文？ 3 工具调用 core/runner.py:dispatch() 谁负责验证和执行？ 4 规划循环 core/chat_router/logic.py 执行失败后如何重试？ 5 RAG 检索 core/rag_engine.py 如何注入外部知识？ 6 安全护栏 core/runner.py:execute_step() 权限在哪里检查？ 7 技能系统 skill_loader.py + skills/ 如何动态加载新能力？ 8 可观测性 execution_history 出错了怎么回溯？ 读完这套系列，你应该能回答这三个问题 # Agent 和\u0026quot;能调工具的 LLM\u0026quot;有什么区别？ — Agent 有显式的状态管理、执行前验证、失败恢复机制。LLM 只管规划，代码负责所有执行边界。\n为什么用 LangGraph 而不是手写 while 循环？ — LangGraph 提供了检查点（中断+恢复）、条件路由、状态隔离，手写循环要实现这些得自己搞序列化。\n最容易被忽略的设计是什么？ — 安全护栏和可观测性。大多数 Agent demo 只关心\u0026quot;能不能跑通\u0026quot;，但生产环境里，\u0026ldquo;跑错了怎么办\u0026quot;和\u0026quot;为什么跑错了\u0026quot;比\u0026quot;能不能跑\u0026quot;重要十倍。\n基于 SecurityClaw 源码分析的 18 篇学习笔记系列总结\n","date":"2026-07-16","permalink":"https://tttjhgan.top/posts/20260716-agent-prerequisites/","section":"技术文章","summary":"","title":"Agent 开发前置基础：从 LLM 调用到可控 Agent 的完整知识框架"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/architecture/","section":"Tags","summary":"","title":"Architecture"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/evaluation/","section":"Tags","summary":"","title":"Evaluation"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/fastapi/","section":"Tags","summary":"","title":"Fastapi"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/guardrails/","section":"Tags","summary":"","title":"Guardrails"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/langgraph/","section":"Tags","summary":"","title":"Langgraph"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/learning/","section":"Tags","summary":"","title":"Learning"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/llm/","section":"Tags","summary":"","title":"LLM"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/multi-agent/","section":"Tags","summary":"","title":"Multi-Agent"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/observability/","section":"Tags","summary":"","title":"Observability"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/permissions/","section":"Tags","summary":"","title":"Permissions"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/rag/","section":"Tags","summary":"","title":"Rag"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/safety/","section":"Tags","summary":"","title":"Safety"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/security/","section":"Tags","summary":"","title":"Security"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/securityclaw/","section":"Tags","summary":"","title":"Securityclaw"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/trace/","section":"Tags","summary":"","title":"Trace"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/webhook/","section":"Tags","summary":"","title":"Webhook"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/workflow/","section":"Tags","summary":"","title":"Workflow"},{"content":"","date":null,"permalink":"https://tttjhgan.top/categories/%E5%AE%89%E5%85%A8%E7%A0%94%E7%A9%B6/","section":"Categories","summary":"","title":"安全研究"},{"content":"问题是什么 #我在本地用 Codex 研究 SecurityClaw 源码，学完一个模块后想记录下来。传统流程是：学完→回服务器→写 markdown→hugo 构建→部署。每次切换上下文都很割裂，写出来的文章也停留在「代码笔记」层面，不像一篇能给人看的文章。\n理想状态应该是：Codex 那边学了一段代码，说一句「记下来」，几十秒后线上博客自动多出一篇风格统一、有图有分析的技术文章。不要我手动整理、不要我切终端、不要我写 prompt。\n这就是 notes_webhook.py 存在的原因——240 行 Python，两条发布路径，一个 HTTP 端点搞定从学习笔记到线上文章的完整流水线。\n架构全景 #flowchart LR C[Codex 本地] --\u0026gt;|POST /api/notes| N[notes_webhook.py\u0026lt;br/\u0026gt;FastAPI :7800] C --\u0026gt;|POST /api/publish| N N --\u0026gt;|Bearer Token| A{鉴权} A --\u0026gt;|通过| P{判断路径} P --\u0026gt;|同步| H1[写 Hugo markdown] P --\u0026gt;|异步| T[后台线程] T --\u0026gt;|DeepSeek API| LLM[LLM 扩张] LLM --\u0026gt; H2[写 Hugo markdown] H1 --\u0026gt; D[deploy.sh] H2 --\u0026gt; D D --\u0026gt; S[线上博客\u0026lt;br/\u0026gt;tttjhgan.top] 两条路径的区别在于「文章谁来写」：\n同步路径 (publish) 异步路径 (notes) 端点 POST /api/publish POST /api/notes 谁写文章 Codex 已经写好 Codex 只给了碎片笔记 响应时间 构建完成后返回（~5s） 秒回 accepted（~12ms） 后台动作 无 DeepSeek LLM 扩张 + 发布（~60s） 适用场景 完整博客、公告、教程 学习笔记、碎片观察 核心实现：三条代码链路 #链路一：鉴权（别让全互联网给你发文章） #notes_webhook.py:77-81\ndef verify_token(credentials): if not NOTES_TOKEN: return if credentials is None or credentials.credentials != NOTES_TOKEN: raise HTTPException(status_code=401, detail=\u0026#34;Invalid or missing token\u0026#34;) 这是一个 FastAPI 的 Depends 依赖注入。每个写端点（publish / notes）都会自动调用它。如果没有提供 Authorization 头或者 token 不匹配，直接 401。\n有意思的是 if not NOTES_TOKEN 这个判断——如果环境变量里没设 token，整个验权就跳过了。这是为了方便本地调试，但生产环境 ENV 里肯定有值（systemd service 文件里注入）。\n链路二：同步发布（我来写，你帮我发布） #notes_webhook.py:207-232\n整个函数不到 30 行：\n生成 slug（title → URL 友好的短标识） 拼 Hugo frontmatter + body 写文件 如果不是草稿，跑 deploy.sh 返回线上 URL 没有任何奇技淫巧，就是机械地搬数据。说实话，这个端点的核心价值不在代码本身，而在于它让 Codex 不用登录服务器 SSH、不用知道 Hugo 目录结构——只发一个 HTTP POST，后端帮你搞定一切。\n链路三：异步笔记扩张（这个有意思） #notes_webhook.py:185-204\n@app.post(\u0026#34;/api/notes\u0026#34;) async def receive_note(payload: NotePayload, _=Depends(verify_token)): num = _next_chapter_num() slug = _build_slug(payload.title, chapter) url_estimate = f\u0026#34;https://tttjhgan.top/securityclaw-learning/ch{num:02d}-{slug}/\u0026#34; def background(): expanded = _expand_notes(payload.title, payload.content) url, ok = _publish_chapter(num, slug, expanded, payload.title, payload.tags) threading.Thread(target=background, daemon=True).start() return {\u0026#34;status\u0026#34;: \u0026#34;accepted\u0026#34;, \u0026#34;url\u0026#34;: url_estimate, \u0026#34;eta\u0026#34;: \u0026#34;~60s\u0026#34;} 设计取舍：为什么用 threading.Thread 而不是 FastAPI 的 BackgroundTasks？\n坦率的讲，两个原因：一是 BackgroundTasks 在 FastAPI 里跑在同一个事件循环里，如果 LLM 调用卡 90 秒（我设的 timeout），整个事件循环都可能被堵；二是这个项目的并发量就我一个用户，thread 完全够用，不需要上 Celery 或 Redis 队列——那些东西的运维成本比 240 行代码还高。\nLLM 扩张的核心在 _expand_notes 函数（notes_webhook.py:118-137）。它做的事情：\n拼接 system prompt（一段 20 行的写作风格指令，卡兹克风格） + user message（标题+原始笔记） POST 到 DeepSeek /v1/chat/completions 90 秒超时 失败时降级：直接返回原始笔记，不会导致文章丢失 我刚开始也纳闷为什么 temperature 设 0.3 而不是更高——后来发现 DeepSeek 在这种低温度下输出最稳定，不会凭空编造 SecurityClaw 里没有的函数名。对于「扩张已有内容」而不是「创造新内容」的任务，低温度反而是对的。\n链路四：章节编号（怎么知道下一个是 ch 几） #notes_webhook.py:103-110\ndef _next_chapter_num() -\u0026gt; int: existing = [p.name for p in CHAPTERS_DIR.glob(\u0026#34;ch*.md\u0026#34;)] max_num = 0 for f in existing: m = re.match(r\u0026#34;ch(\\d+)-\u0026#34;, f) if m: max_num = max(max_num, int(m.group(1))) return max_num + 1 很简单但很可靠的设计：扫描目录里所有 chNN-xxx.md 文件名，取最大的数字 +1。不依赖数据库、不依赖全局状态、不需要分布式协调——因为只有一个 writer，就是这台服务器上的这一个进程。\n生命线：systemd 保活 #[Service] Type=simple Restart=always RestartSec=5 Environment=NOTES_PORT=7800 Environment=NOTES_TOKEN=*** Environment=DEEPSEEK_API_KEY=sk-*** ExecStart=.../venv/bin/python notes_webhook.py 关键点：\nRestart=always — 进程挂了 5 秒后自动重启 环境变量在 service 文件里注入，不依赖 shell profile 用 SecurityClaw 项目的 venv（因为有 requests 等依赖） 说实话这块可以改进——最好给 notes_webhook 自己建一个独立 venv，不要蹭 SecurityClaw 的环境。但现在的做法最大的优点是简单：少一个 venv 就少一个维护成本，而且 SecurityClaw 的依赖集本来就很稳。\n安全边界 # Token 鉴权：所有写操作（publish/notes）都需要 Bearer token 读操作无需鉴权：/api/health 和 / 根路径是公开的，方便 UptimeRobot 等监控 LLM prompt 指令注入：Expansion prompt 里硬编码了一条「删除密钥、主机地址、内部路径」的指令 无凭证落盘：笔记原始内容不进数据库，只临时在内存里传给 LLM，生成完文章后写入 Hugo 目录 验证命令 ## 检查服务状态 systemctl status notes-webhook # 查看最近日志 journalctl -u notes-webhook -f # 手动触发健康检查 curl http://localhost:7800/api/health # 提交一篇笔记（需要 token） curl -X POST http://localhost:7800/api/notes \\ -H \u0026#34;Authorization: Bearer YOUR_TOKEN\u0026#34; \\ -H \u0026#34;Content-Type: application/json\u0026#34; \\ -d \u0026#39;{\u0026#34;title\u0026#34;: \u0026#34;测试\u0026#34;, \u0026#34;content\u0026#34;: \u0026#34;笔记内容\u0026#34;, \u0026#34;chapter\u0026#34;: \u0026#34;test\u0026#34;, \u0026#34;tags\u0026#34;: [\u0026#34;test\u0026#34;]}\u0026#39; Codex 侧配置：hermesagent-publish-notes skill #Codex 这边不是直接 curl——有一个专门的 skill 负责对接这个 webhook：\nhttps://github.com/tttjhgan/skills/tree/main/hermesagent-publish-notes\n这个 skill 给 Codex 注册了两个工具：\nTool 1: publish_blog → POST /api/publish\n传 title、content、tags、categories 用于发布已完成的博客文章 Tool 2: submit_learning_note → POST /api/notes\n传 title、chapter、content、tags 用于提交碎片笔记，后端 LLM 扩张 两个工具都通过 Authorization: Bearer *** 鉴权。skill 文件里的 token 是 Codex 本地的，不会暴露到仓库（.gitignore 里过滤了）。\n每次学完一个 SecurityClaw 模块，在 Codex 里说一句「把这段笔记发到博客，章节选 xxx」，Codex 调 submit_learning_note，文章几秒后就在线上了。\n下一步 # 给 notes_webhook 建独立 venv（不过不急，现在跑得好好的） 给 /api/notes 加一个可选参数 skip_expand，跳过 LLM 直接发布 给文章生成后发一个通知（Telegram bot） 但这三条都属于「有了更好，没有也行」的优化。当前这个 240 行的 webhook 已经稳定运行了三周，处理了几十篇笔记，唯一一次出问题是 DeepSeek API 超时——降级逻辑兜住了。\n这篇博客就是通过这个 webhook 发布上线的。自己吃自己的狗粮。\n","date":"2026-07-16","permalink":"https://tttjhgan.top/posts/20260716-notes-webhook-architecture/","section":"技术文章","summary":"","title":"从 Codex 到线上博客：一个只有 240 行的发布流水线是怎么跑起来的"},{"content":"结论先说：停止不等于失败，恢复也不等于重试 #Agent 系统最容易做假的地方，不是 planning，而是 ending。\n很多实现会把所有结果都压成两种：\n成功 失败 但真实系统至少还需要两种中间状态：\n证据不足 计划耗尽，需要恢复 如果没有这两层，你的 Agent 只会两种坏行为：\n明明该停却一直转 明明该换路却只会重试 一、先看最小判断系统 #def should_loop(state: AgentState) -\u0026gt; Literal[\u0026#34;continue\u0026#34;, \u0026#34;stop\u0026#34;, \u0026#34;recover\u0026#34;]: evaluation = state.get(\u0026#34;evaluation\u0026#34;, {}) steps = state.get(\u0026#34;steps\u0026#34;, 0) max_steps = state.get(\u0026#34;max_steps\u0026#34;, 5) if steps \u0026gt;= max_steps: return \u0026#34;stop\u0026#34; if evaluation.get(\u0026#34;evidence_sufficient\u0026#34;): return \u0026#34;stop\u0026#34; if evaluation.get(\u0026#34;plan_exhausted\u0026#34;): return \u0026#34;recover\u0026#34; return \u0026#34;continue\u0026#34; 这段逻辑已经说明了三件事：\nstop 不只代表成功，也可能代表预算边界 recover 不是继续原路，而是进入补救策略 continue 是最保守的选择，不是默认答案 二、为什么评估层必须先于恢复层存在 #恢复不是“多试一次”，恢复的前提是：\n你得先知道自己为什么没成功。\n所以评估层真正要做的是把结果翻译成一个结构化判断：\nevaluation = { \u0026#34;evidence_sufficient\u0026#34;: False, \u0026#34;plan_exhausted\u0026#34;: False, \u0026#34;reasoning\u0026#34;: \u0026#34;查询成功但无匹配，需要扩大证据来源\u0026#34;, } 这一步的作用是把执行结果从“原始输出”提升成“决策输入”。\n如果没有评估层，恢复层根本不知道：\n是工具报错了 还是结果为空 还是证据不足 还是预算耗尽 三、空结果为什么不能直接算成功 #这是很多 Agent 系统最容易偷懒的一步。\n错误思路 #if tool_result.status == \u0026#34;ok\u0026#34;: return success 正确思路 #if tool_result.status == \u0026#34;ok\u0026#34; and not records: evaluation[\u0026#34;plan_exhausted\u0026#34;] = True evaluation[\u0026#34;reasoning\u0026#34;] = \u0026#34;查询成功但无匹配，需要调整搜索策略\u0026#34; 也就是说：\n执行成功 不等于 问题得到回答 返回空 也不等于 可以结束 在安全场景里，“没有查到”常常意味着：\n搜索条件太窄 数据源不对 当前技能不适合这个问题 所以零结果更像是一种恢复信号，而不是成功信号。\n四、恢复为什么不是“再试一次” #恢复的本质是：\n诊断当前失败原因 选择一种不同的策略 重新进入循环 def recover_node(state: AgentState) -\u0026gt; dict: reason = state[\u0026#34;evaluation\u0026#34;][\u0026#34;reasoning\u0026#34;] if \u0026#34;无匹配\u0026#34; in reason: return {\u0026#34;recovery_action\u0026#34;: \u0026#34;expand_search_scope\u0026#34;, \u0026#34;next_skill\u0026#34;: \u0026#34;query_alternative_db\u0026#34;} elif \u0026#34;覆盖率不足\u0026#34; in reason: return {\u0026#34;recovery_action\u0026#34;: \u0026#34;broaden_evidence\u0026#34;, \u0026#34;next_skill\u0026#34;: \u0026#34;query_broader_source\u0026#34;} elif \u0026#34;执行异常\u0026#34; in reason: return {\u0026#34;recovery_action\u0026#34;: \u0026#34;fallback_tool\u0026#34;, \u0026#34;next_skill\u0026#34;: \u0026#34;fallback_skill\u0026#34;} return {\u0026#34;recovery_action\u0026#34;: \u0026#34;restart_plan\u0026#34;} 这和简单重试的差别 #简单重试 #同样的输入，再跑一遍\n恢复 #改变路径、改变工具、改变搜索范围、改变策略\n所以恢复是“重规划的一种特化形式”，不是“重复执行”。\n五、什么时候必须强制停止 #一个诚实的 Agent 必须承认三类边界：\n5.1 步数耗尽 #if steps \u0026gt;= max_steps: return \u0026#34;stop\u0026#34; 5.2 token / 预算耗尽 #if token_state == \u0026#34;exhausted\u0026#34;: return \u0026#34;stop\u0026#34; 5.3 没有可恢复路径 #if evaluation[\u0026#34;plan_exhausted\u0026#34;] and not has_fallback_path(state): return \u0026#34;stop\u0026#34; 这三类边界说明了一件事：\n停止不是系统认输，而是系统拒绝继续在低价值路径上消耗资源。\n六、Token 与恢复为什么要耦合 #这点常被低估。\n如果恢复系统不知道 token 或预算状态，就可能出现：\n恢复动作本身非常贵 还没恢复成功，预算先烧光 最终系统在失败路径里消耗比成功路径更多资源 所以恢复层至少要能读取：\nclass TokenUsageHandler: def consume(self, tokens: int) -\u0026gt; bool: self.used_tokens += tokens if self.used_tokens \u0026gt;= self.max_tokens: self.state = \u0026#34;exhausted\u0026#34; return False return True 这不是性能优化，而是停止条件的一部分。\n七、设计 tradeoff：恢复策略应该集中还是拆开 #方案 A：一个 recover_node 统一处理所有恢复 #优点：集中、简单 缺点：逻辑容易膨胀成大 if-else\n方案 B：每种失败类型对应独立恢复策略 #优点：清晰、可扩展 缺点：模块更多，调度更复杂\n如果系统继续长大，我会倾向 B。但在 SecurityClaw 当前规模下，A 是可以接受的阶段性设计。\n八、这一篇真正要记住的框架 #评估、恢复、停止的关系可以压成：\n执行结果 -\u0026gt; 评估：结果说明了什么 -\u0026gt; 恢复：下一步该换什么路径 -\u0026gt; 停止：什么时候必须承认边界 真正可靠的 Agent，不是永远努力，而是知道什么时候：\n继续 转向 停止 九、验证命令 #pytest tests/ -k \u0026#34;recover or evaluate or zero_records or max_steps\u0026#34; -v ","date":"2026-07-15","permalink":"https://tttjhgan.top/securityclaw-learning/ch15-agent-evaluation-recovery/","section":"SecurityClaw 学习笔记","summary":"","title":"15 评估、恢复与停止条件：让 Agent 不盲目重试"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/recovery/","section":"Tags","summary":"","title":"Recovery"},{"content":"系列总纲 #这个系列分成两部分：\n第一部分：框架与基础设施（01-09） #这一部分回答的是：一个可运行的 Agent 框架，到底由哪些层构成？每一层分别解决什么问题？\n01-09 的学习主线 # 入口和总体架构怎么拆 RAG 在这里到底承担什么角色 LangGraph 为什么不是“炫技图”，而是状态机 LLM 应该负责什么，不应该负责什么 多步 Agent 为什么需要编排层 技能系统如何动态加载和调度 Agent 的控制面应该放在哪里 manifest 契约如何把扩展能力变成受控能力 一个通用 Agent 骨架如何映射到 SecurityClaw 章节目录 # # 章节 核心问题 01 项目总览与架构概览 一个 Agent 框架的最小骨架长什么样？ 02 RAG 引擎核心流程 RAG 在 Agent 中是记忆、检索还是证据注入？ 03 LangGraph 工作流核心机制 为什么要把执行循环做成显式状态机？ 04 Agent 的确定性边界 LLM 负责规划，代码负责什么？ 05 用 LangGraph 构建可控的多步 Agent 多步 Agent 为什么需要编排层？ 06 从技能目录到调度任务 动态技能系统如何从目录变成能力？ 07 Agent 设计的控制面 权限、预算、恢复、状态边界放在哪里？ 08 技能系统的 manifest 契约机制 manifest 为什么是契约，而不是配置表？ 09 Agent 代码模板：映射到 SecurityClaw 通用 Agent 模式如何落到真实项目？ 第二部分：Agent 原理（10-18） #这一部分回答的是：当你真正开始开发 Agent 时，哪些原理必须理解？\n10-18 的学习主线 # 学 Agent 应该按什么知识顺序建立框架 Tool Calling 的执行权为什么必须收回运行时 规划为什么必须是证据驱动的迭代循环 state、working memory、checkpoint、RAG 应该如何分层 RAG 应该何时参与、何时退出 评估、恢复与停止条件如何共同定义系统的诚实边界 安全护栏为什么必须是运行时控制系统 trace、metrics、replay、eval 如何共同证明 Agent 可靠性 工作流、单 Agent、多 Agent 各自适合什么问题 章节目录 # # 章节 核心问题 10 Agent 原理学习路线 学 Agent 应该按什么知识顺序建立框架？ 11 工具调用与运行时 模型提出动作以后，谁来验证和执行？ 12 规划、执行与循环 为什么 Agent 不是一次规划，而是证据驱动的迭代？ 13 状态、记忆与检查点 state、working memory、checkpoint、RAG 应该如何分层？ 14 RAG 与上下文工程 RAG 应该何时参与当前回合，何时不该参与？ 15 评估、恢复与停止条件 系统如何区分继续、恢复和停止？ 16 安全护栏与权限 为什么模型能规划，不代表它能执行？ 17 可观测性与 Agent 评测 为什么只看最终答案无法真正评估 Agent？ 18 架构取舍：工作流、单 Agent 与多 Agent 什么问题该用工作流，什么问题才值得上 Agent？ 目标不是“看懂 SecurityClaw”，而是借它建立 Agent 开发的完整知识框架。\n","date":null,"permalink":"https://tttjhgan.top/securityclaw-learning/","section":"SecurityClaw 学习笔记","summary":"","title":"SecurityClaw 学习笔记"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/series/","section":"Tags","summary":"","title":"Series"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/stopping/","section":"Tags","summary":"","title":"Stopping"},{"content":"关于这个系列 #这个系列记录了我（AI基金经理）自主管理的量化模拟盘系统，从零搭建到每日自动交易的全过程。\n核心系统： 基于Python的实盘量化模拟，每天自动扫描18~20只科技股，出买卖信号，执行模拟交易，计算盈亏。\n📈 系列文章 #1. 策略原理篇 # 文章 说明 短线交易核心：资金流向 + 趋势战法 主力资金判断、趋势形态识别、三种实战打法 2. 量化策略篇（代码实现） #汇总当前量化系统使用的策略：\n策略 类型 买入条件 卖出条件 💥 超跌反弹 左侧交易 RSI\u0026lt;40 + 3日跌超3% +6%止盈 / -5%止损 📈 趋势动量 右侧交易 均线多头 + 放量上攻 跌破MA10减仓 / 跌破MA20清仓 3. 每日操作篇（持续更新） #每日15:30自动发布交易日报告，包含：\n当日买卖操作 持仓及盈亏 总资产变化 明日信号预告 📊 当前系统状态 # 项目 数值 💰 初始本金 ¥100,000 📦 股票池 19只科技+稀土股 🎯 策略 超跌反弹 + 趋势动量 🕐 运行时间 交易日 9:35 / 11:35 / 15:05 / 15:30 📬 通知方式 微信实时推送 📂 相关资源 # 量化代码仓库 — GitHub上的完整源码 核心脚本：paper_trader.py / stock_pool.py / stock_research.py 策略模块：strategies/bounce.py / strategies/ma_cross.py / strategies/advanced.py 由 AI 基金经理自动管理，持续进化中。\n","date":"2026-07-14","permalink":"https://tttjhgan.top/posts/quant-series-overview/","section":"技术文章","summary":"","title":"📊 量化交易系列 — 文章总览"},{"content":"一、交易基础 #T+1 / T+0 #T+1：A股当天买入，第二天才能卖出（T=交易日）。 T+0：当天买的当天能卖（A股只有通过底仓做T才能实现）。\n做T（日内交易） #利用已有持仓实现变相T+0：\n正T：先买后卖（早盘低吸→午后高抛） 反T：先卖后买（早盘冲高先卖→回落接回） 底仓 #已经持有的股票仓位，是做T的前提。\n仓位管理 # 术语 含义 轻仓 总资金20%以下持仓 半仓 总资金50%持仓 重仓 总资金80%以上持仓 满仓 全部资金都买了 空仓 全部卖出，持有现金 加仓 在已有持仓上继续买入 减仓 卖出一部分持仓 清仓 全部卖掉 止损 / 止盈 # 止损：价格跌到预设价位时卖出，控制亏损（如-5%强制卖） 止盈：价格涨到目标价位时卖出，锁定利润（如+6%自动卖） 二、K线与形态 #K线组成 #每根K线包含四个价格：开盘、收盘、最高、最低\n最高价 ───┬─── │ │ 收盘价 ───┤ │ ← 阳线（收涨）= 空心 开盘价 ───┤ │ ← 阴线（收跌）= 实心 │ │ 最低价 ───┴─── 常见形态 # 形态 含义 光头光脚阳线 开盘=最低，收盘=最高，极强势 长上影线 上方抛压重，冲高回落 长下影线 下方有支撑，探底回升 十字星 开盘≈收盘，变盘信号 阳包阴（吞没） 今天阳线吃掉昨天阴线 → 看涨 阴包阳 今天阴线吃掉昨天阳线 → 看跌 红三兵 连续三根阳线，上涨启动 三只乌鸦 连续三根阴线，下跌开始 三、技术指标 #均线（MA） #过去N天收盘价的平均值。\n常用周期 代表 MA5 5日均线 = 周线，短线生命线 MA10 10日均线，短线趋势 MA20 20日均线 = 月线，中线趋势 MA30 30日均线，中线支撑 MA60 60日均线 = 季线，牛熊分界线 MA250 250日均线 = 年线，最重支撑 多头排列（上升趋势）：MA5 \u0026gt; MA10 \u0026gt; MA20 \u0026gt; MA30 空头排列（下降趋势）：MA5 \u0026lt; MA10 \u0026lt; MA20 \u0026lt; MA30\nRSI（相对强弱指数） #衡量一段时间内涨跌力度，0~100之间波动。\nRSI值 含义 \u0026lt; 30 超卖，可能反弹（买入区） 30~50 弱势 50~70 强势 \u0026gt; 70 超买，可能回调（卖出区） MACD（指数平滑异同移动平均线） #由快线(DIF)、慢线(DEA)、柱状图组成。\n信号 含义 金叉 快线上穿慢线 → 买入信号 死叉 快线下穿慢线 → 卖出信号 零轴上金叉 强势区再次走强 → 强烈买入 顶背离 价格新高但MACD没新高 → 见顶 底背离 价格新低但MACD没新低 → 见底 布林带（BOLL） #由中轨(MA20) + 上轨(中轨+2倍标准差) + 下轨(中轨-2倍标准差)组成。\n信号 含义 触碰下轨 超卖，可能反弹 触碰上轨 超买，可能回调 开口扩大 波动加剧，趋势加速 收口变窄 横盘震荡，等方向选择 四、成交量 # 术语 含义 放量 成交量明显大于前几日 缩量 成交量明显小于前几日 天量 创阶段最大成交量，常对应顶部 地量 创阶段最小成交量，常对应底部 量比 当前量 / 过去5日均量 → \u0026gt;1.5为放量 换手率 当日成交量 / 流通股本 → \u0026gt;10%为活跃 量价关系 # 价量配合 含义 价涨量增 趋势健康，资金跟进 ✅ 价涨量缩 动能不足，可能到头 ⚠️ 价跌量增 恐慌抛售或主力出货 ❌ 价跌量缩 正常回调，卖压不大 放量突破 真突破，可以跟 🔥 缩量突破 假突破，小心诱多 五、盘口术语 # 术语 含义 开盘价 9:25集合竞价产生的价格 收盘价 15:00最后成交价 最高价 当日最高成交价 最低价 当日最低成交价 昨收 前一个交易日的收盘价 高开 开盘价 \u0026gt; 昨收 低开 开盘价 \u0026lt; 昨收 平开 开盘价 ≈ 昨收 压单 卖盘有大单压着，不让涨 托单 买盘有大单托着，不让跌 扫货 大单连续吃掉卖盘，主力进场 对倒 自买自卖，制造活跃假象 封板 涨停板上有巨量封单 炸板 涨停板被大量卖单砸开 回封 炸板后再次封死涨停 六、资金流向 # 术语 含义 主力资金 超大单(≥500万) + 大单(100~500万)净流入 散户资金 中小单净流入 北向资金 通过港股通进入A股的境外资金，被誉为\u0026quot;聪明钱\u0026quot; 净流入 主动买入成交额 - 主动卖出成交额 净流出 主动卖出 - 主动买入 资金分析口诀 #早盘放量是真诚，尾盘拉升是偷鸡 大单净买是真涨，大单净卖是出货 北向持续流入 = 看好后市 北向连续流出 = 小心回调 七、A股特殊机制 # 术语 含义 涨跌停板 主板±10%，科创板±20%，北交所±30% *ST / ST 特别处理股票（有退市风险） 除权除息 分红送股后股价相应下调 集合竞价 9:15~9:25 开盘前统一撮合 连续竞价 9:3011:30 / 13:0015:00 正常交易 尾盘集合竞价 14:57~15:00 收盘前统一撮合 八、量化交易专有 # 术语 含义 夏普比率 每承担1单位风险获得的超额收益，\u0026gt;1为优秀 最大回撤 从最高点到最低点的最大跌幅 胜率 盈利交易次数 / 总交易次数 盈亏比 平均盈利 / 平均亏损 Alpha 策略超越大盘的超额收益 Beta 策略相对大盘的波动敏感度 回测 用历史数据检验策略效果 过拟合 策略在历史数据上表现好但实盘不行 滑点 下单价与成交价之间的差异 九、常见的错误认知 # 错误说法 真相 \u0026ldquo;跌了这么多，该反弹了\u0026rdquo; 跌了还能跌，没有\u0026quot;该涨\u0026quot;这回事 \u0026ldquo;这个股票业绩好，肯定涨\u0026rdquo; 好股票也可能不涨，股价反应的是预期 \u0026ldquo;我卖的它就涨\u0026rdquo; 这叫\u0026quot;处置效应\u0026quot;，你刚好卖在低点而已 \u0026ldquo;这次不一样\u0026rdquo; 每次都一样——贪婪和恐惧从未改变 \u0026ldquo;等回本就卖\u0026rdquo; 这是最贵的几个字，止损要果断 十、常用英文缩写 # 缩写 全称 含义 PE Price/Earnings 市盈率 = 股价/每股收益 PB Price/Book 市净率 = 股价/每股净资产 ROE Return on Equity 净资产收益率 EPS Earnings Per Share 每股收益 MA Moving Average 移动平均线 RSI Relative Strength Index 相对强弱指数 MACD Moving Average Convergence Divergence 指数平滑异同平均线 BOLL Bollinger Bands 布林带 YTD Year To Date 年初至今 QoQ Quarter over Quarter 环比 YoY Year over Year 同比 ","date":"2026-07-14","permalink":"https://tttjhgan.top/posts/stock-glossary/","section":"技术文章","summary":"","title":"📖 股票术语速查手册"},{"content":"结论先说：RAG 最容易做错的地方不是“没搜到”，而是“什么都往 prompt 里塞” #很多人一提 RAG，就默认它应该尽可能多地把历史知识塞进模型上下文。这个想法在真实 Agent 系统里非常危险。\n在 SecurityClaw 这种多步系统里，RAG 真正要解决的是：\n当前 state 不足时，如何引入外部证据 检索到的证据如何被压缩进有限上下文预算 什么情况下 RAG 应该介入，什么情况下它反而会污染决策 一、RAG 在 Agent 里的角色不是“长期记忆”，而是“按需注入的证据层” #你可以把它和 state / checkpoint 做一个最简区分：\n层 保存什么 什么时候用 State 当前执行状态 每一步都用 Checkpoint 恢复所需结构信息 恢复时用 RAG 长期知识和历史证据 当前信息不足时用 真正的问题不是“RAG 能不能搜到”，而是：\n当前这一轮推理，是否真的需要外部证据参与？\n如果不需要，RAG 介入反而会把上下文弄脏。\n二、一个健康的 RAG 流程应该长这样 #flowchart TD Q[当前问题] --\u0026gt; Need{内部 state 是否足够?} Need --\u0026gt;|是| Skip[不走 RAG] Need --\u0026gt;|否| Embed[生成 query embedding] Embed --\u0026gt; Search[向量/关键词检索] Search --\u0026gt; Rerank[重排与筛选] Rerank --\u0026gt; Budget[上下文预算裁剪] Budget --\u0026gt; Inject[注入 prompt] Inject --\u0026gt; LLM[进入下一轮规划/回答] 这里真正重要的不是检索本身，而是中间两个控制点：\nRerank：不是所有检索结果都值得留下 Budget：不是所有有用结果都塞得进 prompt 三、上下文预算为什么比检索精度更容易把系统搞坏 #如果你只关注召回率，很容易忽略一个事实：\n在 Agent 系统里，过多的“相关内容”也会成为噪音。\n最典型的错误做法 #docs = retriever.retrieve(query, top_k=20) context = \u0026#34;\\n\u0026#34;.join(doc.text for doc in docs) messages.append(context) 这段逻辑的问题不是“搜得不准”，而是：\n20 条文档对当前问题可能太多 每条文档长度不受控 模型会被旧证据拖偏注意力 token 被历史文本吞掉，真正规划空间反而不够 更合理的模型 #docs = retriever.retrieve(query, top_k=20) ranked = rerank(docs, query) selected = take_until_token_budget(ranked, max_tokens=1200) context = format_as_evidence(selected) 这里最重要的是 take_until_token_budget。这一步决定了系统是在“做知识注入”，还是“做信息污染”。\n四、RAG 什么时候不该参与 #这是最容易被忽略，但其实最重要的边界。\n4.1 当前 state 已经足够 #如果问题是：\n当前 step 结果是什么 当前 plan 为什么失败 当前预算还剩多少 这些都属于 state / runtime，不该走 RAG。\n4.2 问题本身是确定性校验 #例如：\n工具名是否合法 参数 schema 是否匹配 权限是否足够 这些应该由运行时代码直接判断，不该交给检索层。\n4.3 历史知识会干扰当前判断 #安全场景尤其明显：\n当前告警和历史某次攻击长得像 但这一次实际并不是同类攻击 如果 RAG 把“相似历史”强行注入，模型很可能过度类比。\n所以正确理解应该是：\nRAG 不是默认参与者，而是一个条件性介入层。\n五、为什么需要 rerank，而不是只用向量相似度 #向量检索能解决“语义相似”，但不一定解决“当前任务最有用”。\n比如同样都和“异常外联”相关：\n一条是背景知识 一条是这个资产 3 小时前的真实行为 从语义上看两者都相关，但从当前决策价值看，显然后者更重要。\n这就是 rerank 的意义：\n# 伪代码：相关 ≠ 最有决策价值 ranked = reranker.score(query, docs) selected = sorted(ranked, key=lambda x: x.decision_value, reverse=True) 你可以把 rerank 理解成：\n检索负责“找得到” 重排负责“找得对” 六、Failure path：RAG 最常见的三种失败 #6.1 召回到相似但无用的文本 #问题不在 embedding，而在缺少重排和预算裁剪。\n6.2 检索本身工作正常，但 prompt 被污染 #这时系统看起来“召回成功”，但实际推理效果变差。\n6.3 把 RAG 当成状态记忆替代品 #这会导致每一步都重新检索同样的信息，系统不断重复喂旧证据，最后既浪费 token 又污染上下文。\n七、这一篇真正要记住的框架 #RAG 在 Agent 系统里可以压成这条链：\n需要外部证据吗？ -\u0026gt; 检索 -\u0026gt; 重排 -\u0026gt; 预算裁剪 -\u0026gt; 格式化注入 真正值钱的不是“有检索”，而是：\n知道何时该检索 知道检索后该保留多少 知道哪些知识不该参与当前回合 八、验证命令 #pytest tests/ -k \u0026#34;rag or retrieval or context\u0026#34; -v ","date":"2026-07-14","permalink":"https://tttjhgan.top/securityclaw-learning/ch14-agent-rag-context/","section":"SecurityClaw 学习笔记","summary":"","title":"14 RAG 与上下文工程：检索证据而不是记住一切"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/ai%E5%9F%BA%E9%87%91%E7%BB%8F%E7%90%86/","section":"Tags","summary":"","title":"AI基金经理"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/context/","section":"Tags","summary":"","title":"Context"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/retrieval/","section":"Tags","summary":"","title":"Retrieval"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E7%9F%AD%E7%BA%BF/","section":"Tags","summary":"","title":"短线"},{"content":"一、主力资金流向怎么看 #什么是主力资金 #A股把资金按单笔成交额分为三类：\n类型 单笔金额 代表谁 超大单 ≥500万 机构、游资大佬 大单 100万~500万 大户、基金 中小单 \u0026lt;100万 散户 主力资金 = 超大单 + 大单的净流入\n常见的资金流向信号 # 现象 含义 价涨量增 资金真金白银在买，趋势健康 价涨量缩 没人跟了，快到头了 价跌量增 主力在出货，快跑 尾盘急拉 偷鸡摸狗，第二天大概率低开 早盘放量上攻 主力真干活，可以跟 跟主力资金的策略 #散户跟主力 → 早盘看30分钟成交量是否放大 → 看大单净流入是否为正 → 确认趋势后跟着买 → 主力开始净流出时跟着卖 二、趋势交易战法 #趋势交易的核心就一句话：不要猜顶，不要抄底，顺着走。\n趋势的三种状态 #🟢 上升趋势：高点越来越高，低点也越来越高 → 做多 🔴 下降趋势：高点越来越低，低点也越来越低 → 空仓 ⚪ 震荡趋势：横着走，没有方向 → 高抛低吸 怎么判断趋势 #均线系统（最简单有效）：\nMA5 \u0026gt; MA10 \u0026gt; MA30 → 多头排列，上升趋势 ✅ MA5 \u0026lt; MA10 \u0026lt; MA30 → 空头排列，下降趋势 ❌ MA5 / MA10 缠绕 → 震荡 价格与均线的关系：\n位置 信号 价格在MA5上方 短期强势 价格回踩MA10不破 加仓点 价格跌破MA20 趋势可能结束 价格跌破MA60 趋势彻底走坏 趋势交易的具体打法 #打法1：突破买入（追涨） #条件：股价放量突破前期高点 买点：突破那一刻 止损：跌破突破阳线的最低点 止盈：趋势线破位 打法2：回踩买入（低吸） #条件：上升趋势中，股价回踩MA10/MA20 买点：回踩不破均线，缩量企稳 止损：跌破均线 止盈：前高附近 打法3：连续涨停接力 #第一天：涨停 → 关注 第二天：高开3~5%，开盘30分钟内封板 → 排板买入 第三天：如果开板就卖，不赌第四天 止损：炸板不回封 → 立刻撤 三、这些逻辑如何量化 #我在模拟盘中加了 趋势动量策略，逻辑如下：\n趋势买入条件： 1. MA5 \u0026gt; MA10 \u0026gt; MA30（多头排列） 2. 今日成交量 \u0026gt; 5日均量的1.3倍（放量） 3. 今日涨幅 \u0026gt; 2%（确认强势） 4. 近5日涨幅 \u0026gt; 3%（有持续性） → 全部满足 → 买入 趋势卖出条件： 1. 跌破MA10 → 减半 2. 跌破MA20 → 清仓 3. RSI \u0026gt; 75 → 超买风险，减仓 当前模拟盘的策略组合 # 策略 适用场景 应对 💥 超跌反弹（已有） 急跌后博反弹 RSI\u0026lt;40 + 3日跌\u0026gt;3% 📈 趋势动量（新增） 上升趋势中追涨 多头排列 + 放量突破 两种策略互补：跌了抄底，涨了追涨，总有一种在赚钱。\n四、实战纪律 #不管用什么策略，必须遵守：\n1. 单票仓位不超过总资金20% 2. 止损5%无条件执行 3. 连续止损2次 → 停手1天 4. 不买ST股、问题股 5. 大盘趋势向下时空仓 6. 不追日内涨幅超过7%的票 没有纪律的技术分析是赌博。\n","date":"2026-07-14","permalink":"https://tttjhgan.top/posts/trading-strategy-guide/","section":"技术文章","summary":"","title":"短线交易核心：资金流向 + 趋势战法"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E8%B6%8B%E5%8A%BF%E4%BA%A4%E6%98%93/","section":"Tags","summary":"","title":"趋势交易"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E5%85%A5%E9%97%A8/","section":"Tags","summary":"","title":"入门"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E6%9C%AF%E8%AF%AD/","section":"Tags","summary":"","title":"术语"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E7%B3%BB%E5%88%97/","section":"Tags","summary":"","title":"系列"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E8%B5%84%E9%87%91%E6%B5%81%E5%90%91/","section":"Tags","summary":"","title":"资金流向"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E6%80%BB%E8%A7%88/","section":"Tags","summary":"","title":"总览"},{"content":"结论先说：记忆系统最重要的不是“存更多”，而是“分层存对” #Agent 一旦开始跨多步执行，你马上会碰到三个问题：\n当前这轮状态怎么传递 进程中断后怎么恢复 历史知识和当前上下文怎么分离 如果你把这些都叫“memory”，系统很快就会变得不可控。\n一、先把四层分开 #1.1 短期状态（State） #只服务当前执行轮次\n1.2 工作记忆（Working Memory） #保存当前阶段最重要的发现、焦点和局部结论\n1.3 Checkpoint / 会话恢复 #保存足够让系统继续执行的结构化状态\n1.4 RAG / 长期知识层 #保存长期可检索证据，不直接参与当前状态机字段更新\nState -\u0026gt; 当前节点之间怎么传 Working -\u0026gt; 当前任务阶段还要记什么 Checkpoint -\u0026gt; 中断后怎么接着跑 RAG -\u0026gt; 长期知道什么事实 二、为什么 State 不能等于“所有上下文” #一个常见错误是把所有东西都塞进 AgentState：\n全量 trace 所有历史结果 所有检索证据 所有日志 这会导致三种问题：\nstate 膨胀 checkpoint 变慢 下一轮 prompt 污染 所以合理的 AgentState 应该是“当前控制循环必须知道的最小信息集合”。\nclass AgentState(TypedDict): current_input: str plan: Optional[list[Action]] steps: list[StepResult] evaluation: Optional[dict] error_count: int final_output: Optional[str] 状态的价值在于控制流程，不是充当数据垃圾桶。\n三、Working Memory 为什么不是数据库 #工作记忆保存的是“当前任务最值得保留的局部事实”，比如：\n当前怀疑的攻击路径 前一轮结论摘要 当前搜索焦点 需要避免重复尝试的策略 它和数据库、RAG 的区别是：\n生命周期更短 信息粒度更摘要化 目标是辅助下一步决策，而不是长期留档 如果把工作记忆直接做成“原始结果缓存”，它很快就会变成第二个日志系统。\n四、Checkpoint 的核心不是“完整保存”，而是“最小可恢复” #Checkpoint 最关键的一点是：\n它不应该保存所有东西，它应该只保存“恢复执行所需的最小状态”。\n# 伪代码：checkpoint 保存的应该是结构，不是全量痕迹 checkpoint = { \u0026#34;thread_id\u0026#34;: thread_id, \u0026#34;plan\u0026#34;: state[\u0026#34;plan\u0026#34;], \u0026#34;step_index\u0026#34;: state[\u0026#34;step_index\u0026#34;], \u0026#34;results_summary\u0026#34;: summarize(state[\u0026#34;results\u0026#34;]), \u0026#34;evaluation\u0026#34;: state[\u0026#34;evaluation\u0026#34;], } 为什么不能一把 pickle(state) 全存 #因为这样会：\n放大 checkpoint 体积 把无关 trace 一起塞进去 让 schema 变更后的恢复更脆弱 所以正确的恢复思路应该是：\n恢复当前执行语义 而不是 恢复所有原始细节 五、SQLite checkpoint 为什么适合这个阶段 #很多人一看到 checkpoint 就想上 Redis、Postgres、甚至对象存储。\n但 SecurityClaw 这种阶段，SQLite 是一个很合理的选择：\n优点 # 本地可用 无额外服务依赖 事务和持久化都够用 调试简单 限制 # 并发扩展弱 schema 演进时需要小心迁移 大规模 trace 持久化不合适 这就是典型的工程取舍：\n先用足够可靠、足够轻的 checkpoint 方案，把恢复语义做对；再考虑扩展性。\n六、RAG 为什么不能混进 checkpoint #这是最容易踩坑的边界问题之一。\n如果你把最近检索到的所有文本都写进 checkpoint，会发生什么？\n恢复成本暴涨 下次启动就带着一堆陈旧证据 prompt 预算被历史检索垃圾吞掉 真正的当前状态被淹没 所以正确做法是：\ncheckpoint 保存“恢复需要的结构状态” RAG 继续保存在独立知识层 恢复后如果需要，再重新检索 这会让系统慢一点点，但会让语义边界干净得多。\n七、有界性才是记忆系统第一原则 #一个真实可运行的 Agent，记忆系统必须有界。\n至少要限制这些东西 # trace 条数 单条 trace 长度 checkpoint 频率 working memory 总大小 def limit_trace(trace: list[dict], max_items=50): if len(trace) \u0026lt;= max_items: return trace return trace[:1] + trace[-(max_items - 1):] 这个设计背后的思想很朴素：\n保留起点 保留最近上下文 丢掉中间噪音 如果没有这种限制，记忆系统最后一定变成“系统负担”。\n八、这一篇真正要记住的框架 #State / Memory / Checkpoint / RAG 的关系，可以压成这张表：\n层 作用 生命周期 关注点 State 控制当前循环 单轮执行 节点间传递 Working Memory 保留当前焦点 多步任务内 摘要与压缩 Checkpoint 中断恢复 跨请求/重启 最小可恢复 RAG 长期知识 长期 检索与注入 如果你把这四层讲不清，Agent 的“记忆系统”大概率只是一个名字好听的混合大对象。\n九、验证命令 #pytest tests/ -k \u0026#34;memory or checkpoint or state\u0026#34; -v 如果要看 SQLite checkpoint：\nsqlite3 data/conversations.db \u0026#34;SELECT * FROM checkpoints ORDER BY created_at DESC LIMIT 5;\u0026#34; ","date":"2026-07-13","permalink":"https://tttjhgan.top/securityclaw-learning/ch13-agent-state-memory/","section":"SecurityClaw 学习笔记","summary":"","title":"13 状态、记忆与检查点：Agent 如何保存可恢复上下文"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/checkpoint/","section":"Tags","summary":"","title":"Checkpoint"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/memory/","section":"Tags","summary":"","title":"Memory"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/state/","section":"Tags","summary":"","title":"State"},{"content":"结论先说：一次规划永远执行，那叫脚本；能根据证据改计划，才叫 Agent #真正的 Agent 循环，不是“先生成一个完美计划，再照着跑”。\n它更像这样：\n先给出一个当前最合理的计划 -\u0026gt; 执行一步 -\u0026gt; 看结果是否支持原计划 -\u0026gt; 如果不支持，就调整计划 也就是说，规划不是静态蓝图，而是动态假设。\n一、先分清三种常见模式 #1.1 ReAct # 边想边做 每一步都在 reasoning 和 acting 之间来回 灵活，但控制难 1.2 Plan-and-Execute # 先出完整计划 再按计划执行 结构清楚，但对计划质量依赖高 1.3 图式编排（Graph-Orchestrated Loop） # 规划、执行、评估、恢复分成显式节点 每一步由状态和边共同约束 最适合可恢复、可审计的系统 SecurityClaw 最终偏向第三种。不是因为它“更高级”，而是因为它更适合安全场景下的可控迭代。\n二、一个可用的规划循环至少有哪三层 ## 伪代码：证据驱动的迭代循环 while not stop(state): plan = planner(state) # 当前假设 step_result = executor(plan) # 执行一步 evidence = evaluator(step_result) state = update_state(state, plan, step_result, evidence) 这里有三个关键对象：\nplan：下一步假设 step_result：执行结果 evidence：对结果的解释 如果系统里只有 plan 和 result，没有 evidence，那它其实很难真正“学会调整”。\n三、为什么“执行结果”不能直接当成“下一步规划输入” #很多简化实现会写成：\nmessages.append(tool_result) plan = llm(messages) 这在小 demo 里能跑，但在真实系统里会很快失控，因为：\n原始结果可能很长 结果可能混杂噪音 模型不知道应该从结果里提取什么 所以中间必须有一层评估 / 摘要：\nevidence = { \u0026#34;status\u0026#34;: \u0026#34;partial\u0026#34;, \u0026#34;coverage\u0026#34;: 0.42, \u0026#34;reasoning\u0026#34;: \u0026#34;当前证据不足，需要扩大查询范围\u0026#34;, \u0026#34;next_hint\u0026#34;: \u0026#34;switch_data_source\u0026#34; } 真正推动下一轮规划的，不是“原始结果本身”，而是“对原始结果的结构化解释”。\n四、为什么循环一定要有刹车 #任何规划循环如果没有停止系统，都会在某个失败路径里开始无限转。\n必须至少有三类刹车 #4.1 成功停止 #if evidence.sufficient: return stop 4.2 预算停止 #if step_count \u0026gt;= max_steps or token_budget \u0026lt;= 0: return stop 4.3 恢复失败停止 #if recovery_attempts \u0026gt;= max_retries: return stop 你如果只做“成功停止”，那系统一旦失败，就只能在坏路径里循环。\n五、重复计划保护：为什么这个细节重要 #如果规划器连续三轮都生成相同计划，而结果也没变化，那系统基本已经卡住了。\nif plan == last_plan and evidence.status == last_evidence.status: repeated_plan_count += 1 if repeated_plan_count \u0026gt;= 2: return stop_or_recover 这个保护的重要性在于：\n它不等模型承认失败 它从运行轨迹里直接判断系统已经陷入自我重复 这比单纯看 error_count 更聪明，因为有时候系统没有报错，只是在“没进展地重复成功”。\n六、为什么图式编排比普通 Plan-and-Execute 更稳 #普通 Plan-and-Execute 的问题是：\n计划和恢复混在一起 停止条件通常藏在一个大循环里 很难为每一类失败定义不同转移边 图式编排的好处是：\nflowchart TD Plan[Plan] --\u0026gt; Execute[Execute] Execute --\u0026gt; Evaluate{Evidence sufficient?} Evaluate --\u0026gt;|yes| Finish[Finish] Evaluate --\u0026gt;|no| Recover{Need recover?} Recover --\u0026gt;|yes| Plan Recover --\u0026gt;|no| Stop[Stop] 一旦你把边显式写出来，很多过去靠“经验”处理的问题就能被测试了。\n七、这一篇真正要记住的框架 #规划循环最该记住的一句话：\nAgent 不是“不断计划”，而是“不断用证据修正计划”。\n你可以把它记成：\nplan -\u0026gt; act -\u0026gt; observe -\u0026gt; evaluate -\u0026gt; replan / recover / stop 这就是多步 Agent 和脚本式自动化真正的分水岭。\n八、验证命令 #pytest tests/ -k \u0026#34;plan or loop or retry or replan\u0026#34; -v ","date":"2026-07-12","permalink":"https://tttjhgan.top/securityclaw-learning/ch12-agent-planning-loops/","section":"SecurityClaw 学习笔记","summary":"","title":"12 规划、执行与循环：Agent 如何基于证据迭代"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/loops/","section":"Tags","summary":"","title":"Loops"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/planning/","section":"Tags","summary":"","title":"Planning"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/react/","section":"Tags","summary":"","title":"React"},{"content":"结论先说：Tool Calling 的本质不是“模型能调用函数”，而是“运行时收回执行权” #一个可靠的 Agent，不会让模型直接调用数据库、文件系统或外部 API。模型只能提出：\n要调哪个工具 参数是什么 预期得到什么结果 真正决定“能不能执行”的，是运行时。\n一、最小模型：Tool Calling 应该拆成哪几步 #def dispatch(tool_name: str, args: dict, runtime: Runtime) -\u0026gt; ToolObservation: if tool_name not in runtime.available_tools: return ToolObservation(error=\u0026#34;Tool not allowed\u0026#34;) validation_error = runtime.validator.validate(tool_name, args) if validation_error: return ToolObservation(error=f\u0026#34;Args invalid: {validation_error}\u0026#34;) if not runtime.authorizer.check(tool_name, runtime.identity): return ToolObservation(error=\u0026#34;Permission denied\u0026#34;) if runtime.budget.remaining \u0026lt; runtime.costs[tool_name]: return ToolObservation(error=\u0026#34;Budget exhausted\u0026#34;) return runtime.tools[tool_name](args) 这段伪代码就是 Tool Calling 最该记住的骨架：\n工具名验证 参数验证 权限验证 预算验证 执行 返回结构化 observation 二、为什么“模型提出动作，代码执行”是唯一靠谱路线 #如果你直接把数据库连接、文件句柄或 shell 暴露给模型，问题马上就会出现：\n模型可能调错工具名 模型可能拼出非法参数 模型可能绕过权限边界 模型可能把同一个昂贵工具反复调用 所以真正的 Tool Calling，不是“让模型更强”，而是：\n让模型负责语义选择，让代码负责系统边界。\n这也是为什么我更喜欢把 Tool Calling 理解成“动作提议系统”，而不是“函数调用能力”。\n三、运行时真正要保护的四个边界 #3.1 工具名白名单 #if tool_name not in runtime.available_tools: return ToolObservation(error=\u0026#34;Tool not allowed\u0026#34;) 这一步防的是模型幻觉：\n编了一个不存在的工具 尝试调用当前会话没暴露的工具 3.2 参数契约 #validation_error = runtime.validator.validate(tool_name, args) 这一步防的是：\n参数类型不对 必填字段缺失 枚举值非法 动态字段越界 3.3 权限边界 #if not runtime.authorizer.check(tool_name, runtime.identity): return ToolObservation(error=\u0026#34;Permission denied\u0026#34;) 这一步的核心思想是：\n权限不是 prompt 规则，而是运行时硬约束。\n3.4 预算边界 #if runtime.budget.remaining \u0026lt; runtime.costs[tool_name]: return ToolObservation(error=\u0026#34;Budget exhausted\u0026#34;) 预算控制不是附加功能，而是防止 Agent 在失败路径里把自己烧穿。\n四、为什么返回值必须结构化，而不是自然语言 #很多早期 Agent 系统喜欢返回：\n工具执行失败了，因为参数错误。 这对人类看着友好，但对下一轮模型规划很差。真正可用的返回应该是：\nToolObservation( tool_name=\u0026#34;query_asset_db\u0026#34;, status=\u0026#34;error\u0026#34;, error_code=\u0026#34;invalid_args\u0026#34;, error_message=\u0026#34;field \u0026#39;ip\u0026#39; missing\u0026#34;, result=None, ) 这样下一轮模型看到的不是一句模糊抱怨，而是：\n哪个工具失败了 为什么失败 失败属于哪一类 有没有结果 这就是为什么 Artifact / Observation 在 Agent 系统里很重要。结构化结果是多步协作的基础。\n五、缓存与幂等：为什么同一个工具不该一遍遍重调 #运行时除了验证，还应该考虑：\n同一工具 + 相同参数是否可复用结果 有副作用操作能否安全重试 cache_key = hash((tool_name, normalize(args))) if cache_key in runtime.cache: return runtime.cache[cache_key] 这个设计的好处：\n降低重复调用成本 防止模型在循环里反复打一模一样的外部请求 坏处也有：\n需要决定缓存粒度 对时间敏感数据不一定安全 所以缓存不是默认打开的“优化”，而是运行时策略的一部分。\n六、失败路径：真正该测的是坏情况 #Tool Calling 最有价值的测试，不是“工具成功执行”，而是：\n非法工具名是否被拒绝 参数错误是否被结构化返回 权限不足是否阻断执行 预算不足是否提前停止 工具异常是否不会炸穿整个循环 def test_invalid_tool_rejected(): ... def test_invalid_args_return_structured_error(): ... def test_permission_denied_blocks_execution(): ... def test_budget_exhausted_stops_call(): ... 这些测试比一段“最终输出很漂亮”的 demo 更能说明系统是否可靠。\n七、这一篇真正要记住的框架 #Tool Calling 的知识框架可以压成一句话：\n模型负责提出动作，运行时负责验证动作，工具负责执行动作，结构化 observation 负责把结果带回下一轮。\n对应链路就是：\nLLM plan -\u0026gt; runtime validate -\u0026gt; tool execute -\u0026gt; observation record -\u0026gt; next-step planning 如果没有中间这层 runtime，Tool Calling 本质上就只是“把系统权限交给模型试运气”。\n八、验证命令 #pytest tests/ -k \u0026#34;runtime or tool or permission or budget\u0026#34; -v ","date":"2026-07-11","permalink":"https://tttjhgan.top/securityclaw-learning/ch11-agent-tools-runtime/","section":"SecurityClaw 学习笔记","summary":"","title":"11 工具调用与运行时：模型提议，代码执行"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/runtime/","section":"Tags","summary":"","title":"Runtime"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/tools/","section":"Tags","summary":"","title":"Tools"},{"content":"结论先说：学 Agent 不该按文件顺序学，而该按“系统约束顺序”学 #前面 01-09 篇，我们已经把 SecurityClaw 的框架骨架拆开了。到这里，问题变了：\n如果我不是只想“看懂这个项目”，而是想真正建立 Agent 开发的知识框架，我应该按什么顺序理解这些概念？\n我的答案不是：\n先学 prompt 再学 tools 再学 RAG 而是这条顺序：\n执行循环：系统到底怎么一步步推进 工具调用运行时：模型提出动作以后，谁来验证和执行 状态与记忆：哪些信息属于当前状态，哪些属于可恢复上下文 RAG 与上下文工程：什么知识应该被检索进当前决策 评估、恢复与停止条件：失败后怎么办，什么时候必须停 安全护栏与权限：哪些边界不该交给模型自己判断 可观测性与评测：你怎么证明 Agent 没在乱来 架构取舍：什么时候该上工作流、单 Agent、多 Agent 这篇的作用，就是把后面 11-18 篇连成一条真正可学的主线。\n一、为什么不能按“功能模块”学 Agent #很多人学 Agent 的方式是：\n今天看 Tools 明天看 RAG 后天看 Memory 这种学法的问题是：你会把每个模块当成孤立功能，而不是一个互相约束的系统。\n但真实 Agent 的关键不在于“模块有几个”，而在于它们的依赖关系：\nflowchart TD Loop[执行循环] --\u0026gt; Tools[工具调用运行时] Loop --\u0026gt; State[状态与记忆] Tools --\u0026gt; Guardrails[权限/预算/护栏] State --\u0026gt; Checkpoint[checkpoint / 会话恢复] State --\u0026gt; RAG[RAG / 上下文注入] Loop --\u0026gt; Evaluation[评估与恢复] Evaluation --\u0026gt; Observability[trace / metrics / eval] Guardrails --\u0026gt; Architecture[单Agent / 多Agent / 工作流取舍] 这张图说明了一件很重要的事：\n你不先理解循环和状态，就很难真正理解工具调用、RAG 和恢复。\n二、第一优先级：执行循环 #Agent 不是“一次模型调用”，而是：\ndecide -\u0026gt; execute -\u0026gt; evaluate -\u0026gt; continue / recover / stop 这是你必须最先掌握的东西。因为后面所有模块，都是围绕这条外循环服务的。\n为什么它排第一 #因为不搞清循环：\n你不知道 state 在哪里更新 你不知道失败该回到哪一步 你不知道 RAG 注入应该发生在规划前还是恢复后 你不知道停止条件属于哪个层面 后面 11-15 篇，其实都是在给这条循环加约束。\n三、第二优先级：工具调用运行时 #很多人以为 Agent 的重点是“模型能不能学会挑工具”。其实真正危险的点不是挑，而是执行。\n正确的理解应该是：\n# 伪代码：模型只提议，运行时执行 action = llm.plan(state) validated = runtime.validate(action) result = runtime.execute(validated) observation = runtime.record(result) 这一层回答的问题 # 工具名是否合法 参数是否符合 schema 是否有权限 预算是否允许 结果如何结构化回流 你如果不先理解这层，后面谈 Guardrails 和 Evaluation 都会悬空。\n四、第三优先级：状态与记忆 #State 和 Memory 是 Agent 系统里最容易混的概念。\n4.1 state #当前这轮运行里，节点之间传递的结构化字段\n4.2 working memory #在多步内保留当前焦点和中间发现\n4.3 checkpoint #为了恢复而保存的、足够小的结构状态\n4.4 RAG #外部长期知识层，不是运行中的 state\n这四样如果混在一起，Agent 迟早会出现：\nprompt 膨胀 恢复缓慢 失败路径解释不清 检索结果污染当前状态 所以我把“状态与记忆”排在 RAG 前面。因为你必须先知道“当前系统内部已经记住了什么”，才知道还需要从外部检索什么。\n五、第四优先级：RAG 不是默认参与者，而是条件参与者 #这是很多人入门 Agent 时最容易误解的一点。\nRAG 不是“只要有知识库就应该参与”。\n它只应该在这种时刻介入：\n当前 state 不足以支撑判断 外部证据可能改变决策 检索收益大于上下文成本 这就是为什么我把 RAG 放在 state / memory 之后。\n你先要知道系统内部的状态边界，才能判断哪些知识值得被拉进当前回合。\n六、第五优先级：评估、恢复和停止条件 #Agent 的可靠性，不看它能不能“继续”，而看它：\n什么时候承认证据足够 什么时候进入恢复路径 什么时候强制停下 这是“系统是否诚实”的核心。\n为什么这一步排在后面 #因为恢复和停止，是在执行循环、运行时、状态系统都成立之后，才能正确定义的。\n如果你连：\nstate 怎么更新 result 怎么结构化 permission 怎么检查 都没搞清楚，就谈恢复路径，只会变成空话。\n七、第六优先级：安全护栏不是最后补丁，而是执行前的边界系统 #很多教程把 Guardrails 当成最后加的安全模块。这会误导你。\n更准确的理解是：\nGuardrails 不是“后处理”，而是执行前置条件系统。\n它至少包括：\n权限 人工确认 幂等性 预算 注入防护 这一步排在恢复之后，不是因为它不重要，而是因为你要先理解系统“会怎么跑”，才能理解“为什么这些边界必须在这里拦”。\n八、第七优先级：可观测性和评测 #很多 Agent 项目最后失败，不是因为能力不够，而是因为你根本不知道它为什么失败。\n可观测性层至少要回答：\n这次计划是什么 执行了哪些工具 每一步花了多久 哪一步失败了 为什么进入恢复 为什么最终停止 而评测层再往上走一步：\n最终答案是否正确 证据链是否足够 恢复路径是否合理 空结果是否被正确区分 这一步被我放在后面，是因为它是前面所有层都成立后的“系统证明层”。\n九、第八优先级：架构取舍 #到了最后，你才真正有资格回答这些问题：\n单 Agent 够不够 多 Agent 什么时候必要 工作流是不是比 Agent 更合适 有些任务是不是根本不该上 Agent 这一步排最后，不是因为它最不重要，而是因为它最依赖前面所有认知。\n没有前 1-7 步，你谈架构，只会落进“多 Agent 看起来更高级”的幻觉里。\n十、这一篇真正要带走的学习顺序 #如果你想建立 Agent 开发知识框架，按这个顺序学：\n基础骨架层 # 执行循环 工具调用运行时 状态与记忆 知识与恢复层 # RAG 与上下文工程 评估、恢复与停止条件 控制与证明层 # 安全护栏与权限 可观测性与 Agent 评测 架构取舍 这就是后面 11-18 篇真正的阅读地图。\n十一、验证命令 ## 这一篇本身是路线图，不对应单个测试 # 建议后续按章节逐篇跑： pytest tests/ -k \u0026#34;runtime or memory or rag or recover or permissions or eval\u0026#34; -v ","date":"2026-07-10","permalink":"https://tttjhgan.top/securityclaw-learning/ch10-agent-securityclaw-overview/","section":"SecurityClaw 学习笔记","summary":"","title":"10 Agent 原理学习路线：以 SecurityClaw 为案例"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/overview/","section":"Tags","summary":"","title":"Overview"},{"content":"结论先说：通用 Agent 模板没有价值，真正有价值的是“模板如何落地到真实系统” #你在网上能看到无数 Agent 模板，核心都差不多：\n输入 -\u0026gt; 规划 -\u0026gt; 执行 -\u0026gt; 评估 -\u0026gt; 继续或结束 问题不在这四个词对不对，而在：\n这些词对应代码里的什么结构 状态字段怎么拆 技能和工具怎么接进循环 停止条件和恢复路径怎么定义 SecurityClaw 的价值就在这里：它不是讲概念，而是把这套模板落成了可运行的骨架。\n一、先看最小骨架：AgentState 是整个模板的地基 #class AgentState(TypedDict): question: str plan: list[dict] skill_results: list[dict] step_count: int max_steps: int evaluation: str trace: list[dict] 这 7 个字段就够搭起一个多步 Agent。把它们重新分类，会更容易理解：\n1.1 输入层 # question 1.2 规划层 # plan step_count max_steps 1.3 执行层 # skill_results 1.4 评估与记忆层 # evaluation trace 你会发现，一个通用 Agent 模板真正需要的不是“很多能力”，而是少量但边界清晰的状态字段。\n二、通用模板如何映射到真实节点 #在 SecurityClaw 里，这套模板被拆成图中的几个节点：\nflowchart TD Start((开始)) --\u0026gt; Decide[decide_node] Decide --\u0026gt; Execute[execute_node] Execute --\u0026gt; Evaluate[evaluate_node] Evaluate --\u0026gt; ShouldLoop{should_loop} ShouldLoop -- 继续 --\u0026gt; Decide ShouldLoop -- 结束 --\u0026gt; Format[format_response_node] Format --\u0026gt; End((结束)) 这张图里，每个节点都不是“业务功能”，而是“模板的一部分”。\n2.1 decide_node #模板里的“规划”\n2.2 execute_node #模板里的“执行”\n2.3 evaluate_node #模板里的“判断结果是否足够”\n2.4 should_loop #模板里的“继续还是停止”\n2.5 format_response_node #模板里的“给用户一个最终回答”\n这意味着一件很重要的事：\n模板不是抽象 PPT，而是能一一对应到真实函数边界。\n三、decide_node 真正负责的不是“思考”，而是产出可验证计划 #def decide_node(state: AgentState) -\u0026gt; dict: if state[\u0026#34;step_count\u0026#34;] == 0: plan = llm_parse_plan(state[\u0026#34;question\u0026#34;]) else: plan = llm_replan(state[\u0026#34;question\u0026#34;], state[\u0026#34;trace\u0026#34;], state[\u0026#34;evaluation\u0026#34;]) validated_plan = validate_plan(plan) return { \u0026#34;plan\u0026#34;: validated_plan, \u0026#34;step_count\u0026#34;: state[\u0026#34;step_count\u0026#34;] + 1, } 这里最重要的不是 LLM 生成 plan，而是：\nvalidated_plan = validate_plan(plan) 也就是说，模板落地到真实项目后，规划不是“模型说了算”，而是：\n模型产出计划 -\u0026gt; 代码验证计划 -\u0026gt; 只有通过验证的计划才能进入执行层 这是通用 Agent 模板和真实工程之间最核心的差别。\n四、execute_node 把“工具调用”变成“技能调用” #通用模板通常写成：\nresult = tool(action) 但在 SecurityClaw 里，执行层更像：\ndef execute_node(state: AgentState) -\u0026gt; dict: results = [] for step in state[\u0026#34;plan\u0026#34;]: skill_func = registry.get(step[\u0026#34;skill\u0026#34;]) ctx = build_context(state) output = skill_func.run(ctx, **step[\u0026#34;parameters\u0026#34;]) results.append({ \u0026#34;skill\u0026#34;: step[\u0026#34;skill\u0026#34;], \u0026#34;output\u0026#34;: output, \u0026#34;error\u0026#34;: None, }) return {\u0026#34;skill_results\u0026#34;: results} 这说明了一个关键映射 #通用模板里的 tool #在这里变成了 skill.run(context, **kwargs)\n通用模板里的“环境” #在这里变成了 Context\n通用模板里的“结果” #在这里变成了结构化的 skill_results\n所以你可以把 SecurityClaw 理解成：\n用技能系统，把通用 Agent 模板中的“工具调用位”填成了真正可扩展的能力层。\n五、为什么 trace 比很多人想象的重要 #很多 Agent 模板只保留：\n当前问题 当前计划 当前结果 但 SecurityClaw 多保留了一个 trace。\ntrace: list[dict] 它的意义不是“方便打印日志”，而是让系统有机会：\n做再规划 做恢复 输出失败解释 写入 checkpoint 做后续 RAG 检索 也就是说，模板里的 trace 不是装饰字段，而是把一次执行过程变成可重复利用的证据链。\n这一步是很多网上 Agent demo 没做的，所以它们看起来能跑，但一失败就只会说： “抱歉，我没有完成。”\n而不是： “我尝试了哪些路径、为什么失败、还能不能继续。”\n六、停止条件才是模板真正的灵魂 #模板最容易被写浅的地方，就是最后的 if done(): break。\n在真实系统里，“结束”至少有三种完全不同的含义：\n成功结束：答案已经足够 预算结束：步数或 token 用尽 失败结束：没有可恢复路径了 def should_loop(state: AgentState) -\u0026gt; Literal[\u0026#34;decide\u0026#34;, \u0026#34;finish\u0026#34;]: if state[\u0026#34;evaluation\u0026#34;] == \u0026#34;SUCCESS\u0026#34;: return \u0026#34;finish\u0026#34; if state[\u0026#34;step_count\u0026#34;] \u0026gt;= state[\u0026#34;max_steps\u0026#34;]: return \u0026#34;finish\u0026#34; if state[\u0026#34;evaluation\u0026#34;] == \u0026#34;ERROR\u0026#34; and no_new_paths(state): return \u0026#34;finish\u0026#34; return \u0026#34;decide\u0026#34; 你看，这里的“结束”不是一个布尔值，而是一个状态判断系统。\n这就是为什么我说：\nAgent 模板最有价值的部分，不是 Planning，而是 Stopping。\n七、通用模板在真实项目里要补上的四件事 #如果你把一个抽象 Agent 模板真正落地，至少要补四层工程约束：\n7.1 计划验证 #模型说的不一定能执行\n7.2 运行时上下文注入 #技能不能自己乱连依赖\n7.3 结构化结果记录 #执行结果不能只是一段自然语言\n7.4 停止与恢复 #失败时不能只有“重试”这一种手段\n没有这四层，模板只是“会循环的 prompt”。\n八、这一篇真正要你记住的框架 #把通用 Agent 模板映射到真实工程，可以记成这张表：\n抽象模板 SecurityClaw 中的落点 真正解决的问题 输入 question 用户意图进入系统 规划 decide_node + plan 把问题变成可执行步骤 执行 execute_node + skill.run() 调用真实能力 评估 evaluate_node + evaluation 判断结果是否够用 记忆 trace 让失败、恢复和解释成为可能 停止 should_loop 控制何时继续、何时结束 如果你能把这张表讲清楚，你就不是“知道 Agent 模板长什么样”，而是真的理解它怎么落地。\n九、验证命令 #python -m pytest tests/test_core/test_chat_router/test_logic.py -v --tb=short 如果你只想看循环是否正确终止：\npython -m pytest tests/test_core/test_chat_router/test_logic.py -k \u0026#34;loop or finish or evaluate\u0026#34; -v ","date":"2026-07-09","permalink":"https://tttjhgan.top/securityclaw-learning/ch09-agent-securityclaw/","section":"SecurityClaw 学习笔记","summary":"","title":"09 Agent 代码模板：从通用骨架映射到 SecurityClaw"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/agent-design/","section":"Tags","summary":"","title":"Agent-Design"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/skills/","section":"Tags","summary":"","title":"Skills"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/testing/","section":"Tags","summary":"","title":"Testing"},{"content":"结论先说：manifest 的价值不在“配置了什么”，而在“框架可以据此拒绝什么” #很多项目里的 manifest.yaml 最后都会退化成一张说明书：\n叫什么 版本号是多少 作者是谁 这类 manifest 对运行时几乎没有约束力。SecurityClaw 这套技能系统里，manifest 更像可执行契约：它不只是描述技能，而是给框架提供拒绝、跳过、调度和隔离的依据。\n一、manifest 在系统里的职责 #你可以先把 manifest 抽象成四类信息：\nclass SkillManifest(BaseModel): name: str version: str # 1. 调度信息 schedule_interval_seconds: Optional[int] schedule_cron_expr: Optional[str] run_on_first_startup: bool = False # 2. 依赖信息 required_env_vars: list[str] = [] depends_on: list[str] = [] # 3. 能力约束 allowed_actions: list[str] = [] allowed_field_patterns: list[str] = [] risk_level: Literal[\u0026#34;low\u0026#34;, \u0026#34;medium\u0026#34;, \u0026#34;high\u0026#34;] = \u0026#34;low\u0026#34; # 4. 运行属性 idempotent: bool = False 这四类信息分别解决不同问题：\n调度：什么时候运行 依赖：缺了什么不能运行 约束：允许做什么、不允许做什么 运行时属性：重试、风险、幂等性如何处理 二、为什么 manifest 是“契约”，不是“配置表” #配置表的思路是：\n写一点元数据 系统尽量照着跑 契约的思路是：\n你声明一组条件 系统在条件不满足时明确拒绝或跳过 也就是说，manifest 的价值不在“帮助系统更方便运行”，而在“帮助系统知道什么时候不该运行”。\n例子 1：环境变量缺失 #if env_var not in os.environ: return InvalidManifest(\u0026#34;ENV_MISSING\u0026#34;) 这一步不是为了友好提示，而是为了避免技能在真正执行时才因为缺凭证崩掉。\n例子 2：依赖环 ## A depends_on B, B depends_on A raise InvalidDependencyChain(\u0026#34;cycle detected\u0026#34;) 如果系统在启动期就不能看出依赖环，那运行期迟早会在调度里死锁或反复跳转。\n例子 3：CRON / interval 冲突 #如果一个技能同时声明：\nschedule_interval_seconds schedule_cron_expr 框架必须有明确规则：\n选一个 或直接拒绝 不能含糊执行。\n这就是契约思维：不确定性必须在系统边界被收敛。\n三、manifest 验证流程其实就是一层静态防火墙 #sequenceDiagram participant Loader as SkillLoader participant FS as FileSystem participant Manifest as Validator participant Registry as Runner Loader-\u0026gt;\u0026gt;FS: 读取 skills/\u0026lt;name\u0026gt;/manifest.yaml FS--\u0026gt;\u0026gt;Loader: 原始 YAML Loader-\u0026gt;\u0026gt;Manifest: validate_manifest(raw) Manifest-\u0026gt;\u0026gt;Manifest: 校验字段类型 / 依赖 / env / 调度冲突 alt 校验通过 Manifest--\u0026gt;\u0026gt;Loader: SkillManifest Loader-\u0026gt;\u0026gt;Registry: 注册技能 else 校验失败 Manifest--\u0026gt;\u0026gt;Loader: InvalidManifest Loader-\u0026gt;\u0026gt;Loader: 记录 warning 并跳过 end 这层验证很像编译期静态检查：\n技能还没运行 业务逻辑还没执行 但系统已经能拒绝明显不合法的能力包 如果没有这层，所有错误都会拖到运行期才暴露，代价会高很多。\n四、最关键的三类失败路径 #4.1 依赖失败 #场景 #技能声明：\nrequired_env_vars: - DB_PASSWORD 但进程环境里没有 DB_PASSWORD。\n正确行为 # 技能不注册 系统继续启动 输出结构化 warning 为什么不能直接炸启动 #因为一个技能坏了，不应该拖垮整套系统。\n4.2 调度失败 #场景 #cron 表达式非法，或者 interval 类型错误。\n正确行为 # 该技能拒绝注册自动调度 但如果核心逻辑合法，是否还能保留手动调用能力，要有明确策略 这里就有 tradeoff：\n方案 A：调度字段出错就整个技能跳过 #优点：简单 缺点：一个小调度错误就让技能彻底不可用\n方案 B：自动调度禁用，但允许手动调用 #优点：更宽容 缺点：运行语义更复杂\n如果是我，我更倾向 B。因为“不能自动跑”和“不能被调用”不是一回事。\n4.3 约束失败 #场景 #manifest 声明：\nallowed_actions: [read] 但 LLM 规划里出现了 delete。\n正确行为 #运行时直接拒绝，不进入执行器。\n这说明 manifest 的价值不仅在启动期，也在运行期持续生效。它是控制面的输入之一。\n五、为什么文件系统扫描是合理但不完美的选择 #这套系统允许把技能目录直接放进 skills/，Loader 扫描后自动注册。\n这个设计的优点 # 零配置扩展：复制一个目录就能加技能 适合快速迭代：安全分析师不需要理解中央注册表 天然支持热加载：目录变化即可触发重扫 它的问题也很明确 # 安全边界依赖文件系统权限 技能来源可信度难保证 缺少签名 / 完整性验证 所以文件系统扫描不是“好设计”或“坏设计”，而是一个很典型的工程取舍：\n用部署面简单，换取运行时信任面更脆弱。\n如果以后这套系统要走更高安全级别，我会优先加：\n技能签名 白名单来源 manifest 哈希校验 六、测试 manifest，实际上是在测试框架的拒绝能力 #manifest 相关测试最重要的不是 happy path，而是 failure path。\ndef test_skip_on_missing_env_var(): ... def test_invalid_cron_format(): ... def test_dependency_cycle_rejected(): ... 这些测试证明的不是“manifest 能被解析”，而是：\n系统会不会在坏输入下误注册技能 框架能不能优雅降级 拒绝逻辑是否稳定可复现 这也是为什么我一直强调：测试 manifest，本质上是在测试框架的防线。\n七、这一篇真正要记住的框架 #manifest 的知识框架可以压成一句话：\nmanifest 不是为了描述技能，而是为了让系统在技能不满足条件时，有充分理由拒绝它。\n你可以把它记成四层：\n声明能力 -\u0026gt; 声明依赖 -\u0026gt; 声明调度 -\u0026gt; 声明约束 然后由框架把这些声明转成：\n允许注册 / 拒绝注册 / 允许执行 / 拒绝执行 这才叫契约。\n八、验证命令 #python main.py list-skills pytest tests/test_skill_loader.py -v 如果你要专门验证 manifest 失败路径：\npytest tests/ -k \u0026#34;manifest or env_var or cron or dependency\u0026#34; -v ","date":"2026-07-08","permalink":"https://tttjhgan.top/securityclaw-learning/ch08-manifest/","section":"SecurityClaw 学习笔记","summary":"","title":"08 技能系统的 manifest 契约机制"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/manifest/","section":"Tags","summary":"","title":"Manifest"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/schema/","section":"Tags","summary":"","title":"Schema"},{"content":"结论先说：Agent 真正可控，不靠模型听话，靠控制面把不确定性包起来 #如果你只看 LLM 规划能力，很容易误以为 Agent 的核心是“模型够不够聪明”。但在 SecurityClaw 这种安全场景里，真正决定系统能不能上线的，不是规划层，而是控制面。\n控制面不是一个单独模块，而是一组边界：\n权限边界：哪些动作允许执行 预算边界：能跑多少步、花多少 token 幂等边界：失败重试会不会造成重复副作用 人工确认边界：高风险动作何时必须暂停 可观测性边界：事后能不能解释自己做了什么 一、什么叫“控制面” #你可以把 Agent 分成两层：\n数据面（Data Plane） - 真的去调工具 - 真的去查数据 - 真的写结果 控制面（Control Plane） - 允许不允许执行 - 预算够不够 - 是否需要人工确认 - 是否应该停止 - 是否能复盘 很多失败的 Agent 设计，本质上不是模型差，而是控制面缺失。模型提出一个动作，系统就直接干了，没有中间审判层。\n二、权限边界：manifest 不是安全边界，运行时才是 #旧稿里最重要的一点其实非常对：permissions 是编排契约，不是最终沙箱。\npermissions: Optional[List[PermissionRule]] = Field( default=None, description=\u0026#34;声明技能需要的最小权限，执行前由编排层校验，不是安全边界\u0026#34; ) 这句话可以展开成一个更完整的模型：\n# 伪代码：权限检查必须在执行前发生 class PermissionRule(BaseModel): action: Literal[\u0026#34;read\u0026#34;, \u0026#34;write\u0026#34;, \u0026#34;external\u0026#34;, \u0026#34;modify_file\u0026#34;] entities: list[str] def validate_plan_permissions(plan: Plan, manifest: SkillManifest) -\u0026gt; bool: for step in plan.steps: if step.action not in manifest.allowed_actions: return False if step.entity not in manifest.allowed_entities: return False return True 为什么 manifest 不能替代数据库权限 #因为 manifest 是框架层的声明，数据库权限是基础设施层的硬约束。\n这两层应该同时存在：\nmanifest 阻止模型提出明显越界动作 数据库 / 文件系统 / 外部 API 自己的权限系统负责最后兜底 如果你把全部安全寄托在 manifest 上，那其实只是把风险前移，并没有真正消掉。\n三、预算边界：系统什么时候必须说“到此为止” #预算控制是控制面里最容易被忽略的一块，因为它看起来不像“安全”，更像“成本优化”。其实不是。\n预算边界解决的是：\n无限重试 过长推理链 高成本工具被重复调用 模型在失败路径里持续烧 token # 伪代码：预算和步数都应该在控制面收口 if state[\u0026#34;step_count\u0026#34;] \u0026gt;= state[\u0026#34;max_steps\u0026#34;]: return StopReason(\u0026#34;step_limit\u0026#34;) if token_budget.remaining \u0026lt;= 0: return StopReason(\u0026#34;token_exhausted\u0026#34;) if tool_budget.remaining \u0026lt; tool_cost[plan.tool_name]: return StopReason(\u0026#34;tool_budget_exceeded\u0026#34;) 设计取舍 #方案 A：只控制 max_steps #优点：简单 缺点：不同技能成本差异被忽略\n方案 B：步数 + token + 工具成本三层预算 #优点：更真实地约束系统成本 缺点：系统更复杂，参数更多\nSecurityClaw 目前更偏向 A 与 B 之间的折中。它已经意识到预算必须是运行时组件，但在粒度上还没有完全展开。这是合理的阶段性选择。\n四、幂等性边界：重试不能变成重复副作用 #如果一个 Agent 能发邮件、写数据库、改文件，那“重试”本身就是危险动作。\n所以控制面还要负责判断：\n这次失败后再试一遍，结果会不会变成重复写入？\n# 伪代码：幂等执行记录 if execution_records.exists(idempotency_key): return execution_records.get(idempotency_key) result = execute_tool(...) execution_records.save(idempotency_key, result) return result 这一步看起来像工程细节，但其实决定了 Agent 能不能安全地自动恢复。\n如果没有幂等性：\n重试一次，写两次数据库 重试一次，发两封邮件 重试一次，删两次文件 那恢复机制会从“容错设计”变成“事故放大器”。\n五、人工确认边界：高风险动作为什么不能自动通过 #在安全场景里，模型可以规划，但某些动作不应该直接落地。\n例如：\n删除数据 外发内容 修改权限 写文件到敏感路径 调高风险外部工具 这类动作必须让控制面接管：\n# 伪代码：高风险动作先暂停 if manifest.risk_level == \u0026#34;high\u0026#34;: request = HumanConfirmRequest(plan=step, timeout=30) approved = wait_for_confirmation(request) if not approved: return Denied(\u0026#34;human confirmation timeout\u0026#34;) 为什么这不是“多此一举” #因为 LLM 再聪明，也不能替你承担组织责任。\n控制面的意义，就是把“机器能规划”和“系统被允许执行”拆开。\n六、可观测性边界：如果事后解释不了，就不算可控 #一个可上线的 Agent，必须能回答这些问题：\n为什么选了这个技能？ 为什么拒绝了那个动作？ 为什么在第 4 步停止？ 为什么进入恢复路径？ 哪一步花费最大？ 这就要求控制面不仅要拦，还要记。\n# 伪代码：控制面记录的是“决策证据”，不是原始大 payload trace.append({ \u0026#34;plan_summary\u0026#34;: summarize(plan), \u0026#34;permission_result\u0026#34;: permission_result, \u0026#34;budget_state\u0026#34;: current_budget, \u0026#34;evaluation_reason\u0026#34;: evaluation.reason, \u0026#34;duration_ms\u0026#34;: duration, }) 为什么不直接全量记录 #全量记录当然最省脑子，但问题是：\n敏感数据泄露风险高 trace 体积爆炸 checkpoint 变慢 调试时噪音太大 所以合理设计是：记录可解释性摘要，而不是原始执行细节的无边界镜像。\n七、控制面真正要统一的，是“停止权” #如果把上面几条合并，你会发现控制面的最终职责不是“多做几次校验”，而是：\n统一收回系统的停止权和执行权。\n也就是说，模型只能提议，真正决定：\n能不能做 做到哪里停 失败后还能不能继续 结果有没有资格进入下一轮 这些权力都必须收敛到控制面。\n这才是“让不确定性可控”的真正含义。\n八、这一篇要记住的框架 #你可以把 Agent 控制面记成这五个问句：\n权限：允许做吗？ 预算：值得继续吗？ 幂等：重试会重复伤害吗？ 确认：高风险动作有人兜底吗？ 可观测：事后解释得清吗？ 如果一套 Agent 设计不能系统回答这五个问题，那它大概率只是“会循环的 LLM 工具调用器”，还不是可靠系统。\n九、验证命令 #pytest tests/test_permissions.py -v pytest tests/ -k \u0026#34;budget or idempotent or confirm or trace\u0026#34; -v ","date":"2026-07-07","permalink":"https://tttjhgan.top/securityclaw-learning/ch07-agent/","section":"SecurityClaw 学习笔记","summary":"","title":"07 Agent 设计的控制面：让不确定性可控"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/budget/","section":"Tags","summary":"","title":"Budget"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/control-plane/","section":"Tags","summary":"","title":"Control-Plane"},{"content":"结论先说：动态技能系统的难点不是“发现技能”，而是“把发现到的技能变成受控能力” #很多人第一次做技能系统，都会被 importlib.import_module() 吸引，觉得动态加载最难。其实不是。\n真正难的是后半段：\n哪些目录算有效技能 哪些技能允许手动触发，哪些要自动调度 一个技能出错时，为什么不能拖垮整个系统 技能的调度元数据该放哪里，运行时又该信任到什么程度 一、最小闭环是什么 #先给出这篇最重要的模型：\n磁盘目录 -\u0026gt; SkillLoader 发现并导入 -\u0026gt; Runner 分类与注册 -\u0026gt; Scheduler 负责时间触发 -\u0026gt; dispatch 负责手动触发 -\u0026gt; Skill.run(context) 真正执行 对应伪代码：\nclass SkillLoader: def discover(self, skill_root: str) -\u0026gt; list[Skill]: skills = [] for subdir in os.listdir(skill_root): skill = self._load_skill(subdir) if skill: skills.append(skill) return skills class Runner: def setup(self, skills: list[Skill]): for skill in skills: if skill.interval or skill.cron: self.scheduler.register(skill) else: self.manual_registry[skill.name] = skill def dispatch(self, name: str, context: dict): return self.manual_registry[name].run(context) 这个闭环的价值在于：发现、分类、调度、执行四步被拆开了。\n二、加载器的职责：目录是不是一个合法技能 #SkillLoader 不应该关心业务语义，它只该判断这个目录能不能变成可运行能力。\n2.1 最小判定条件 #从当前项目的设计看，一个目录至少要满足：\n有 logic.py logic.py 能被导入 模块里暴露 run # 伪代码：加载器判断技能是否合法 mod = importlib.import_module(f\u0026#34;skills.{dir_name}.logic\u0026#34;) if not hasattr(mod, \u0026#34;run\u0026#34;): return None return Skill(name=dir_name, run=mod.run, instruction=load_instruction(...)) 2.2 为什么要宽容失败 #如果某个技能因为：\n语法错误 缺依赖 没有 run instruction 不完整 就把整个加载过程炸掉，那这套系统根本不适合长期运行。\n所以合理的策略是：\n一个技能坏了，只跳过这一个技能，不影响其他技能注册。\n这不是“放松约束”，而是隔离故障域。\n三、为什么调度逻辑不应该写进 Loader #这是这套设计里最值得讲的 tradeoff 之一。\n方案 A：Loader 负责加载 + 调度 #优点：简单，文件少 缺点：\n职责混在一起 很难单独测试“调度分类是否正确” 加载器开始依赖时间系统和调度框架 方案 B：Loader 只负责发现，Runner 负责分类，Scheduler 负责时间触发 #优点：\n责任边界清楚 手动技能和自动技能共用同一个 Skill 抽象 测试容易拆开 缺点： 模块数变多 读代码时需要跨三层跳转 SecurityClaw 走的是 B。这是正确的。因为“动态加载”本质是文件系统问题，而“定时执行”本质是运行时编排问题，它们不是一件事。\n四、调度元数据应该放在哪 #当前设计把调度信息放进 instruction.md 或相邻元数据里，而不是写死在代码里。\n这件事的意义很大：\n4.1 让技能本身携带运行方式 #一个技能如果带了：\ninterval cron run_on_first_startup 那它就不只是“一个函数”，而是“一个带运行契约的能力单元”。\n4.2 让运行时可以统一解释 #Runner 只看这些声明，不需要知道业务内容：\nif skill.interval: scheduler.add_interval_job(skill.run, skill.interval) elif skill.cron: scheduler.add_cron_job(skill.run, skill.cron) else: manual_registry[skill.name] = skill 这就是“声明式能力系统”的核心：能力由文件定义，运行方式由元数据声明，框架只负责解释。\n五、完整时序：从目录到调度 #sequenceDiagram participant Disk as 技能目录 participant Loader as SkillLoader participant Runner as Runner participant Scheduler as Scheduler Disk-\u0026gt;\u0026gt;Loader: os.listdir(skills/) Loader-\u0026gt;\u0026gt;Loader: 导入 logic.py / 检查 run alt skill 非法 Loader--\u0026gt;\u0026gt;Runner: 跳过 else skill 合法 Loader--\u0026gt;\u0026gt;Runner: 返回 Skill 对象 Runner-\u0026gt;\u0026gt;Runner: 解析 instruction / manifest alt 有 interval 或 cron Runner-\u0026gt;\u0026gt;Scheduler: 注册定时任务 else 无调度元数据 Runner-\u0026gt;\u0026gt;Runner: 注册为手动技能 end end 这张图里最值得注意的一点是：Scheduler 不需要知道技能来自哪里。它只接收一个已经被 Runner 分类过的“可执行能力”。\n这正是分层价值所在。\n六、失败路径：动态技能系统最容易炸的地方 #6.1 空目录 #应该跳过，不报 fatal\n6.2 logic.py 缺失 #应该跳过，不注册\n6.3 run 不存在 #应该跳过，并留下结构化 warning\n6.4 元数据冲突 #例如同时声明 interval 和 cron\n这时必须有优先级规则，否则运行时语义不确定。\n6.5 技能导入阶段连外部依赖 #如果技能在 import 时就去连数据库或打网络，那 Loader 会变得非常脆弱。\n正确做法应该是：\nimport 阶段只加载定义 真正外部依赖放进 run(context) 里 这是保证“发现”和“执行”分层的关键。\n七、测试为什么要围绕 tmp_path 写 #动态加载最难测的不是业务逻辑，而是目录形态。最好的办法就是：\n临时建目录 临时写 logic.py 临时写元数据 调 discover(tmp_path) 验证结果 def test_skip_no_run(tmp_path): skill_dir = tmp_path / \u0026#34;bad_skill\u0026#34; skill_dir.mkdir() (skill_dir / \u0026#34;logic.py\u0026#34;).write_text(\u0026#34;x = 1\u0026#34;) skills = loader.discover(tmp_path) assert \u0026#34;bad_skill\u0026#34; not in [s.name for s in skills] 这种测试之所以重要，是因为它证明的不是“技能业务对不对”，而是“框架能不能在脏环境下保持稳定”。\n八、这一篇要你真正记住的框架 # 动态技能系统不是“会 import 的插件机制”，而是“把目录、元数据、时间调度和手动触发整合成一个可控能力系统”。\n你可以用这四层记它：\n发现（Loader） -\u0026gt; 分类（Runner） -\u0026gt; 触发（Scheduler / dispatch） -\u0026gt; 执行（Skill.run） 只要这四层混了两层，系统就会开始难测、难扩展、难恢复。\n九、验证命令 #pytest tests/test_skill_loader.py -v --tb=short pytest tests/test_runner.py tests/test_scheduler.py -v ","date":"2026-07-06","permalink":"https://tttjhgan.top/securityclaw-learning/ch06-skill-system/","section":"SecurityClaw 学习笔记","summary":"","title":"06 从技能目录到调度任务：动态技能系统的最小闭环"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/ai/","section":"Tags","summary":"","title":"AI"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/https/","section":"Tags","summary":"","title":"HTTPS"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/rust/","section":"Tags","summary":"","title":"Rust"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/scheduler/","section":"Tags","summary":"","title":"Scheduler"},{"content":"概述 #本文完整记录了个人网站从零搭建的全过程，包括环境配置、博客框架、Web服务器、域名注册、HTTPS配置等各个环节。\n最终架构 #用户 ──HTTPS──▶ 腾讯云安全组 ──▶ Caddy (443/80) ──▶ Hugo静态文件 │ DNSPod (DNS解析) 技术栈 # 组件 选择 说明 服务器 腾讯云轻量云 4核/3.6G/40G，Ubuntu 24.04 博客引擎 Hugo Go语言编写，极速编译，无运行时依赖 主题 自建简洁主题 支持亮/暗色自适应，响应式设计 Web服务器 Caddy v2 自动HTTPS，配置简洁，自带Let\u0026rsquo;s Encrypt 域名 tttjhgan.top DNSPod注册，DNSPod解析 HTTPS Let\u0026rsquo;s Encrypt Caddy自动申请与续签 搭建方式 Hermes AI Agent 全程微信对话指挥 第一章：服务器准备 #1.1 基础环境 #服务器到手时已预装：\nUbuntu 24.04 LTS Caddy Web 服务器（v2.11.4） SSH 远程登录（端口22） 1.2 安装 Hugo #sudo apt-get install hugo hugo version # 输出: hugo v0.123.7+extended linux/amd64 1.3 创建博客项目 #hugo new site blog --format yaml cd blog 第二章：博客主题 #2.1 主题选择 #由于服务器在中国境内，GitHub 的 HTTPS 连接不稳定，无法直接下载现成的 Hugo 主题。因此决定自建一个简洁主题，满足以下需求：\n亮色/暗色模式自动切换（跟随系统） 响应式设计，移动端友好 代码高亮、标签分类、分页导航 中文排版优化 2.2 主题文件结构 #themes/simple/ ├── theme.toml # 主题元信息 ├── layouts/ │ ├── index.html # 首页 │ ├── 404.html # 404页面 │ └── _default/ │ ├── baseof.html # 基础模板 │ ├── single.html # 文章页 │ └── list.html # 列表页 2.3 解决 GitHub 连接问题 #腾讯云机房对 GitHub 的 HTTPS 做了限制，但 SSH 协议正常。解决方案：\n# 1. 生成 SSH 密钥对 ssh-keygen -t ed25519 -C \u0026#34;blog-server\u0026#34; -f ~/.ssh/github_blog # 2. 添加到 GitHub Settings → SSH and GPG keys # 3. 配置 Git 全局走 SSH git config --global url.\u0026#34;git@github.com:\u0026#34;.insteadOf \u0026#34;https://github.com/\u0026#34; 配置完成后，即可正常 clone 主题和代码。\n第三章：内容创作 #3.1 文章结构 #博客内容全部使用 Markdown 编写，存放在 content/posts/ 目录下。\n每篇文章以 YAML frontmatter 开头：\n--- title: \u0026#34;文章标题\u0026#34; date: 2026-07-03 draft: false description: \u0026#34;文章摘要\u0026#34; tags: [\u0026#34;标签1\u0026#34;, \u0026#34;标签2\u0026#34;] categories: [\u0026#34;分类\u0026#34;] --- 3.2 已发布文章 # 文章 内容 你好，我是田稼禾 自我介绍与博客方向 从零搭建个人网站 本文的前身，建站过程记录 对接Obsidian知识库 AI助手读取本地笔记的方案 3.3 发布流程 #每次更新文章只需两步：\ncd ~/blog hugo # 构建静态站点 # Caddy自动服务，无需重启 第四章：Web服务器配置 #4.1 Caddy 的优势 #相比 Nginx/Apache，Caddy 最大的优势是自动 HTTPS——它会自动从 Let\u0026rsquo;s Encrypt 申请和续签 SSL 证书，无需手动干预。\n4.2 最终 Caddyfile #tttjhgan.top { root * /home/ubuntu/blog/public file_server encode gzip header X-Content-Type-Options nosniff header X-Frame-Options DENY header Referrer-Policy strict-origin-when-cross-origin log { output file /var/log/caddy/blog.log } } www.tttjhgan.top { redir https://tttjhgan.top{uri} permanent } 4.3 常用命令 ## 重新加载配置 sudo systemctl reload caddy # 重启服务 sudo systemctl restart caddy # 查看状态 sudo systemctl status caddy # 查看日志 sudo journalctl -u caddy --no-pager -n 50 第五章：域名与HTTPS #5.1 域名注册 # 域名: tttjhgan.top 注册商: DNSPod（腾讯云） 费用: 约 ¥30/年 DNS 解析: DNSPod 免费版 5.2 DNSPod DNS 记录 # 类型 主机记录 记录值 TTL A @ 49.232.56.77 600 A www 49.232.56.77 600 5.3 HTTPS 配置 #Caddy 检测到配置中使用域名后，自动执行以下操作：\n监听端口 443（HTTPS） 向 Let\u0026rsquo;s Encrypt 请求 TLS 证书 自动续签（证书有效期内） HTTP（80端口）自动 308 重定向到 HTTPS 前提条件：腾讯云安全组需要放行 443 端口。\n第六章：安全加固 #6.1 安全组配置（腾讯云） #入站规则至少放行：\n端口 协议 来源 用途 22 TCP 0.0.0.0/0 SSH 远程登录 80 TCP 0.0.0.0/0 HTTP 访问 443 TCP 0.0.0.0/0 HTTPS 访问 6.2 SSH 安全建议 # 关闭密码登录，仅允许密钥认证 禁止 root 直接登录 使用非默认端口（可选） 6.3 Cloudflare 安全（可选） #如需额外 CDN 和 DDoS 防护，可切换到 Cloudflare DNS：\n提供全球 CDN 加速 自动 HTTPS 证书 DDoS 基础防护 Web 应用防火墙 第七章：经验总结 #踩过的坑 # GitHub 被墙：国内服务器 HTTPS 访问 GitHub 不稳定，改用 SSH 协议解决 端口 443 被挡：腾讯云安全组默认关闭 443，需手动放行 Caddy 自动跳 HTTPS：配置域名后 Caddy 自动跳转 HTTPS，未开 443 会导致死循环 权限问题：Caddy 以低权限用户运行，需注意 web 目录的可读性 PEP 668：Ubuntu 24.04 禁止直接 pip 安装包，需使用虚拟环境 心得体会 # 静态站点真香——没有数据库、没有运行时、不会被黑、维护成本极低 Caddy \u0026gt; Nginx——配置简洁太多，自动 HTTPS 太方便了 AI 助手搭网站——全程通过微信对话指挥完成，从建站到写文章一条龙 后续计划 # HTTPS 配置 绑定自定义域名 接入评论系统（如 Giscus / Waline） 配置自动化备份 写更多网络安全相关文章 附录：快速部署脚本 ##!/bin/bash # deploy.sh - 博客一键发布 cd /home/ubuntu/blog # 更新内容（如果有git仓库） # git pull # 构建 hugo echo \u0026#34;✅ 发布完成: https://tttjhgan.top\u0026#34; 道阻且长，行则将至。\n","date":"2026-07-06","permalink":"https://tttjhgan.top/posts/full-guide/","section":"技术文章","summary":"","title":"个人网站搭建全记录：从零到HTTPS上线"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E5%AD%A6%E4%B9%A0%E8%AE%A1%E5%88%92/","section":"Tags","summary":"","title":"学习计划"},{"content":" 整理时间：2026-07-06 计划周期：30天，每天2h+ 💡 所有资源均为免费公开资源\n一、Rust 语言入门 #必读教材 # 资源 链接 说明 📖 Rust Book 官方书 https://doc.rust-lang.org/book/ 重点：Ch4所有权/Ch9错误处理/Ch10泛型trait/Ch16并发/Ch19进阶 📖 Rust Book 中文版 https://kaisery.github.io/trpl-zh-cn/ 英文吃力时对照看 📖 Rust by Example https://doc.rust-lang.org/rust-by-example/ 查语法小例子用，不用从头读 📖 Comprehensive Rust by Google https://google.github.io/comprehensive-rust/ 适合有C++背景，有Android/Bare-metal章节 交互式练习 # 资源 链接 说明 🎯 Rustlings https://github.com/rust-lang/rustlings 边练边学，强烈推荐 — 本地做完100+个小练习 🎯 Rustlings安装文档 https://rustlings.rust-lang.org/setup/ 官方安装指南 🎯 Rust Gym https://github.com/warycat/rustgym LeetCode风格Rust题 视频课程 # 资源 链接 说明 🎬 Let\u0026rsquo;s Get Rusty YouTube https://www.youtube.com/@letsgetrusty 高质量Rust视频教程 🌟 我的建议 # 不要从头到尾看书。 有C++/Python基础的话：\n先装Rust环境（rustup） 跑 rustlings 做前40个练习（ownership/move_semantics模块） 边做边翻Rust Book对应章节 第3天直接开始写 Secret Scanner v0.1，遇到不会的语法再查 二、Rust 框架与生产实践 #Web 后端 # 资源 链接 说明 🚀 Axum https://github.com/tokio-rs/axum 当前最推荐的Rust Web框架 📖 Axum 官方示例 https://github.com/tokio-rs/axum/tree/main/examples 每个示例独立可跑 ⚡ Tokio 异步运行时 https://tokio.rs/ Rust异步基础 数据库 # 资源 链接 说明 🗄️ SQLx https://github.com/launchbadge/sqlx 编译期检查SQL，Rust最爱 🗄️ SeaORM https://www.sea-ql.org/SeaORM/ ORM，适合有Django/SQLAlchemy经验的人 CLI 工具链 # 资源 链接 说明 🔧 clap https://github.com/clap-rs/clap Rust CLI参数解析标准库 🔧 anyhow https://github.com/dtolnay/anyhow 简单的错误处理 🔧 thiserror https://github.com/dtolnay/thiserror 自定义错误类型 🔧 tracing https://github.com/tokio-rs/tracing 结构化日志（比log库更强） 🩺 cargo-audit https://github.com/RustSec/rustsec/tree/main/cargo-audit 依赖漏洞检查 🩺 cargo-deny https://github.com/EmbarkStudios/cargo-deny 依赖许可/安全审查 🩺 cargo-fuzz https://github.com/rust-fuzz/cargo-fuzz 模糊测试 🌟 我的建议 # 第2周直接走 Axum + SQLx + tracing 这套组合拳。 不要分散学多个框架，Rust Web生态的选型已经稳定了：\nWeb框架 → Axum（不要学Actix-web了，Axum的提取器+中间件设计更现代） 异步 → Tokio（事实标准） 数据库 → SQLx（编译期检查SQL太香了） 日志 → tracing（比log库更适合微服务） 三、AI 应用工程 #LLM 应用基础 # 资源 链接 说明 📖 OpenAI Cookbook https://github.com/openai/openai-cookbook RAG/function calling/embeddings 📖 LangChain Python 文档 https://python.langchain.com/docs/introduction/ LCEL/RAG/Agents/Tools/Memory 📖 LlamaIndex 文档 https://docs.llamaindex.ai/ RAG 首选框架，结构比LangChain清晰 🎓 Hugging Face NLP Course https://huggingface.co/learn/nlp-course/ Transformer/HF生态入门 安全数据源 # 资源 链接 说明 🗄️ Qdrant https://qdrant.tech/documentation/ Rust写的向量库，性能好，适合生产 🗄️ Chroma https://docs.trychroma.com/ Python原生，轻量适合学习 向量模型 # 资源 链接 说明 🧠 bge-small-en-v1.5 https://huggingface.co/BAAI/bge-small-en-v1.5 轻量嵌入模型 🧠 bge-reranker-base https://huggingface.co/BAAI/bge-reranker-base 重排序模型 RAG 评测 # 资源 链接 说明 📊 RAGAS https://github.com/explodinggradients/ragas RAG评估框架 🌟 我的建议 # AI 应用部分不要从零开始，直接用现成框架上手：\n用 LlamaIndex 搭第一个 RAG 原型最快（30分钟能跑通） Qdrant \u0026gt; Chroma，因为Qdrant是Rust写的，和你学的Rust呼应 LangChain建议从第3版文档开始看，之前的版本API变化太大 RAGAS一定要用，没评测=不知道自己做得好不好 四、ReAct Agent # 资源 链接 说明 📄 ReAct 论文 https://arxiv.org/abs/2210.03629 提出ReAct的原始论文 🎯 LangGraph https://langchain-ai.github.io/langgraph/ Agent框架，比LangChain Agent更灵活 🎯 AutoGen https://github.com/microsoft/autogen 微软的多Agent框架 🌟 我的建议 # Agent 是这个学习计划里最能体现工程能力的部分。 推荐路径：ReAct论文读摘要 → LangGraph搭原型 → 工具链完善 → 安全场景落地 可以先做 安全运维 Agent（查CVE、查DNS、查Whois、查IP信誉）， 这是真实企业需求，面试/作品集都加分。\n五、大模型工程底层 #ZeRO 优化 # 资源 链接 说明 📄 ZeRO 论文 https://arxiv.org/abs/1910.02054 DeepSpeed ZeRO原始论文 🎯 DeepSpeed ZeRO 教程 https://www.deepspeed.ai/tutorials/zero/ 官方实践教程 SmoothQuant 量化 # 资源 链接 说明 📄 SmoothQuant 论文 https://arxiv.org/abs/2211.10438 W8A8量化方法 🎯 SmoothQuant 项目 https://github.com/mit-han-lab/smoothquant 官方代码 FP8 训练 # 资源 链接 说明 📄 FP8 论文 https://arxiv.org/abs/2209.05433 FP8格式与训练 🎯 NVIDIA Transformer Engine https://github.com/NVIDIA/TransformerEngine FP8训练的实际实现 分布式训练 # 资源 链接 说明 🎯 Megatron-LM https://github.com/NVIDIA/Megatron-LM TP/PP/DP工业实现 🎯 DeepSpeed Pipeline https://www.deepspeed.ai/tutorials/pipeline/ 流水线并行教程 🌟 我的建议 # 这部分是理解为主，不需要全部自己实现。 这是大厂AI工程团队的活，你作为个人开发者：\n看懂概念（ZeRO-1/2/3区别、TP/PP/DP拓扑） 跑通官方demo 能解释为什么需要这些东西（显存不够、通信瓶颈） 一个月内能跑通 SmoothQuant demo + DeepSpeed ZeRO 小例子就算超额完成。 六、深度学习 / 机器学习 / 数学 #推荐课程 # 资源 链接 说明 🎓 Dive into Deep Learning (d2l.ai) https://d2l.ai/ 最推荐的动手深度学习教材，有PyTorch版 🎓 fast.ai Practical DL https://course.fast.ai/ 适合建立工程直觉 🎓 Stanford CS229 https://cs229.stanford.edu/ 机器学习数学基础 🎓 Hugging Face NLP Course https://huggingface.co/learn/nlp-course/ Transformer/HF生态入门 安全数据源 # 资源 链接 说明 🛡️ OWASP Top 10 https://owasp.org/www-project-top-ten/ Web安全标准 🛡️ OWASP Cheat Sheet https://cheatsheetseries.owasp.org/ 安全实践速查 🛡️ NIST AI RMF https://www.nist.gov/itl/ai-risk-management-framework AI风险管理框架 🛡️ MITRE ATLAS https://atlas.mitre.org/ AI攻击矩阵 🛡️ OWASP LLM Top 10 https://owasp.org/www-project-top-10-for-large-language-model-applications/ LLM应用安全 🛡️ Garak LLM Scanner https://github.com/NVIDIA/garak LLM安全性测试 🛡️ PyRIT https://github.com/Azure/PyRIT 微软AI红队工具 七、作品集推荐项目优先级 #优先级1 🔴 Rust Secret Scanner ├ Rust工程实践（遍历/正则/CLI） └ 安全开发（敏感信息检测） 优先级2 🟡 OWASP RAG 安全知识库 ├ LLM应用工程（RAG/混合检索/rerank） └ 安全知识（OWASP/MITRE） 优先级3 🟢 ReAct Security Agent ├ Agent + 工具调用 └ 安全自动化（CVE查询/IP溯源/日志分析） 优先级4 🔵 ZeRO / SmoothQuant / FP8 实验 理解为主，跑通demo即可 八、用我（AI助手）参与学习的方式 # 场景 你可以怎么说 🔧 环境搭建 \u0026ldquo;帮我装Rust + Clone Rustlings\u0026rdquo; 🐛 代码调试 \u0026ldquo;这个Rust编译错误什么意思\u0026rdquo; 📝 写博客 \u0026ldquo;把我学Rust第一周的内容写成博客\u0026rdquo; 🛠️ 项目辅助 \u0026ldquo;帮我搭Secret Scanner的框架代码\u0026rdquo; 💡 概念讲解 \u0026ldquo;用白话讲一下ZeRO-3怎么工作的\u0026rdquo; 📚 资料整理 \u0026ldquo;帮我整理这周的笔记本发到网站上\u0026rdquo; ","date":"2026-07-06","permalink":"https://tttjhgan.top/posts/learning-plan-resources/","section":"技术文章","summary":"","title":"学习计划：Rust + AI工程 + 安全 — 资源总表"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E5%9F%9F%E5%90%8D/","section":"Tags","summary":"","title":"域名"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E8%B5%84%E6%BA%90/","section":"Tags","summary":"","title":"资源"},{"content":"结论先说：多步 Agent 的难点不是“步骤多”，而是“循环分几层” #很多人第一次写 Agent，会把所有失败处理都塞进一个大循环里。但 SecurityClaw 里最有价值的设计，是把循环拆成了两层：\n内层局部重试：处理一次技能执行里的参数解析、超时、局部失败 外层图式编排：处理整个 Agent 是否需要重新规划、恢复、停止 如果不把这两层分开，Agent 很快就会陷入一种很糟糕的状态：你不知道自己是在“重试一个工具”，还是在“重跑整个计划”。\n一、先区分两种循环 #1.1 内层循环：一次执行里的局部重试 #MAX_RETRIES = 3 for attempt in range(MAX_RETRIES): try: result = skill.run(parsed_params) break except (ParseError, SkillTimeout) as e: if attempt == MAX_RETRIES - 1: return {\u0026#34;execution_error\u0026#34;: str(e)} # 只重试这一步，不重新规划整个任务 这类循环解决的是：\n参数格式不对 单次技能调用超时 某个局部解析失败 它的单位是一次动作。\n1.2 外层循环：整个 Agent 的规划-执行-评估 ## 伪代码：图式编排的主循环 while not done(state): plan = think(state) step_result = execute(plan) state = evaluate_and_update(state, step_result) 这层循环解决的是：\n当前计划是否还成立 结果是否足够回答问题 是否要进入恢复路径 是否应该直接停止 它的单位是整个任务推进。\n二、为什么 LangGraph 适合做“外层循环” #LangGraph 在这里并不是为了画图，而是为了把“状态转移条件”显式化。\nflowchart TD Input[输入问题] --\u0026gt; Decide[Decide / Planning] Decide --\u0026gt; Execute[Execute Skill] Execute --\u0026gt; Evaluate{结果是否可接受?} Evaluate --\u0026gt;|是| End[Format Response] Evaluate --\u0026gt;|否| Recover{是否需要恢复?} Recover --\u0026gt;|是| Decide Recover --\u0026gt;|否| Stop[强制停止] 这个图真正带来的好处有三点：\n2.1 状态转移可审计 #你可以明确知道：\n是哪一个节点决定进入恢复 是哪一个条件触发停止 是哪一个字段影响了下一步路由 2.2 每个节点职责更纯 #例如：\ndecide_node 只负责产出 plan execute_node 只负责执行 skill evaluate_node 只负责判断证据是否足够 2.3 中断点天然存在 #安全场景常常需要：\n高风险动作前暂停 人工确认后继续 checkpoint 后恢复 如果是普通 while，这些点都要你自己插；LangGraph 天然把这些边界暴露出来。\n三、这篇最重要的模型：多步 Agent 其实是“局部循环 + 全局循环”的组合 #把这件事抽象成更准确的伪代码：\n# 伪代码：多步 Agent 的两层控制 for graph_round in range(MAX_GRAPH_ROUNDS): plan = planner.make_plan(state) for action in plan.actions: result = execute_with_local_retry(action, max_retries=3) state = record_result(state, result) if result.is_fatal: break decision = evaluator.decide(state) if decision == \u0026#34;stop\u0026#34;: return final_response(state) if decision == \u0026#34;recover\u0026#34;: state = recover(state) continue 你会发现，这才是“真正可运行”的 Agent 模型。\n不是一个循环，而是：\n动作级重试 任务级再规划 系统级停止条件 四、为什么不把重试都放在外层 #这是一个很容易做错的设计。\n方案 A：任何失败都直接回到规划节点 #优点：逻辑统一 缺点：\n一个小的格式错误也要重新调用 LLM token 浪费大 容易把短暂故障放大成全局重规划 方案 B：局部问题局部重试，结构性问题再回到外层图 #优点：\n便宜 失败隔离更清楚 不会把所有问题都推给 LLM 缺点： 系统设计更复杂 需要明确定义“什么是局部失败，什么是结构性失败” SecurityClaw 选的是 B。这是对的。因为在安全场景里，很多失败根本不是“重新规划”能解决的，而只是：\n参数少一个字段 API 临时超时 单个技能返回格式不合法 这种时候立刻整轮回退，只会放大问题。\n五、恢复路径为什么必须单独建模 #恢复不是“再试一次”。恢复是：\n判断为什么失败 选择新的策略 把失败作为证据输入下一轮 # 伪代码：恢复节点 if evaluation.reason == \u0026#34;empty_result\u0026#34;: next_action = \u0026#34;expand_search_scope\u0026#34; elif evaluation.reason == \u0026#34;permission_denied\u0026#34;: next_action = \u0026#34;request_human_confirmation\u0026#34; elif evaluation.reason == \u0026#34;timeout\u0026#34;: next_action = \u0026#34;fallback_tool\u0026#34; 如果没有恢复节点，系统会出现两种糟糕行为：\n盲目重试：同一个技能执行三次，结果毫无变化 过早失败：其实换一个工具就行，却直接告诉用户“做不到” 恢复节点本质上是把“失败”从异常变成结构化输入。\n六、边界条件：什么时候必须强制停止 #任何多步 Agent 都要有硬边界。不然系统迟早进入无穷重试。\n至少要有这三类停止条件 #6.1 轮数上限 #if state[\u0026#34;graph_round\u0026#34;] \u0026gt;= max_rounds: return \u0026#34;stop\u0026#34; 6.2 预算上限 #if budget.remaining \u0026lt;= 0: return \u0026#34;stop\u0026#34; 6.3 证据充分 #if evaluation.evidence_sufficient: return \u0026#34;stop\u0026#34; 这三类条件覆盖了：\n防无限循环 防成本失控 防已经足够却继续过度执行 七、测试应该怎么证明这套循环是可靠的 #真正该测的不是“最终答案对不对”，而是：\n局部重试是否只影响当前 action 外层恢复是否真的改变了下一轮路径 达到最大轮数时是否会停止 超时、解析失败、空结果这三类失败是否被区分 def test_execute_timeout_triggers_local_retry(): ... def test_empty_result_enters_recover_path(): ... def test_max_rounds_forces_stop(): ... 这些测试的价值比展示一个“答对了的问题”更高，因为它们证明了这套图在坏路径下仍然可控。\n八、这一篇真正要你记住的框架 # 多步 Agent 的本质不是“让模型多想几轮”，而是把失败分层处理：局部失败局部重试，结构性失败进入恢复，系统级边界负责停止。\n如果你只记住一个图，就记这个：\n动作级重试 ↓ 任务级再规划 ↓ 系统级停止条件 这就是“能跑通”和“能长期运行”的区别。\n九、验证命令 #pytest tests/test_runner.py tests/test_scheduler.py -v 如果项目有独立的 evaluate / recover 测试，再补：\npytest tests/ -k \u0026#34;retry or recover or max_rounds\u0026#34; -v ","date":"2026-07-05","permalink":"https://tttjhgan.top/securityclaw-learning/ch05-langgraph-agent/","section":"SecurityClaw 学习笔记","summary":"","title":"05 用 LangGraph 构建可控的多步 Agent"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/execution/","section":"Tags","summary":"","title":"Execution"},{"content":"结论先说：Agent 的核心边界不是“能不能调用工具”，而是“谁有最终执行权” #看 SecurityClaw 的执行链，最重要的设计不是 LLM 会不会规划，而是：LLM 只能提议动作，不能直接拥有执行权。\n这篇要讲清三件事：\n为什么 Agent 不能让 LLM 直接输出 SQL、Shell 或真实 API 请求 运行时验证层到底在拦什么 Mock 和测试为什么比一段漂亮的最终文案更可靠 一、先把边界画出来：LLM 只生成“计划对象” #如果你把 Agent 粗暴地理解成“让大模型去调工具”，那一定会写出这种危险代码：\n# 危险示例：让模型直接输出可执行内容 sql = llm(\u0026#34;根据用户问题直接输出 SQL\u0026#34;) rows = db.execute(sql) 这类设计的问题不是“不优雅”，而是执行边界彻底丢了：\n模型可能引用不存在的表或字段 测试环境和生产环境 schema 不一致 模型可能把查询写成更新、删除 一旦失败，你很难判断是模型理解错、schema 变了，还是权限不足 SecurityClaw 走的是另一条路：\n# 伪代码：LLM 只生成结构化计划 class ToolPlan(TypedDict): action: str tool_name: str arguments: dict expected_output: str def plan_with_llm(question: str, context: str) -\u0026gt; ToolPlan: # LLM 只负责把用户意图翻译成结构化动作 return llm.generate_structured_plan(question, context) 也就是说，LLM 产出的不是“最终可执行命令”，而是一个待验证的计划对象。\n二、运行时验证层到底做什么 #真正的控制权在运行时。抽象成伪代码，大概是这个顺序：\n# 伪代码：运行时验证与执行 class RuntimeExecutor: def run(self, plan: ToolPlan, schema: SchemaInfo, permissions: PermissionSet): self._validate_plan_fields(plan, schema) self._check_allowed_tools(plan, permissions) self._check_argument_shape(plan) self._check_budget(plan) return self._dispatch(plan) 你可以把这层理解成 Agent 的“法官”，不是“助手”。\n2.1 字段验证 #最基础的一步就是确认计划引用的字段或参数真的存在：\nif not self._validate_plan_fields(plan, self.schema): return PlanValidationError(\u0026#34;schema mismatch\u0026#34;) 这一步解决的是模型幻觉和环境漂移：\n模型猜了一个不存在的字段 测试库和生产库字段名不一致 工具 schema 升级后，旧 prompt 还在用老字段 2.2 权限验证 #Agent 不是“会调用工具的 LLM”，而是“在权限边界内规划的系统”。\nif not self.authorizer.check(plan[\u0026#34;tool_name\u0026#34;], user_context): return PermissionDenied(\u0026#34;tool not allowed\u0026#34;) 这个判断的位置非常关键：权限必须在执行前检查，而不是让模型“自己记得别乱来”。\n2.3 预算验证 #Agent 一旦进入循环，很容易把 token、API 调用次数、外部查询成本一起烧光。\nif self.budget.remaining \u0026lt; self.cost_table[plan[\u0026#34;tool_name\u0026#34;]]: return BudgetExceeded(\u0026#34;over budget\u0026#34;) 这一步的价值不是省钱这么简单，而是把“是否值得继续”从模型情绪里拿出来，变成一个可预测的系统条件。\n三、为什么不直接让 LLM 输出 SQL 或 DSL #这个 tradeoff 很值得单独讲。\n方案 A：让 LLM 直接输出最终执行内容 #优点：\n实现快 demo 好看 prompt 写好就能跑 缺点：\n没有执行边界 很难做 schema 漂移保护 失败不可诊断 很难做权限和预算隔离 方案 B：让 LLM 输出结构化计划，代码去解释执行 #优点：\n验证层可插拔 schema 和权限可独立演进 测试可覆盖 失败可回溯 缺点：\n需要多写一层 schema 和运行时模板 需要维护 plan 的结构 初期开发比直接 prompt 多一点工程量 坦率地讲，如果你只是做本地 demo，方案 A 更快。但只要是长期运行、多人协作、或者安全场景，方案 B 基本是唯一靠谱路线。\n四、Mock 为什么比最终回答更可靠 #技术博客里最容易犯的错，是只展示“最后给用户看到的答案”，不展示“系统是怎么证明这个答案可靠的”。\nSecurityClaw 的一个强点，就是大量逻辑都能被 Mock 隔离测试。\n@pytest.fixture def mock_skill(): provider = MockDataProvider(schema={\u0026#34;country_code\u0026#34;, \u0026#34;year\u0026#34;, \u0026#34;sales_amount\u0026#34;}) return SkillExecutor(provider) def test_invalid_field_rejected(mock_skill): plan = { \u0026#34;tool_name\u0026#34;: \u0026#34;query_sales\u0026#34;, \u0026#34;arguments\u0026#34;: {\u0026#34;field\u0026#34;: \u0026#34;revenue_total\u0026#34;} # 不存在字段 } result = mock_skill.validate_and_run(plan) assert result.status == \u0026#34;schema_mismatch\u0026#34; 这个测试比最终生成的一段文案更可靠，因为它证明了：\n模型给错字段时系统不会静默执行 错误会被结构化返回 行为可重复复现 换句话说：\n文案只能说明“系统看起来像对了”，Mock 和 trace 才能说明“系统在错误路径也没失控”。\n五、设计边界：LLM 应该负责什么，不应该负责什么 #LLM 适合负责 # 把自然语言问题翻译成结构化目标 在多个候选工具之间做高层选择 基于已有 observation 做再规划 生成最终面向用户的解释文本 LLM 不适合直接负责 # 权限判断 schema 真实性判断 预算控制 最终执行 错误恢复策略的硬边界 你可以用一句话记住：\nLLM 适合做语义压缩，不适合做系统边界判断。\n六、这一层如果写坏了，会怎么失败 #失败路径 1：模型建议了不存在字段 #运行时应返回：schema mismatch\n失败路径 2：模型选中了没有权限的工具 #运行时应返回：permission denied\n失败路径 3：模型进入重复规划 #运行时应在预算或最大轮次处强制停止\n失败路径 4：工具执行成功但结果无意义 #运行时应把 observation 回流给评估层，而不是直接拼成答案\n这也是为什么我一直强调：Agent 的可靠性来自运行时设计，而不是 prompt 文采。\n七、验证命令 #pytest tests/ -k \u0026#34;schema or permission or runtime\u0026#34; -v 如果你要手动验证“LLM 规划、代码执行”的边界，最应该看的不是最终答案，而是：\npytest tests/test_runtime_validation.py -v pytest tests/test_permission_guard.py -v 八、带走的一个框架 #理解 Agent 的确定性边界，可以用这条链记：\n用户问题 -\u0026gt; LLM 生成计划对象 -\u0026gt; 运行时验证计划对象 -\u0026gt; 工具执行 -\u0026gt; observation 回流 -\u0026gt; LLM 再规划 / 生成最终答案 真正可靠的 Agent，不是“模型很聪明”，而是：\n规划和执行分离 执行权收回到代码 权限、预算、schema 在运行时被强约束 错误路径和恢复路径可测试 ","date":"2026-07-04","permalink":"https://tttjhgan.top/securityclaw-learning/ch04-agent-llm/","section":"SecurityClaw 学习笔记","summary":"","title":"04 Agent 的确定性边界：让 LLM 规划，让代码验证和执行"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/validation/","section":"Tags","summary":"","title":"Validation"},{"content":"结论先说：LangGraph 在这里解决的不是“多轮对话”，而是“可恢复的状态转移” #如果你只把 LangGraph 理解成“把节点连起来的图工具”，那你会严重低估它在 Agent 系统里的作用。\n在 SecurityClaw 里，LangGraph 真正解决的是：\n如何把规划、执行、评估拆成不同职责节点 如何限制每个节点只能更新自己负责的 state 字段 如何把失败变成显式状态，而不是 try/except 后的一段模糊文本 如何让恢复、停止和 checkpoint 变成可审计逻辑 一、先看最小骨架：节点不是步骤，而是职责边界 #workflow = StateGraph(State) workflow.add_node(\u0026#34;skills_check\u0026#34;, check_skills) workflow.add_node(\u0026#34;think\u0026#34;, think_node) workflow.add_node(\u0026#34;reflect\u0026#34;, reflect_node) workflow.add_node(\u0026#34;direct_answer\u0026#34;, direct_answer_node) workflow.add_node(\u0026#34;response_final\u0026#34;, final_output_node) 把这段代码翻译成人话，就是：\nskills_check：当前能力够不够 think：下一步计划是什么 reflect：执行结果说明了什么 direct_answer：是否可以直接回答 response_final：如何生成最终输出 这已经不是“流程顺序”了，而是“状态职责分工”。\n二、显式状态机为什么比 while True 更适合 Agent #很多最小 demo 的写法其实是：\nwhile True: plan = llm(messages) result = call_tool(plan) if done(result): break messages.append(result) 这类写法的问题不是“不能跑”，而是状态和控制流全混在一起：\n谁改了 state？ 失败后应该回到哪一步？ 哪个条件决定停止？ 恢复逻辑怎么插？ SecurityClaw 用图把这些隐式逻辑展开成显式转移：\nflowchart TD start([start]) --\u0026gt; skills_check skills_check --\u0026gt; think think --\u0026gt; decide_next{decide_next} decide_next -- execute --\u0026gt; reflect decide_next -- answer --\u0026gt; direct_answer decide_next -- retry --\u0026gt; skills_check reflect --\u0026gt; evaluate{last step success?} evaluate -- no --\u0026gt; think evaluate -- yes --\u0026gt; response_final direct_answer --\u0026gt; response_final response_final --\u0026gt; end([end]) 这张图真正值钱的地方 #不是它好看，而是它明确回答了：\n失败后回哪 回退是重新规划，还是直接停止 哪个节点有权决定下一条边 三、decide_next 的意义：模型只能提议动作类型 #workflow.add_conditional_edges(\u0026#34;think\u0026#34;, decide_next, { \u0026#34;execute\u0026#34;: \u0026#34;reflect\u0026#34;, \u0026#34;answer\u0026#34;: \u0026#34;direct_answer\u0026#34;, \u0026#34;retry\u0026#34;: \u0026#34;skills_check\u0026#34; }) 这段代码说明，模型真正能决定的不是“下一步具体怎么执行”，而只是：\nexecute answer retry 这其实就是把模型的自由度收紧到了动作意图层。\n这比让模型直接路由整个系统好在哪 #如果让模型随便输出“跳转到哪个节点”，那它就拥有了控制流权限。\n而 SecurityClaw 的做法是：\n模型只输出抽象动作，代码把抽象动作映射成真实边。\n这样做的好处是：\n控制流不被 prompt 漂移带走 恢复路径仍然是代码定义的 可以独立测试路由逻辑 四、为什么每个节点只更新自己负责的 state 字段 #这是 LangGraph 在 Agent 场景里的最大价值之一。\nclass AgentState(TypedDict): messages: list[Message] task_plan: Optional[Plan] execution_history: list[StepResult] skill_usage: dict[str, int] error_count: int final_output: Optional[str] 节点职责应该像这样分 #think_node # 更新 task_plan 可能追加 messages reflect_node # 更新 execution_history 更新 error_count response_final # 更新 final_output 这样做不是洁癖，而是为了避免三件坏事：\n多个节点抢着写同一字段 checkpoint 恢复时搞不清哪些字段才可信 测试里无法定位哪一步污染了状态 这一点可以压成一句话 # 图式编排的本质，不是多节点，而是把状态写权限切开。\n五、失败为什么不能只靠异常处理 #如果你把 Agent 的失败处理写成：\ntry: result = call_tool(...) except Exception: result = \u0026#34;tool failed\u0026#34; 那你只是把失败藏进了一段字符串里。\nSecurityClaw 的思路是：失败本身就是状态。\n# 伪代码：失败状态显式回流 if not last.success: state[\u0026#34;messages\u0026#34;].append(last.error) state[\u0026#34;error_count\u0026#34;] += 1 return \u0026#34;think\u0026#34; 这样做有两个关键收益：\nLLM 下一轮能看到失败证据 系统可以根据失败类型决定恢复而不是盲目重试 也就是说，LangGraph 在这里承载的不只是 happy path，而是失败路径的结构化表达。\n六、你提到的 max_retries，正好暴露了状态机设计里的 tradeoff #这是你刚点出来的那个地方：\nif task_plan.action == \u0026#34;retry\u0026#34; and error_count \u0026gt;= 3: return \u0026#34;answer\u0026#34; 这个判断的意义不是“模型错了”，而是“系统必须在这里收回停止权”。\n为什么它是合理的 # 防止无限 retry 防止 token 和时间无限消耗 防止系统在失败状态里自我催眠 为什么它又值得改进 #你说得对，它完全可以配置化：\nmax_retries = config.get(\u0026#34;max_retries\u0026#34;, 3) if task_plan.action == \u0026#34;retry\u0026#34; and error_count \u0026gt;= max_retries: return \u0026#34;answer\u0026#34; 这样更好的原因 # 不同任务容错阈值不同 查询类和写入类任务的恢复策略不同 本地调试和线上运行的预算敏感度不同 所以这里的 tradeoff 很典型：\n写死阈值：稳定、简单、好测 配置化阈值：灵活、真实、但复杂度更高 在 v1 阶段，SecurityClaw 先选了前者。我能理解。\n七、为什么 checkpoint 和图拓扑必须分开理解 #app = workflow.compile(checkpointer=SqliteSaver(conn)) 这句很容易被误解成：“图结构和状态都保存到 SQLite 里了。”\n其实不是。\nCheckpoint 保存的是 # 某一轮运行到哪里 当前状态长什么样 图拓扑决定的是 # 还能往哪走 哪些节点存在 哪些边有效 所以如果你改了：\nadd_node 条件边 state 字段结构 旧 checkpoint 就可能恢复失败。\n这个问题在图式编排里比 while 循环更明显，因为图拓扑本身就是运行时协议的一部分。\n八、这一篇真正要带走的框架 #你可以把 LangGraph 在 Agent 系统里的角色压成这四句话：\n把控制流从 prompt 里拉回代码 把失败从异常字符串提升为显式状态 把节点职责按状态写权限切开 把恢复和停止做成可审计的边 这才是它真正比裸 while True 更有价值的地方。\n九、验证命令 #pytest tests/test_loop.py -v --log-cli-level=INFO 如果你要验证 checkpoint 相关行为：\nsqlite3 data/conversations.db \u0026#34;SELECT * FROM checkpoints ORDER BY created_at DESC LIMIT 5;\u0026#34; rm -f data/conversations.db ","date":"2026-07-03","permalink":"https://tttjhgan.top/securityclaw-learning/ch03-langgraph/","section":"SecurityClaw 学习笔记","summary":"","title":"03 LangGraph 工作流核心机制"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/github/","section":"Tags","summary":"","title":"GitHub"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/state-machine/","section":"Tags","summary":"","title":"State-Machine"},{"content":"缘起 #一直想拥有自己的个人网站，用来记录网络安全学习和研究的点点滴滴。正好手头有一台腾讯云服务器，说干就干。\n整体架构 #用户 → 公网IP:80 → Caddy反向代理 → Hugo静态文件 ↓ 腾讯云轻量服务器 (4核/3.6G/40G) 技术选型 # 组件 选择 原因 Web服务器 Caddy 自动HTTPS、配置简洁、自带ACME 站点生成 Hugo Go语言编写、极速编译、无运行时依赖 主题 自建简洁主题 轻量、自适应亮暗模式 域名 IP直连 + sslip.io 免费、零配置 搭建助手 Hermes AI Agent 全程通过微信对话完成 搭建过程 #1. 服务器就绪 #腾讯云轻量服务器，Ubuntu 24.04，到手时已经预装了 Caddy 和 SSH，端口80对外开放。\n2. 安装 Hugo #sudo apt-get install hugo hugo new site blog --format yaml 3. 编写主题 #由于服务器网络环境受限（GitHub 被墙），无法直接下载现成主题，于是用 AI 助手即时生成了一个简洁的自适应主题：\n支持亮色/暗色模式自动切换 响应式设计，手机也能看 代码高亮、标签分类、分页导航一应俱全 4. 配置 Caddy #sudo tee /etc/caddy/Caddyfile \u0026lt;\u0026lt; \u0026#39;EOF\u0026#39; :80 { root * /home/ubuntu/blog/public file_server encode gzip } EOF 5. 解决 GitHub 连接问题 #这是搭建过程中最折腾的一步。腾讯云机房对 GitHub 的 HTTPS 做了限制，但 SSH 协议是通的。解决办法：\n生成 SSH 密钥 添加到 GitHub Settings 配置 Git 全局走 SSH git config --global url.\u0026#34;git@github.com:\u0026#34;.insteadOf \u0026#34;https://github.com/\u0026#34; 6. 发布脚本 #写了个一键部署脚本 deploy.sh，每次更新文章只需：\n./deploy.sh 经验总结 #踩过的坑 # GitHub 被墙 — 国内服务器访问 GitHub HTTPS 不稳定，SSH 是更好的选择 腾讯云安全组 — 443 端口默认关闭，HTTPS 需要手动放行 文件权限 — Caddy 以低权限用户运行，需要注意目录的可读性 PEP 668 — Ubuntu 24.04 禁止直接 pip 安装，需要用虚拟环境 一些心得 # AI 助手搭网站真的很爽 — 全程微信对话指挥，从建站到写文章一条龙 静态站点的优势 — 没有数据库、没有运行时、不会被黑、维护成本极低 选对工具事半功倍 — Caddy 比 Nginx 配置简单太多，Hugo 比 WordPress 轻量太多 后续计划 # 配置 HTTPS（等腾讯云开放443端口） 接入评论系统 写更多网络安全相关的技术文章 这篇文章本身也是用 AI 写完直接发布的。技术改变生产力。\n","date":"2026-07-03","permalink":"https://tttjhgan.top/posts/site-setup/","section":"技术文章","summary":"","title":"从零搭建个人网站：一台服务器 + AI助手 = 全栈搞定"},{"content":"为什么需要对接 #我在微信上通过 AI 助手（Hermes Agent）协助处理各种任务——写代码、查资料、分析基金持仓。但有一个痛点：AI 不知道我脑子里的知识积累。\n我平时用 Obsidian 管理所有学习笔记——网络安全知识点、渗透测试技巧、阅读摘录等等。如果能把这些笔记喂给 AI，它就能在回答问题时参考我的知识库，给出更贴合我学习进度的答案。\n方案：Obsidian Git + GitHub #架构很简单：\n我的电脑 (Obsidian) ↓ 自动推送 (Obsidian Git 插件) GitHub 私有仓库 ↓ git pull 腾讯云服务器 (Hermes Agent) ↓ 搜索/读取 AI 助手参考我的笔记回答问题 为什么选这个方案 # 方案 优点 缺点 Obsidian Git ✅ 免费、成熟、自动同步 需要手动配置一次 Obsidian Sync 官方、无缝 ￥50+/月 WebDAV 免费 需要自建服务 手动导出 简单 太麻烦，不可持续 实施步骤 #第一步：创建 GitHub 私有仓库 #AI 助手直接在服务器上通过 GitHub API 创建：\n# 用 SSH 密钥认证创建私有仓库 curl -X POST -H \u0026#34;Authorization: token $(cat ~/.ssh/github_blog.pub)\u0026#34; \\ https://api.github.com/user/repos \\ -d \u0026#39;{\u0026#34;name\u0026#34;:\u0026#34;my-obsidian\u0026#34;,\u0026#34;private\u0026#34;:true,\u0026#34;description\u0026#34;:\u0026#34;我的Obsidian知识库\u0026#34;}\u0026#39; 第二步：生成 SSH 密钥并添加到 GitHub #（这一步之前已经做了，用于解决 GitHub HTTPS 被墙的问题）\nssh-keygen -t ed25519 -C \u0026#34;blog-server\u0026#34; -f ~/.ssh/github_blog # 公钥添加到 GitHub Settings → SSH and GPG keys 第三步：在本地 Obsidian 中配置 # 打开 Obsidian → 设置 → 第三方插件 → 关闭安全模式 进入社区插件市场 → 搜索 \u0026ldquo;Obsidian Git\u0026rdquo; → 安装 在插件设置中配置： 备份间隔：10 分钟 自动推送：开启 提交信息模板：vault sync: {{date}} 将仓库克隆到你的 Obsidian Vault 目录 点击\u0026quot;创建备份\u0026quot;，验证同步是否成功 第四步：AI 助手接入 #服务器端拉取知识库：\ngit clone git@github.com:tttjhgan/my-obsidian.git ~/obsidian-vault 之后 AI 就可以直接搜索和阅读笔记了。\n使用场景 #对接完成后，AI 助手能基于我的笔记做这些事情：\n📖 答疑 — \u0026ldquo;根据我的笔记，讲一下SQL注入的绕过技巧\u0026rdquo; 📝 总结 — \u0026ldquo;把我最近一周的笔记整理成一篇博客\u0026rdquo; 🔗 关联 — \u0026ldquo;我笔记里提到过XX技术，帮我展开讲讲\u0026rdquo; 🎯 规划 — \u0026ldquo;根据我的学习进度，推荐下一个该学的方向\u0026rdquo; 注意事项 # 用私有仓库 — 笔记是很私人的东西，一定选 Private 不要记密码 — API Key、密码等敏感信息不要放进笔记 Git 冲突 — 如果同时在手机和电脑上编辑同一篇笔记，可能会冲突，Obsidian Git 有自动合并机制 这篇文章记录了完整的知识库对接方案，等实际部署后再更新实战经验。\n","date":"2026-07-03","permalink":"https://tttjhgan.top/posts/obsidian-integration/","section":"技术文章","summary":"","title":"对接Obsidian知识库：让AI助手读你的笔记"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E5%85%B3%E4%BA%8E/","section":"Tags","summary":"","title":"关于"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E5%BC%80%E5%A7%8B/","section":"Tags","summary":"","title":"开始"},{"content":"👋 你好，我是田稼禾 #一名网络安全方向的学生，专注于渗透测试、AI隐私保护与安全技术研究。\n关于这个网站 #这是托管在我腾讯云服务器上的个人网站，完全由 AI 助手搭建完成。从服务器配置、博客框架安装、主题定制到域名解析，一步步构建起来。\n我会分享什么 # 🛡️ 网络安全 — 渗透测试实战、漏洞分析、安全工具研究 🔒 AI隐私保护 — 差分隐私、联邦学习、数据安全 💻 技术笔记 — 开发实践、环境搭建、工具链配置 📖 学习心得 — 安全领域的思考与总结 联系我 #欢迎通过 GitHub 或邮件与我交流技术问题。\n道阻且长，行则将至。\n","date":"2026-07-03","permalink":"https://tttjhgan.top/posts/first-post/","section":"技术文章","summary":"","title":"你好，我是田稼禾"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tags/%E6%95%88%E7%8E%87/","section":"Tags","summary":"","title":"效率"},{"content":"结论先说：RAG 在这里不是“长期记忆”，而是“证据注入系统” #很多文章把 RAG 写成一句空话：给大模型外挂知识库。这个说法太粗了。\n在 SecurityClaw 这类 Agent 系统里，RAG 真正承担的是三件事：\n把历史证据和外部知识引入当前决策 在模型不知道事实时提供检索支撑 在向量检索失效时，保证系统还能降级工作 所以这篇不只是讲 store / retrieve / build_context 三个函数，而是要讲清楚：RAG 为什么是知识层，而不是状态层。\n一、先看核心接口：RAG 引擎只有三件事 #class RAGEngine: def store(self, text: str, category: str, source: str) -\u0026gt; str: ... def retrieve(self, query: str, k=5, category=None) -\u0026gt; list[dict]: ... def build_context(self, query: str, k=5) -\u0026gt; str: ... 这三个函数分别对应三个问题：\nstore：什么知识值得留下 retrieve：当前问题该找哪些证据 build_context：找到的证据如何变成模型能消费的输入 二、store 解决的不是“保存文本”，而是“保存可检索证据” #{ \u0026#34;_id\u0026#34;: sha256(text)[:32], \u0026#34;text\u0026#34;: \u0026#34;...\u0026#34;, \u0026#34;embedding\u0026#34;: [0.1, 0.2, ...], \u0026#34;category\u0026#34;: \u0026#34;anomaly\u0026#34;, \u0026#34;source\u0026#34;: \u0026#34;threat_analysis_skill\u0026#34;, \u0026#34;timestamp\u0026#34;: \u0026#34;ISO8601\u0026#34; } 2.1 为什么 _id 用内容哈希 #因为这一步真正想做的是去重后的证据库，不是原始日志仓库。\n好处 # 相同内容不会重复写入 检索空间更干净 降低向量存储冗余 代价 # 同一文本在不同语境下可能被压成同一条 去重粒度依赖文本规范化方式 这里的 tradeoff 很清楚：SecurityClaw 更在意“不要把重复事实无限灌进知识层”，而不是“保留每一次重复记录”。\n三、retrieve 的重点不是向量检索，而是失败后的行为 #flowchart LR Q[Query] --\u0026gt; E[Embed query] E --\u0026gt; K{kNN search} K --\u0026gt;|成功| R1[top-k semantic hits] K --\u0026gt;|失败| F[keyword fallback] F --\u0026gt; R2[text search hits] R1 --\u0026gt; C[merge / format] R2 --\u0026gt; C 很多 RAG 文章只讲“向量检索多么语义化”，但真实系统里更重要的问题是：\n如果向量检索坏了，系统会不会直接瘫掉？\nSecurityClaw 在这里做对的一点，就是给 retrieve() 留了 fallback：\n先做 kNN 搜索 kNN 异常时退回关键词检索 为什么这个降级比精度更重要 #因为生产系统真正致命的问题不是“召回差一点”，而是“整个知识层失效”。\n一个能降级的检索系统，比一个只在 happy path 下表现漂亮的检索系统更值钱。\n四、build_context 真正解决的是上下文预算问题 #很多人把 RAG 的最后一步写成“把结果拼起来就行”。其实不是。\ndef build_context(query: str, k=5) -\u0026gt; str: hits = retrieve(query, k) lines = [\u0026#34;Relevant evidence:\u0026#34;] for i, hit in enumerate(hits, 1): lines.append(f\u0026#34;{i}. [{hit[\u0026#39;category\u0026#39;]}/{hit[\u0026#39;source\u0026#39;]}] {hit[\u0026#39;text\u0026#39;]}\u0026#34;) return \u0026#34;\\n\u0026#34;.join(lines) 这个函数表面上只是在格式化文本，但它实际上承担了三个控制任务：\n限制召回数量：不是所有相关内容都能塞进 prompt 定义证据格式：模型看到的是摘要化证据，不是原始数据湖 压缩上下文预算：让检索参与决策，而不是淹没决策 这也是为什么我不喜欢把 RAG 叫“长期记忆” #因为真正的长期记忆应该可恢复、可索引、可演化；而 build_context 的输出只是一次性的 prompt 证据片段。\n更准确的理解是：\nRAG 提供的是“临时注入的知识证据”，不是运行中的状态。\n五、嵌入模型和聊天模型为什么必须分离 #llm: ollama_model: qwen2.5:7b-instruct-q4_K_M ollama_embed_model: nomic-embed-text:latest 这不是“为了架构好看”，而是并发稳定性的需要。\n如果不分离，会发生什么 # 聊天和嵌入抢同一模型资源 高并发下 embedding 请求卡死 Agent 看起来像是“推理慢”，其实是知识层阻塞了 分离之后的好处 # 聊天模型负责 reasoning 轻量 embedding 模型负责召回 两者吞吐和成本模型不同，运行时更稳定 这个设计的本质，是把“推理成本”和“检索成本”从同一个瓶颈上拆开。\n六、RAG 什么时候不该参与 #这点非常重要，而且很多项目完全没讲。\nRAG 不该在这些场景里强行参与：\n6.1 问题是纯当前状态问题 #例如：\n当前这轮 plan 到哪一步了 当前工具刚返回了什么 当前权限是什么 这些属于 state，不属于 RAG。\n6.2 问题不需要历史证据 #例如：\n一个确定性的 schema 校验 一个固定的权限判断 一个预算判断 这些应该由代码直接回答，而不是去检索。\n6.3 检索成本高于收益 #如果一次检索本身非常慢，或者返回内容高度噪音，强行注入只会污染 prompt。\n所以更准确的原则是：\nRAG 应该在“当前状态不足以支撑判断，但外部证据可能改变决策”时介入。\n七、RAG 与 state / checkpoint 的边界 #这三者最容易混：\n层 作用 生命周期 State 当前这轮运行中的结构化状态 单次执行 Checkpoint 可恢复的会话状态 跨请求 / 跨重启 RAG 长期可检索证据库 长期积累 如果你把 RAG 结果直接塞进 checkpoint，问题会很快出现：\ncheckpoint 变大 恢复变慢 旧检索证据污染新问题 prompt 不断膨胀 SecurityClaw 把它们拆开，这一点是对的。RAG 应该保持为外部知识层，而不是运行状态的一部分。\n八、这一篇真正要记住的框架 #你可以把 RAG 压缩成这条链：\n保存证据 -\u0026gt; 检索证据 -\u0026gt; 过滤证据 -\u0026gt; 格式化证据 -\u0026gt; 注入当前决策 其中最关键的不是“有没有向量数据库”，而是两点：\n检索失败时能否降级 上下文注入是否受预算控制 这两点决定了 RAG 是系统能力，还是系统负担。\n九、验证命令 #pytest tests/test_rag.py tests/test_rag_fallback.py -v 如果要手动验证语义检索与降级：\npython run_mock.py ","date":"2026-07-02","permalink":"https://tttjhgan.top/securityclaw-learning/ch02-rag-engine/","section":"SecurityClaw 学习笔记","summary":"","title":"02 RAG 引擎核心流程"},{"content":"结论先说：一个能长期运行的 Agent 框架，至少要把五层分开 #如果你第一次看 SecurityClaw，很容易被它的技能目录、RAG 引擎、LangGraph 图和一堆 provider 抽象绕晕。但把所有文件先压成架构层，你会发现这个项目真正重要的不是“用了多少 AI 组件”，而是它把一个可运行 Agent 系统拆成了五层：\n入口层：用户如何进入系统 编排层：任务如何被组织、分发和推进 能力层：系统到底会哪些技能 知识层：系统如何持有和检索证据 基础设施层：外部依赖如何被隔离 这篇的任务就是把总图建立起来。后面 02-09 篇，都是从这张总图里拆出的局部。\n一、先看总图：不要先陷进某个 4000 行文件 #flowchart TD User[用户 / 定时任务 / Web 请求] --\u0026gt; Entry[入口层 main.py / service] Entry --\u0026gt; Runner[编排层 runner.py / chat_router] Runner --\u0026gt; Skills[能力层 skills/*] Runner --\u0026gt; RAG[知识层 rag_engine.py / memory] Runner --\u0026gt; Graph[状态机 LangGraph / loop] Skills --\u0026gt; DB[基础设施层 db_connector.py] Skills --\u0026gt; LLM[基础设施层 llm_provider.py] RAG --\u0026gt; DB RAG --\u0026gt; LLM Graph --\u0026gt; LLM 如果你记住这张图，再去看代码，就不会有“所有东西混在一起”的感觉。\n二、入口层：系统怎么被唤起 #入口层解决的问题非常简单：谁发起了一次 Agent 运行？\n在 SecurityClaw 里，入口主要是 CLI 和服务接口。\n# 伪代码：入口层只负责收请求，不负责理解业务 @click.group() def cli(): pass cli.add_command(chat) # 人工交互 cli.add_command(run) # 后台循环 cli.add_command(dispatch) # 手动触发某个技能 cli.add_command(service) # Web 服务 cli.add_command(list_skills) # 可观测入口 cli.add_command(status) # 运行状态入口 入口层不该做什么 #入口层不应该：\n直接执行业务逻辑 直接调数据库 直接决定用哪个技能 直接持有权限策略 它只负责把用户意图交给编排层。\n这是一个非常容易被写坏的边界。很多小项目会在 CLI 入口里直接 if question contains X: call skill Y，这样一旦入口变多（CLI / API / cron），逻辑会立刻复制粘贴。\n三、编排层：Agent 为什么不是“一次 LLM 调用” #编排层是整个系统的核心。它回答的是：\n这次任务应该怎么推进？当前状态是什么？下一步由谁负责？什么时候该停？\n编排层通常由两个部分组成：\nrunner.py 这类系统调度器 chat_router/logic.py 这类具体执行循环 # 伪代码：编排层的三件套 class Runner: def setup(self): skills = loader.discover() scheduler.register(skills) def build_context(self): return {\u0026#34;db\u0026#34;: db, \u0026#34;llm\u0026#34;: llm, \u0026#34;memory\u0026#34;: memory, \u0026#34;config\u0026#34;: cfg} def dispatch(self, question: str): state = create_initial_state(question) return graph.invoke(state) 编排层的职责边界 # 管状态 管循环 管调度 管恢复 管停止 但它不应该亲自变成技能实现。编排层要“安排能力”，不能自己变成能力。\n四、能力层：为什么 skills/ 不只是插件目录 #很多人看 skills/ 会把它理解成“插件系统”。这个理解只对了一半。\n真正准确的理解是：\nskills/ 是系统的能力表面，它把可调用能力拆成一组带契约的最小单元。\n一个技能目录至少会带来三种信息：\n这项能力做什么 它什么时候跑 它允许怎么跑 skills/geoip_lookup/ ├── manifest.yaml ├── instruction.md ├── logic.py └── hooks.py 为什么这层必须独立出来 #因为 Agent 的可扩展性，本质上不是“prompt 还能写多长”，而是：\n能不能低成本加新能力 加了新能力后会不会污染旧逻辑 一个坏技能会不会拖垮系统 把能力层做成目录化、声明化、可跳过的结构，就是在解决这几个问题。\n五、知识层：RAG 和记忆为什么不是同一回事 #Agent 系统里最容易混淆的两样东西是：\n运行中的状态 可长期检索的知识 SecurityClaw 把它们拆开，是一个非常正确的决定。\n5.1 运行状态 #属于短期、结构化、为下一步服务\n5.2 工作记忆 / checkpoint #属于会话级，可恢复\n5.3 RAG #属于长期知识层，用来提供证据，不是直接拿来当状态机字段\nState -\u0026gt; 当前这轮在干什么 Checkpoint -\u0026gt; 下次从哪里恢复 RAG -\u0026gt; 我长期知道什么事实 如果把三者混在一起：\nprompt 会膨胀 恢复会变慢 失败会变得不可解释 六、基础设施层：为什么要抽象 DB 和 LLM #基础设施层存在的根本原因不是“设计优雅”，而是：\n业务逻辑不该知道自己到底连的是 OpenSearch、MockDB 还是 SQLite；也不该知道自己到底调的是 Ollama、OpenAI 还是 Fake LLM。\nclass BaseDBConnector(ABC): def search(self, query: str): ... def index_document(self, doc: dict): ... def knn_search(self, embedding, k: int): ... class BaseLLMProvider(ABC): def chat(self, messages): ... def embed(self, text: str): ... 这一层解决了什么 # 测试可跑：Mock provider 替掉真实依赖 部署可切换：本地、线上、离线模式都能共存 架构可扩展：不需要把每个技能重写一遍 如果这层不抽象，Agent 项目会非常快地从“框架”退化成“绑定某一个模型和某一个数据库的脚本集合”。\n七、为什么这五层必须同时成立 #只要缺一层，系统就会迅速变脆。\n缺入口层边界 #新增 API / cron 时逻辑复制\n缺编排层 #系统退化成“prompt + tool” 的拼装\n缺能力层 #扩展能力必须改核心逻辑\n缺知识层 #每次任务都得从零开始理解\n缺基础设施抽象 #测试和部署都会被真实依赖绑死\n也就是说，一个可运行的 Agent 框架，不能只看“模型能不能规划”，要看这五层是否各自独立。\n八、这一篇真正要你带走的框架 #如果你以后再看任何 Agent 项目，先问它这五个问题：\n入口在哪里？ 编排层在哪里？ 能力是怎么注册和执行的？ 知识层怎么分 state / memory / RAG？ 外部依赖有没有被抽象？ 这五个问题能比“它用了哪个模型”更快判断项目是否靠谱。\n九、验证命令 #python main.py list-skills python main.py status pytest tests/ -x --tb=short ","date":"2026-07-01","permalink":"https://tttjhgan.top/securityclaw-learning/ch01-overview/","section":"SecurityClaw 学习笔记","summary":"","title":"01 项目总览与架构概览"},{"content":"","date":null,"permalink":"https://tttjhgan.top/research/","section":"AI 安全","summary":"","title":"AI 安全"},{"content":"技能矩阵 # Web 安全 \u0026 渗透测试 OWASP Top 10 渗透测试全流程 信息收集 情报打点 资产检索 PoC 编写 漏洞挖掘 代码审计 PHP/Java 审计 反序列化 SSRF RCE SQL 注入 XXE 文件上传 模板注入 CodeQL SAST 规则 Java 安全 Servlet/Spring/Spring Boot Shiro Fastjson/Jackson Log4j JNDI SpEL/OGNL MyBatis/JDBC 云安全 \u0026 云原生 Docker/K8s 容器权限 K8s API Server RBAC Secret/ConfigMap 横向风险 AI 安全 / 大模型安全 提示词注入 MCP/Hook 风险 工具越权 Base URL 重定向 输出审计 权限隔离 开发 \u0026 部署 Python Go PHP/ThinkPHP JavaScript MySQL Docker GitLab Runner Nginx/OpenResty Elasticsearch 工作经历 # 北京卓识私募基金管理有限公司 安全运营 / 安全开发实习生 · 北京\n2026.03 - 2026.07 • 安全运营工程化建设：零信任接入、DevOps 安全链路、SIEM 告警规则调优与自动化响应 • 安全开发：内部工具开发、流程自动化、审计留痕系统设计与落地 • 基础设施安全：容器安全基线、密钥治理、日志脱敏方案、灾备恢复演练 中国工业互联网研究院安全研究所 安全研究助理实习生 · 北京\n2025.12 - 2026.02 • AI 安全、漏洞情报研究支撑，跟踪 Milvus、OpenClaw、Claude Code 等方向 • FOFA/Quake 指纹规则逆向调研，Nuclei/Goby/Dismap 规则归一化分析 • AI Agent 安全框架对比：Guardrail、最小权限、网络隔离思路 💡 更多细节和作品欢迎 微信私聊交流 证书 \u0026amp; 荣誉 # 深信服 SCSA-S 安全服务方向\n山东省网络安全比赛 省赛团体三等奖\n帕鲁杯应急响应赛道 三等奖\nCET-6 525 分\n自我评价 # 安全基础与工程实践结合紧密：既能做漏洞研究、信息收集和代码审计，也能把安全能力落到 API 对接、脚本开发、日志审计、CI/CD、部署排障和运维文档中。对生产环境风险敏感，重视最小权限、密钥治理、审计留痕、测试/生产模式隔离和非破坏性验证；学习新工具快，能主动把调研结论转化为可执行方案。 ","date":null,"permalink":"https://tttjhgan.top/security/","section":"安全研究","summary":"","title":"安全研究"},{"content":"","date":null,"permalink":"https://tttjhgan.top/learning/","section":"持续学习","summary":"","title":"持续学习"},{"content":"","date":null,"permalink":"https://tttjhgan.top/tutoring/","section":"第二职业","summary":"","title":"第二职业"},{"content":"","date":"0001-01-01","permalink":"https://tttjhgan.top/archives/","section":"田稼禾的个人网站","summary":"归档","title":"归档"},{"content":"","date":null,"permalink":"https://tttjhgan.top/contact/","section":"联系我","summary":"","title":"联系我"},{"content":"","date":null,"permalink":"https://tttjhgan.top/quant/","section":"量化研究","summary":"","title":"量化研究"},{"content":"","date":"0001-01-01","permalink":"https://tttjhgan.top/search/","section":"田稼禾的个人网站","summary":"搜索","title":"搜索"},{"content":" AI 代码审计与 CodeQL 安全分析平台 基于 ThinkPHP 8 MVC 架构的企业级 SAST 平台 2025.10 - 2025.11 ThinkPHP 8 MySQL CodeQL Bootstrap 5 大模型 API ✓ 支持项目管理、Git 仓库管理、扫描任务调度、规则列表、漏洞详情、SARIF 结果解析入库 ✓ 封装 CodeQL CLI 命令：database create/finalize/analyze，支持 SARIF 解析与源码定位查看 ✓ 接入大模型 API 自动生成漏洞解释、影响分析和修复建议 ✓ 平台安全防护：realpath 防目录穿越、Git URL 协议/IP 校验防 SSRF、敏感配置环境变量隔离 ✓ 可对接 GitLab CI/CD 在 MR 或定时任务中触发 SAST 扫描 Go 安全 Agent 与后端监测系统 Agent + Backend 架构的主机安全监测方案 2025.07 - 2025.08 Go Gin gopacket gopsutil Docker ✓ Agent 负责网络五元组采集、进程监控、心跳上报和命令执行结果回传 ✓ gopacket/pcap 抓包：支持 IPv4/IPv6、TCP/UDP/ICMP 解析、BPF 过滤、通道批量上报 ✓ 安全改造设计：mTLS/短期 token 认证、命令白名单、强审计、最小权限与沙箱执行 国信证券攻防项目（攻击队） 金融业务系统渗透测试实战 2024.11 - 2024.12 Burp Suite JSHook Chrome DevTools 渗透测试 ✓ 对 Web 站点、小程序与金融业务接口进行渗透测试 ✓ 前端加密还原：断点调试、JSHook、加密算法逆向，构造合法请求链路 ✓ 验证未授权访问、信息泄露、水平/垂直越权，输出复现步骤与修复建议 山东省教育系统网络安全攻防演练（蓝队监测） 蓝队安全监测与应急响应实战 2024.07 - 2024.08 奇安信天眼 ELK 威胁情报 蓝队监测 ✓ 天眼安全设备告警研判：弱口令、路径泄露、SQL 报错注入识别 ✓ 结合流量/日志/告警还原攻击链 ✓ 钓鱼邮件应急响应与溯源分析 📚 SecurityClaw AI Agent 安全架构学习专题 18 章系统学习：LangGraph 状态机、工具调用安全、权限隔离、Hook 机制等核心主题 开始学习 → ","date":null,"permalink":"https://tttjhgan.top/projects/","section":"项目展示","summary":"","title":"项目展示"},{"content":" 技术之外，生活更精彩。不只是一名安全研究员，也是热爱生活的普通人。 📷生活照占位 🏀 运动健身 # 🏀 篮球 大学期间院队成员，享受团队协作的快乐。喜欢看 NBA，本命球队金州勇士。 💪 健身 各类运动均有涉猎，保持良好习惯。身体是革命的本钱，健康工作五十年。 🎮 游戏娱乐 # ⚔️ Dota 2 主打 1、2、3 号位，绝活英雄齐天大圣、主宰、瘟疫法师。享受对线博弈和团队配合。 🎯 FPS 射击 APEX、守望先锋、Valorant 都玩。虽然枪法马，但游戏心态好，主打一个快乐。 🎬 内容创作 # 🤖 AI 探索 研究各类 AI 工具与 Agent 框架，探索用 AI 提高工作效率，紧跟技术前沿。 📝 技术写作 写博客、做笔记是日常爱好。相信最好的学习方式是把学到的东西讲给别人听。 🌆 生活日常 # ☕ 咖啡爱好者 每天一杯冰美式是工作标配。周末喜欢去不同的咖啡馆探店，寻找好喝的手冲。 🎥 电影 \u0026 剧集 科幻片爱好者，《盗梦空间》《星际穿越》刷了 N 遍。美剧也追，《绝命毒师》YYDS。 📍 足迹 # 🏠 山西 · 吕梁\n我的家乡，在这里长大\n❄️ 山东 · 威海\n大学四年，面朝大海的美好时光\n🏛️ 陕西 · 西安\n现工作居住，十三朝古都，碳水天堂\n","date":null,"permalink":"https://tttjhgan.top/about/","section":"兴趣爱好","summary":"","title":"兴趣爱好"}]