基于手写 Agent Loop 的本地单智能体 AI 工作台
让大模型在可控边界内使用文件、命令、长期记忆、动态技能与定时任务
项目定位 · 快速开始 · 系统架构 · 记忆与上下文 · 安全体系 · 技能扩展
OmniClaw 是一个终端原生的本地 AI Agent 运行框架。系统采用标准的
agent -> tools -> agent 循环:由一个 LLM Agent 负责理解请求、选择工具并完成多步任务,
确定性的运行时组件负责权限、路径、命令、持久化和审计。
它不是把模型直接接入 Shell 的聊天封装,而是围绕本地 Agent 的长期运行补齐以下工程能力:
- 单智能体工具循环:减少路由调用、状态分叉和多 Agent 协作开销。
- 分层记忆:近期完整轮次、增量摘要、长期用户画像各自承担不同职责。
- 确定性安全边界:高风险意图、命令策略、路径校验和执行后端逐层拦截。
- 持久化运行:SQLite state store、定时任务、线程切换和进程状态可以跨会话保留。
- 动态技能:只注册技能元数据,使用时再加载完整说明和结构化清单。
- 可观测性:模型输入、工具调用、工具结果和生命周期事件写入 JSONL 审计日志。
Important
OmniClaw 仍处于积极开发阶段。默认 Shell 策略优先保证可控性,不支持管道、重定向、 通配符、解释器包装,以及未经审批的删除、覆盖和远程 Git 操作。
OmniClaw 不试图解释 LLM 的内部推理过程,而是把模型外部的执行行为纳入确定性的本地运行时管理。
模型负责理解任务和选择工具;文件、命令、网络、记忆、上下文、定时任务和审计日志则由可测试的 本地组件负责执行、约束和记录。
这使系统具备清晰的工程边界:
- 可观测:每次模型输入、工具调用、工具结果和生命周期事件写入 JSONL 审计日志。
- 可测试:Agent loop、工具策略、路径沙盒、上下文裁剪和任务调度都有单元测试覆盖。
- 可回滚:取消或异常时回滚当前轮次新增消息,避免污染后续上下文。
- 可控:高风险命令、路径越界、SSRF 和工具调用超预算由确定性策略拦截。
简言之,LLM 推理本身仍是黑盒,但 OmniClaw 将 Agent 运行时做成白盒。
| 能力 | 当前实现 |
|---|---|
| Agent 运行时 | 手写单智能体 agent <-> tools 循环 |
| 白盒治理 | 模型外部行为可观测、可测试、可回滚、可审计 |
| 本地文件 | 限定在 workspace/office/,支持读取、写入、目录浏览和写前备份 |
| 命令执行 | 静态策略分级,shell=False,参数数组执行 |
| 网络读取 | 仅允许公网 HTTPS 文本,阻断本机、内网和保留地址 |
| 上下文管理 | Token Budget、完整轮次裁剪、增量摘要、超大结果转存 |
| 长期记忆 | 独立用户画像文件与 SQLite 对话状态 |
| 定时任务 | 单次、每小时、每天和每周任务,SQLite 事务存储 |
| 技能系统 | Markdown 元数据懒加载与 skill.json 结构化执行 |
| 运行控制 | 异步模型/工具调用、超时、取消、异常轮次回滚 |
| 审计监控 | JSONL 事件日志与独立终端 Monitor |
- Python 3.11+
- macOS 或 Linux
- 至少一个可用的模型 API Key,或本地 Ollama 服务
git clone https://github.com/your-account/OmniClaw.git
cd OmniClaw
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -e .Anthropic 和 Ollama 使用可选 Provider 依赖:
python -m pip install langchain-anthropic
# 或
python -m pip install langchain-community推荐使用交互式配置向导:
omniclaw config向导会选择 Provider、模型、API Key 和 Base URL,并在写入 .env 前测试连接。
也可以手动创建配置:
cp .env.example .envOpenAI 兼容接口示例:
DEFAULT_PROVIDER=deepseek
DEFAULT_MODEL=deepseek-chat
OPENAI_API_KEY=your-api-key支持 OpenAI、DeepSeek、阿里云、MiniMax、Moonshot、SiliconFlow、腾讯混元、智谱、 Anthropic、Ollama 以及自定义 OpenAI 兼容服务。
omniclaw run
终端原生异步 REPL,启动时展示当前 Provider 与模型
常用 REPL 命令:
| 命令 | 作用 |
|---|---|
/help |
查看快捷命令 |
/clear |
清空终端显示,不删除持久化状态 |
/new |
创建新对话线程,继续共享画像、文件和定时任务 |
/cancel |
取消当前模型或工具执行;流式输出期间可按 Ctrl+C |
/token-budget |
查看当前上下文 Token 预算 |
/token-budget <正整数|reset> |
修改后续轮次预算,或恢复启动配置 |
/exit |
退出当前会话 |
Esc + Enter |
输入换行 |
/token-budget 修改的是发送给模型的历史消息预算,不等于模型的完整上下文窗口。
设置在当前进程内生效并跨 /new 保留;重启后重新读取
OMNICLAW_CONTEXT_TOKEN_BUDGET。运行时还会根据模型总窗口扣除系统提示词、
工具 schema、输出预留和安全余量;总窗口由 OMNICLAW_MODEL_CONTEXT_WINDOW 配置。
在另一个终端查看实时审计事件:
omniclaw monitorMonitor 会区分“日志可读取”和“主程序在线”。即使进程被强制终止,也会通过 PID 存活性检查避免将旧日志误判为运行中实例。
OmniClaw 当前只有一个负责推理的 Agent 节点。工具、记忆、安全和持久化都是该 Agent 之外的确定性运行时组件。
flowchart LR
U["终端 REPL"] --> Q["异步输入队列"]
Q --> A["手写 Agent Loop"]
A -->|"调用工具"| T["ToolExecutor"]
T --> A
A -->|"最终回复"| U
A --- C["上下文管理"]
A --- P["用户画像"]
A --- S["SQLite State Store"]
T --- F["文件与命令工具"]
T --- H["定时任务"]
T --- K["动态技能"]
A --- L["JSONL 审计日志"]
一次完整请求的执行过程:
- REPL 将用户输入写入异步队列,
AgentSession创建本轮运行任务。 - Agent 先处理内部提醒事件、高风险批量删除意图和上下文预算。
- 模型从 13 个默认工具、动态技能和调用方自定义工具中选择能力。
- 手写
ToolExecutor在执行前完成预算、重复抓取和安全拦截,再返回工具结果。 - 无工具调用时输出最终回复,消息与摘要由 SQLite state store 持久化。
- 取消或异常时回滚本轮新增消息,避免污染后续会话状态。
选择单智能体架构的原因:
- 减少额外的路由模型调用、首包延迟和 Token 成本。
- 避免路由 JSON 不稳定导致任务进入错误 Worker。
- 所有工具只需在一个注册表中维护。
- 自定义工具能够直接进入模型的
bind_tools。 - 手写 loop 让工具执行前审批、预算治理、取消和状态恢复更容易验证。
详细设计见 当前系统架构。
OmniClaw 不把所有历史消息无限塞入模型,也不直接按消息数量粗暴截断。系统将记忆拆成 “近期对话、任务摘要、长期画像、持久化状态、外部产物”五类。
flowchart LR
H["完整历史消息"] --> B{"超过 Token Budget?"}
B -->|"否"| R["保留完整轮次"]
B -->|"是"| K["最近完整轮次"]
B -->|"是"| O["较早完整轮次"]
O --> S["增量融合摘要"]
K --> N["下一次模型输入"]
S --> N
P["长期用户画像"] --> N
D["SQLite State Store"] <--> H
X["超大工具结果"] --> F["Artifact 文件"]
F --> N
| 层级 | 作用 |
|---|---|
| 近期完整轮次 | 保留最近的 HumanMessage -> AIMessage -> ToolMessage 链,不拆散工具调用 |
| 增量摘要 | 将较早任务、已完成步骤、关键决策和下一步动作融合进现有摘要 |
| 用户画像 | 保存稳定偏好和背景信息,不与短期任务摘要混在一起 |
| SQLite state store | 保存消息、摘要和线程状态,进程重启后可以恢复 |
| Artifact | 工具结果超过默认 6,000 字符时保存完整原文,上下文只保留预览和路径 |
默认上下文预算为 12,000 Token。优先使用 tiktoken 估算;不可用时回退到字符启发式。
摘要模型失败时会使用最近消息生成规则化降级摘要,主对话不会因此中断。
安全判断不依赖模型“自觉”。OmniClaw 在模型前、工具前、路径解析和进程执行位置设置 确定性防线。
flowchart LR
U["用户请求"] --> I["意图前置检查"]
I -->|"批量破坏请求"| X["拒绝"]
I --> A["模型选择工具"]
A --> C["命令策略"]
C -->|"DENY"| X
C -->|"REQUIRE_CONFIRM"| X
C -->|"ALLOW"| P["路径与链接校验"]
P -->|"越出工作区"| X
P --> E["受限执行后端"]
E --> O["工具结果"]
O --> L["JSONL 审计"]
- 模型调用前拒绝“删除全部文件”“清空工作区”等批量破坏请求。
- 用户随后回复“确认”“继续”也不能创建审批凭证。
- 同一工具和参数连续失败 3 次后停止重试。
- 模型或工具异常时回滚当前轮次新增消息。
命令被划分为三个风险等级:
| 等级 | 行为 | 示例 |
|---|---|---|
ALLOW |
允许进入执行后端 | pwd、ls、mkdir |
REQUIRE_CONFIRM |
当前版本阻断 | rm file.txt、mv、Git 清理 |
DENY |
永久拒绝 | sudo、rm -rf .、解释器命令、git push --force |
策略同时拒绝管道、重定向、变量展开、通配符、命令替换和通过路径直接调用可执行文件。
- 文件工具只能访问
workspace/office/。 - 拒绝绝对路径和
../路径穿越。 - 使用
realpath + commonpath重新校验符号链接目标。 - 覆盖写入前保存备份,并使用临时文件原子替换。
默认本地后端使用参数数组和 shell=False。设置
OMNICLAW_EXECUTION_BACKEND=docker 后,可将已通过策略的命令放入禁网、只读根文件系统、
移除 Linux capabilities 且限制 CPU、内存和 PID 的容器中执行。
Warning
这是应用层纵深防护,不等同于虚拟机或专用沙箱。处理完全不可信代码时,应使用 Docker 后端,并继续限制宿主机挂载、容器权限和敏感凭证。
OmniClaw 默认注册 13 个工具:
| 类别 | 工具 |
|---|---|
| 文件 | list_office_files、read_office_file、write_office_file |
| 执行 | execute_office_shell |
| 网络 | fetch_https_text |
| 基础能力 | calculator、get_current_time、get_system_model_info |
| 记忆 | save_user_profile |
| 定时任务 | schedule_task、list_scheduled_tasks、modify_scheduled_task、delete_scheduled_task |
fetch_https_text 是受限网页读取工具,不是搜索引擎。它只接受明确的公网 HTTPS URL,
限制响应大小,并在每次重定向后重新执行 SSRF 校验。
动态技能存放在:
workspace/office/skills/<skill-name>/
├── SKILL.md
└── skill.json
SKILL.md 提供名称、描述和使用说明;skill.json 声明固定入口、权限和参数:
{
"entrypoint": ["echo", "{message}"],
"permissions": ["shell.read_only"],
"parameters": {
"message": {
"required": true,
"description": "输出文本"
}
}
}加载流程:
扫描 SKILL.md 元数据
↓
只向模型注册名称和描述
↓
需要时使用 help 读取完整说明
↓
使用 run 提交清单声明的结构化参数
↓
统一经过命令策略和执行后端
- 技能目录扫描结果缓存 60 秒。
- 完整说明使用 LRU 缓存,单次最多注入 3,000 字符。
- 未知参数、缺失参数、未知权限和未声明占位符会被拒绝。
- 没有
skill.json的旧技能默认只能读取帮助,不能执行自由命令。
定时任务由独立 SQLite 数据库存储。Heartbeat 动态等待最近任务的截止时间;任务发生增删改时,
通过 asyncio.Event 立即唤醒并重新计算,不使用固定间隔轮询。
flowchart LR
U["自然语言提醒"] --> A["Agent"]
A --> T["schedule_task"]
T --> D["tasks.sqlite3"]
D --> H["Heartbeat"]
H -->|"到期"| E["内部结构化事件"]
E --> R["直接输出提醒"]
❯ 分析 office 目录中的销售数据,生成 Markdown 摘要
[✓] 完成调用: list_office_files
[✓] 完成调用: read_office_file
[✓] 完成调用: write_office_file
OmniClaw
分析结果已写入 report.md。
❯ 删除工作区中的所有文件
OmniClaw
批量删除全部文件或清空工作区属于高风险操作,当前版本不会执行。
| Provider | DEFAULT_PROVIDER |
Key |
|---|---|---|
| OpenAI | openai |
OPENAI_API_KEY |
| DeepSeek | deepseek |
OPENAI_API_KEY |
| 阿里云 DashScope | aliyun |
OPENAI_API_KEY |
| MiniMax | minimax |
OPENAI_API_KEY |
| Moonshot | moonshot |
OPENAI_API_KEY |
| SiliconFlow | siliconflow |
OPENAI_API_KEY |
| 腾讯混元 | tencent |
OPENAI_API_KEY |
| 智谱 | zhipu |
OPENAI_API_KEY |
| Anthropic | anthropic |
ANTHROPIC_API_KEY |
| Ollama | ollama |
不需要 |
| 其他兼容服务 | other |
OPENAI_API_KEY |
| 环境变量 | 默认值 | 说明 |
|---|---|---|
OMNICLAW_WORKSPACE |
<project>/workspace |
状态、记忆、任务和文件根目录 |
OMNICLAW_LANG |
zh |
Agent 提示语言,支持 zh、en |
OMNICLAW_EXECUTION_BACKEND |
local |
local 或 docker |
OMNICLAW_CONTEXT_TOKEN_BUDGET |
12000 |
启动时的历史消息 Token 预算;运行中可用 /token-budget 修改 |
OMNICLAW_MODEL_CONTEXT_WINDOW |
65536 |
模型完整上下文窗口,用于请求硬包络校验 |
OMNICLAW_CONTEXT_OUTPUT_RESERVE_TOKENS |
1024 |
为模型输出预留的 Token |
OMNICLAW_CONTEXT_SAFETY_MARGIN_PERCENT |
10 |
完整上下文窗口的安全余量百分比 |
OMNICLAW_MAX_TOOL_CALLS_PER_TURN |
5 |
每轮工具调用次数硬上限 |
OMNICLAW_MAX_HTTPS_FETCHES_PER_TURN |
3 |
每轮 HTTPS 抓取硬上限 |
OMNICLAW_TOOL_BUDGET_UNITS |
8 |
每轮加权工具预算 |
OMNICLAW_TOOL_RESERVE_UNITS |
2 |
只供读取和验证工具使用的收尾预算 |
OMNICLAW_TOOL_BUDGET_SOFT_PERCENT |
50 |
进入节制调用阶段的剩余预算百分比 |
OMNICLAW_ARTIFACT_THRESHOLD_CHARS |
6000 |
工具结果转存阈值 |
OMNICLAW_MAX_WRITE_BYTES |
1000000 |
单文件写入上限 |
OMNICLAW_MAX_FETCH_BYTES |
200000 |
HTTPS 文本响应上限 |
OMNICLAW_LLM_TIMEOUT_SECONDS |
90 |
模型调用超时 |
OMNICLAW_TOOL_TIMEOUT_SECONDS |
60 |
工具节点超时 |
完整示例见 .env.example。
OmniClaw 同时治理模型的认知资源和行动资源:
- 上下文预算:优先保留最近完整轮次;单个最新轮次仍过大时,只压缩模型输入副本, 保留消息类型、工具调用和工具结果之间的协议结构。
- 工具预算:同时限制调用次数、HTTPS 抓取次数和加权单位。普通工具成本为
1, 网页抓取和写入类操作通常为2,Shell、Python 和删除类操作为3。 - 软阈值:剩余预算降至默认
50%后,提示模型优先选择高信息增益调用。 - 收尾预算:最后默认
2个单位只开放读取、计算和状态检查类工具,禁止开始新的写入或探索。 - 硬终止:任一硬预算耗尽后,模型改用未绑定工具的实例,只能基于已有证据回答。
硬终止不是正确性保证。最终回答会携带终止原因,并要求区分“已确认事实”“未确认事项” “结论”和“当前限制”,防止在证据不足时将推测写成事实。
Docker Compose 使用独立命名卷保存容器内工作区和日志,不会直接读写宿主机现有的
workspace/。
cp .env.example .env
docker compose build
docker compose run --rm omniclaw运行隔离测试:
docker compose run --rm test更多说明见 Docker 指南。
workspace/
├── state.sqlite3
├── tasks.sqlite3
├── current_thread_id
├── memory/
│ └── user_profile.md
├── artifacts/
├── backups/
│ └── office/
└── office/
└── skills/
logs/
└── <thread-id>.jsonl
workspace/、logs/ 和 .env 默认被 .gitignore 排除。
python -m pip install -e .
python -m pytest tests/ -q当前测试基线:
242 passed
测试覆盖 Agent loop 结构、异步运行时、上下文裁剪、Artifact、任务调度、Provider、 动态技能、路径沙盒、命令策略、执行后端、线程状态和 Monitor。
OmniClaw/
├── entry/
│ ├── cli.py
│ ├── main.py
│ └── monitor.py
├── omniclaw/core/
│ ├── agent.py
│ ├── runtime.py
│ ├── context.py
│ ├── artifact_store.py
│ ├── execution.py
│ ├── provider.py
│ ├── skill_loader.py
│ ├── heartbeat.py
│ ├── task_store.py
│ ├── logger.py
│ └── tools/
│ ├── builtins.py
│ ├── sandbox_tools.py
│ └── command_policy.py
├── tests/
├── docs/
├── Dockerfile
├── compose.yaml
└── setup.py
REQUIRE_CONFIRM已完成风险识别,但尚未接入真人审批界面。- Shell 工具不支持管道、重定向、通配符和解释器命令。
- 文件工具主要面向 UTF-8 文本,不处理通用二进制文件。
- Docker 执行后端依赖宿主机 Docker daemon,默认仍使用本地受限后端。
- 进程必须持续运行,定时任务才能按时触发。
- Web Search 仍处于设计阶段,尚未进入默认工具集。
本项目使用 MIT License。
项目在产品形态和交互上参考了以下开源项目,核心运行时、安全策略和上下文管理由 OmniClaw 独立实现:



