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:
@@ -18,6 +18,8 @@ tools:
|
||||
2. **获取坐标** — 调用 `geocode` 工具获取城市经纬度
|
||||
3. **加载 API 知识** — 调用 `skill("msn-weather-api")` 获取 MSN 天气 API 的请求参数
|
||||
4. **请求数据** — 用获取到的坐标和 API 参数,通过 `http-get` 请求天气数据
|
||||
- 一般查询:调用 `current` + `dailyforecast`(days=10)
|
||||
- 逐小时询问(如"今天几点下雨""下午热不热"):额外调用 `hourlyforecast`
|
||||
5. **分析回答** — 解析 JSON 并给出清晰、有用的回答
|
||||
|
||||
## 追问处理
|
||||
@@ -25,10 +27,12 @@ tools:
|
||||
- 如果用户追问(如"适合穿什么?""风大不大?"),优先基于已有数据回答,无需重复 API 调用
|
||||
- 如果用户问另一个城市,重新执行完整流程
|
||||
- 如果数据明显过时(超过 2 小时),重新请求
|
||||
- 如果之前只请求了日预报,用户转而问逐小时问题,额外调用 `hourlyforecast`
|
||||
|
||||
## 输出规范
|
||||
|
||||
回答要清晰友好,包含关键信息:
|
||||
- 当前温度、体感温度、天气状况
|
||||
- 湿度、风速、空气质量
|
||||
- 逐小时回答时标明具体时间点,如"13:00 约 25°C,多云"
|
||||
- 根据天气给出实用建议(如"建议带伞""适合户外"等)
|
||||
|
||||
@@ -24,34 +24,70 @@
|
||||
## Agent 定义规范(.md 文件)
|
||||
|
||||
- 必须包含 YAML frontmatter(以 `---` 包裹)
|
||||
- frontmatter 必需字段:`name`, `description`, `tools`
|
||||
- frontmatter 必需字段:`name`, `description`, `type`, `tools`
|
||||
- `type` 可选值:`main`(主持者/对话 Agent,唯一入口)、`sub`(领域专家,被 task 调)
|
||||
- `cache` 可选字段:`ttl`(过期秒数)、`keys`(从 args 中提取的缓存 key 字段列表)
|
||||
- tools 为数组,声明 agent 需要的工具名(在 tool.go 中注册)
|
||||
- body 为 system prompt,**只定义行为逻辑**(角色、工作流程、输出规范)
|
||||
- **关键技术细节(URL、apiKey、请求头、JSON 路径等)不要 inline 在 agent skill 中**,改为:
|
||||
- 放到 `skills/*/SKILL.md` 中,由 agent 调用 `skill("name")` 按需加载
|
||||
- 或注册为 tool(确定性操作),由 agent 声明 tools 即可调用
|
||||
- session 文件存在 `~/.config/weather-cli/session.json`,不污染项目目录
|
||||
- session 文件存在 `~/.config/yunshu/session.json`,不污染项目目录
|
||||
|
||||
### 示例
|
||||
### 主持者示例(type: main)
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: weather-agent
|
||||
description: 天气情报官
|
||||
name: dialog
|
||||
type: main
|
||||
description: 个人助理,负责闲聊和调度
|
||||
tools:
|
||||
- task
|
||||
- memory.read
|
||||
- memory.write
|
||||
---
|
||||
|
||||
# 对话助理
|
||||
你是用户的私人助理...
|
||||
|
||||
你可以调度以下子 Agent:
|
||||
- weather: 天气查询
|
||||
- earthquake: 地震信息
|
||||
|
||||
用 task("agent_name", {args}) 调度。
|
||||
不自己回答领域问题。
|
||||
```
|
||||
|
||||
### 子 Agent 示例(type: sub)
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: weather
|
||||
type: sub
|
||||
description: 天气查询专家
|
||||
cache:
|
||||
ttl: 7200
|
||||
keys: ["city", "forecast_type"]
|
||||
tools:
|
||||
- http-get
|
||||
- geocode
|
||||
- skill
|
||||
---
|
||||
|
||||
# 天气情报官
|
||||
你是专业的天气情报官。
|
||||
# 天气专家
|
||||
你是天气领域的专家。被调时才回答。
|
||||
|
||||
## 工作流程
|
||||
1. 识别城市 → 调用 geocode 获取坐标
|
||||
2. 调用 skill("msn-weather-api") 获取 API 参数
|
||||
3. 调用 http-get 请求天气数据
|
||||
4. 分析并输出
|
||||
被调时你会收到:
|
||||
- args: 查询参数
|
||||
- cache_data: 上次缓存的原始数据(有时)
|
||||
|
||||
有 cache_data 且未过期 → 直接回答,不调 API。
|
||||
无 cache_data → 调 http-get 获取新数据。
|
||||
|
||||
返回格式:
|
||||
你的回答文本
|
||||
---CACHE---(只有数据更新时带)
|
||||
{原始 JSON 数据}
|
||||
```
|
||||
|
||||
## Session 规范
|
||||
@@ -127,3 +163,15 @@ tools:
|
||||
2. **ANSI 光标移动需要启用输出 VT 处理**:`\033[A`(光标上移)/ `\033[J`(清屏)等输出序列需要输出句柄设置 `ENABLE_VIRTUAL_TERMINAL_PROCESSING`(0x0004)才能生效。只设置输入句柄的 mode 不够,输入输出句柄是两个独立的控制台句柄。
|
||||
|
||||
3. **ReadLineWithCompletion / Completer 类型已移除**。`completer.go` 清空,`main.go` 回到 `termui.ReadLine()`。`cmdCompleter`、`commonPrefix`、相关测试一并移除。`input.go` 中 `ReadConsoleInputW` 相关的 `keyEventRecord`/`inputRecord` 结构和 proc 也一并清理。
|
||||
|
||||
### 2026-05-11
|
||||
|
||||
1. **MSN 天气存在 `hourlyforecast` 端点**:`https://assets.msn.cn/service/weather/hourlyforecast`,返回未来 10 天逐小时预报数据,参数和 dailyforecast 一致。之前文档遗漏了该端点。`weathertrends` 端点已失效(500 Internal Server Error)。
|
||||
|
||||
2. **MSN 的 `assets.msn.cn` 国内城市接口正常**:用经纬度查询 `assets.msn.cn/service/weather/current` 对国内城市返回正确数据。之前 Agent 误报"返回也门数据"是因为用了 `api.msn.cn` 的城市名接口(该接口本就有文档标注的问题:城市名匹配不可靠)。**用 `assets.msn.cn` + 经纬度即可正常获取国内城市数据,不需要切换到 wttr.in。**
|
||||
|
||||
3. **会议室架构决策**:确定从单 Agent 升级为 1 主持(dialog, type:main)+ N 领域专家(type:sub)+ 共享黑板(memory)的架构。主持者保持极薄(只有人格 + 调度规则 + `task` + `memory` 工具),子 Agent 只做领域工作,用完即毁。详见 `docs/会议室架构计划书.md`。
|
||||
|
||||
4. **Cache 设计原则**:Frontmatter 声明 `cache.keys` 和 `cache.ttl`,`task` 工具机械化从 args 取值拼 key、查/写缓存。子 Agent 不感知缓存存在,只需在回答末尾可选带 `---CACHE---` + JSON 供 task 存储。一个 Agent 一个缓存 JSON 文件,MD5 hash 做 key。
|
||||
|
||||
5. **记忆系统规则**:共享黑板模式,所有 Agent 可读,仅 memory Agent 可写。dialog-agent 是最小写入者(只写 `dialog_context`),memory Agent 负责从对话中提取用户画像写入长期记忆。
|
||||
|
||||
@@ -19,13 +19,14 @@
|
||||
|
||||
| 接口 | URL | 功能 | 稳定性 |
|
||||
|------|-----|------|--------|
|
||||
| **当前天气** | `https://assets.msn.cn/service/weather/current` | 获取实时天气 | ✅ 稳定可用 |
|
||||
| **每日预报** | `https://assets.msn.cn/service/weather/dailyforecast` | 未来10天预报 | ✅ 稳定可用 |
|
||||
| **天气趋势** | `https://assets.msn.cn/service/weather/weathertrends` | 历史+趋势+日历 | ✅ 可用(参数复杂) |
|
||||
| **当前天气** | `https://assets.msn.cn/service/weather/current` | 获取实时天气+nowcasting | ✅ 稳定可用 |
|
||||
| **逐小时预报** | `https://assets.msn.cn/service/weather/hourlyforecast` | 未来10天逐小时预报 | ✅ 稳定可用(新增) |
|
||||
| **每日预报** | `https://assets.msn.cn/service/weather/dailyforecast` | 未来10天每日汇总 | ✅ 稳定可用 |
|
||||
| **天气趋势** | `https://assets.msn.cn/service/weather/weathertrends` | 历史+趋势+日历 | ❌ 已失效(500错误) |
|
||||
| api.msn.cn 当前 | `https://api.msn.cn/weather/current` | 用城市名获取 | ✅ 可用(但城市名不准) |
|
||||
| api.msn.cn 预报 | `https://api.msn.cn/weather/forecast` | 预报 | ❌ 500错误 |
|
||||
|
||||
**推荐**:只用 `assets.msn.cn` 的两个接口即可满足大部分需求。
|
||||
**推荐**:只用 `assets.msn.cn` 的三个接口(current + hourlyforecast + dailyforecast)即可满足大部分需求。
|
||||
|
||||
---
|
||||
|
||||
@@ -90,6 +91,16 @@ foreach ($day in $days) {
|
||||
$d = $day.daily
|
||||
Write-Host " $($d.valid.ToString().Substring(0,10)): $($d.tempLo)-$($d.tempHi)C, 降水$($d.precip)%, 风速$($d.windMax)km/h"
|
||||
}
|
||||
|
||||
# 获取逐小时预报(今天剩余小时 + 后续几天)
|
||||
$uri_hourly = "https://assets.msn.cn/service/weather/hourlyforecast?apiKey=$apiKey&lat=39.904172&lon=116.407417&units=C&locale=zh-cn"
|
||||
$hourlyResp = Invoke-RestMethod -Uri $uri_hourly -Headers $headers
|
||||
$todayHourly = $hourlyResp.value[0].responses[0].weather[0].days[0].hourly
|
||||
|
||||
Write-Host "`n今天逐小时预报:"
|
||||
foreach ($h in $todayHourly) {
|
||||
Write-Host " $($h.valid.ToString("HH:mm")): $($h.temp)C, $($h.cap), 体感$($h.feels)C, 降水$($h.precip)%, 湿度$($h.rh)%, 风速$($h.windSpd)km/h"
|
||||
}
|
||||
```
|
||||
|
||||
### curl 示例
|
||||
@@ -104,6 +115,11 @@ curl -H "User-Agent: Mozilla/5.0" \
|
||||
curl -H "User-Agent: Mozilla/5.0" \
|
||||
-H "Referer: https://www.msn.com/zh-cn/weather" \
|
||||
"https://assets.msn.cn/service/weather/dailyforecast?apiKey=j5i4gDqHL6nGYwx5wi5kRhXjtf2c5qgFX9fzfk0TOo&lat=39.904172&lon=116.407417&units=C&locale=zh-cn&days=7"
|
||||
|
||||
# 逐小时预报(今天剩余+未来几天)
|
||||
curl -H "User-Agent: Mozilla/5.0" \
|
||||
-H "Referer: https://www.msn.com/zh-cn/weather" \
|
||||
"https://assets.msn.cn/service/weather/hourlyforecast?apiKey=j5i4gDqHL6nGYwx5wi5kRhXjtf2c5qgFX9fzfk0TOo&lat=39.904172&lon=116.407417&units=C&locale=zh-cn"
|
||||
```
|
||||
|
||||
---
|
||||
@@ -180,6 +196,52 @@ curl -H "User-Agent: Mozilla/5.0" \
|
||||
}
|
||||
```
|
||||
|
||||
### hourlyforecast 接口响应
|
||||
|
||||
```json
|
||||
{
|
||||
"@odata.context": "api.msn.com/weather/$metadata#hourlyforecast",
|
||||
"value": [{
|
||||
"responses": [{
|
||||
"weather": [{
|
||||
"days": [
|
||||
{
|
||||
// days[0] = 今天,从当前小时开始到23点
|
||||
// days[1..9] = 未来9天,每天24个整点
|
||||
"hourly": [
|
||||
{
|
||||
"valid": "2026-05-11T07:00:00+08:00", // 时间
|
||||
"temp": 19.0, // 温度 °C
|
||||
"feels": 23.0, // 体感温度 °C
|
||||
"cap": "晴", // 天气描述
|
||||
"precip": 0.0, // 降水概率 %
|
||||
"rh": 61.0, // 相对湿度 %
|
||||
"baro": 1009.0, // 气压 hPa
|
||||
"windSpd": 4.0, // 风速 km/h
|
||||
"windDir": 355, // 风向(度)
|
||||
"windGust": 18.0, // 阵风 km/h
|
||||
"uv": 1.0, // 紫外线指数
|
||||
"cloudCover": 6.0, // 云量 %
|
||||
"vis": 10.0, // 能见度 km
|
||||
"dewPt": 11.0, // 露点 °C
|
||||
"rainAmount": 0.0, // 降雨量 mm
|
||||
"snowAmount": 0.0, // 降雪量 mm
|
||||
"icon": 1, // 图标代码
|
||||
"symbol": "d000", // 天气符号
|
||||
"sky": "CLR" // 天空状况代码
|
||||
}
|
||||
// ... 更多小时
|
||||
]
|
||||
}
|
||||
]
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
> **注意**:`days[0].daily` 为 null(今天尚未结束),逐小时数据从 `days[0].hourly` 获取。
|
||||
|
||||
---
|
||||
|
||||
## 六、多城市验证结果
|
||||
@@ -246,15 +308,21 @@ function Get-MSNWeather {
|
||||
$forecastUri = "https://assets.msn.cn/service/weather/dailyforecast?apiKey=$apiKey&lat=$Lat&lon=$Lon&units=C&locale=$Locale&days=7"
|
||||
$forecast = Invoke-RestMethod -Uri $forecastUri -Headers $headers
|
||||
|
||||
# 逐小时预报
|
||||
$hourlyUri = "https://assets.msn.cn/service/weather/hourlyforecast?apiKey=$apiKey&lat=$Lat&lon=$Lon&units=C&locale=$Locale"
|
||||
$hourly = Invoke-RestMethod -Uri $hourlyUri -Headers $headers
|
||||
|
||||
return @{
|
||||
Current = $current.value[0].responses[0].weather[0].current
|
||||
Forecast = $forecast.value[0].responses[0].weather[0].days
|
||||
Hourly = $hourly.value[0].responses[0].weather[0].days
|
||||
}
|
||||
}
|
||||
|
||||
# 使用示例
|
||||
$weather = Get-MSNWeather -Lat 39.904172 -Lon 116.407417
|
||||
$weather.Current.temp # 当前温度
|
||||
$weather.Hourly[0].hourly # 今天逐小时数据
|
||||
```
|
||||
|
||||
---
|
||||
@@ -290,8 +358,15 @@ http://img-s-msn-com.akamaized.net/tenant/amp/entityid/AAehR3S.img
|
||||
| 是否有免费 API | ✅ 有(非公开内部接口) |
|
||||
| 国内速度 | ✅ 快(msn.cn 国内节点) |
|
||||
| 稳定性 | ⚠️ 未知(非官方,随时可能变) |
|
||||
| 数据完整性 | ✅ 完整(当前+预报+AQI+紫外线等) |
|
||||
| 数据完整性 | ✅ 完整(当前+逐小时+每日预报+AQI+紫外线+nowcasting) |
|
||||
| 推荐用途 | 个人项目、内部工具、原型开发 |
|
||||
| 不推荐用途 | 商业产品、长期运行服务 |
|
||||
|
||||
## 现状更新
|
||||
|
||||
**2026-05-11 更新:**
|
||||
- `weathertrends` 接口已失效(500 Internal Server Error)
|
||||
- 新发现 `hourlyforecast` 接口,提供未来 10 天逐小时预报数据,与 `current`、`dailyforecast` 同样稳定
|
||||
- 云图(卫星/雷达)无 REST API 可用,MSN 网页使用 tile 图片服务(`assets.msn.com/weathermapdata/`),不适合程序化调用
|
||||
|
||||
**建议**:如果用于生产环境,推荐同时准备备用方案(如和风天气、OpenWeatherMap 等)。
|
||||
|
||||
@@ -69,15 +69,47 @@ pkg/
|
||||
| geocode | 城市名 → 坐标 | Go(调 wttr.in) |
|
||||
| read-file | 读取文件 | Go |
|
||||
|
||||
## 当前 tools
|
||||
|
||||
| 工具名 | 作用 | 实现 |
|
||||
|--------|------|------|
|
||||
| http-get | HTTP GET 请求 | Go |
|
||||
| skill | 按需加载知识 | Go |
|
||||
| geocode | 城市名 → 坐标 | Go(调 wttr.in) |
|
||||
| read-file | 读取文件 | Go |
|
||||
| task | 调度子 Agent(含缓存管理) | Go(阶段一新增) |
|
||||
| memory.read | 读长期记忆 | Go(阶段一新增) |
|
||||
| memory.write | 写长期记忆 | Go(阶段一新增) |
|
||||
|
||||
## 后续演进
|
||||
|
||||
### 当前(单 Agent)
|
||||
```
|
||||
云枢·Agent (三层分离+单agent)
|
||||
↓
|
||||
河虾 claw (三层分离+主-从)
|
||||
├─ master: 意图识别+任务分发
|
||||
├─ weather-subagent
|
||||
├─ tts-subagent
|
||||
├─ asr-subagent
|
||||
└─ ...更多 subagent
|
||||
yunshu (三层分离+单agent)
|
||||
└─ weather-agent.md (type: main,既是入口也是天气专家)
|
||||
```
|
||||
|
||||
### 阶段一(会议室架构基础)
|
||||
```
|
||||
yunshu (会议室架构)
|
||||
├── dialog-agent.md (type: main,入口+调度)
|
||||
├── weather-sub.md (type: sub,天气领域)
|
||||
├── memory-sub.md (type: sub,记忆管理)
|
||||
└── narrator-sub.md (type: sub,汇报员,成熟期)
|
||||
```
|
||||
|
||||
### 阶段二(多领域扩展)→ 河虾 Claw
|
||||
```
|
||||
yunshu / hxclaw (多领域主-从)
|
||||
├── dialog-agent.md (type: main,入口+调度)
|
||||
├── weather-sub.md (type: sub,天气)
|
||||
├── earthquake-sub.md (type: sub,地震)
|
||||
├── volcano-sub.md (type: sub,火山)
|
||||
├── nuclear-sub.md (type: sub,核电监测)
|
||||
├── memory-sub.md (type: sub,记忆)
|
||||
└── narrator-sub.md (type: sub,汇报)
|
||||
```
|
||||
|
||||
## 架构文档
|
||||
|
||||
详细架构计划见 `docs/会议室架构计划书.md`。
|
||||
|
||||
@@ -2,6 +2,42 @@
|
||||
|
||||
> 坐看云卷云舒,静听花开花落
|
||||
|
||||
## [2.0.0-planning] - 2026-05-11
|
||||
|
||||
### 架构规划:会议室模式
|
||||
|
||||
完成从单 Agent 到"会议室架构"的完整设计,核心变更:
|
||||
|
||||
**新增角色体系**:
|
||||
- `type: main` — 主持者(对话 Agent),唯一用户入口
|
||||
- `type: sub` — 发言人(领域子 Agent),被 `task` 调才说话
|
||||
|
||||
**新增工具**(待实现):
|
||||
- `task` — 调度子 Agent + 缓存管理
|
||||
- `memory.read` / `memory.write` — 长期记忆读写
|
||||
|
||||
**新增 Cache 机制**:
|
||||
- 子 Agent Frontmatter 声明 `cache.ttl` + `cache.keys`
|
||||
- `task` 工具机械化拼 key、查/写缓存
|
||||
- 一个 Agent 一个缓存 JSON 文件,子 Agent 无感知
|
||||
|
||||
**设计文档**:
|
||||
- `docs/会议室架构计划书.md` — 完整架构方案
|
||||
- `docs/architecture.md` — 更新后续演进章节
|
||||
- `docs/AGENTS.md` — 更新 Agent 定义规范(type, cache 字段)
|
||||
- `docs/taolun.md` — 追加 2026-05-11 讨论历史
|
||||
|
||||
**MSN 天气接口更新**:
|
||||
- 新增 `hourlyforecast` 端点文档
|
||||
- 标记 `weathertrends` 为已失效
|
||||
- 更新 `skills/msn-weather-api/SKILL.md` 和 `agents/weather-agent.md`
|
||||
|
||||
### 技术细节
|
||||
- Frontmatter 新增 `type` 字段(main/sub)
|
||||
- Frontmatter 新增 `cache` 字段(`{ttl: int, keys: [string]}`)
|
||||
- 用户配置目录 `~/.config/yunshu/` 下可选覆盖 agents/
|
||||
- 详见 `docs/会议室架构计划书.md`
|
||||
|
||||
## [1.1.0] - 2026-05-09
|
||||
|
||||
### 发布摘要
|
||||
|
||||
@@ -242,3 +242,90 @@ Markdown 渲染器完成 AST 解析后,需要确定标题的终端展示风格
|
||||
### 验证
|
||||
- 19/19 单元测试通过
|
||||
- 构建成功,二进制运行正常
|
||||
|
||||
---
|
||||
|
||||
## 2026-05-11 会议室架构:从单 Agent 到主-从调度
|
||||
|
||||
### 背景
|
||||
用户发现 `weathertrends` 接口失效,逐小时数据缺失。在排查中意外发现 `hourlyforecast` 端点存在且正常工作,文档之前遗漏了。同时回顾了与天气 Agent 的对话,发现 Agent 汇报 MSN 接口"国内城市不可用"的判断有误——实际 `assets.msn.cn` 按经纬度查一直正常,Agent 用的是 `api.msn.cn` 城市名接口(其文档本就标注"北京返回了也门萨那")。
|
||||
|
||||
这暴露了单 Agent 架构的局限性:Agent 的自我判断不可靠,上下文一多就容易出偏差。
|
||||
|
||||
### 讨论历程
|
||||
|
||||
#### 从"PicoClaw 为什么崩"出发
|
||||
|
||||
| PicoClaw 痛点 | 原因 |
|
||||
|--------------|------|
|
||||
| 上下文污染 | 所有知识/工具/历史全混在同一个 system prompt |
|
||||
| Skill 污染 | skill 内联大量技术细节,退化为长 prompt |
|
||||
| 逃避执行 | LLM 倾向"自己回答"而非调工具 |
|
||||
| 扩展困难 | 加能力 = 改代码或改长 prompt |
|
||||
|
||||
用户想从 0 实现一个干净的架构,先在云枢上验证,再移植到 HxClaw(河虾Claw)。
|
||||
|
||||
#### 方案演进
|
||||
|
||||
| 轮次 | 方案 | 问题 |
|
||||
|------|------|------|
|
||||
| 1 | CLI 切换主 Agent(`--agent weather/earthquake`) | 家庭用户记不住命令,跨域查询(火山附近天气)没法做 |
|
||||
| 2 | 唯一对话入口 + task 调度子 Agent | 但担心记忆管理和上下文混乱 |
|
||||
| 3 | 命名空间隔离记忆 | 无法共享用户画像(住通州 → 查地震也要知道住通州) |
|
||||
| 4 | **会议室模式**(最终方案) | 共享黑板(记忆)+ 主持者 + 发言人,角色隔离而非数据隔离 |
|
||||
|
||||
#### 关键设计决策
|
||||
|
||||
1. **主 Agent 即对话 Agent**(`type: main`),用户唯一入口,扮演"个人助理"角色
|
||||
2. **子 Agent**(`type: sub`)是领域专家,被 `task` 工具调才说话,不直接面对用户
|
||||
3. **`task` 工具**负责:加载子 Agent → 查/写缓存 → 调子 Agent LLM → 返回文本
|
||||
4. **Cache 机制**:Frontmatter 声明 `cache.keys`,`task` 工具机械化拼 key、查/写文件
|
||||
5. **记忆管理员**(`memory` 子 Agent):负责从对话中提取用户画像,写入记忆数据库
|
||||
6. **所有子 Agent 回答经过对话 Agent 返回给用户**,保持单一入口
|
||||
|
||||
#### 最终角色定义
|
||||
|
||||
| Agent | type | 职责 | 工具 | 缓存 |
|
||||
|-------|------|------|------|------|
|
||||
| dialog | main | 入口 + 聊天 + 调度 | `task`, `memory.read/write` | 无(本身就是对话历史) |
|
||||
| weather | sub | 查天气 | `http-get`, `geocode`, `skill` | `keys: [city, forecast_type]`, ttl: 7200 |
|
||||
| earthquake | sub | 查地震 | `http-get`, `skill` | `keys: [region]`, ttl: 300 |
|
||||
| memory | sub | 管理画像/长期记忆 | 读 memory.db | 无 |
|
||||
| narrator | sub(成熟期) | 格式化回答 | `memory.read` | 无 |
|
||||
|
||||
#### Cache 设计
|
||||
|
||||
```json
|
||||
// ~/.config/yunshu/cache/weather.json
|
||||
{
|
||||
"<hash_of_keys>": {
|
||||
"created_at": "2026-05-11T06:00:00+08:00",
|
||||
"ttl": 7200,
|
||||
"data": {...}, // 原始 API 数据
|
||||
"raw": {"city": "北京", "forecast_type": "today"} // 原始参数
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- 子 Agent 每次回答末尾可选带 `---CACHE---` + JSON(只在数据更新时带)
|
||||
- `task` 工具查缓存:HIT → 把 `cache.data` 作为 `cache_data` 传给子 Agent;MISS → 子 Agent 自己查 API
|
||||
- 一个 Agent 一个缓存 JSON 文件,里面一个 map,key 是 hash
|
||||
|
||||
#### 会话存储
|
||||
|
||||
| 类型 | 位置 | 内容 | 生命周期 |
|
||||
|------|------|------|---------|
|
||||
| 对话历史 | `session.json` | 只存 user <-> dialog 的消息 | 每次启动清空 |
|
||||
| 子 Agent 内部 | 临时 | tool_calls、LLM 调用 | 用完即毁 |
|
||||
| 长期记忆 | 记忆数据库 | 用户画像、偏好、异常记录 | 持久 |
|
||||
|
||||
#### 对比结论
|
||||
|
||||
| | PicoClaw | 新方案 |
|
||||
|---|---|---|
|
||||
| 架构 | 单 Agent 全能 | 1 主持 + N 领域专家 |
|
||||
| 上下文 | 全混在 system prompt | Host 只有人格+调度,Sub 只有领域 |
|
||||
| 扩展 | 改代码或改长 prompt | 加一个 `.md` 文件 |
|
||||
| 记忆 | 无 | 共享黑板,Memory Agent 管写入 |
|
||||
| 工具污染 | 所有工具混在一起 | 按角色过滤 |
|
||||
| 失败影响 | 一个坏 tool_call 可能污染全部 | 子 Agent 用完即毁 |
|
||||
|
||||
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 的存在
|
||||
@@ -11,6 +11,7 @@ description: MSN 天气 API 详细知识
|
||||
|------|-----|
|
||||
| 当前天气 | `https://assets.msn.cn/service/weather/current` |
|
||||
| 每日预报 | `https://assets.msn.cn/service/weather/dailyforecast` |
|
||||
| 逐小时预报 | `https://assets.msn.cn/service/weather/hourlyforecast` |
|
||||
|
||||
## 必须参数
|
||||
|
||||
@@ -21,7 +22,7 @@ description: MSN 天气 API 详细知识
|
||||
|
||||
## 可选参数
|
||||
|
||||
- `days`: 预报天数(最大 10)
|
||||
- `days`: 预报天数(最大 10,仅 dailyforecast 和 hourlyforecast 可用)
|
||||
|
||||
## 必须请求头
|
||||
|
||||
@@ -49,6 +50,32 @@ value[].responses[].weather[].days[].daily.{
|
||||
}
|
||||
```
|
||||
|
||||
### hourlyforecast 接口
|
||||
返回未来 10 天每天逐小时预报(今天从当前小时开始,后续每天 24 个点)。
|
||||
|
||||
```
|
||||
value[].responses[].weather[].days[].hourly[].{
|
||||
valid, // ISO 时间戳,如 "2026-05-11T07:00:00+08:00"
|
||||
temp, // 温度 °C
|
||||
feels, // 体感温度 °C
|
||||
cap, // 天气描述(中文)
|
||||
precip, // 降水概率 %
|
||||
rh, // 相对湿度 %
|
||||
baro, // 气压 hPa
|
||||
windSpd, // 风速 km/h
|
||||
windDir, // 风向(度)
|
||||
windGust, // 阵风 km/h
|
||||
uv, // 紫外线指数
|
||||
cloudCover, // 云量 %
|
||||
vis, // 能见度 km
|
||||
dewPt, // 露点 °C
|
||||
rainAmount, // 降雨量 mm
|
||||
snowAmount, // 降雪量 mm
|
||||
icon, symbol, // 天气图标代码
|
||||
sky, wx // 天空状况代码
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
- 数据源为微软 MSN 天气后台接口
|
||||
- 国内访问速度快(msn.cn 国内节点)
|
||||
|
||||
Reference in New Issue
Block a user