Skip to content

kinetal/ari-api

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ARI API

ari-api 是一个面向 Agent Runtime 的接口定义仓库。

它想解决的问题,与 Kubernetes 的 CRI(Container Runtime Interface)非常相似:

  • 上层系统希望依赖一组稳定接口
  • 下层运行时实现却高度异构
  • 需要把“核心能力”与“可选能力”清晰分层
  • 需要让调度器、UI、平台桥接层、远程网关都能用同一套语义驱动不同实现

对 ARI(Agent Runtime Interface)而言,下层不是 containerd / CRI-O,而是 codexclaudecodecursorgeminiopencodeqoder 这类 Agent CLI,或未来的远程 Agent Gateway。

一句话概括:

ARI 想成为 Agent 世界里的 CRI:为异构 Agent Runtime 提供稳定、可组合、可远程化的统一接口。


为什么要做 ARI

今天不同 Agent Runtime 的差异非常大:

  • 会话创建方式不同
  • 事件输出格式不同
  • 权限审批机制不同
  • 模型/Provider/模式切换能力不同
  • 历史记录、记忆文件、技能、命令目录等扩展能力分布不一致

如果上层平台直接依赖某个具体 CLI:

  • 适配成本会越来越高
  • 新增 runtime 会牵动整个系统
  • UI 与调度层很难形成稳定抽象
  • 很多“本来属于协议层”的语义会被写死在实现细节里

ARI 的目的,就是把这些差异收敛为一套稳定协议:

  • 上层只关心 SessionTurnEventPermissionRequest
  • 下层 runtime 负责把自己的私有行为映射成统一语义
  • 可选能力通过 capability 暴露,而不是污染核心接口

参考来源

ARI 的设计直接借鉴两类成熟实践:

1. Kubernetes cri-api

cri-api 给出的最重要启发不是 “使用 gRPC”,而是:

  • 把复杂系统拆成多个小接口
  • 用顶层 service 聚合最小必需能力
  • 在接口注释中写清楚语义契约、线程安全和生命周期要求
  • 保持上层与具体 runtime 实现解耦

对应参考文件:cri-api/pkg/apis/services.go

2. codeclaw/agent

codeclaw 已经沉淀出一套非常实用的 Agent 抽象,尤其体现在:

  • core.Agent
  • core.AgentSession
  • HistoryProvider
  • ModelSwitcher
  • ProviderSwitcher
  • ModeSwitcher
  • MemoryFileProvider
  • SkillProvider
  • CommandProvider
  • ContextCompressor
  • SessionDeleter

对应参考文件:

  • codeclaw/core/interfaces.go
  • codeclaw/core/message.go
  • codeclaw/agent/*

codeclaw 的价值在于,它已经证明了 Agent Runtime 的真正核心并不大;复杂度主要来自“扩展能力分层”。


ARI 的定位

ARI 不是:

  • 模型厂商 API 的统一层
  • Prompt 标准
  • Agent 内部工具系统标准
  • Memory/Skill 实现规范
  • 针对某个产品的私有 SDK

ARI 是:

  • Agent Runtime 的统一抽象层
  • 面向会话、事件流、权限审批的协议语义
  • 本地 SDK 和远程 RPC 协议的共同语义基座
  • 上层调度器、控制面、聊天桥接层、IDE 插件、审计系统之间的稳定契约

也就是说,ARI 关心的是:

  • 如何创建/恢复会话
  • 如何发送一轮输入
  • 如何接收持续事件流
  • 如何处理权限请求
  • 如何发现 runtime 的可选能力
  • 如何用稳定错误语义表达失败原因

而不是关心 runtime 内部到底通过什么 prompt、什么 CLI flag、什么进程模型来实现这些能力。


设计目标

必须做到

  • 屏蔽不同 Agent CLI 的调用差异
  • Session 作为第一等资源
  • 把事件流作为核心交互通道
  • 把权限审批作为核心协议概念,而不是插件能力
  • 支持本地嵌入式调用,也支持未来的 gRPC / HTTP 远程 runtime
  • 支持 capability discovery,让高级能力按需探测
  • 让上层系统只依赖 ARI,而不依赖具体 runtime 实现

明确不做

  • 不试图统一 OpenAI / Anthropic / Google 等底层推理 API
  • 不规定 Agent 内部 prompt、记忆、工具框架如何组织
  • 不要求所有 runtime 在行为体验上完全一致
  • 不把每个产品特性都塞进核心接口
  • 不在 v1 解决跨 runtime session 迁移、复杂多租户治理、细粒度资源调度

核心概念

Runtime

Runtime 表示一个可被上层系统驱动的 Agent 执行后端,例如:

  • Codex CLI runtime
  • Claude Code runtime
  • Cursor Agent runtime
  • Gemini CLI runtime
  • OpenCode runtime
  • Qoder runtime
  • 远程 Agent Gateway runtime

它负责:

  • 汇报版本与状态
  • 创建、恢复、查询会话
  • 暴露支持的 capabilities
  • 管理全局资源与关闭逻辑

Session

Session 是 ARI 的第一等资源,对应一个长期存在的逻辑对话上下文。

它通常包含:

  • runtime 内部维护的会话标识
  • 工作目录/项目上下文
  • 对话历史的连续性
  • 一条长期存在的事件流

这与 codeclaw 中的 AgentSession 高度一致,但 ARI 会把它提升为协议级资源。

Turn

Turn 表示一次用户输入驱动的一轮执行:

  • 一次 SendMessage 触发一个 turn
  • 一个 turn 可能产生多条事件
  • 一个 turn 可能包含工具调用、权限请求、中间文本、最终结果或错误

Turn 可以是显式资源,也可以只是事件与请求中的关联字段;但语义上必须存在。

Event

事件流是 ARI 的核心交互方式。

参考 codeclaw/core/message.go,ARI 需要稳定支持下列事件族:

  • text
  • thinking
  • tool_call
  • tool_result
  • permission_request
  • result
  • error
  • lifecycle

ARI 不强制所有 runtime 提供 token 级流式输出,但必须允许支持流式事件。

Permission Request

权限请求是 Agent Runtime 区别于普通 LLM API 的关键点之一。

对 ARI 来说,它必须是协议核心,而不是扩展插件,因为很多 coding agent 的真实工作流都依赖:

  • shell 命令审批
  • 文件编辑审批
  • 网络访问审批
  • 危险操作审批

一个最小权限请求至少应包含:

  • request_id
  • session_id
  • turn_id
  • tool_name
  • tool_input_summary
  • tool_input_raw
  • message / reason
  • 超时或取消信息(可选)

一个最小响应至少应包含:

  • allow / deny
  • updated_input(可选)
  • message(可选)

借鉴关系:CRI × codeclaw

ARI 的设计不是简单复制其中一个项目,而是做一次“概念嫁接”:

来源 借鉴点 在 ARI 中的体现
cri-api 小接口拆分 RuntimeVersionerSessionManagerInteractionManagerEventManager
cri-api 顶层服务聚合 RuntimeService
cri-api 语义契约优先 线程安全、幂等、状态语义、错误语义
codeclaw/agent Session 为核心对象 Session 成为 ARI 第一等资源
codeclaw/agent 事件驱动交互 SessionEvents() / streaming API
codeclaw/agent 权限回调机制 RespondPermission()
codeclaw/agent 可选能力分层 model/provider/mode/history/skill/memory 等 capability

可以把 ARI 的核心思路概括为:

  • cri-api 的方式设计接口边界
  • codeclaw 的方式定义会话与事件语义

设计原则

1. 核心接口必须小

核心接口只保留几乎所有 runtime 都会支持的能力:

  • 版本
  • 状态
  • 创建/恢复会话
  • 发送消息
  • 接收事件
  • 响应权限
  • 关闭会话/关闭 runtime

2. 扩展能力必须接口化

以下能力不应进入最小核心接口:

  • 历史检索
  • 模型切换
  • Provider 切换
  • 权限模式切换
  • 技能发现
  • 命令发现
  • 记忆文件发现
  • 上下文压缩

这些能力应该像 codeclaw/core/interfaces.go 一样,单独做 capability interface。

3. 语义优先于实现

对于上层系统来说:

  • runtime 是持久进程,还是每轮拉起再 resume
  • 会话 ID 是本地文件名,还是远程线程 ID
  • 权限请求通过 stdio、socket 还是 RPC 发送

都不重要。

ARI 需要稳定暴露的是:

  • 逻辑会话
  • 逻辑 turn
  • 统一事件流
  • 统一权限回调语义

4. 请求/响应对象优先

像 CRI 一样,ARI 应避免接口中出现一长串裸参数。

统一使用显式请求/响应对象,便于:

  • 未来兼容扩展
  • gRPC / protobuf 映射
  • 审计与日志记录
  • 保持接口演进稳定

5. 能力发现优先于能力假设

上层不能假定所有 runtime 都支持:

  • 删除 session
  • 获取历史
  • 动态切换模型
  • 动态切换 provider
  • 取消 turn
  • 上下文压缩

因此 ARI 需要支持 capability discovery。


推荐接口分层

下面的分层方式直接对应 cri-api/pkg/apis/services.go 的思路。

1. 版本与状态

package ari

import "context"

type RuntimeVersioner interface {
    // Version returns runtime name, runtime version and ARI API version.
    Version(ctx context.Context, apiVersion string) (*VersionResponse, error)
}

type RuntimeStatuser interface {
    // Status returns health, readiness and capability summary of the runtime.
    Status(ctx context.Context, verbose bool) (*StatusResponse, error)
}

2. 会话管理

package ari

import "context"

type SessionManager interface {
    CreateSession(ctx context.Context, req *CreateSessionRequest) (*CreateSessionResponse, error)
    ResumeSession(ctx context.Context, req *ResumeSessionRequest) (*ResumeSessionResponse, error)
    GetSession(ctx context.Context, req *GetSessionRequest) (*GetSessionResponse, error)
    ListSessions(ctx context.Context, filter *SessionFilter) ([]*Session, error)
    CloseSession(ctx context.Context, sessionID string) error
    DeleteSession(ctx context.Context, sessionID string) error
}

3. 交互执行

package ari

import "context"

type InteractionManager interface {
    SendMessage(ctx context.Context, req *SendMessageRequest) (*SendMessageResponse, error)
    RespondPermission(ctx context.Context, req *RespondPermissionRequest) (*RespondPermissionResponse, error)
    CancelTurn(ctx context.Context, req *CancelTurnRequest) (*CancelTurnResponse, error)
}

约束建议:

  • SendMessage 不要求同步返回完整结果
  • 完整输出主要通过事件流返回
  • CancelTurn 可选;不支持时返回 UnimplementedErrNotSupported

4. 事件流

package ari

import "context"

type EventManager interface {
    SessionEvents(ctx context.Context, req *SessionEventsRequest) (<-chan *Event, error)
}

如果未来提供 RPC 版本,优先映射为 server streaming:

rpc SessionEvents(SessionEventsRequest) returns (stream Event);

5. 可选能力接口

package ari

import "context"

type HistoryManager interface {
    GetSessionHistory(ctx context.Context, req *GetSessionHistoryRequest) (*GetSessionHistoryResponse, error)
}

type ModelManager interface {
    GetModel(ctx context.Context, req *GetModelRequest) (*GetModelResponse, error)
    SetModel(ctx context.Context, req *SetModelRequest) (*SetModelResponse, error)
    ListModels(ctx context.Context, req *ListModelsRequest) (*ListModelsResponse, error)
}

type ModeManager interface {
    GetMode(ctx context.Context, req *GetModeRequest) (*GetModeResponse, error)
    SetMode(ctx context.Context, req *SetModeRequest) (*SetModeResponse, error)
    ListModes(ctx context.Context, req *ListModesRequest) (*ListModesResponse, error)
}

type ProviderManager interface {
    GetActiveProvider(ctx context.Context, req *GetActiveProviderRequest) (*GetActiveProviderResponse, error)
    SetActiveProvider(ctx context.Context, req *SetActiveProviderRequest) (*SetActiveProviderResponse, error)
    ListProviders(ctx context.Context, req *ListProvidersRequest) (*ListProvidersResponse, error)
}

6. 顶层运行时服务

package ari

import "context"

type RuntimeService interface {
    RuntimeVersioner
    RuntimeStatuser
    SessionManager
    InteractionManager
    EventManager

    // Close shuts down the runtime client or underlying connection.
    Close(ctx context.Context) error
}

事件模型建议

参考 codeclaw/core/message.go,建议定义稳定事件结构:

package ari

type EventType string

const (
    EventText              EventType = "text"
    EventThinking          EventType = "thinking"
    EventToolCall          EventType = "tool_call"
    EventToolResult        EventType = "tool_result"
    EventPermissionRequest EventType = "permission_request"
    EventResult            EventType = "result"
    EventError             EventType = "error"
    EventLifecycle         EventType = "lifecycle"
)

type Event struct {
    Type         EventType
    SessionID    string
    TurnID       string
    RequestID    string
    Content      string
    ToolName     string
    ToolInput    string
    ToolInputRaw map[string]any
    ToolResult   string
    Done         bool
    Error        *ErrorStatus
    Timestamp    int64
}

设计建议:

  • SessionID 始终带上,便于多路复用
  • TurnID 显式化,便于取消、中断、审计和并发扩展
  • Done 表示当前 turn 结束,不表示 session 关闭
  • 错误尽量结构化,不只返回字符串
  • lifecycle 用于会话创建、恢复、关闭、心跳等状态变化

请求/响应对象建议

ARI 应优先使用显式请求/响应对象,而不是裸参数。

例如:

package ari

type SendMessageRequest struct {
    SessionID      string
    Message        *Message
    IdempotencyKey string
    Metadata       map[string]string
}

type Message struct {
    Role    string
    Content string
    Parts   []*ContentPart
}

type ContentPart struct {
    Type     string // text, image, audio, file
    Text     string
    MimeType string
    Uri      string
    Data     []byte
    Name     string
}

这样做的收益:

  • 易于支持多模态输入
  • 易于从本地 SDK 演进到远程协议
  • 易于做幂等、审计、回放与 tracing
  • 避免未来为了加字段而频繁破坏接口

错误模型建议

ARI 需要稳定错误语义,而不是只依赖字符串错误信息。

建议至少定义这些语义级错误:

  • NotFound
  • AlreadyExists
  • InvalidArgument
  • FailedPrecondition
  • PermissionDenied
  • Unimplemented
  • Unavailable
  • DeadlineExceeded
  • Canceled
  • Internal

如果未来有 gRPC 版本:

  • 本地实现内部可以继续使用语言原生错误
  • 对外传输统一映射到稳定状态码

Capability 暴露方式

ARI 需要同时支持两种能力发现模式。

本地 SDK 模式:小接口断言

适合嵌入式调用:

if m, ok := runtime.(ModelManager); ok {
    // runtime supports model switching
}

远程协议模式:显式 capability 列表

适合 gRPC / HTTP:

package ari

type Capability struct {
    Name    string
    Version string
    Enabled bool
    Details map[string]string
}

然后在 StatusResponse 里声明:

  • 支持哪些能力
  • 当前是否启用
  • 是否实验性
  • 是否只对新 session 生效

建议两种方式并存:

  • 本地用接口断言
  • 远程用 capability 列表

codeclaw/agent 的映射

如果未来把 codeclaw/agent 作为 ARI 的一组 adapter,实现关系大致如下。

核心映射

codeclaw ARI
core.Agent.StartSession CreateSession / ResumeSession
core.Agent.ListSessions ListSessions
core.Agent.Stop RuntimeService.Close
core.AgentSession.Send SendMessage
core.AgentSession.RespondPermission RespondPermission
core.AgentSession.Events SessionEvents
core.AgentSession.Close CloseSession

扩展能力映射

codeclaw ARI 建议归属
HistoryProvider HistoryManager
ModelSwitcher ModelManager
ProviderSwitcher ProviderManager
ModeSwitcher ModeManager
MemoryFileProvider MemoryManager 或 capability
SkillProvider SkillManager 或 capability
CommandProvider CommandManager 或 capability
ContextCompressor SessionUtilityManager 或 capability
SessionDeleter DeleteSession

一个关键判断

codeclaw 当前更像“嵌入式 SDK 抽象”; ari-api 更适合演进成“语言无关的接口/协议定义仓库”。

因此 ARI 不应机械复制 codeclaw/core.Agent,而应:

  • 保留它最重要的会话/事件/权限语义
  • 用 CRI 风格重新拆分服务接口
  • 显式区分 mandatory core 与 optional capability

生命周期与线程安全约束

cri-api 在接口设计上非常强调 thread-safe;ARI 也应该直接写明类似约束。

建议明确以下规则:

  • RuntimeService 的公开方法必须线程安全
  • CloseSession 应尽量幂等
  • DeleteSession 对不存在对象应尽量幂等
  • 同一 Session 可以被多个消费者订阅事件流
  • 同一 Session 是否允许并发 turn,必须显式定义
  • 如果 runtime 不支持同 session 并发 turn,应返回 FailedPrecondition
  • Close 后的对象不可继续发送消息或订阅事件

版本与兼容性建议

ARI v1 应优先保证“语义稳定”,而不是一次性覆盖所有能力。

建议采用以下分层:

Level 1:最小必需能力

所有 runtime 都应支持:

  • Version
  • Status
  • CreateSession / ResumeSession
  • SendMessage
  • SessionEvents
  • RespondPermission
  • CloseSession
  • Close

Level 2:强烈建议能力

  • ListSessions
  • GetSession
  • GetSessionHistory
  • CancelTurn
  • DeleteSession

Level 3:增强能力

  • 模型切换
  • Provider 切换
  • 模式切换
  • 技能发现
  • 命令发现
  • 记忆文件发现
  • 上下文压缩

原则是:

  • 核心接口一旦进入 v1,应尽量长期稳定
  • 增强能力通过 capability 扩展,而不是频繁修改核心接口

仓库形态建议

ARI 本质上是“接口定义”,不是某个 runtime 的业务实现。

因此更推荐仓库长期演进成下面两层之一:

形态 A:语言无关协议仓库

适合把文档、proto、规范放在一起:

ari-api/
  README.md
  docs/
    overview.md
    object-model.md
    error-model.md
    versioning.md
  proto/
    ari/runtime/v1/api.proto

形态 B:Go 风格接口仓库(最贴近 cri-api

如果你希望最直接借鉴 cri-api

ari-api/
  README.md
  doc.go
  go.mod
  pkg/
    apis/
      services.go
      types.go
      errors.go
      testing/
        fake_runtime_service.go
      runtime/
        v1/
          api.proto
          constants.go
          api.pb.go
          api_grpc.pb.go

当前仓库的现实情况

当前仓库是一个 Rust 初始化项目(Cargo.toml 已存在),这不妨碍先把 ARI 定义为语言无关协议。

因此推荐做法是:

  • 先把 README 和对象模型写清楚
  • 再决定“规范主仓”究竟用 Rust、Go 还是 proto-first 方式维护
  • 不要因为当前仓库语言选择,提前锁死协议形态

推荐的首版范围

如果现在要启动 ari-api,建议 v1 只覆盖最核心的运行时抽象。

必须进入 v1 的接口

  • RuntimeVersioner
  • RuntimeStatuser
  • SessionManager
  • InteractionManager
  • EventManager
  • RuntimeService

必须进入 v1 的类型

  • Session
  • SessionState
  • Message
  • ContentPart
  • Event
  • PermissionRequest
  • PermissionResponse
  • Capability
  • VersionResponse
  • StatusResponse

可以暂缓的内容

  • Skill / Command / Memory 的详细文件系统协议
  • Provider 配置细节标准化
  • 多租户鉴权模型
  • 资源配额与调度语义
  • 跨 runtime session 迁移
  • 复杂审计与回放规范

推荐实施顺序

第一步:先稳定语义

先补齐:

  • README.md
  • docs/object-model.md
  • docs/error-model.md
  • docs/versioning.md

目标是把概念边界、生命周期与错误语义写稳。

第二步:产出最小接口定义

无论最终选 Go、Rust 还是 proto-first,都先定义:

  • services
  • types
  • errors

并用 fake runtime 验证:

  • session 创建/恢复
  • turn 执行
  • 事件流
  • 权限审批
  • 历史查询

第三步:做 codeclaw adapter PoC

优先接一个最典型的 runtime:

  • codex
  • claudecode

因为这两类 runtime 在会话、权限、事件、模式切换上都足够典型,最适合验证 ARI 抽象是否成立。

第四步:再决定远程协议

只有当本地抽象稳定后,再继续:

  • api.proto
  • gRPC 映射
  • HTTP gateway
  • capability registry

换句话说:

先稳定语义,再稳定接口,最后再稳定传输协议。


一个可接受的 v1 判断标准

如果 ARI v1 满足下面几点,就已经足够开始落地:

  • 上层系统可以不关心具体 agent 类型,统一创建/恢复会话
  • 上层系统可以统一发送消息并消费事件流
  • 上层系统可以统一处理权限请求
  • runtime 可以按 capability 暴露增强能力
  • 至少能把 codeclaw/agent 中 2 个不同 runtime 映射到 ARI 而不出现语义扭曲

只要做到这些,ARI 就已经具备独立价值。


总结

ari-api 最值得从 cri-api 借鉴的,不是某种具体技术栈,而是接口设计方法:

  • 小接口拆分
  • 顶层服务聚合
  • 核心/扩展能力分层
  • 语义契约优先于实现细节

ari-api 最值得从 codeclaw/agent 借鉴的,是 Agent Runtime 的真实工作流语义:

  • Session 是核心资源
  • Event Stream 是核心交互方式
  • Permission Request 是第一等协议概念
  • 很多能力本质上应该是 optional capability

因此,ARI 最合理的方向是:

用 CRI 风格定义接口边界,用 codeclaw 风格定义会话、事件与权限语义。

如果后续继续推进,这个仓库最自然的下一步是把 README 中的设计,收敛成三类正式产物:

  1. services:服务接口定义
  2. types:对象模型与事件模型
  3. errors:稳定错误语义

等这三块稳定下来,再去做 adapter、fake runtime、proto/gRPC,成本会低很多,也更不容易返工。

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors