From 0898188086708a889ad1de69e5d13466b45daa5c Mon Sep 17 00:00:00 2001 From: titor Date: Mon, 11 May 2026 08:32:30 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E4=BC=9A=E8=AE=AE=E5=AE=A4=E6=9E=B6?= =?UTF-8?q?=E6=9E=84=E8=A7=84=E5=88=92=20+=20MSN=20hourlyforecast=20?= =?UTF-8?q?=E7=AB=AF=E7=82=B9=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 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 支持逐小时查询 --- agents/weather-agent.md | 4 + docs/AGENTS.md | 72 +++++- docs/MSN天气API探索报告.md | 85 ++++++- docs/architecture.md | 48 +++- docs/changelog.md | 36 +++ docs/taolun.md | 87 +++++++ docs/会议室架构计划书.md | 439 ++++++++++++++++++++++++++++++++ skills/msn-weather-api/SKILL.md | 29 ++- 8 files changed, 774 insertions(+), 26 deletions(-) create mode 100644 docs/会议室架构计划书.md diff --git a/agents/weather-agent.md b/agents/weather-agent.md index 57ce99a..a00610c 100644 --- a/agents/weather-agent.md +++ b/agents/weather-agent.md @@ -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,多云" - 根据天气给出实用建议(如"建议带伞""适合户外"等) diff --git a/docs/AGENTS.md b/docs/AGENTS.md index 8238ffe..6b0a155 100644 --- a/docs/AGENTS.md +++ b/docs/AGENTS.md @@ -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 负责从对话中提取用户画像写入长期记忆。 diff --git a/docs/MSN天气API探索报告.md b/docs/MSN天气API探索报告.md index 088e120..385a3ae 100644 --- a/docs/MSN天气API探索报告.md +++ b/docs/MSN天气API探索报告.md @@ -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 等)。 diff --git a/docs/architecture.md b/docs/architecture.md index 44f2c54..353abe4 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -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`。 diff --git a/docs/changelog.md b/docs/changelog.md index 624d96f..2c36dd9 100644 --- a/docs/changelog.md +++ b/docs/changelog.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 ### 发布摘要 diff --git a/docs/taolun.md b/docs/taolun.md index 3c08627..75236f9 100644 --- a/docs/taolun.md +++ b/docs/taolun.md @@ -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 +{ + "": { + "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 用完即毁 | diff --git a/docs/会议室架构计划书.md b/docs/会议室架构计划书.md new file mode 100644 index 0000000..111804d --- /dev/null +++ b/docs/会议室架构计划书.md @@ -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 +{ + "": { + "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 的存在 diff --git a/skills/msn-weather-api/SKILL.md b/skills/msn-weather-api/SKILL.md index cb4a321..f501331 100644 --- a/skills/msn-weather-api/SKILL.md +++ b/skills/msn-weather-api/SKILL.md @@ -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 国内节点)