Files
YunShu/docs/会议室架构计划书.md
titor 0898188086 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 支持逐小时查询
2026-05-11 08:32:30 +08:00

440 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 云枢·Agent 会议室架构计划书
> **生成日期**2026-05-11
> **目的**:从单 Agent 架构升级为"会议室模式"1 主持 + N 领域专家 + 共享黑板)
> **最终目标**:在云枢上验证通过后,移植到 HxClaw河虾 Claw
---
## 一、架构总览
```
用户
┌──────▼──────────────────────────────────────┐
│ 主持者dialog-agenttype: main │
│ 人格 + 调度规则 + task + memory 工具 │
│ 唯一入口,用户只和它对话 │
└──────┬───────────────────────────────────────┘
│ task("weather", {city: "北京"})
│ task("earthquake", {region: "通州"})
│ task("memory", {action: "read", ...})
┌───────────────────────────────────────────┐
│ 发言人(领域子 Agenttype: sub │
│ weather / earthquake / memory / narrator │
│ 被调才说话,返回文本 + 可选缓存数据 │
│ 各自的 cache / skills / tools 互相隔离 │
└───────────────────────────────────────────┘
│ 读写
┌───────────────────────────────────────────┐
│ 记录者(记忆系统) │
│ 共享黑板:用户画像、偏好、异常记录 │
│ memory Agent 负责从对话中提取有价值信息 │
│ 所有 Agent 只读,仅 memory Agent 写入 │
└───────────────────────────────────────────┘
```
---
## 二、角色定义
### 2.1 主持者dialog-agenttype: 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-subtype: 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-subtype: sub预留
**职责**:响应地震信息查询
**Frontmatter**
```yaml
name: earthquake
type: sub
description: 地震信息查询
cache:
ttl: 300
keys: ["region", "time_range"]
tools:
- http-get
- skill
```
### 2.4 记录者memory-subtype: 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-subtype: 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. 返回显示文本给 Hostdialog 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.jsonMVP 阶段)
```
### 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 # 长期记忆数据库
```
---
## 六、调用流程示例
```
用户: "北京明天多少度?"
HOSTruntime.go:
1. 加载 dialog-agent.md → system prompt
2. 读 session.json → 恢复上下文
3. 调 LLMsession + 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
HOSTruntime.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 的存在