Core features: - Microkernel architecture with Actor model - Session management with JSONL persistence - Tool system (5 built-in tools) - Skill system with SKILL.md parsing - Sandbox security execution - Ollama integration with gemma4:e4b - Prompt-based tool calling (compatible with native function calling) - REPL interface 11 packages, all tests passing
417 lines
16 KiB
Markdown
417 lines
16 KiB
Markdown
---
|
||
date: 2026-05-07
|
||
topic: "Go Agent Framework - Orca"
|
||
status: validated
|
||
---
|
||
|
||
# Go Agent Framework (Orca) 设计文档
|
||
|
||
## Problem Statement
|
||
|
||
构建一个基于 Go 的基础 Agent 框架,支持多 Agent 协作、持久化会话记忆、Skill 技能自动识别、沙箱安全执行、自定义 Tool 注册扩展,并接入 Ollama 本地模型(gemma4:e4b)。
|
||
|
||
**核心挑战:**
|
||
- 如何在 Go 中实现轻量、高并发的多 Agent 系统
|
||
- 如何安全地执行用户命令和 Skill 脚本
|
||
- 如何设计可扩展的插件机制(Skill / Tool)
|
||
- 如何管理会话上下文和记忆
|
||
|
||
## Constraints
|
||
|
||
1. **语言约束:** 纯 Go 实现,最小化外部依赖
|
||
2. **存储约束:** 使用 JSON Lines(无 SQLite/数据库依赖)
|
||
3. **隔离约束:** 进程级限制(chroot + 资源限制),不依赖 Docker
|
||
4. **模型约束:** 仅接入 Ollama 本地模型,默认 gemma4:e4b
|
||
5. **Skill 目录:** 读取 `~/.agents/skills/` 下的 Skill 定义
|
||
6. **部署约束:** 单二进制文件,零配置启动
|
||
|
||
## Approach
|
||
|
||
### 架构风格:微内核 + Actor 模型
|
||
|
||
采用**微内核架构**作为基础,所有功能(Skill、Tool、LLM 驱动)都以**插件**形式注册到核心。
|
||
|
||
每个 **Agent 实例是一个独立的 Actor**,通过 **消息总线(Message Bus)** 进行通信。这完美契合 Go 的 goroutine + channel 并发模型。
|
||
|
||
**为什么选择这个组合?**
|
||
- 微内核保证核心最小化,Skill 和 Tool 热插拔
|
||
- Actor 模型天然支持高并发,避免共享状态
|
||
- 两者结合 = 轻量级、高扩展、Go 原生友好
|
||
|
||
**放弃的其他方案:**
|
||
- Docker 沙箱:太重,违背最小依赖原则
|
||
- SQLite 存储:增加依赖,JSONL 已足够
|
||
- 中央协调器:单点瓶颈,不如 Actor 模型灵活
|
||
|
||
## Architecture
|
||
|
||
### 整体架构图
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ CLI / API Layer │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Core Kernel (微内核) │
|
||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
|
||
│ │ Message Bus │ │ Plugin Reg │ │ Session Manager │ │
|
||
│ │ (channel) │ │ (registry) │ │ (JSONL-based) │ │
|
||
│ └──────────────┘ └──────────────┘ └──────────────────┘ │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
│
|
||
┌───────────────┼───────────────┐
|
||
▼ ▼ ▼
|
||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||
│ Agent Actor │ │ Agent Actor │ │ Agent Actor │
|
||
│ (Specialist 1) │ │ (Specialist 2) │ │ (Orchestrator) │
|
||
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
|
||
│ │ │
|
||
└───────────────────┼───────────────────┘
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Plugin Layer │
|
||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐│
|
||
│ │ Skills │ │ Tools │ │ Ollama │ │ Custom Tools ││
|
||
│ │(Skill Mgr)│ │(Tool Mgr)│ │ (Driver) │ │ (Registry) ││
|
||
│ └──────────┘ └──────────┘ └──────────┘ └──────────────┘│
|
||
└─────────────────────────────────────────────────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Sandbox Layer │
|
||
│ (Process-level isolation + Resource limits) │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 模块职责
|
||
|
||
| 模块 | 职责 |
|
||
|------|------|
|
||
| **Core Kernel** | 消息路由、插件生命周期管理、会话协调 |
|
||
| **Message Bus** | 基于 Go channel 的异步消息传递系统 |
|
||
| **Plugin Registry** | 统一的 Skill/Tool/LLM 驱动注册中心 |
|
||
| **Session Manager** | 基于 JSONL 的会话历史读写和上下文窗口管理 |
|
||
| **Agent Actor** | 独立 goroutine,持有状态,接收/发送消息 |
|
||
| **Skill Manager** | 扫描 `~/.agents/skills/`,解析 SKILL.md,加载技能 |
|
||
| **Tool Manager** | 管理内置工具和自定义工具的注册/调用 |
|
||
| **Ollama Driver** | 封装 Ollama HTTP API,支持流式响应 |
|
||
| **Sandbox** | 安全执行 shell 命令和脚本,限制资源和时间 |
|
||
|
||
## Components
|
||
|
||
### 1. Core Kernel (微内核)
|
||
|
||
**职责:** 框架的最小化核心,只负责消息路由和插件生命周期。
|
||
|
||
**设计要点:**
|
||
- 使用 Go 的 `interface{}` 或泛型定义插件契约
|
||
- 启动时加载所有已注册的插件
|
||
- 提供事件总线供插件间通信
|
||
- **不**包含任何业务逻辑(如 LLM 调用、命令执行)
|
||
|
||
**核心接口:**
|
||
```
|
||
// 所有插件必须实现
|
||
Plugin interface {
|
||
Name() string
|
||
Init(kernel *Kernel) error
|
||
Shutdown() error
|
||
}
|
||
|
||
// 消息总线
|
||
MessageBus interface {
|
||
Publish(topic string, msg Message) error
|
||
Subscribe(topic string, handler Handler) (Subscription, error)
|
||
}
|
||
```
|
||
|
||
### 2. Actor System (多 Agent 引擎)
|
||
|
||
**职责:** 管理 Agent 生命周期和消息通信。
|
||
|
||
**设计要点:**
|
||
- 每个 Agent 是一个独立的 goroutine,通过 channel 接收消息
|
||
- Agent 持有自己的状态(角色、上下文、工具列表)
|
||
- 支持三种 Agent 类型:Orchestrator(协调者)、Worker(执行者)、Specialist(专家)
|
||
- 消息类型:`TaskRequest`、`TaskResponse`、`ToolCall`、`Observation`
|
||
|
||
**Agent 状态机:**
|
||
```
|
||
Idle → Processing → [ToolCall] → WaitingForTool → Processing → Completed
|
||
↓
|
||
[Error] → Failed
|
||
```
|
||
|
||
### 3. Session Manager (会话记忆)
|
||
|
||
**职责:** 持久化会话历史,支持上下文窗口管理。
|
||
|
||
**设计要点:**
|
||
- 每个会话一个 JSONL 文件:`~/.orca/sessions/{session_id}.jsonl`
|
||
- 每行一个 JSON 对象:`{role, content, timestamp, metadata}`
|
||
- 提供 `GetContext(windowSize)` 方法,返回最近的 N 条消息
|
||
- 支持会话列表、搜索、归档
|
||
|
||
**为什么 JSON Lines?**
|
||
- 追加写入 O(1),无需加载整个文件
|
||
- 人类可读,便于调试
|
||
- 零依赖,无需数据库驱动
|
||
- 通过简单文件锁保证并发安全
|
||
|
||
### 4. Skill Manager (技能系统)
|
||
|
||
**职责:** 自动发现和加载 Skill。
|
||
|
||
**设计要点:**
|
||
- 启动时扫描 `~/.agents/skills/` 下的所有子目录
|
||
- 解析每个 Skill 目录下的 `SKILL.md`
|
||
- 提取元数据:`name`、`description`、`triggers`(触发词)
|
||
- Skill 可以包含脚本文件(`scripts/` 目录)
|
||
- 提供 `FindSkill(query string)` 方法,基于触发词匹配
|
||
|
||
**Skill 结构:**
|
||
```yaml
|
||
name: "md2pdf"
|
||
description: "Convert Markdown to PDF..."
|
||
triggers: ["pdf", "markdown", "export"]
|
||
scripts:
|
||
- "scripts/convert.py"
|
||
- "scripts/setup.sh"
|
||
```
|
||
|
||
### 5. Tool Manager (工具系统)
|
||
|
||
**职责:** 管理可执行工具的注册和调用。
|
||
|
||
**设计要点:**
|
||
- **内置工具:** `exec`(执行命令)、`read_file`、`write_file`、`list_dir`
|
||
- **Skill 工具:** 从 Skill 的 `scripts/` 目录自动注册
|
||
- **自定义工具:** 通过代码注册,实现 `Tool` 接口
|
||
- 每个工具定义:名称、描述、参数 schema、执行函数
|
||
- LLM 通过 Function Calling 调用工具
|
||
|
||
**Tool 接口:**
|
||
```
|
||
Tool interface {
|
||
Name() string
|
||
Description() string
|
||
Parameters() JSONSchema
|
||
Execute(ctx Context, args map[string]any) (Result, error)
|
||
}
|
||
```
|
||
|
||
### 6. Ollama Driver (LLM 驱动)
|
||
|
||
**职责:** 封装 Ollama API,提供统一的 LLM 调用接口。
|
||
|
||
**设计要点:**
|
||
- 默认模型:`gemma4:e4b`
|
||
- 支持流式响应(SSE)
|
||
- 支持 Function Calling(通过 tools 参数)
|
||
- 自动处理上下文窗口截断
|
||
- 可配置参数:temperature、top_p、max_tokens
|
||
|
||
**API 封装:**
|
||
```
|
||
LLMClient interface {
|
||
Chat(messages []Message, tools []Tool) (Response, error)
|
||
ChatStream(messages []Message, tools []Tool) (Stream, error)
|
||
}
|
||
```
|
||
|
||
### 7. Sandbox (沙箱执行)
|
||
|
||
**职责:** 安全地执行终端命令和脚本。
|
||
|
||
**设计要点:**
|
||
- 使用 `os/exec` 创建子进程
|
||
- 资源限制:CPU 时间、内存、输出大小
|
||
- 超时控制:默认 30 秒,可配置
|
||
- 工作目录限制:可选 chroot 或指定工作目录
|
||
- 环境变量隔离:只允许白名单环境变量
|
||
- **不**使用 Docker,保持轻量
|
||
|
||
**安全策略:**
|
||
```yaml
|
||
sandbox:
|
||
timeout: 30s
|
||
max_memory: 512MB
|
||
max_output: 64KB
|
||
allowed_env: [PATH, HOME, USER]
|
||
working_dir: /tmp/orca-sandbox
|
||
read_only_dirs: []
|
||
blocked_commands: [rm -rf /, mkfs, dd]
|
||
```
|
||
|
||
## Data Flow
|
||
|
||
### 典型交互流程
|
||
|
||
```
|
||
用户输入
|
||
│
|
||
▼
|
||
┌─────────────┐
|
||
│ CLI/API │
|
||
└──────┬──────┘
|
||
│
|
||
▼
|
||
┌─────────────┐ ┌─────────────┐
|
||
│ Session Mgr │────▶│ 加载历史上下文 │
|
||
└──────┬──────┘ └─────────────┘
|
||
│
|
||
▼
|
||
┌─────────────┐
|
||
│ Orchestrator │ (Agent Actor)
|
||
│ Agent │
|
||
└──────┬──────┘
|
||
│
|
||
▼
|
||
┌─────────────┐ ┌─────────────┐
|
||
│ Skill Mgr │────▶│ 匹配相关 Skill │
|
||
└──────┬──────┘ └─────────────┘
|
||
│
|
||
▼
|
||
┌─────────────┐ ┌─────────────┐
|
||
│ Ollama Driver│────▶│ 发送 prompt │
|
||
└──────┬──────┘ └─────────────┘
|
||
│
|
||
▼
|
||
┌─────────────┐
|
||
│ LLM Response │
|
||
│ (Function │
|
||
│ Calling) │
|
||
└──────┬──────┘
|
||
│
|
||
▼
|
||
┌─────────────┐ ┌─────────────┐
|
||
│ Tool Call │────▶│ 执行 Tool/ │
|
||
│ │ │ 沙箱命令 │
|
||
└──────┬──────┘ └─────────────┘
|
||
│
|
||
▼
|
||
┌─────────────┐
|
||
│ Observation │ (工具执行结果)
|
||
└──────┬──────┘
|
||
│
|
||
▼
|
||
┌─────────────┐ ┌─────────────┐
|
||
│ Orchestrator │────▶│ 决策:继续/完成 │
|
||
└──────┬──────┘ └─────────────┘
|
||
│
|
||
▼
|
||
┌─────────────┐
|
||
│ 保存会话 │
|
||
│ 返回结果 │
|
||
└─────────────┘
|
||
```
|
||
|
||
### 消息类型定义
|
||
|
||
```go
|
||
type Message struct {
|
||
ID string
|
||
Type MessageType // TaskRequest, TaskResponse, ToolCall, Observation, Error
|
||
From string // Agent ID
|
||
To string // Agent ID or "broadcast"
|
||
Content interface{} // 根据 Type 不同而变化
|
||
Timestamp time.Time
|
||
}
|
||
|
||
type TaskRequest struct {
|
||
Query string
|
||
SessionID string
|
||
Context []ChatMessage
|
||
}
|
||
|
||
type ToolCall struct {
|
||
ToolName string
|
||
Arguments map[string]interface{}
|
||
}
|
||
|
||
type Observation struct {
|
||
ToolCallID string
|
||
Output string
|
||
Error string
|
||
}
|
||
```
|
||
|
||
## Error Handling
|
||
|
||
### 策略
|
||
|
||
1. **分层错误处理:**
|
||
- **Kernel 层:** 插件加载失败 → 记录日志,跳过该插件,继续启动
|
||
- **Agent 层:** 任务执行失败 → 返回错误消息,让 Orchestrator 决策重试或终止
|
||
- **Tool 层:** 工具执行失败 → 返回结构化错误,LLM 可据此调整策略
|
||
- **Sandbox 层:** 命令超时/内存超限 → 强制终止进程,返回错误
|
||
|
||
2. **重试机制:**
|
||
- LLM API 调用:指数退避重试 3 次
|
||
- 工具执行:不重试(避免循环),由 LLM 决策
|
||
|
||
3. **优雅降级:**
|
||
- Ollama 不可用 → 提示用户检查服务
|
||
- Skill 解析失败 → 跳过该 Skill,不影响其他
|
||
- 沙箱执行失败 → 返回错误信息,LLM 可尝试其他工具
|
||
|
||
### 错误类型
|
||
|
||
```go
|
||
type ErrorCategory int
|
||
|
||
const (
|
||
ErrCategoryKernel ErrorCategory = iota // 内核错误
|
||
ErrCategoryAgent // Agent 错误
|
||
ErrCategoryTool // 工具错误
|
||
ErrCategorySandbox // 沙箱错误
|
||
ErrCategoryLLM // LLM 错误
|
||
ErrCategoryNetwork // 网络错误
|
||
)
|
||
```
|
||
|
||
## Testing Strategy
|
||
|
||
### 测试金字塔
|
||
|
||
1. **单元测试(60%):**
|
||
- `Kernel`:插件注册/卸载、消息路由
|
||
- `SessionManager`:JSONL 读写、上下文窗口截断
|
||
- `SkillManager`:Skill 解析、触发词匹配
|
||
- `Sandbox`:资源限制、超时控制
|
||
- `OllamaDriver`:HTTP 请求封装(使用 mock server)
|
||
|
||
2. **集成测试(30%):**
|
||
- Agent + Tool:端到端任务执行
|
||
- Agent + LLM:使用 mock LLM 测试 Function Calling 流程
|
||
- Skill + Sandbox:加载 Skill 并执行其脚本
|
||
|
||
3. **E2E 测试(10%):**
|
||
- 完整 CLI 工作流
|
||
- 多 Agent 协作场景
|
||
|
||
### Mock 策略
|
||
|
||
- `LLMClient`:使用接口,测试时注入 mock
|
||
- `Sandbox`:提供 `DryRun` 模式,记录命令但不执行
|
||
- `MessageBus`:内存实现,用于测试
|
||
|
||
## Open Questions
|
||
|
||
1. **Skill 执行方式:** Skill 脚本是用 Shell 调用还是直接在 Go 中执行?当前设计倾向 Shell 调用(通过 Sandbox),但 Python/Node 脚本需要对应运行时。
|
||
- **假设:** 用户环境已安装所需运行时(Python、Node 等),Sandbox 只负责安全执行。
|
||
|
||
2. **Function Calling 格式:** gemma4:e4b 对 Function Calling 的支持程度?
|
||
- **假设:** 使用 Ollama 的 `tools` 参数格式,如果不支持则 fallback 到 prompt-based tool calling。
|
||
|
||
3. **多 Agent 协作粒度:** Agent 之间是平等协作还是有层级?
|
||
- **假设:** 支持两种模式:层级(Orchestrator + Workers)和平等(对等协作),由用户配置。
|
||
|
||
4. **会话共享:** 多个 Agent 是否可以共享同一个会话上下文?
|
||
- **假设:** 是,Session Manager 通过文件锁支持并发读取,但同一时间只有一个 Agent 写入。
|
||
|
||
5. **Tool 参数 Schema:** 使用 JSON Schema 还是简化格式?
|
||
- **假设:** 使用简化版 JSON Schema(支持 string/number/boolean/array/object + description)。
|