如何构建你自己的 Agent 运行时
本机暂存
<p>2026年5月28日 · Mike Piccolo, iii 创始人兼 CEO</p><hr><p>![image-20260602000705691.png<img src="/2026/06/11/build-your-own-agent-runtime/image-20260602000705691.png" class=""></p><p>大多数 agent 团队不构建运行时。他们采用一个。LangChain、LangGraph、OpenAI Agents SDK、Anthropic SDK、CrewAI、AutoGen——循环、工具、记忆、编排,都是作为一个单一决策从货架上挑选的。运行时是一个你 import 的框架。如果里面的什么东西不合适,你就 fork 它、跟它斗争、或者绕过它。</p><span id="more"></span><p>我认为这种形态是错的,这就是为什么每个长期运行的 agent 团队最终都会从头重写它的运行时。运行时不是一个东西。它是十到十二个不同的东西被捆绑在一起,因为周围的生态系统没有给你组合它们的方式。Pi agent 的包化走在了正确的路上,但它们仍然处在"再添加一个服务并与所有其他服务集成"的范式中。iii 引擎将所有 worker 一视同仁,完全移除了集成逻辑。provider 路由器、凭证保管库、策略引擎、审批网关、模型目录、会话存储、预算追踪器、调用后 hook 扇出、持久化的 turn 循环——这些都是独立的关注点。它们全都可以与你的队列、HTTP/API 服务器、流式传输、甚至浏览器 worker 互操作。一个把它们当作整体发布的框架,是在卖给你一个你本不必做的权衡。</p><p>iii 底层的赌注是:它们不应该是一个整体。应该有一组 worker 运行在共享引擎上,每一个都可替换,每一个可独立版本化,每一个通过一个单一原语连接起来:一个触发器(<code>iii.trigger()</code>),其他每个 worker 也都使用它。运行时变成一组可安装的 worker 堆栈,"构建你自己的"不再意味着"fork 一个框架",而是意味着"替换几个 worker"。</p><p>本文带你看看这实际上是什么样的。今天驱动一个 iii agent turn 的完整技术栈,为什么每一层都是自己的 worker,以及你如何替换其中任何一层。</p><h2 id="Agent-运行时必须完成的-15-项工作"><a href="#Agent-运行时必须完成的-15-项工作" class="headerlink" title="Agent 运行时必须完成的 15 项工作"></a>Agent 运行时必须完成的 15 项工作</h2><p>如果你把一个生产级 agent 运行时剥离回它的职责,你会得到大致像这样的一个列表:</p><ol><li><p>接受来自客户端的 turn 请求并持久化它</p></li><li><p>为被调用的模型 provider 解析凭证</p></li><li><p>查询所选模型实际能做什么(视觉、工具、流式传输、上下文窗口)</p></li><li><p>驱动每个 turn 的状态机:预配、流式传输 assistant、运行工具、引导、清理</p></li><li><p>加载并提供 skill 描述体,说明每个函数的请求格式、错误码和使用说明</p></li><li><p>组装系统提示词:模式段落、身份前言、工作目录、默认 skill 附录</p></li><li><p>当模型产生 token 时将 token 流式推送回客户端</p></li><li><p>在运行前检查每个工具调用(这只是一个函数)是否符合策略</p></li><li><p>暂停需要人工决策的工具调用,并将决策结果路由回正确的 turn</p></li><li><p>按工作空间或 agent 跟踪 LLM 支出</p></li><li><p>在工具调用前后运行 hook(日志记录、脱敏、自定义副作用)</p></li><li><p>将会话持久化为分支树,以便 fork 和恢复正常工作</p></li><li><p>当上下文窗口填满时压缩会话历史</p></li><li><p>发出 UI 订阅的事件流</p></li><li><p>我看到每家 agent 公司构建中都缺失的一块:在每一步携带一条 OpenTelemetry 追踪,这样你才能调试它</p></li></ol><p>每个严肃的 agent 运行时处理其中大多数。昂贵的处理全部。廉价的走捷径,然后在进入生产环境后重新构建那些捷径部分。框架将它们捆绑成一个单体,并发布每样东西的一个版本。最后这一点是让你付出代价的部分,因为一年后,你会发现你想要的策略引擎不是框架自带的那个策略引擎,而替换它意味着替换整个运行时。</p><p>iii 运行时将这十三项工作中的每一项都作为独立的 worker 部署在 workers.iii.dev 注册中心上。每个使用相同的 WebSocket 协议。每个在相同的引擎总线上注册函数和触发器。每一个都可以 <code>iii worker add</code>、可替换、可用 SDK 以任何语言编写。</p><h2 id="按-worker-拆解技术栈"><a href="#按-worker-拆解技术栈" class="headerlink" title="按 worker 拆解技术栈"></a>按 worker 拆解技术栈</h2><p>以下是来自 iii-hq/workers 单体仓库的实际生产级技术栈,每个 worker 的职责用一句话概括。整个代码包发布在 github.com/iii-hq/workers/harness:</p><table><thead><tr><th>Worker</th><th>职责</th></tr></thead><tbody><tr><td><code>iii-directory</code></td><td>Skill 和提示词注册中心。Worker 以 <code>iii://<worker>/<function></code> 发布 skill;agent 通过 <code>directory::skills::get</code> 按需获取。随 iii 引擎一同发布(Rust)。</td></tr><tr><td><code>harness</code></td><td>Meta-worker。加载 <code>iii-permissions.yaml</code>。暴露 <code>policy::check_permissions</code> 和 <code>ui::*</code> 平面。将 <code>agent::events</code> 推送给订阅的浏览器。</td></tr><tr><td><code>turn-orchestrator</code></td><td>驱动每个 agent turn 的持久化 11 状态 FSM。拥有 <code>run::start</code>、<code>turn::step</code>、<code>turn::get_state</code>。还在预配阶段组装系统提示词。</td></tr><tr><td><code>approval-gate</code></td><td>操作员决策的总线入口点。将 <code>approval::resolve</code> 路由到编排器注册的每个调用的恢复函数。</td></tr><tr><td><code>session</code></td><td>分支会话存储。<code>session-tree::*</code> 用于父链接条目树;<code>session-inbox::*</code> 用于每个会话的队列。</td></tr><tr><td><code>llm-budget</code></td><td>工作空间 + agent 支出上限。14 个 <code>budget::*</code> 函数,包括检查、记录、告警、预测、周期切换。</td></tr><tr><td><code>hook-fanout</code></td><td>在流主题上通用发布并收集。每个 iii hook 构建于其上的模式。</td></tr><tr><td><code>auth-credentials</code></td><td><code>auth::*</code> 下基于文件的 provider 凭证保管库。</td></tr><tr><td><code>models-catalog</code></td><td>静态模型能力目录。<code>models::list</code>、<code>models::get</code>、<code>models::supports</code>。</td></tr><tr><td><code>provider-anthropic</code></td><td>Anthropic Messages API SSE 流式推送到 iii channel。</td></tr><tr><td><code>provider-openai</code></td><td>OpenAI Chat Completions SSE 流式推送到 iii channel。</td></tr><tr><td><code>provider-kimi</code></td><td>Kimi(Moonshot)Chat Completions SSE。</td></tr><tr><td><code>provider-lmstudio</code></td><td>用于桌面开发的本地 LM Studio SSE。</td></tr><tr><td><code>context-compaction</code></td><td>可选的 <code>agent::events</code>,在 token 数超过阈值时压缩会话历史。</td></tr></tbody></table><p>十一个 worker。一个引擎。每个都有发布的版本。每个都可以作为独立进程运行(开发时 <code>pnpm dev:<worker></code>,作为发布二进制时 <code>iii worker add <specific-worker></code>),或者作为将它们一起启动的组合入口点的一部分。</p><p>这之所以重要:表中的每个框都是一个别人可以递给你一个不同 worker、而你可以保留其余部分的地方。不喜欢静态模型目录?换一个注册了 <code>models::list</code> 并从实时 API 读取的 worker。不喜欢基于文件的凭证?换一个注册了 <code>auth::get_token</code> 并从密钥管理器读取的 worker。想要一个针对不同分支 workflow 的不同 turn FSM?替换 <code>turn-orchestrator</code>——每个依赖方通过相同的总线调用 <code>run::start</code> 并读取 <code>turn_state</code>,所以技术栈的其余部分不会改变。</p><h2 id="循环的实际运行方式"><a href="#循环的实际运行方式" class="headerlink" title="循环的实际运行方式"></a>循环的实际运行方式</h2><p>一个 turn 的形态如下,按 worker 的触发顺序逐一遍历。</p><p>浏览器、CLI 或聊天客户端通过 <code>harness::trigger</code> POST 一个 turn,携带 <code>{session_id, message_id, payload}</code>。harness meta-worker 将 payload 转发给 <code>run::start</code>。这一跳的存在是为了让 OpenTelemetry span 包装器可以将 session ID 和 message ID 作为 baggage 植入,传播到技术栈中每个 worker 的每个嵌套 <code>iii.trigger</code> 调用。另一端的追踪树是一个连接的图。</p><p><code>run::start</code> 落在 turn-orchestrator 上。它持久化运行请求,在 iii state 的 <code>session/<sid>/turn_state</code> 处植入初始的 TurnStateRecord,然后立即返回。实际工作在持久化的每状态机内部完成,由发布到 turn-step FIFO 的消息唤醒。</p><p>两个终止状态是 <code>stopped</code>(通过 <code>finishSession()</code> 正常退出)和 <code>failed</code>(未预期的处理程序抛出被路由到此处,ack 队列使其停止重试,并发出 <code>message_complete{stop_reason:'error'}</code> 加上 <code>agent_end</code>,以便 UI 显示原因)。Teardown 是一个内联的 <code>finishSession()</code> 调用,从任何 turn 结束路径调用,而不是单独的入队步骤。</p><p><code>provisioning</code> 做三件事。如果运行需要隔离执行,它启动一个 iii-sandbox 微虚拟机。它为 <code>system_default_skills</code>(默认为 <code>["iii://iii-directory/index"]</code>)中的每个命名空间调用 <code>directory::skills::download</code>,使 iii-directory 预先缓存运行启动时所需的 skill 描述体。然后它分三层组装系统提示词:从 <code>run_request.mode</code> 中选取的模式段落(<code>plan</code>、<code>ask</code> 或 <code>agent</code>),iii 身份前言教给模型 <code>agent_trigger</code> 约定和 <code>directory::skills::get</code> 按需发现模式,以及 agent 启动时附带的默认 skill 索引。调用方可以通过在 <code>run::start</code> 上传递 <code>system_prompt</code> 来覆盖整个提示词;否则由编排器构建它。函数 schema 来自实时引擎目录。</p><p><code>assistant_streaming</code> 在匹配此次运行的 provider 字段的 provider worker 上调用 <code>provider::<name>::stream</code>。provider worker 通过 <code>auth::get_token</code>(auth-credentials)拉取凭证,将模型的 SSE 响应流式推送到 iii channel 中,编排器消费该 channel,在 <code>agent::events</code> 上发出 <code>message_update</code> 事件供 UI 扇出。Channel 创建和读取循环位于 <code>provider-stream.ts</code> 中基于拉取的 MessagePump 之后,因此流式状态专注于状态转换。</p><p>当 assistant 返回工具调用时,FSM 进入 <code>function_execute</code>。每个工具调用都经过 <code>dispatchWithHook</code>——编排器中唯一的卡控点。<code>consultBefore</code> 直接调用 <code>policy::check_permissions</code>,带有 5 秒超时。策略 worker(在默认技术栈中,就是 harness meta-worker)读取 <code>iii-permissions.yaml</code>,将调用的 <code>function_id</code> 与规则集进行匹配,返回三种结果之一:</p><ul><li><code>allow</code>:继续分发;编排器触发目标函数并写入结果</li><li><code>deny</code>:以 DenialEnvelope 短路分发,结果成为一条拒绝记录</li><li><code>needs_approval</code>:单个调用被停放到 turn 的 <code>awaiting_approval</code> 列表中。批次的其余部分继续分发。仅当有一个或多个待审批条目时,turn 才会转换到 <code>function_awaiting_approval</code></li></ul><p>审批唤醒是响应式且共享的。编排器在 scope <code>approvals</code> 上注册了正好一个 <code>turn::on_approval</code> 状态触发器。当控制台调用 <code>approval::resolve</code> 时,approval-gate worker 将 <code>approvals/<sid>/<cid> = {decision, reason}</code> 写入 iii state。该写入触发 <code>turn::on_approval</code>,推进受影响的会话。<code>function_awaiting_approval</code> 只读取刚刚到达的决策,在每个决策到达时分发它(<code>allow</code> 成为预批准的分发,<code>deny</code> 或 <code>aborted</code> 成为合成的拒绝),并在 <code>awaiting_approval[]</code> 为空时推进。无需为每个调用注册恢复函数。无需启动时重新扫描来恢复待处理的审批。一个触发器覆盖所有会话。</p><p>默认拒绝是构造性的:如果策略 worker 不可达,或者 5 秒超时触发,<code>consultBefore</code> 以 <code>gate_unavailable</code> 信封拒绝调用。如果 <code>iii::durable::publish</code> 本身出错,hook fanout 返回 <code>publish_failed: true</code>,编排器将其视为拒绝。</p><p>这种形态带来了几项延迟优化。当没有持久化订阅者为该主题注册时,函数调用后 hook 通过订阅者存在缓存短路 <code>publish_collect</code>,每个执行的函数调用移除大约 500 毫秒。<code>tearing_down</code> 被内联到 <code>finishSession()</code> 中,每个 turn 移除一次持久化队列跃点。<code>context-compaction</code> 订阅了编排器在 turn 边界发出的专用 <code>agent::turn_end</code> 流,因此压缩器的唤醒是每个 turn 一次而非每个事件一次。session-create 扇出状态触发器仅通过 scope 进行门控并在进程内匹配,因此之前每次写入的 <code>harness::session::is_create_event</code> RPC 已经消失。</p><p>批次完成后,<code>steering_check</code> 决定是继续、停止还是达到 <code>max_turns</code>。如果继续,循环回 <code>assistant_streaming</code>。如果停止或达到上限,<code>finishSession()</code> 内联运行:发出 <code>agent_end</code>,释放 sandbox,转换到 <code>stopped</code>。</p><p>在整个运行过程中,每个参与的 worker 发出的 OTel span 都带有 <code>iii.session.id</code>、<code>iii.message.id</code> 和 <code>iii.function.id</code> 标签。这些标签正是引擎的 <code>engine::traces::group_by</code> 读取的内容,用于在追踪 UI 中填充"按会话分组"/"按消息分组"/"按函数分组"。埋点是自动的:<code>src/runtime/worker.ts</code> 将每个 <code>registerFunction</code> 包装在 Proxy 中,因此不必有任何 worker 代码记住要添加 span。</p><h2 id="构建你自己的"><a href="#构建你自己的" class="headerlink" title="构建你自己的"></a>构建你自己的</h2><p>有趣的部分在于,以上所有 worker 都不是特殊的。每一个都是一个打开 WebSocket 连接到引擎、注册一些函数和触发器并运行的进程。这个契约与每个应用 worker 使用的契约完全相同。运行时构建在与你的业务逻辑相同的原语之上。</p><p>这意味着"构建你自己的运行时"分解成与"编写任何 worker"相同的操作。你选择你想替换的层,你写一个在总线上注册相同函数的 worker,你 <code>iii worker add</code> 它,技术栈的其余部分就开始使用你的 worker。</p><p>有两个层没有出现在上述 worker 表中,但对运行时的行为很重要。Skills 是每个 worker 告知其函数功能的机制。每个 worker 可以在 <code>iii://<worker>/<function></code> 处发布一个 skill,agent 在首次调用该函数之前通过 <code>directory::skills::get</code> 获取它。系统提示词在每个 turn 由模式段落、iii 身份前言以及运行配置的默认 skill 描述体组装而成。两者都是总线驱动的:skill 由 iii-directory worker 提供服务,系统提示词由 turn-orchestrator 组装。两者都可替换。</p><p>五个具体示例。</p><p><strong>用实时 API 替换模型目录。</strong> 写一个 worker,注册 <code>models::list</code>、<code>models::get</code>、<code>models::supports</code>。让它每 N 分钟从你 provider 的目录端点获取数据并缓存。发布它。<code>iii worker add your-org/dynamic-models-catalog</code>。停止静态的 models-catalog worker。turn-orchestrator 感知不到任何差异。它调用 <code>iii.trigger('models::list')</code>,引擎路由到最近注册了该函数 ID 的任意 worker。</p><p><strong>添加新的 provider。</strong> <code>provider-kimi</code> 和 <code>provider-lmstudio</code> 已经证明了这种形态。每个都是一个 worker,注册 <code>provider::<name>::stream</code> 和 <code>provider::<name>::complete</code>,将来自上游 API 的 SSE 流导入 iii channel,并通过 <code>budget::record</code> 将其模型用量写入 llm-budget。添加第五个 provider 就是写一个文件夹,包含一个 <code>iii.worker.yaml</code> 和一个 <code>register.ts</code>。发布到注册中心,或保留在本地。turn-orchestrator 通过每次运行的 provider 字段选择 provider;新 provider 在 worker 连接的瞬间即可使用。</p><p><strong>从私有制品库提供 skill。</strong> 写一个 worker,注册 <code>directory::skills::get</code> 和 <code>directory::skills::list</code>,后端是内部文档系统或私有 S3 存储桶。断开或重命名默认的 iii-directory worker。编排器的 bootstrap 为每个命名空间调用 <code>directory::skills::download</code>;你的 worker 来响应。agent 的"在调用新函数前获取每个函数的 skill"模式保持不变,因为 wire 格式相同。</p><p><strong>完全覆盖系统提示词。</strong> <code>run::start</code> 接受一个可选的 <code>system_prompt</code> 字段。传入它,编排器将逐字使用你的字符串,跳过模式段落 + 身份前言 + skill 附录的组装。当你有一个已有的提示词资产,想让运行时原封不动地遵守时,这很有用。Skill 下载仍在 bootstrap 中运行,因此 agent 即使使用自定义提示词也保留 <code>directory::skills::get</code> 按需发现能力。</p><p><strong>替换审批网关的 UI 界面。</strong> 默认的 approval-gate worker 注册 <code>approval::resolve</code>。wire 模式是一次函数调用:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">iii.trigger('approval::resolve', {</span><br><span class="line"> session_id: '...',</span><br><span class="line"> function_call_id: '...',</span><br><span class="line"> decision: 'allow' | 'deny' | 'aborted',</span><br><span class="line"> reason: 'optional human text',</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p>处理程序将 <code>approvals/<sid>/<cid> = {decision, reason}</code> 持久化到 iii state。编排器唯一的 <code>turn::on_approval</code> 状态触发器捕获该写入并唤醒正确的会话。如果你想从 Slack 而非控制台驱动审批,写一个 Slack worker,监听 <code>/approve <id></code> 和 <code>/deny <id></code> 斜杠命令,然后用正确的 payload 调用 <code>approval::resolve</code>。编排器感知不到任何差异。整个 approval-gate worker 保持原封不动。你添加了一个新 worker;你没有替换已有的那个。</p><p>如果你想要不同的策略引擎(OPA、Cedar、你自己的 DSL),写一个 worker,注册 <code>policy::check_permissions</code>,返回 <code>{ decision, rule_id?, matched_constraint? }</code>。断开默认的策略 worker(它包含在 harness meta-worker 中,所以你需要禁用那个处理程序或者运行一个精简版的 meta-worker)。turn-orchestrator 的 <code>consultBefore</code> 感知不到任何差异。相同的 5 秒超时、相同的 fail-closed 语义、相同的 wire 格式。</p><p>这些示例的重点不在于具体的替换项。而在于操作的形态。iii 技术栈中的每个运行时层都可以通过总线上的一两个函数 ID 访问。替换一个层就是写一个注册了这些 ID 的 worker。系统的其余部分保持不变。</p><h2 id="运行时是一个滑块,而不是分岔路口"><a href="#运行时是一个滑块,而不是分岔路口" class="headerlink" title="运行时是一个滑块,而不是分岔路口"></a>运行时是一个滑块,而不是分岔路口</h2><p>经典的运行时争论以薄 vs 厚的框架来表述。Anthropic 的薄循环 vs LangGraph 的显式 DAG。这种框架假设你选择一边并接受它。</p><p>当运行时由同一总线上的 worker 组合而成时,薄 vs 厚只是你安装了多少个 worker 的计数。薄运行时是 <code>turn-orchestrator</code> 加上 <code>provider-anthropic</code> 加上 <code>auth-credentials</code> 加上最小化的 harness meta-worker。仅此而已。没有审批、没有预算、没有策略引擎、没有 hook fanout。运行任何东西。信任模型。适用于自主研究 agent、实验性循环、任何内部用途。</p><p>厚运行时是所有 worker 加上 <code>context-compaction</code> 加上自定义策略 worker 加上自定义 approval-gate 加上 Slack 集成的审批界面加上按工作空间执行支出上限的预算 worker。适用于运行面向客户 workflow 的 agent,其中每个工具调用都需要可审计,每次模型支出都需要汇总到财务仪表板。</p><p>薄与厚之间的架构距离不是重写。它是一个配置更改。相同的原语、相同的 wire 协议、相同的追踪格式、相同的可观测性方案。通过在 <code>config.yaml</code> 中添加和移除 worker 来移动滑块。其他一切保持不变。</p><p>这也适用于单个 worker 内部。turn-orchestrator 刚刚发布了一个重构,将其 FSM 从十一个状态压缩为七个,删除了每个调用的 <code>turn::approval_resume::<sid>/<cid></code> 机制,代之以在 scope <code>approvals</code> 上的一个响应式 <code>turn::on_approval</code> 状态触发器,并将 <code>tearing_down</code> 内联到 <code>finishSession()</code> 调用中。技术栈中的其他每个 worker(approval-gate、session、llm-budget、providers、models-catalog、auth-credentials、hook-fanout、context-compaction)保持不变。<code>approval::resolve</code> wire 格式没有变化。契约保持住了。这就是组合性带给你的特性:一个 worker 的重大内部重写是自包含的变更,因为每个邻居都通过总线级的函数 ID 与它通信。</p><p>这是框架模型无法给你的部分。框架替你选择了滑块上的一个位置并锁定你。worker 模型将滑块留在你手中。</p><h2 id="这在实际中意味着什么"><a href="#这在实际中意味着什么" class="headerlink" title="这在实际中意味着什么"></a>这在实际中意味着什么</h2><p>如果你一直在某个框架之上运行 agent,并且感受到大多数团队在规模化时遇到的相同的边界问题,那么答案很可能不是"用我们自己的框架重写运行时"。策略引擎无法按你需要的方式扩展。审批 UI 被捆绑在框架的聊天界面里。凭证存储无法与你的密钥管理器对话。预算追踪器位于追踪无法看到的 sidecar 数据库中。答案是切换到一个运行时从一开始就被解耦的基座。</p><p>最快感受到这个论点的方法是 clone github.com/iii-hq/workers,<code>pnpm install</code>,<code>pnpm build</code>,然后运行组合入口点。你将获得指向 iii 引擎的完整十四 worker 运行时。你可以通过从启动列表中移除 worker 条目来禁用任何 worker。你可以通过写一个注册了相同函数 ID 的替代品来替换任何 worker。你可以通过向其 hook 主题添加订阅者来扩展任何 worker。<code>hook-fanout::publish_collect</code> 是每个 iii hook 构建于其上的通用机制。</p><p>文档在 iii.dev/docs。引擎在 github.com/iii-hq/iii。Worker 注册中心在 workers.iii.dev。运行时代码包在 github.com/iii-hq/workers/harness。</p><h2 id="赌注"><a href="#赌注" class="headerlink" title="赌注"></a>赌注</h2><p>运行时不是你安装的东西。运行时是你的系统为了让一个 agent 持久化、安全、可观测地运行而必须做的一组工作。框架时代之所以将这些工作捆绑在一起,是因为底层没有任何东西给你组合它们的方式。</p><p>iii 的赌注是:一个原语——一个 worker 通过 WebSocket 连接到引擎并注册函数和触发器——小到足以分别吸收这每一项工作,并且由此产生的技术栈比任何框架都更有用,因为每一层都可以独立替换。</p><p>你不是"采用" iii 运行时。你安装你想要的 worker,编写你需要的 worker,最终获得一个完全匹配你系统形态的运行时。每一层相同的协议。每一次调用相同的追踪。从注册中心拿来的组件和你自行发布的组件,使用同样的 <code>iii worker add</code>。</p><p>这就是当基座形态正确时,"构建你自己的 agent 运行时"该有的样子。选择 worker。编写缺失的。组合。运行时就是这种组合。</p><p>加入我们,一起构建现代世界所需的完美 agent 运行时:discord.gg/iiidev</p><p>iii 是开源的。从 iii.dev/docs 开始。运行时 worker 在 github.com/iii-hq/workers,引擎在 github.com/iii-hq/iii。</p><p>— Mike Piccolo, 创始人兼 CEO</p>
同分类推荐文章
- 00 卷首语:当 Karpathy 说他半年没写一行代码 (2026-06-21 21:20:27)
- LLM 究竟是如何工作的? (2026-06-21 11:09:44)
- Loop Engineering 实践:一次批量实现 8 个 issue,完成夔牛工具的开发 (2026-06-17 04:00:24)
建议继续学习
- 15个最好的免费开源电子商务平台 (累计阅读 12,507)
- 好的API设计 (累计阅读 12,382)
- Twitter/微博客的学习摘要 (累计阅读 12,241)
- 面试题 – 为什么我的朋友圈不见了? (累计阅读 11,932)
- Facebook 网站架构 (累计阅读 11,097)
- Feed架构-我们做错了什么 (累计阅读 8,717)
- 架构师给程序员的一封信 (累计阅读 7,972)
- Java技术路线 (累计阅读 7,706)
- 聊聊ThoughtWorks面试 (累计阅读 7,567)
- 一种基于长连接的社交游戏服务器程序构架 (累计阅读 7,471)