2026-05-08 10:12:31 +08:00
|
|
|
|
# 云枢·Agent 架构参考
|
|
|
|
|
|
|
|
|
|
|
|
> 详细架构白皮书见 `~/Desktop/yunshu-architecture.md`
|
|
|
|
|
|
>
|
|
|
|
|
|
> 本文档为项目内部精简参考
|
|
|
|
|
|
|
|
|
|
|
|
## 项目命名
|
|
|
|
|
|
|
|
|
|
|
|
- **中文名**:云枢·Agent(坐看云卷云舒,静听花开花落)
|
|
|
|
|
|
- **英文名**:YunShu / yunshu
|
|
|
|
|
|
- **配置目录**:`~/.config/yunshu/`
|
|
|
|
|
|
|
|
|
|
|
|
## 三层分离架构
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
Agent Skill (agents/*.md) → 纯行为(~40行,全程在 system prompt)
|
|
|
|
|
|
普通 Skill (skills/*/SKILL.md) → 纯知识(按需加载,用完即走)
|
|
|
|
|
|
Tool (src/tool.go 注册) → 确定性执行(Go 代码,仅返回结果)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 四种能力对比
|
|
|
|
|
|
|
|
|
|
|
|
| 维度 | Agent Skill | 普通 Skill | Tool | MCP |
|
|
|
|
|
|
|------|------------|-----------|------|-----|
|
|
|
|
|
|
| 本质 | 角色定义("我是谁") | 知识手册("怎么用") | 确定性执行("帮我做") | 外部服务("远程调用") |
|
|
|
|
|
|
| 加载方式 | 启动即加载 | `skill("name")` | 声明即注册 | 外部进程协议 |
|
|
|
|
|
|
| 上下文影响 | 全程 | 仅该轮 | 仅结果文本 | 同 tool |
|
|
|
|
|
|
| 实现形式 | .md frontmatter+body | .md body | Go 函数 | 外部 server |
|
|
|
|
|
|
|
|
|
|
|
|
## 判断准则
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
"做什么" → Agent Skill
|
|
|
|
|
|
"怎么做" → 继续问
|
|
|
|
|
|
"知识" → 普通 Skill
|
|
|
|
|
|
"操作" → 继续问
|
|
|
|
|
|
"本地操作" → Tool
|
|
|
|
|
|
"远程服务" → MCP
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 和 picoclaw 的关键区别
|
|
|
|
|
|
|
|
|
|
|
|
| | picoclaw | 云枢·Agent |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| 上下文 | 行为+知识+工具全堆在一起 | 三层分离,各司其职 |
|
|
|
|
|
|
| 角色 | 一个 prompt 塞 N 个角色 | 一个 agent = 一个角色 |
|
|
|
|
|
|
| 知识加载 | 预置或直接塞入 | 按需加载,仅该轮存在 |
|
|
|
|
|
|
| 工具执行 | 依赖 LLM 构造 URL 解析 JSON | Tool 用 Go 代码,100% 可靠 |
|
|
|
|
|
|
|
2026-05-09 03:55:56 +08:00
|
|
|
|
## 包结构
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
pkg/
|
|
|
|
|
|
├── mdprint/ Markdown → ANSI 终端渲染(AST 架构)
|
|
|
|
|
|
│ ├── mdprint.go Node 类型定义 + Print() 入口
|
|
|
|
|
|
│ ├── parse.go 块级解析器(状态机)
|
|
|
|
|
|
│ ├── inline.go 行内解析器(递归)
|
|
|
|
|
|
│ └── render.go ANSI 渲染器(type switch)
|
|
|
|
|
|
├── style/ 终端颜色样式库(8 色 ANSI + 24-bit 真彩色)
|
|
|
|
|
|
└── termui/ 终端交互(行输入、模式设置)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-05-16 17:21:29 +08:00
|
|
|
|
## 项目文件
|
2026-05-08 10:12:31 +08:00
|
|
|
|
|
2026-05-16 17:21:29 +08:00
|
|
|
|
```
|
|
|
|
|
|
main.go CLI 入口(onboard/help/version/log 子命令)
|
|
|
|
|
|
types.go 核心类型(AgentDef, Schema, ToolDef, Message…)
|
|
|
|
|
|
loader.go .md 解析(frontmatter + body)
|
|
|
|
|
|
catalog.go CatalogAgent 生成 + tools.yml 输出
|
|
|
|
|
|
registry.go AgentRegistry(ScanAgents, GetMain, GetSub…)
|
|
|
|
|
|
llm.go LLM API 封装(豆包/OpenAI,sync.Once 延迟加载)
|
|
|
|
|
|
tool.go 工具注册 + safeMemoryPath + ExecuteTool + 7 个工具 handler
|
|
|
|
|
|
toolschema.go 泛型+反射工具注册(NewTool[T], structToSchema)
|
|
|
|
|
|
runtime.go RunAgent + RunSubAgent(maxToolCalls=2)+ cache + session
|
|
|
|
|
|
logger.go charmbracelet/log v2 全局实例(→ stderr)
|
|
|
|
|
|
log.go 双写 wrapper(warnLog/errorLog/infoLog)+ log.yml + yunshu log 命令
|
|
|
|
|
|
```
|
2026-05-08 10:12:31 +08:00
|
|
|
|
|
2026-05-11 08:32:30 +08:00
|
|
|
|
## 当前 tools
|
|
|
|
|
|
|
2026-05-16 17:21:29 +08:00
|
|
|
|
| 工具名 | 作用 | 注册方式 |
|
|
|
|
|
|
|--------|------|---------|
|
|
|
|
|
|
| http-get | HTTP GET 请求 | `NewTool[HTTPGetInput]` |
|
|
|
|
|
|
| skill | 按需加载知识 | `NewTool[SkillInput]` |
|
|
|
|
|
|
| geocode | 城市名 → 坐标(调 wttr.in) | `NewTool[GeocodeInput]` |
|
|
|
|
|
|
| read-file | 读取文件 | `NewTool[ReadFileInput]` |
|
|
|
|
|
|
| task | 调度子 Agent(含缓存管理 + 多步骤编排) | `NewTool[TaskInput]` |
|
|
|
|
|
|
| memory.read | 读取 config/session/notes 等记忆文件 | `NewTool[MemoryReadInput]` |
|
|
|
|
|
|
| memory.write | 写入记忆文件(.md 按 ## 标题合并,.yml 按 key 合并) | `NewTool[MemoryWriteInput]` |
|
2026-05-11 08:32:30 +08:00
|
|
|
|
|
2026-05-16 17:21:29 +08:00
|
|
|
|
所有工具都通过 `NewTool[T]` 泛型函数注册,输入结构体自动反射生成 JSON Schema,handler 内参数为类型安全的结构体字段。
|
|
|
|
|
|
|
|
|
|
|
|
## 核心流程
|
2026-05-08 10:12:31 +08:00
|
|
|
|
|
|
|
|
|
|
```
|
2026-05-16 17:21:29 +08:00
|
|
|
|
用户输入
|
|
|
|
|
|
↓
|
|
|
|
|
|
RunAgent → CallLLMStream (SSE 流式,\n\n 段落缓冲 → mdprint 渲染)
|
|
|
|
|
|
├─ 流内容到达 → tryFlushBlocks 检测 \n\n
|
|
|
|
|
|
│ ├─ 完整 block → mdprint.Print 渲染到 stdout
|
|
|
|
|
|
│ └─ 残段 → 留在 blockBuf 继续缓冲
|
|
|
|
|
|
├─ 流结束 → mdprint.Print(blockBuf) 刷残段
|
|
|
|
|
|
├─ 返回 tool_calls(累积重建)→ 继续循环
|
|
|
|
|
|
│ ├─ task(weather/train/hotel/…) → RunSubAgent → TEXT → 回对话
|
|
|
|
|
|
│ │ ├── maxToolCalls=2 兜底
|
|
|
|
|
|
│ │ └── 每步写 infoLog/warnLog → log.yml
|
|
|
|
|
|
│ ├─ task(profile) → 提取用户画像写入 config/user.md
|
|
|
|
|
|
│ ├─ task(note) → 保存/查询笔记 (notes.md / notes/*.md)
|
|
|
|
|
|
│ ├─ memory.read → 读 config/user.md / session/dialog.yml / soul.md / notes.md
|
|
|
|
|
|
│ ├─ memory.write → 写 config/ session/ notes/ 各文件
|
|
|
|
|
|
│ └─ 其他工具 (http-get, geocode, …)
|
|
|
|
|
|
└─ 返回 text → 流已渲染完毕 → 结束
|
|
|
|
|
|
单 Agent 查询跳过 observation + summary;综合查询合并同一轮写
|
2026-05-08 10:12:31 +08:00
|
|
|
|
```
|
2026-05-11 08:32:30 +08:00
|
|
|
|
|
2026-05-16 17:21:29 +08:00
|
|
|
|
## 当前状态(2026-05-16 v2.3.0)
|
|
|
|
|
|
|
2026-05-11 08:32:30 +08:00
|
|
|
|
```
|
2026-05-16 17:21:29 +08:00
|
|
|
|
yunshu (会议室架构 — 核心引擎 + 日志 + 画像 + 备忘录)
|
|
|
|
|
|
├── dialog-agent.md (type: main,主持者)
|
|
|
|
|
|
├── weather-sub.md (type: sub,天气) ✅
|
|
|
|
|
|
├── profile-sub.md (type: sub,用户画像) ✅
|
|
|
|
|
|
├── note-sub.md (type: sub,备忘录) ✅
|
|
|
|
|
|
├── ✨ 日志系统 ✅ charmbracelet/log v2 + log.yml + yunshu log
|
|
|
|
|
|
├── ✨ LLM 延迟加载 ✅ sync.Once,--help 不读 config
|
|
|
|
|
|
├── ✨ 路径安全 ✅ EvalSymlinks + filepath.Rel
|
|
|
|
|
|
├── ✨ 热加载 ✅ 交互模式每轮 ScanAgents()
|
|
|
|
|
|
├── ✨ 会话裁剪 ✅ LoadSession 限 40 条
|
|
|
|
|
|
├── ✨ 流式输出 ✅ SSE 流式 + \n\n 段落缓冲 + mdprint
|
|
|
|
|
|
├── ✨ 性能优化 ✅ note 一步完成 + obs/summary 合并 + maxToolCalls=2
|
|
|
|
|
|
├── earthquake-sub.md (type: sub,地震) ❌ 待实现
|
|
|
|
|
|
├── train-sub.md (type: sub,火车票) ❌ 待实现
|
|
|
|
|
|
├── hotel-sub.md (type: sub,住宿) ❌ 待实现
|
|
|
|
|
|
└── narrator-sub.md (type: sub,汇报员/成熟期) ❌ 待实现
|
2026-05-11 08:32:30 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-05-16 17:21:29 +08:00
|
|
|
|
## 存储结构
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
~/.config/yunshu/
|
|
|
|
|
|
├── config/
|
|
|
|
|
|
│ ├── config.yml ← LLM 配置(已有)
|
|
|
|
|
|
│ ├── soul.md ← AI 灵魂(用户可编辑)
|
|
|
|
|
|
│ └── user.md ← 用户画像(profile-sub 写 ## 画像,dialog 写 ## AI观察到)
|
|
|
|
|
|
├── session/
|
|
|
|
|
|
│ ├── session.json ← 完整对话历史(直接 POST API)
|
|
|
|
|
|
│ └── dialog.yml ← 对话摘要(dialog 每轮写入)
|
|
|
|
|
|
├── notes.md ← 备忘录(note-sub 维护,列表格式)
|
|
|
|
|
|
├── notes/ ← 独立笔记文件(复杂内容用)
|
|
|
|
|
|
├── log.yml ← YAML 序列日志(yunshu log 命令查看)
|
|
|
|
|
|
├── cache/ ← 子 Agent 缓存
|
|
|
|
|
|
├── agents/ ← Agent 定义
|
|
|
|
|
|
├── skills/ ← 知识技能
|
|
|
|
|
|
└── memory.json ← ❌ 已删除(迁移完毕)
|
2026-05-11 08:32:30 +08:00
|
|
|
|
```
|
2026-05-16 17:21:29 +08:00
|
|
|
|
|
|
|
|
|
|
## 写入策略
|
|
|
|
|
|
|
|
|
|
|
|
| 文件 | 写入方式 | 说明 |
|
|
|
|
|
|
|------|---------|------|
|
|
|
|
|
|
| `config/user.md` | `##` 标题合并 | 各板块独立更新,互不覆盖 |
|
|
|
|
|
|
| `session/dialog.yml` | key 合并 | 每轮覆写对话摘要 |
|
|
|
|
|
|
| `notes.md` | 全文覆写 | note-sub 全量管理 |
|
|
|
|
|
|
| `notes/` 独立文件 | 全文覆写 | 每个笔记一个文件 |
|
|
|
|
|
|
| `log.yml` | YAML 序列追加 | 读→追加→Marshal→写,双写模式 |
|
2026-05-11 08:32:30 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 架构文档
|
|
|
|
|
|
|
2026-05-16 17:21:29 +08:00
|
|
|
|
- `docs/会议室架构计划书.md` — 完整设计方案
|
|
|
|
|
|
- `docs/AGENTS.md` — 编码规范
|
|
|
|
|
|
- `docs/changelog.md` — 变更日志
|
|
|
|
|
|
- `docs/taolun.md` — 讨论历史
|