- 新增 docs/会议室架构计划书.md 完整架构方案(主持者+子Agent+task+cache+记忆) - 更新 taolun.md 追加 2026-05-11 讨论历史 - 更新 AGENTS.md 规范(type, cache 字段) - 更新 architecture.md 后续演进章节 - 更新 changelog.md 架构规划里程碑 - 修复 MSN 天气接口文档:新增 hourlyforecast,标记 weathertrends 已失效 - 更新 skills/msn-weather-api/SKILL.md 新增 hourlyforecast 端点 - 更新 agents/weather-agent.md 支持逐小时查询
13 KiB
13 KiB
云枢·Agent 会议室架构计划书
生成日期:2026-05-11 目的:从单 Agent 架构升级为"会议室模式"(1 主持 + N 领域专家 + 共享黑板) 最终目标:在云枢上验证通过后,移植到 HxClaw(河虾 Claw)
一、架构总览
用户
│
┌──────▼──────────────────────────────────────┐
│ 主持者(dialog-agent)type: main │
│ 人格 + 调度规则 + task + memory 工具 │
│ 唯一入口,用户只和它对话 │
└──────┬───────────────────────────────────────┘
│ task("weather", {city: "北京"})
│ task("earthquake", {region: "通州"})
│ task("memory", {action: "read", ...})
▼
┌───────────────────────────────────────────┐
│ 发言人(领域子 Agent)type: sub │
│ weather / earthquake / memory / narrator │
│ 被调才说话,返回文本 + 可选缓存数据 │
│ 各自的 cache / skills / tools 互相隔离 │
└───────────────────────────────────────────┘
│ 读写
▼
┌───────────────────────────────────────────┐
│ 记录者(记忆系统) │
│ 共享黑板:用户画像、偏好、异常记录 │
│ memory Agent 负责从对话中提取有价值信息 │
│ 所有 Agent 只读,仅 memory Agent 写入 │
└───────────────────────────────────────────┘
二、角色定义
2.1 主持者:dialog-agent(type: main)
职责:
- 用户的唯一入口
- 有血有肉的个人助理,能闲聊
- 识别用户意图,调度对应的子 Agent
- 读/写记忆(用户画像、上下文摘要)
工具列表:
task— 调度子 Agentmemory.read— 读长期记忆memory.write— 写长期记忆
System Prompt 包含:
- 人格(从
memory.personality加载) - 调度规则(何时调哪个子 Agent)
- 不含任何领域知识
System Prompt 不包含:
- 天气知识、地震知识等
http-get、geocode等具体工具
Session:
session.json只存 user ↔ dialog 的对话轮次- 子 Agent 内部的 tool_calls 不写入
2.2 发言人:weather-sub(type: sub)
职责:
- 响应天气查询
- 被
task调才执行,不直接面对用户 - 返回显示文本 + 可选缓存数据
工具列表:http-get, geocode, skill
Frontmatter:
name: weather
type: sub
description: 天气查询专家
cache:
ttl: 7200
keys: ["city", "forecast_type"]
tools:
- http-get
- geocode
- skill
2.3 发言人:earthquake-sub(type: sub,预留)
职责:响应地震信息查询
Frontmatter:
name: earthquake
type: sub
description: 地震信息查询
cache:
ttl: 300
keys: ["region", "time_range"]
tools:
- http-get
- skill
2.4 记录者:memory-sub(type: sub)
职责:
- 阅读对话历史,提取用户画像
- 把有价值的信息写入长期记忆数据库
- 响应其他 Agent 的记忆查询
- 记录子 Agent 的异常(如 API 失效)
工具列表:memory.read, memory.write, read-file, write-file
Frontmatter:
name: memory
type: sub
description: 记忆管理员
tools:
- memory.read
- memory.write
- read-file
- write-file
2.5 汇报员:narrator-sub(type: sub,成熟期)
职责:把结构化数据翻译成个性化回答
name: narrator
type: sub
description: 个性化回答生成器
tools:
- memory.read
三、核心工具:task
3.1 职责
task(agent_name, arguments)
│
├── 1. 加载 {agent_name}-sub.md Frontmatter
│ ├── name, type, tools, cache
│ ├── cache.keys → ["city", "forecast_type"]
│ └── cache.ttl → 7200
│
├── 2. 拼缓存 key
│ ├── 遍历 cache.keys → 从 arguments 提取值
│ ├── 拼接 → "city=北京&forecast_type=today"
│ └── hash → "a1b2c3d4e5f6"
│
├── 3. 读缓存文件 ~/.config/yunshu/cache/{agent_name}.json
│ ├── HIT → cache_data = {temp: 25, ...}
│ └── MISS → cache_data = null
│
├── 4. 调子 Agent LLM
│ ├── system = {agent_name}-sub.md 内容
│ ├── user = {
│ │ "args": arguments,
│ │ "cache_data": cache_data // 有缓存传数据,没有传 null
│ │ }
│ └── 子 Agent 返回文本 + 可选 ---CACHE--- + JSON
│
├── 5. 处理子 Agent 返回
│ ├── 有 ---CACHE--- → 提取后面的 JSON → 写缓存
│ └── 无 ---CACHE--- → 只传文本
│
└── 6. 返回显示文本给 Host(dialog Agent 的 LLM)
3.2 缓存文件格式
// ~/.config/yunshu/cache/{agent_name}.json
{
"<hash>": {
"created_at": "2026-05-11T06:00:00+08:00",
"ttl": 7200,
"data": {
"temp": 25,
"condition": "晴"
},
"raw": {
"city": "北京",
"forecast_type": "today"
}
}
}
- hash 由
cache.keys从arguments中提取值 → 拼接 → SHA256 取前 12 位 raw存原始参数,方便调试和遍历- 每次读缓存时惰性清理过期条目
3.3 传给子 Agent 的参数
{
"args": {
"city": "北京",
"forecast_type": "today",
"units": "C"
},
"cache_data": {
"temp": 25,
"condition": "晴"
}
}
args是 dialog 传过来的原始参数cache_data是缓存的数据(有缓存时),子 Agent 据此直接回答,省一次 API 调用- 两者都是原始数据,不是处理过的文本
四、记忆系统
4.1 存储位置
~/.config/yunshu/memory.db (或 memory.json,MVP 阶段)
4.2 数据模型
{
"personality": "你是个幽默风趣的北京大妞,说话带点贫",
"user_profile": {
"location": "北京通州",
"unit": "C",
"allergies": ["花粉"],
"interests": ["户外"],
"mood_today": null
},
"agent_errors": {
"weather": ["msn_api_500 at 2026-05-11T06:00:00"],
"earthquake": []
},
"dialog_context": {
"last_agent": "weather",
"last_topic": "北京天气",
"summary": "用户问了北京天气"
}
}
4.3 读写规则
| 操作 | 谁做 | 时机 |
|---|---|---|
memory.read |
dialog / 子 Agent | 需要画像时 |
memory.write |
只有 memory Agent | 从对话中提取画像后 |
memory.write("dialog_context") |
dialog | 每次回答后 |
4.4 memory Agent 的工作流
用户: "我住北京通州,最近花粉过敏厉害"
→ dialog 聊天回应
→ dialog: task("memory", {action: "extract", text: "用户说住通州、花粉过敏"})
→ memory: 提取 → memory.write("user_profile.location", "北京通州")
memory.write("user_profile.allergies", ["花粉"])
用户: "今天天气怎么样?"
→ dialog: task("memory", {action: "read_context"}) → 有 location
→ dialog: task("weather", {city: "北京通州"})
五、文件结构
yunshu/
├── main.go # CLI 入口
├── types.go # 核心类型(AgentDef, ToolDef 等)
├── loader.go # .md 解析(Frontmatter + Body)
├── registry.go # Agent 注册中心(扫描 + 按 type 分类)
├── llm.go # LLM API 封装
├── tool.go # 工具注册表 + ExecuteTool
├── runtime.go # RunAgent 主循环
│
├── agents/
│ ├── dialog-agent.md # type: main — 主持者
│ ├── weather-sub.md # type: sub — 天气
│ ├── earthquake-sub.md # type: sub — 地震(预留)
│ ├── memory-sub.md # type: sub — 记忆管理员
│ └── narrator-sub.md # type: sub — 汇报员(成熟期)
│
├── skills/
│ ├── msn-weather-api/SKILL.md
│ └── geocoding/SKILL.md
│
├── docs/
│ ├── taolun.md
│ ├── 会议室架构计划书.md
│ ├── changelog.md
│ ├── architecture.md
│ └── AGENTS.md
│
└── pkg/
├── mdprint/
├── style/
└── termui/
用户配置目录
~/.config/yunshu/
├── config.yaml # LLM 配置
├── session.json # 对话历史(仅 user ↔ dialog)
├── agents/
│ ├── dialog-agent.md # 用户可覆盖对话 Agent
│ └── weather-sub.md # 用户可覆盖天气子 Agent
├── skills/ # 用户可扩展知识
├── cache/
│ ├── weather.json
│ ├── earthquake.json
│ └── ...
├── data/
│ └── weather/ # 子 Agent 自己的数据目录
└── memory.db # 长期记忆数据库
六、调用流程示例
用户: "北京明天多少度?"
HOST(runtime.go):
1. 加载 dialog-agent.md → system prompt
2. 读 session.json → 恢复上下文
3. 调 LLM(session + system + tools)
4. LLM 返回 tool_call: task("weather", {city: "北京", forecast_type: "tomorrow"})
task 工具:
1. 加载 weather-sub.md Frontmatter
→ cache.keys: ["city", "forecast_type"], ttl: 7200
2. 拼 key → "city=北京&forecast_type=tomorrow" → hash
3. 查 weather.json → MISS(首次查明天)
4. 调子 Agent LLM:
system = weather-sub.md
user = {args: {city: "北京", forecast_type: "tomorrow"}, cache_data: null}
5. 子 Agent:
├── geocode("北京") → (39.9, 116.4)
├── skill("msn-weather-api") → 接口参数
├── http-get(URL) → JSON
└── 返回: "北京明天 18-31°C,晴"
---CACHE---
{temp_lo: 18, temp_hi: 31, condition: "晴"}
6. task 提取 CACHE → 写 weather.json
7. 返回 "北京明天 18-31°C,晴" 给 dialog
HOST(runtime.go):
1. tool 返回 → LLM 继续
2. LLM 生成最终回答:
"北京明天 18到31度,大晴天,适合出去浪~"
3. dialog: task("memory", {action: "update_context", agent: "weather", city: "北京"})
4. 追加 session.json
5. 输出给用户
七、实施阶段
阶段一:基础架构(当前 → 1周)
| 任务 | 说明 |
|---|---|
| 1.1 Frontmatter 扩展 | 解析 type: main|sub、cache 字段 |
| 1.2 Agent 注册中心 | registry.go 扫描 agents/ 和 ~/.config/yunshu/agents/,按 type 分类 |
1.3 task 工具 |
实现子 Agent 加载、LLM 调用、缓存读写 |
| 1.4 Cache 系统 | cache/ 目录管理、JSON 文件读写、过期清理 |
1.5 memory.read/write 工具 |
简单的 JSON 文件读写 |
| 1.6 dialog-agent.md | 重写为主持者(极薄:人格 + 调度规则) |
| 1.7 weather-sub.md | 从旧 weather-agent.md 改造 |
阶段二:记忆系统(阶段一完成后)
| 任务 | 说明 |
|---|---|
| 2.1 memory-sub.md | 记忆管理员 Agent(从对话提取画像) |
| 2.2 记忆数据库 | 结构化存储(画像、偏好、异常记录) |
| 2.3 画像自动提取 | memory Agent 定期从对话中提取有用信息 |
阶段三:扩展(可选)
| 任务 | 说明 |
|---|---|
| 3.1 earthquake-sub | 地震信息查询 |
| 3.2 narrator-sub | 个性化回答生成 |
| 3.3 更多数据源 | 台风、核电、火山... |
八、与 PicoClaw 的对比
| 维度 | PicoClaw | 云枢·会议室模式 |
|---|---|---|
| 入口 | 单 Agent(用户直接对话) | 对话 Agent(唯一入口)+ 背后一堆子 Agent |
| 上下文 | 所有轮次 + 系统 prompt 混在一起 | session 只存 user↔dialog,子 Agent 用完即毁 |
| 知识 | 预置或长 prompt | skill 按需加载 |
| 工具 | 所有工具混着用 | 按角色过滤,dialog 只有 task + memory |
| 记忆 | 无 | 共享黑板,memory Agent 管写入 |
| 扩展 | 改代码或改 prompt | 加一个 .md 文件 |
| 失败隔离 | 坏 tool_call 可能污染全部 | 子 Agent 独立,坏就坏一个 |
| 用户自定义 | 不可能 | 在 ~/.config/yunshu/agents/ 放 .md 即可 |
九、设计原则
- 主持者保持极薄 — 只有人格 + 调度规则,不做领域知识
- 子 Agent 不自知 — 不知道缓存存在、不管理自己的 session,只回答当前问题
- 机械化的不做 LLM — 缓存 key 拼装、文件读写都是 Go 代码,LLM 不参与
- 数据隔离 — 子 Agent 的 cache 文件、data 目录互相独立
- 记忆共享 — 黑板机制,所有 Agent 可读,仅 memory Agent 可写
- 一个入口 — 用户永远只和 dialog-agent 对话,感受不到子 Agent 的存在