elizaOS 深度解构:18.9K stars 的本地优先 AI Agent OS 到底在做什么
posts posts 2026-08-03T15:42:00+08:0018,902 stars 的 elizaOS/eliza 不只是又一个 agent 框架。它把自己定位成 agentic operating system,把 runtime、agent loop、plugin model、memory/state primitives、整机的 Linux/Android 系统镜像、桌面/移动 app、optional cloud 全塞进一个 monorepo。本文逐层拆开。技术笔记agent, ai-os, typescript, open-source, architecture, elizaOSelizaOS 深度解构:18.9K stars 的本地优先 AI Agent OS 到底在做什么
仓库:github.com/elizaOS/eliza
代码量:仅 @elizaos/core/src/runtime.ts 一文件就 11,782 行
维护方:elizaOS(原 ai16z 团队,基于 Shaw 的 eliza 框架演进)
最新版本:v2.0.4 · MIT 协议
本文核对(GitHub API 2026-08-05 验证):18,902 stars / 5,606 forks / 492 issues / 146 watchers
1. 一句话定位:Agent 的操作系统
elizaOS 把自己叫 agentic operating system——这个定语不是修辞。
读 README 第一段就能看出它和 LangChain / CrewAI / AutoGen 这类 agent 框架的差别:
| 维度 | 框架 | elizaOS |
|---|---|---|
| 部署形态 | Python/Node 包,嵌入到你的 app | 一套完整 OS(可启动的 Linux desktop、Android system image)+ 跨平台 app(web/desktop/mobile)+ runtime + cloud |
| 数据归属 | 调用方服务,数据上云 | local-first,agent、数据、模型全在设备上,cloud 完全可选 |
| 模型来源 | 单一 provider 或多 provider 调用 | 自家 Eliza-1 端侧模型 + OpenAI / Anthropic / Gemini / Grok / Llama 全部可选 |
| 运行时 | 一次推理调用 | AgentRuntime(约 1.1 万行的类),长期驻留进程 |
| 边界 | 业务代码 | app 一等公民——plugin 可以成为 surface,在 runtime 里被 install/launch/track,跨重启存活 |
LangChain 是写一个调用 LLM 的程序。elizaOS 是装一个会说话的操作系统。差别是部署形态、生命周期和资产归属三个维度同时翻转。
把整个仓库摊开看,里面其实有四个逐层套着的系统,一条消息从外到内只走 runtime 的核心 loop:
下文按这条主线展开:plugin 贡献能力,runtime 装配并跑它,OS 和 app 只是把 runtime 挂到不同形态的壳上。
2. 仓库结构:四层分层的全景
packages/ 下不是按 feature 切,而是按 runtime / surface / capability / tooling 四层切:
packages/
├── 运行时核心
│ ├── core/ ← @elizaos/core:AgentRuntime + plugin model
│ ├── agent/ ← @elizaos/agent:AgentRuntime 实例化层
│ └── elizaos/ ← CLI:create / info / upgrade
│
├── Surface(用户看得见的壳)
│ ├── app/ ← Eliza app UI(Vite + React)
│ ├── app-core/ ← app 运行的 API + dashboard host
│ ├── cloud-ui/ ← 云端 dashboard
│ └── homepage/ ← elizaos.ai 官网
│
├── 操作系统本体
│ ├── os/linux/ ← amd64 / arm64 / riscv64 bootable Linux desktop
│ ├── os/android/ ← Android system image,Eliza 当 launcher
│ ├── native/ ← 硬件抽象层
│ └── eliza-computer/ ← 整机协调
│
├── Capability 插件
│ ├── plugin-browser/ ← 浏览器自动化
│ ├── plugin-documents/ ← RAG
│ ├── plugin-phone/ ← 电话 / SMS
│ ├── plugin-task-coordinator/
│ ├── plugin-anthropic/ plugin-openai/ plugin-groq/ plugin-zai/
│ ├── plugin-local-inference/ plugin-ollama/
│ ├── plugin-agent-orchestrator/
│ └── plugin-sql/ ← Postgres(PGlite)+ 关系数据库适配
│
├── 云端(可选)
│ ├── cloud/ ← 托管后端
│ ├── contracts/ ← 链上合约
│ └── eliza-hub/ ← app marketplace
│
└── 工具链
├── docs/ ← 文档
├── benchmarks/ ← lifeops-bench 等
├── scenario-runner/ ← e2e 场景
├── corpus-tools/ ← 训练数据
├── training/ ← 模型训练
├── evidence/ ← PR review evidence store
└── registry/ ← plugin registrypackages/ 顶层 22 个目录,引擎 node@24.15.0,按 workspace 编排。OS、app、plugin、cloud 都收进同一个 monorepo,和"一个函数库"的产品形态完全不同。
3. 运行时核心:AgentRuntime 11,782 行
@elizaos/core/src/runtime.ts 是整个仓库的"宪法"。文件头注释(原文翻译):
AgentRuntime是每个 Eliza agent 跑在上面的中央编排器,具体实现IAgentRuntime。一个实例拥有一个 agent 的整个世界:它的 actions / providers / evaluators / services、model-handler registry 和useModeldispatch/routing/fallback 层、plugin 集和它的生命周期(register / unload / reload / config)、memory 和 state(database adapter / embeddings /stateCache/ working memory),以及跑 provider → model → action → evaluator 的 message loop。Plugin 贡献 capability,runtime 装配并跑它们。@elizaos/core里几乎所有的代码,以及每个 plugin,最终都要和这个类对话。 文件大约 1 万行——按符号导航,不要从上往下读。
3.1 三条不变式
runtime.ts 文件头注释里写明了三条 invariant:
不变式 1:多租户不读 env
// getSetting() 解析 per-agent config,DELIBERATELY 永不读 process.env
// ——在多租户进程里,这会把宿主秘密泄漏到每个 agent
// 宿主应该把 dotenv 折进构造函数 settings map
这种注释级别说明作者考虑过多租户场景下的 env 泄漏问题。
不变式 2:embedding 宽度 pin 到首次应答的 provider
// Embedding 宽度 pin 在首次回答 boot dimension probe 的 TEXT_EMBEDDING provider 上
// 来自不同 provider 的后续 embedding 可能 emit 一个 SQL adapter 静默丢掉的宽度 (#8769)
// 如果所有 provider 都 fail probe,initialize() 非致命 catch EmbeddingDimensionProbeError
// 并禁用 embedding generation 而不是 crash boot
#8769 是 GitHub issue 编号——跨文件引用 issue 是生产级代码的常见做法。
不变式 3:无 database adapter 的降级路径
// 没有 database adapter 时,initialize() 仅在 ALLOW_NO_DATABASE 时
// 才会 fallback 到 in-memory adapter
ALLOW_NO_DATABASE 是显式 opt-in 开关——默认拒绝 in-memory fallback,要求显式选择内存模式,这是个安全/正确性的取舍。
3.2 消息循环骨架
message → providers(state/context)
→ dynamicPrompt
→ model(useModel dispatch,带 fallback chain)
→ response handlers
→ actions(模型可调用)
→ evaluators(后置评估)
→ memory persist(stateCache → DB)
→ post-delivery tasksuseModel 是 runtime 提供的统一调用入口,内部走:
resolveChain决定调用顺序executeChainWithFallback跑 + fallbackmaybeReroute处理错误重路由
模型无关(model-agnostic)的意思是换 provider 只是配置改动,不用改代码。
3.3 一条消息完整流过一次 runtime
把上面这条 loop 套到一次具体对话,看各层到底各干了什么。假设用户从 app 里发来一句"给 Alex 打个招呼":
- providers 先把外部上下文摊进提示词——当前页面摘要、用户状态、角色设定,统一拼给模型。
- dynamicPrompt 把内容切成 system 与 user 两块,其中稳定段标记为可缓存。
- useModel 经
resolveChain选中一个 provider,executeChainWithFallback发起生成;失败就沿 fallback chain 换下一家。 - 模型返回的动作命中 §5.1 的
GREET_USER,handler 回调把"Hello, Alex!“写回对话。 - evaluators 对刚发生的一段对话做后置评估,判断是否值得沉淀成 memory。
- memory persist 把 state 写回 adapter,
stateCache随后刷新。
同样的胶水代码挂在 app、plugin、OS 镜像上时,变的只是最外层壳,loop 本身不动——这是"OS 而不是框架"最直观的地方。
4. 2026 年的最新演进:从 CHANGELOG 看到的三个方向
读 @elizaos/core/CHANGELOG.md(Unreleased 部分),能看到 elizaOS 在 2026 年的三个工程方向。
4.1 Prompt caching 段标记(prompt segments)
传统 prompt 调用的问题是每次调用重发整个 system prompt。2026 年 Anthropic ephemeral cache、OpenAI/Gemini prefix cache 都要求主动声明"哪些段是稳定的”。
elizaOS 的解法:GenerateTextParams 新增可选字段 promptSegments?: PromptSegment[],每个 segment 是 { content, stable }。runtime 在 dynamicPromptExecFromState 里把动态 prompt 按 stable 边界切段:
| 段 | stable? |
|---|---|
| format prefix | ✅ |
| variable block | ❌ |
| validation/middle block | ❌ |
| format suffix | ✅ |
| end block | ❌ |
CHANGELOG 原文解释:“Marking validation or variable content as stable would prevent cache hits because that content changes every call; splitting format from validation ensures the stable segments are actually cacheable.”
然后 plugin 层各自实现:
- Anthropic plugin:每个 segment 一个 content block,stable 的打
cache_control: { type: "ephemeral" } - OpenAI / Gemini plugin:stable 段前置(前缀缓存靠的就是"前面那 N token 一样")
core 表达语义、plugin 做 provider-specific 优化,这是 runtime 层的合理切分。
4.2 跨 runtime 任务调度器(cross-runtime task scheduler)
TaskService 在 2026 之前是每个 agent 一个 setInterval。问题显而易见:N 个 agent = N 个 DB query / 秒。
新版提供三种调度模式,按部署形态选:
| 模式 | 适用 | 行为 |
|---|---|---|
| local timer | 单进程 | 每个 TaskService 一个 setInterval |
| per-daemon | 多 agent 守护进程 | host 调 startTaskScheduler(adapter),共享 timer + 批 getTasks(agentIds) |
| serverless | 无长进程 | runtime.serverless === true,host 用 cron / per-request 调 runDueTasks() |
serverless?: boolean 是 AgentRuntime 构造参数。elizaOS 已经准备好跑在 Lambda / Vercel / Cloudflare Workers 这种 ephemeral runtime 里,这是传统 agent 框架较少考虑的场景。
任务系统本身也升级:TaskMetadata 加了 notBefore / notAfter / paused / failureCount / maxFailures / lastError / baseInterval,dead-letter 机制首次出现。
4.3 共享 batch queue 子系统
之前的痛点:每个 service 都自己写一个 queue + retry + task 的小循环,然后这些实现慢慢 drift。
2026 年的统一:utils/batch-queue 模块提供 PriorityQueue / BatchProcessor(信号量并发 + retry)/ TaskDrain / 组合 BatchQueue / 共享 Semaphore。
CHANGELOG 原文解释:“The runtime is not globally ‘batching-bound’; a minimal fix in one service could be a few lines. The goal here is forward-looking consolidation so embedding drains, action-index embedding, batcher affinity scheduling, and shared throttling do not each grow a bespoke queue + task + retry stack that drifts over time.”
5. Plugin model:四件套 primitives
文档明确写了 plugin 的四类出口:
export interface Plugin {
actions: Action[] // agent 能做什么
providers: Provider[] // 给 prompt 提供上下文
services: Service[] // 长生命周期单例
evaluators: Evaluator[] // 后置处理(reflection, summarization)
}加上面提到的 character(角色设定 / system prompt 主体)和 routes(HTTP API),一个 plugin 就把"能干、能想、能持久、能演"四件事都接上。
举例:
plugin-browser提供一个service(浏览器连接池)+ 一组actions(click/type/navigate)+ 一个provider(当前 URL/DOM 摘要)plugin-anthropic只贡献一个model handler,把useModel调用转发到 Anthropic API
贡献的是 capability,不是孤立的脚本。
5.1 实战:跑一个最小 plugin
# 1. 装 CLI
bun add -g elizaos@beta
# 2. 起一个新 plugin workspace
elizaos create my-plugin -t plugin
# 输出:一个带 package.json + src/index.ts 的最小 plugin 工程
# 3. 写 src/index.tsimport type { Plugin, Action } from "@elizaos/core";
const greetAction: Action = {
name: "GREET_USER",
similes: ["say_hi", "wave_hello"],
description: "Greets the user by name.",
validate: async (runtime, message) => true,
handler: async (runtime, message, state, options, callback) => {
const name = state?.values?.name ?? "stranger";
const text = `Hello, ${name}!`;
await callback({ text });
return { success: true, text };
},
examples: [
[{ user: "user", content: { text: "say hi to Alex" } },
{ user: "assistant", content: { text: "Hello, Alex!" } }],
],
};
export const myPlugin: Plugin = {
name: "my-plugin",
description: "Tiny greeting plugin.",
actions: [greetAction],
};# 4. 在 dev runtime 里挂上
bun run dev
# 你的 plugin workspace 通过 turbo task 被 runtime 自动加载
# 在 app 里发 "say hi to Alex" → agent 调 GREET_USER → 回复 "Hello, Alex!"5.2 实战:在 plugin 内部调 model
import { elizaLogger } from "@elizaos/core";
import type { Action } from "@elizaos/core";
const summarizeAction: Action = {
name: "SUMMARIZE_TEXT",
description: "Summarize a long text using the configured model.",
validate: async (runtime, message) => {
return (message.content as any)?.text?.length > 200;
},
handler: async (runtime, message, state, _options, callback) => {
const input = (message.content as any).text as string;
// useModel 是统一入口,内部走 resolveChain + fallback chain
const result = await runtime.useModel(
"TEXT_LARGE", // model type (注册于 model-gateway)
{
prompt: `Summarize:\n\n${input}`,
// Prompt segment 切分 — runtime 复用 prompt cache
promptSegments: [
{ content: "You are a concise summarizer.\n\n", stable: true },
{ content: `Text: ${input}`, stable: false },
{ content: "\n\nReply in one sentence.", stable: true },
],
temperature: 0.2,
maxTokens: 120,
}
);
const summary = (result as string).trim();
await callback({ text: summary });
elizaLogger.info("summarize.done", {
agentId: runtime.agentId,
inputLen: input.length,
outputLen: summary.length,
modelUsed: result?.$meta?.provider, // fallback 用了哪个 provider
});
return { success: true, text: summary };
},
examples: [
[{ user: "user", content: { text: "a long text…" } },
{ user: "assistant", content: { text: "a one-line summary" } }],
],
};这一段把 4.1 节的 prompt segments、3.2 节的 useModel 调用、6 节的 logger 串在一起,对应真实业务里最常用的一次模型调用。
整个过程不用碰 YAML、不用写 Dockerfile、不用写 deployment script。
6. 安全与隐私
@elizaos/core/src/security/ 下三个关键目录:
redact/—— 日志/对象/字符串 secret 脱敏,redactSecrets/redactObjectSecrets/redactLogArgs/redactSensitiveTextsecret-swap/——SecretSwapSession,运行时用一次性 placeholder 替换真实 secret,只在出站前还原index.ts—— PII 识别 + owner-exclusive disclosure,带PseudonymSession和GuardedStreamScanner
README 上专门一段叫 “Private by default”,并承诺语音本地推理、图像本地描述。ALLOW_NO_DATABASE 这种 opt-in 开关,以及 #8769 这种跨文件 issue 引用,都指向安全/隐私默认 ON 的设计取向。
7. 操作系统层:packages/os
只看 app/ 会觉得这是一个 AI 桌面应用。看 packages/os/ 才会意识到这是一个真操作系统。
packages/os/
├── linux/ ← amd64 / arm64 / riscv64 bootable Linux desktop
├── android/ ← Android system image,Eliza 当 launcherREADME 上明说:
packages/osis the real, bootable distribution. Downloads and hardware are at os.elizacloud.ai.
- Linux — boots a full desktop with Eliza built in from a USB stick. amd64 · arm64 · riscv64.
- Android — Eliza is the system launcher and assistant, on Pixel-class devices.
支持 riscv64,不是 x86 专属。scripts/build:riscv64-artifacts / verify:riscv64 / check:riscv64-artifacts 都真实存在于 root package.json,是实际构建目标。
8. 商业层:Eliza Cloud
Cloud 是 optional 的。README 反复强调:
Eliza Cloud (optional) — Optional managed backend for going beyond one device. Never required — local-only is first-class.
Cloud 做的事:
- Auth(OAuth / SIWS,Solana-attested login)
- Hosted inference + 跨 provider 模型路由
- Deploy —— 把 agent/app push 到容器,带自己的 domain
- Sync & bridge —— 跨设备状态同步,从云 dashboard 驱动本地的 agent
- Monetization —— app / agent / MCP 可以 metered + creator 收益
第五点是 agent 经济系统,不只是 serving 平台。contracts/ 目录对应链上合约部分,负责 creator earnings 分账。
9. 给工程师的 takeaway
- runtime ≠ framework——elizaOS 把 runtime 当 OS。思考"长期驻留进程 + 多 capability 装配",不是"调用一次函数"
- local-first 是工程承诺——
ALLOW_NO_DATABASE/#8769/security/redact都指向安全默认 ON 的设计原则 - prompt cache 段标记是 2026 必修课——Anthropic/OpenAI/Gemini 都按"stable 段"计费,你不切段就是在烧钱
- serverless runtime 是新坐标——
serverless?: boolean意味着 agent 不一定活在 long-lived 进程里 - plugin 四件套是 agent runtime 的通用语——
actions / providers / services / evaluators这套 vocabulary 会被更多 framework 复用
10. 什么时候值得认真评估 elizaOS
如果你要的是"给自己/团队装一个能长期驻留、本地优先、跨设备的 agent 环境",elizaOS 的定位直接对得上。runtime、plugin、OS 镜像、app、cloud 集成为一套,适合当成完整运行环境来评估,而不是当库调用。
反过来,下面几种情况不必急着换:
- 只想在现有服务里调一次 LLM 或加一个工具函数——用框架或直接调 API 更轻。
- 要求 Python 生态或固定技术栈——elizaOS 是 TypeScript / Bun 栈。
- 目标是纯云端多租户 SaaS——Cloud 虽可选,但本地优先才是它的设计重心。
判断入口:先看 packages/os 是否命中你的部署形态,再读一遍 runtime.ts 的注释级别,最后用 plugin 四件套评估够不够覆盖你要的能力。多数场景从 plugin 起步,不急着碰 OS 镜像。
参与讨论
使用 GitHub 登录。欢迎补充事实、异议与实践。
讨论暂时无法加载。