无重(姚继涛)· 2026-06-15
一个个人 AI Agent 系统,运行了 6 个月。管理 1093 篇文档、执行 11 个定时任务、接入 4 个外部数据源、维护三层记忆。这篇文章讲它的设计——不只是 prompt engineering,是 Agent 作为一个操作系统应该怎么架构。
Oh My KB Agent OS
│
├─ 决策层:路由 Agent(CLAUDE.md + 19 个 skill 入口)
│ ├─ 管道型 (13):抓取 8 + 报告 3 + 记忆 2
│ ├─ 工具型 (7) :行情 / 社区 / 部署 / 地图 / Logo / 搜索 / 热点
│ └─ 路由器 (2) :财务子 Agent / 记忆子 Agent
│
├─ 感知层:4 路数据源,各有 fallback
│ ├─ 富途 OpenD(行情) → 离线时回退 WebSearch
│ ├─ TrendRadar Docker(热点)→ 离线时回退 WebSearch
│ ├─ 雪球 Playwright(社区) → 超时则跳过
│ └─ WebSearch(宏观/研报) → 最后的兜底层
│
├─ 记忆层:三层递进,每周蒸馏
│ ├─ L1 原始记录(JSONL / 飞书消息)
│ ├─ L2 结构化摘要(会话 / 飞书 / 日报)
│ └─ L3 语义记忆(用户档案 / 项目状态 / 行为纠正)
│
├─ 运维层:11 个 cron + 健康检查 + 自我修复
│ ├─ 投资 3 个:早盘 09:17 / 盘后 21:15 / 周回顾 周日
│ ├─ 知识 3 个:编译 02:00 / 日报 22:30 / 健康检查 周一
│ ├─ 记忆 4 个:飞书 21:45 / 会话 22:00 / 蒸馏 周日 / 审查 每月
│ └─ 其他 1 个:GitHub 趋势 09:00
│
└─ 存储层
├─ raw/ (1093 篇原始素材,只读只追加)
├─ wiki/ (编译管道自动生成,LLM 维护)
├─ memory/ (结构化记忆,分 TTL 管理)
└─ outputs/ (报告 / 分析 / 问答存档)
用户说一句话,系统怎么路由到正确的技能?三层决策:
19 个 skill 各有 description 字段声明触发条件。Agent 看到用户输入,匹配触发词最高的那个:
用户:"https://x.com/xxx/status/123"
→ description "x.com/twitter.com URL" 匹配 → kb-fetch-x
用户:"帮我看看 BABA 的行情"
→ description "富途行情/看行情/持仓数据" 匹配 → kb-tools-futu
用户:"早盘分析"
→ description "早盘分析/盘后分析/投资分析" 匹配 → kb-finance
→ kb-finance 是路由器,启动 kb-finance-agent 子 Agent
→ 子 Agent 持有 kb-invest-report + kb-report-finance
匹配到 skill 后,Agent 不是一次性加载所有文件——它按导航表分阶段加载:
SKILL.md 导航表:
| 阶段 | 读哪个文件 | 何时读 |
|------|-----------|--------|
| 全程清单 | checklist.md | 每次执行前 |
| 前置检查 | preflight.md | 脚本运行前 |
| 输出总结 | post-fetch.md | 脚本运行后,写总结前 |
| 总结参照 | example.md | 写总结时对照 |
Agent 的执行路径被文件依赖关系硬编码。它不能跳过"读 post-fetch.md"就去写总结——因为它不知道总结格式和硬约束。
每个 skill 声明了 allowed-tools。抓取类用 Bash+Read+Write,分析类加 WebSearch,部署类只 Bash。Agent 不会被不需要的工具干扰。
这是整个系统最容易被忽视但最重要的部分。没有记忆,Agent 每次对话都是从零开始。
L1 原始记录(自动生成,不可删除)
├─ Claude Code JSONL(每次对话自动记录)
├─ 飞书消息 JSON(lark-cli 每日拉取)
└─ TrendRadar 热搜 DB(每 30 分钟抓取)
L2 结构化摘要(每日 cron 自动生成)
├─ 飞书日报 (21:45) → 从 L1 飞书消息提取关键工作事项
├─ 会话摘要 (22:00) → 从 L1 对话日志提取决策和待办
└─ 知识库日报 (22:30) → 从原始素材汇总今日收录和编译
L3 语义记忆(每周蒸馏,TTL 管理)
├─ user_profile.md → 用户信息/偏好(TTL 90 天)
├─ mayfair-status.md → 项目进展/团队状态(TTL 14 天)
├─ feedback.md → 行为纠正规则(永不过期)
└─ recent.md → 近期事件速记(10 天滑动窗口)
每周日 09:00,kb-memory-distill 执行。不是简单的"汇总本周内容"——是三路并行提取 + 冲突检测 + 验证门控:
Step 1: 启动 3 个子 Agent 并行读取近 10 天数据
├─ Agent A:读会话摘要 → 提取用户决策/偏好/待办
├─ Agent B:读飞书摘要 → 提取项目进展/人员动态/关键事件
└─ Agent C:读聊天记录 → 提取隐性信息和背景线索
Step 2: 主 Agent 合并三路输出
→ 去重(同一事实多源提到 → 标记多源)
→ 冲突检测(A 说 X,B 说 Y → 标记 ⚠️ 待确认)
→ 分类写入(事实 / 纠正 / 决策 / 待跟进)
如果每次对话都搜索全部原始数据,上下文爆炸。如果只存摘要没有原文,丢失细节。
三层让 Agent 按需下沉:日常对话读 L3(~2KB),需要细节时读 L2(~20KB),深度回溯时读 L1(完整记录)。不是存更多数据——是让 Agent 在正确的时间读到正确的粒度。
Agent 系统最大的敌人不是 bug——是退化。运行几周后,Agent 开始跳过步骤、忽略约束、输出质量下降。
根源:Agent 靠 prompt 驱动,prompt 里所有指令平权。关键约束("Cookie 过期必须检查")和普通信息("文件命名规则")混在一起。时间一长,Agent 会自主判断某些步骤"不需要"。
不要试图让 Agent 更聪明。设计结构让 Agent 无法犯错。错误的机会不应该存在——不是 Agent 选择不走错路,是错路根本不存在。
四个机制实现这个原则:
每条约束有:判定条件(怎么判断违规)、失败动作(违规做什么)、阈值(多少项触发)。Agent 无法模糊带过。
管道型 skill 有 checklist.md 和 post-fetch.md。工具型 skill 没有。为什么不创建空的写上"本 skill 不需要"?
因为 Agent 需要读内容才知道不需要。文件缺席的话,Agent 在查找的那一刻就得到了答案——零 token 判断。命名前缀同理:kb-tools-futu 中的 tools- 直接告诉 Agent 不需要找 checklist 和 post-fetch。
| 数据源 | 检查 | 挂了的后果 | Fallback |
|---|---|---|---|
| 富途 OpenD | lsof -i :11111 | 14 只标的价格没了 | WebSearch |
| TrendRadar | docker ps --filter name=trendradar | 宏观热点没了 | WebSearch |
| 雪球讨论 | 脚本返回条数 > 0 | 社区情绪没了 | 跳过(非致命) |
| WebSearch | 始终在线 | 兜底也挂 | 仅用已有数据 |
管道不会因为一个数据源挂掉就静默失败。每个源有独立的健康检查和回退路径。
每周一自动扫描知识库:
1. 摘要完整性:每篇摘要有三段(结论+证据+关联)?
2. 概念健康:来源 > 0?无失效 wikilink?更新 < 90 天?
3. 索引一致性:raw 文件数 vs All-Sources 条目数 = 0?
4. 断裂链接修复:标点标准化 → 前缀截断匹配 → 僵尸引用标记
发现断裂链接自动修复。不能自动修的(如"这个概念可能过时")生成报告通知用户。
普通 AI 回答"BABA 怎么样"——模型生成一段文字。我们的投资分析管线是同时拉四个数据源,交叉验证,结构化输出:
Step 1: 数据采集(并行)
├─ futu API:14 只标的一秒拿完 → {code, price, change, volume}
├─ trendradar SQL:11 平台热搜,关键词筛选 12 个投资词
├─ xueqiu Playwright:6 只 ✅ 标的逐个拉讨论,提取情绪信号
└─ WebSearch:补汇率/大宗/海外宏观
Step 2: 标准化 → Step 3: 四维评分(技术面+基本面+情绪面+事件驱动)
→ Step 4: 风险闸(BLOCK/WARN/PASS)→ Step 5: 五板块输出 → Step 6: Critic
每个标的不是得到一个 AI 生成的模糊评价——是得到一个结构化的评分,有来源可追溯:价格来自 OpenD 实时数据(不是训练数据),情绪来自雪球真实讨论(3 分钟内的帖子),宏观来自 11 个平台的热搜交叉验证(crawl_count 越高越可信)。
1. Agent 需要"读结果→做判断→写总结"?→ 管道型
文件:checklist + preflight + post-fetch + examples
2. Agent 跑完脚本就结束?→ 工具型
文件:preflight 就够了(不要 checklist/post-fetch)
3. 技能需要启动子 Agent?→ 路由器
文件:只 10 行,声明路由目标和子 Agent 路径
管道型: 工具型:
SKILL.md (入口+导航) SKILL.md (入口+导航)
checklist.md (门控) codemap.md
codemap.md procedures/
procedures/ preflight.md (唯一的 process)
preflight.md references/
post-fetch.md scripts/
references/
examples/
scripts/
---
name: kb-fetch-xxx
description: "触发条件"
allowed-tools: Bash Read Write
context: default
---
# 标题
一句话描述。
## 快速执行
```bash
grep -r "source: URL" "dir/" --include="*.md" -l # 重复检测
python3 scripts/fetcher.py "URL" # 执行
```
## 执行导航
> 先读 `checklist.md` 了解全程,再按阶段读对应文件。
| 阶段 | 读哪个文件 | 何时读 |
|------|-----------|--------|
| 全程清单 | `checklist.md` | 每次执行前 |
| 前置检查 | `procedures/preflight.md` | 脚本运行前 |
| 输出总结 | `procedures/post-fetch.md` | 脚本运行后 |
| 总结参照 | `examples/example.md` | 写总结时 |
| 技能 | 类型 | 重构前 | 重构后 |
|---|---|---|---|
| kb-fetch-wx | 管道 | 155 | 37 |
| kb-fetch-web | 管道 | 181 | 37 |
| kb-report-daily | 管道 | 192 | 35 |
| kb-memory-distill | 管道 | 228 | 47 |
| kb-tools-github | 工具 | 200 | 37 |
| kb-tools-logo | 工具 | 196 | 27 |
平均 182→36 行(-80%)。不是行数重要——是每个阶段 Agent 上下文从"20% 相关"变成"接近 100% 相关"。
即使有门控和硬约束,Agent 仍然会犯错。关键是从错误中学到东西,且不因偶发错误过度修正。
每周蒸馏结束后:
A. 收集 patterns.md 最近 10 天 Critic 记录(FAIL + PASS 都要)
B. 每条 FAIL 独立找根因
→ 偶发个例 → 丢弃
→ 系统性模式 → 输出优化提案
C. 层次归并
→ ≥3 条不同的 FAIL 指向同一个根因 → 保留
→ 1-2 次个例 → 丢弃为噪声
D. 验证门控(不可跳过)
→ 模拟应用新规则到上周全部 Critic 记录
→ a) 是否召回至少 1 条漏掉的 FAIL?
→ b) 是否误伤任何 PASS?
→ 两项通过 → 自动升级到 skill 的 Critic 标准
→ 任一不通过 → 丢弃或软化
旧版"≥3 次就升级"。问题:同一条 bug 在 3 次蒸馏里重复标记 → 假阳性 → 规则膨胀。新版要求 ≥3 条 不同的 FAIL + 验证门。一字之差,精度完全不同。
设计一个 AI Agent 系统,不是写一份很长的 prompt。是设计五层架构:决策层负责路由和技能选择,感知层负责多源数据融合和 fallback,记忆层负责信息粒度和时效管理,运维层负责定时任务和健康检查,存储层负责只读只追加的不可变数据。
技能不过是决策层的一个子模块。
当前系统:19 个 skill,11 个 cron,4 个数据源,3 层记忆,2 个子 Agent。运行 6 个月。所有设计决策有对应代码可验证。