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

174 lines
7.5 KiB
Markdown
Raw Permalink 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 架构参考
> 详细架构白皮书见 `~/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` — 讨论历史