docs: 会议室架构规划 + MSN hourlyforecast 端点更新
- 新增 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 支持逐小时查询
This commit is contained in:
439
docs/会议室架构计划书.md
Normal file
439
docs/会议室架构计划书.md
Normal file
@@ -0,0 +1,439 @@
|
||||
# 云枢·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` — 调度子 Agent
|
||||
- `memory.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**:
|
||||
|
||||
```yaml
|
||||
name: weather
|
||||
type: sub
|
||||
description: 天气查询专家
|
||||
cache:
|
||||
ttl: 7200
|
||||
keys: ["city", "forecast_type"]
|
||||
tools:
|
||||
- http-get
|
||||
- geocode
|
||||
- skill
|
||||
```
|
||||
|
||||
### 2.3 发言人:earthquake-sub(type: sub,预留)
|
||||
|
||||
**职责**:响应地震信息查询
|
||||
|
||||
**Frontmatter**:
|
||||
|
||||
```yaml
|
||||
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**:
|
||||
|
||||
```yaml
|
||||
name: memory
|
||||
type: sub
|
||||
description: 记忆管理员
|
||||
tools:
|
||||
- memory.read
|
||||
- memory.write
|
||||
- read-file
|
||||
- write-file
|
||||
```
|
||||
|
||||
### 2.5 汇报员:narrator-sub(type: sub,成熟期)
|
||||
|
||||
**职责**:把结构化数据翻译成个性化回答
|
||||
|
||||
```yaml
|
||||
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 缓存文件格式
|
||||
|
||||
```json
|
||||
// ~/.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 的参数
|
||||
|
||||
```json
|
||||
{
|
||||
"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 数据模型
|
||||
|
||||
```json
|
||||
{
|
||||
"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 即可 |
|
||||
|
||||
---
|
||||
|
||||
## 九、设计原则
|
||||
|
||||
1. **主持者保持极薄** — 只有人格 + 调度规则,不做领域知识
|
||||
2. **子 Agent 不自知** — 不知道缓存存在、不管理自己的 session,只回答当前问题
|
||||
3. **机械化的不做 LLM** — 缓存 key 拼装、文件读写都是 Go 代码,LLM 不参与
|
||||
4. **数据隔离** — 子 Agent 的 cache 文件、data 目录互相独立
|
||||
5. **记忆共享** — 黑板机制,所有 Agent 可读,仅 memory Agent 可写
|
||||
6. **一个入口** — 用户永远只和 dialog-agent 对话,感受不到子 Agent 的存在
|
||||
Reference in New Issue
Block a user