Files
YunShu/docs/architecture.md
titor c4a0e3ef53 feat: v2.3.0 流式输出 + 日志系统 + 会议室架构全面升级
- 流式输出: SSE 逐 token 接收, \\n\n\ 段落缓冲后 mdprint 彩色渲染
- 日志系统: charmbracelet/log v2 双写(stderr + log.yml), yunshu log 命令
- 会议室架构: dialog(main) + weather/profile/note(sub) 多 Agent 编排
- 泛型工具注册: NewTool[T] 反射推导 JSON Schema, 类型安全
- 安全加固: safeMemoryPath 三段校验(EvalSymlinks+Rel), maxToolCalls=2
- 性能优化: sync.Once 延迟加载, note 一步完成, obs/summary 合并
- Prompt 适配: 流式输出原则(先调工具不说话), 单 Agent 查询跳过 obs+summary
- 文档: AGENTS.md + architecture.md + changelog.md 全部同步至 v2.3.0
2026-05-16 17:21:29 +08:00

7.5 KiB
Raw Permalink Blame History

云枢·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% 可靠

包结构

pkg/
├── mdprint/      Markdown → ANSI 终端渲染AST 架构)
│   ├── mdprint.go     Node 类型定义 + Print() 入口
│   ├── parse.go       块级解析器(状态机)
│   ├── inline.go      行内解析器(递归)
│   └── render.go      ANSI 渲染器type switch
├── style/        终端颜色样式库8 色 ANSI + 24-bit 真彩色)
└── termui/       终端交互(行输入、模式设置)

项目文件

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    AgentRegistryScanAgents, GetMain, GetSub…
llm.go         LLM API 封装(豆包/OpenAIsync.Once 延迟加载)
tool.go        工具注册 + safeMemoryPath + ExecuteTool + 7 个工具 handler
toolschema.go  泛型+反射工具注册NewTool[T], structToSchema
runtime.go     RunAgent + RunSubAgentmaxToolCalls=2+ cache + session
logger.go      charmbracelet/log v2 全局实例(→ stderr
log.go         双写 wrapperwarnLog/errorLog/infoLog+ log.yml + yunshu log 命令

当前 tools

工具名 作用 注册方式
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]

所有工具都通过 NewTool[T] 泛型函数注册,输入结构体自动反射生成 JSON Schemahandler 内参数为类型安全的结构体字段。

核心流程

用户输入
  ↓
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-16 v2.3.0

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汇报员/成熟期)       ❌ 待实现

存储结构

~/.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           ← ❌ 已删除(迁移完毕)

写入策略

文件 写入方式 说明
config/user.md ## 标题合并 各板块独立更新,互不覆盖
session/dialog.yml key 合并 每轮覆写对话摘要
notes.md 全文覆写 note-sub 全量管理
notes/ 独立文件 全文覆写 每个笔记一个文件
log.yml YAML 序列追加 读→追加→Marshal→写双写模式

## 架构文档

- `docs/会议室架构计划书.md` — 完整设计方案
- `docs/AGENTS.md` — 编码规范
- `docs/changelog.md` — 变更日志
- `docs/taolun.md` — 讨论历史