Kimi Agent SDK 深度解析:MoonshotAI 的 CLI-as-Engine Agent 架構


在 AI Agent 框架百花齊放的當下,MoonshotAI 推出的 Kimi Agent SDK 走了一條截然不同的路。它不是又一個自行實作 Agent Loop 的框架,而是將已有的 Kimi Code CLI 作為底層引擎,透過薄薄一層語言原生客戶端暴露給應用程式。這種「CLI-as-Engine」的架構哲學,決定了它從 Wire Protocol 到工具系統的每一個設計抉擇。

本文將從原始碼角度,逐層拆解 Kimi Agent SDK 的核心設計。


一、定位與架構哲學

Kimi Agent SDK 是 MoonshotAI 為 Kimi Code CLI 提供的多語言 SDK 套件,支援 Go、Node.js (TypeScript) 與 Python 三種語言。它的核心定位非常明確:不是 Agent 框架,而是 Agent 引擎的 wrapper。

與 LangGraph、CrewAI、OpenAI Agents SDK 等框架不同,Kimi Agent SDK 本身不實作任何 Agent 邏輯。真正的 Agent loop、tool execution、memory management 全部發生在 Kimi CLI 內部。SDK 的職責是:

  • 啟動並管理 CLI 子進程
  • 透過 JSON-RPC 2.0 Wire Protocol 進行雙向通訊
  • 串流事件處理(文字、思考、工具呼叫、核准請求)
  • 外部工具註冊與呼叫代理
  • Session 生命週期管理

這意味著 SDK 與 CLI 之間有嚴格的責任邊界:SDK 負責「通訊與呈現」,CLI 負責「思考與行動」。


二、整體架構:CLI-as-Engine

整個架構可以用以下分層表示:

Application Code (Go / Node.js / Python)
         |
    Kimi Agent SDK  <-- thin client
         |
    Wire Protocol (JSON-RPC 2.0 over stdio)
         |
    kimi CLI 子進程  <-- 真正的 Agent 引擎
         |
    LLM API (Moonshot / OpenAI / Anthropic / Google / etc.)

架構圍繞三個核心概念展開:

  1. Session — 與 CLI 進程的連線。管理底層進程的啟動、恢復(resume)和關閉。
  2. Turn — 一次對話來回(user prompt 到 agent response)。每個 Turn 包含串流式的事件輸出。
  3. Step — Turn 內的處理步驟。Agent 推理、呼叫工具、產生輸出時,一個 Turn 可能包含多個 Step。

CLI-as-Engine 的優勢在於:SDK 與 CLI 行為保證一致,所有設定、內建工具、MCP 伺服器完全復用,CLI 版本升級時 SDK 自動受益,且大幅降低了 SDK 層的維護負擔。

劣勢則是:Go 和 Node.js 版本必須在環境中安裝 kimi CLI,SDK 功能受限於 CLI 所暴露的能力,跨進程通訊也讓除錯變得更加繁瑣。


三、Wire Protocol:雙向 JSON-RPC 2.0

SDK 與 CLI 之間的通訊協議是整個架構的骨幹。目前的協議版本為 1.3,基於 JSON-RPC 2.0,透過 CLI 進程的 stdin/stdout 進行。

3.1 SDK 發送給 CLI 的 RPC 方法

  • initialize:握手階段,傳遞協議版本和外部工具定義
  • prompt:發送使用者輸入,啟動一個新的 Turn
  • cancel:取消當前正在執行的 Turn
  • replay:重放歷史事件(Wire 1.3 新增)

3.2 CLI 發送給 SDK 的通知與請求

  • event:串流事件通知(單向,不需回應)
  • request:需要 SDK 回應的請求,包含 ApprovalRequest(核准請求)和 ToolCallRequest(外部工具呼叫請求)

3.3 事件類型系統

事件分為兩大類。第一類是資訊性事件(Events),不需回應:

事件說明
TurnBegin / TurnEndTurn 開始/結束
StepBegin / StepInterruptedStep 開始/中斷
ContentPart文字、思考 (think)、圖片、音訊等內容
ToolCall / ToolCallPart / ToolResult工具呼叫與結果
StatusUpdateToken 使用量與 context 使用率
CompactionBegin / CompactionEndContext 壓縮事件
SubagentEvent子 Agent 巢狀事件

第二類是請求事件(Requests),必須回應,否則 Session 會無限期阻塞:

  • ApprovalRequest:請求使用者核准(如執行 shell 命令、修改檔案)
  • ToolCallRequest:外部工具呼叫請求(由 SDK 自動攔截並處理)

3.4 Go 的 JSON-RPC 2.0 Codec 實作

Go SDK 的 jsonrpc2.Codec 實作值得特別關注。它在一個 io.ReadWriteCloser(即 stdin/stdout)上同時扮演 client 和 server 角色:

codec := jsonrpc2.NewCodec(&stdio{stdin, stdout},
    jsonrpc2.ClientMethodRenamer(...),  // Go RPC 方法名 -> JSON-RPC 方法名
    jsonrpc2.ServerMethodRenamer(...),  // JSON-RPC 方法名 -> Go RPC 方法名
)

這個 Codec 支援串流操作(StreamOpen / StreamSync / StreamClose)、方法名稱雙向重新命名,以及完整的生命週期管理(graceful shutdown、idle timeout),同時復用了 Go 標準庫的 net/rpc 介面,但完全自訂了編解碼邏輯。這是一個精巧的工程設計。


四、Agent Loop 執行流程

Agent Loop 由 CLI 內部驅動,SDK 側的執行流程如下:

1. 建立 Session(啟動 kimi CLI 子進程)
2. 發送 Initialize RPC(握手、註冊外部工具)
3. 發送 Prompt RPC(使用者輸入)
4. 接收串流事件:
   a. TurnBegin -> 標記 Turn 開始
   b. StepBegin -> 新的推理/工具步驟開始
   c. ContentPart -> 輸出文字或思考過程
   d. ToolCall -> Agent 呼叫工具
   e. ToolResult -> 工具返回結果
   f. ApprovalRequest -> 等待使用者核准(必須回應)
   g. ToolCallRequest -> 外部工具呼叫(SDK 自動處理)
   h. StatusUpdate -> Token/Context 使用量更新
   i. CompactionBegin/End -> Context 壓縮
   j. TurnEnd -> Turn 結束
5. 取得 PromptResult(finished / cancelled / max_steps_reached)
6. 繼續下一個 Turn,或關閉 Session

這裡有幾個關鍵設計約束需要特別注意:

  • Sequential Prompts:必須依序發送 Prompt,等前一個 Turn 完成(或取消)後才能開始下一個。
  • Must Consume:必須消耗完所有事件或明確 Cancel,否則會阻塞。
  • Must Respond:ApprovalRequest 必須回應,否則 Session 無限期等待。
  • Resource Cleanup:必須 Close Session,確保 CLI 子進程正確終止。

4.1 Go: Turn 事件遍歷核心

Turn.traverse() 是 Go SDK 事件處理的核心,透過 channel 實現串流消費:

func (t *Turn) traverse(incoming <-chan wire.Message, steps chan<- *Step) {
    // 等待 TurnBegin
    msg := <-incoming
    if _, is := msg.(wire.TurnBegin); !is { return }

    for msg := range incoming {
        switch x := msg.(type) {
        case wire.TurnEnd:
            return  // Turn 完成

        case wire.Request:
            outgoing <- x  // ApprovalRequest 轉發到 step.Messages

        case wire.Event:
            switch x.EventType() {
            case wire.EventTypeStepBegin:
                close(outgoing)
                outgoing = make(chan wire.Message)
                steps <- &Step{Messages: outgoing}

            case wire.EventTypeStatusUpdate:
                // CAS loop 原子性累計 Token 使用量
                update := x.(wire.StatusUpdate)
                // ...

            default:
                outgoing <- x  // ContentPart, ToolCall, etc.
            }
        }
    }
}

Go SDK 使用 range 迭代 channel 消費串流,搭配 sync/atomic 做無鎖狀態管理。當收到 StepBegin 事件時,關閉舊的 Step channel 並建立新的,讓消費者自然知道步驟切換。

4.2 Turn 取消機制

Go 版本支援三種取消方式:

// 1. 直接取消
turn.Cancel()

// 2. 透過 context 取消
ctx, cancel := context.WithCancel(parentCtx)
defer cancel()

// 3. 超時自動取消
ctx, cancel := context.WithTimeout(parentCtx, 30*time.Second)
defer cancel()

取消後 Session 仍然可用,可以繼續發送新的 Prompt。


五、工具系統

5.1 內建工具

Kimi CLI 內建了豐富的工具集,包括:Shell(執行命令)、ReadFile / WriteFile / StrReplaceFile(檔案操作)、Glob / Grep(搜尋)、SearchWeb / FetchURL(網頁)、Task(子 Agent 任務)、SetTodoList(Todo 管理)。這些工具在 CLI 內部執行,SDK 只接收事件通知。

5.2 外部工具:三種語言的不同風格

外部工具是 SDK 最具特色的擴展機制,三種語言各自採用了最符合語言慣例的實作方式。

Go 版本 — 泛型加反射,自動產生 JSON Schema:

type WeatherArgs struct {
    Location string `json:"location" description:"City name"`
    Unit     string `json:"unit,omitempty" description:"Temperature unit"`
}

tool, _ := kimi.CreateTool(getWeather,
    kimi.WithName("get_weather"),
    kimi.WithDescription("Get current weather"),
)

session, _ := kimi.NewSession(kimi.WithTools(tool))

CreateTool 使用 Go 1.18+ 泛型 CreateTool[T any, U any],透過 reflect 從 struct tag 自動生成 JSON Schema。omitempty 或指標類型自動標記為 optional,函式名稱透過 runtime.FuncForPC 自動偵測。這是三種語言中技術上最優雅的實作。

其 Schema 生成邏輯遞迴處理 struct、指標、slice 等型別:

func generateSchema(t reflect.Type, fieldDescs map[string]string) (*jsonSchema, error) {
    switch t.Kind() {
    case reflect.Struct:
        schema.Type = "object"
        for i := 0; i < t.NumField(); i++ {
            field := t.Field(i)
            jsonName, desc, isRequired := parseFieldTags(field)
            fieldSchema := generateSchema(field.Type, nil)  // 遞迴
            schema.Properties[jsonName] = fieldSchema
            if isRequired { required = append(required, jsonName) }
        }
    case reflect.Ptr:
        return generateSchema(t.Elem(), fieldDescs)  // 展開指標
    case reflect.Slice:
        schema.Type = "array"
        schema.Items = generateSchema(t.Elem(), nil)
    }
}

Node.js 版本 — 使用 Zod schema,透過 zod-to-json-schema 轉換:

const weatherTool = createExternalTool({
  name: 'get_weather',
  description: 'Get weather information',
  parameters: z.object({
    city: z.string().describe('City name'),
    unit: z.enum(['celsius', 'fahrenheit']).optional(),
  }),
  handler: async (params) => ({
    output: `Weather in ${params.city}: 22 degrees`,
    message: 'Weather fetched successfully',
  }),
});

Node.js 版本同時支援 Zod v3 和 v4,對生態系的相容性考量相當周到。

Python 版本 — 使用 Pydantic model 加 agent YAML 檔:

class Params(BaseModel):
    directory: str = Field(default=".", description="The directory to list files from.")

class Ls(CallableTool2):
    name: str = "Ls"
    description: str = "List files in a directory."
    params: type[Params] = Params

    async def __call__(self, params: Params) -> ToolReturnValue:
        files = os.listdir(params.directory)
        return ToolOk(output="\n".join(files))

Python 的自訂工具需要在 agent YAML 檔案中以 module:ClassName 格式註冊,而非直接傳入 SDK。這與 Go/Node.js 的方式有本質差異。

5.3 外部工具呼叫流程

Model 決定呼叫外部工具
    -> CLI 發送 ToolCallRequest (JSON-RPC request) 到 SDK
    -> SDK 自動攔截並呼叫已註冊的函式
    -> SDK 回傳 ToolResult 到 CLI
    -> CLI 將結果送回 Model

Go 版本在 Responder.Request() 中比對工具名稱、反序列化參數、呼叫函式、包裝結果。Node.js 版本在 ProtocolClient.handleToolCallRequest() 中完成相同邏輯。

值得注意的是,外部工具需要 Wire Protocol Version >= 1.1。SDK 在初始化時自動偵測版本,舊版 CLI 會靜默忽略外部工具定義。

5.4 核准流程

內建工具執行前可能觸發核准請求。SDK 提供三種回應選項:

  • approve:核准本次操作
  • approve_for_session:核准並自動核准後續類似操作
  • reject:拒絕操作

此外也支援 auto_approve / yolo 模式,自動核准所有請求,適用於 CI/CD 等無人互動場景。


六、記憶體與上下文管理

6.1 Session 持久化

對話歷史由 Kimi CLI 管理,儲存在本地檔案系統。各語言 SDK 提供不同層級的 Session 管理能力:

~/.kimi/
  config.toml               # 設定檔
  mcp.json                  # MCP 伺服器設定
  sessions/
    {workDir_hash}/         # 按工作目錄分組
      {sessionId}/
        context.jsonl       # 對話歷史

Go SDK 透過 WithSession(id) 指定現有 Session ID 恢復對話;Node.js SDK 提供 listSessions()deleteSession()parseSessionEvents() 等完整管理工具;Python SDK 則支援 Session.resume() 方法。

6.2 Context 壓縮

當 context window 使用率接近 100% 時,CLI 會自動觸發 context compaction:發送 CompactionBegin 事件,將舊訊息壓縮為摘要,再發送 CompactionEnd 事件。SDK 透過 StatusUpdate 事件追蹤 context_usage(0.0 到 1.0 的浮點數)。

6.3 Token 使用量追蹤

type TokenUsage struct {
    InputOther         int  // 一般輸入 token
    Output             int  // 輸出 token
    InputCacheRead     int  // 快取讀取 token
    InputCacheCreation int  // 快取建立 token
}

6.4 迴圈控制

type LoopControl struct {
    MaxStepsPerRun    int  // 每次執行最大步數(預設 25)
    MaxRetriesPerStep int  // 每步最大重試次數(預設 3)
}

Python 額外支援 max_ralph_iterations 參數,用於 Ralph Loop 模式的迭代次數控制。


七、三語言實作差異

Kimi Agent SDK 的三種語言實作不僅是語法層面的差異,在架構層面也有根本性的不同。

7.1 通訊機制

Go 和 Node.js 版本都透過 stdio 子進程通訊,遵循標準的 Wire Protocol。但 Python 版本是唯一不走 stdio 的:它直接 import kimi_cli.app.KimiCLI,在同一個進程中運行 Agent 引擎。

class Session:
    def __init__(self, cli: KimiCLI):
        self._cli = cli  # 直接持有 KimiCLI 實例

    @staticmethod
    async def create(work_dir, ...):
        cli_session = await CliSession.create(work_dir_path, session_id)
        cli = await KimiCLI.create(cli_session, config=config, ...)
        return Session(cli)

這意味著 Python 版本與 CLI 的耦合度最高(pyproject.toml 中版本鎖定為 kimi-cli>=1.12.0,<1.13.0),但省去了 IPC 開銷,效能可能最好。

7.2 併發模型

三種語言各自採用最自然的併發機制:

  • Go:channel + goroutine。Turn.Steps<-chan *StepStep.Messages<-chan wire.Message,使用 range 迭代消費。
  • Node.js:自訂的 createEventChannel<T>() 實作 AsyncIterable,支援 Symbol.asyncIteratorSymbol.asyncDispose
  • Python:基於 asyncio 的 async generator,使用 async for 消費串流。

7.3 差異摘要

面向GoNode.jsPython
與 CLI 通訊stdio + JSON-RPC 2.0stdio + JSON-RPC 2.0直接 import CLI
併發模型channel + goroutineAsyncIteratorasyncio generator
Schema 驗證反射 + struct tagZodPydantic
外部工具定義泛型函式Zod schema + handleragent YAML + Pydantic
額外功能精簡核心VSCode Extension, storage高/低階雙層 API
最低版本Go 1.22+ES2022+Python 3.12+

Go SDK 最精簡,只有六個核心檔案加上 wire 層;Node.js SDK 最豐富,附帶完整的 VSCode Extension;Python SDK 則提供高階 prompt() 函式和低階 Session 類別的雙層 API。


八、技術亮點

8.1 Ralph Loop 模式

Ralph Loop 是一種「迭代驗證」模式,核心原則是永遠不信任 Agent 輸出,用外部命令驗證:

  1. 發送任務 prompt
  2. Agent 執行
  3. 執行外部驗證命令(如跑測試、lint 檢查)
  4. 若驗證失敗,帶著失敗結果重新 prompt
  5. 重複直到驗證通過或達到最大迭代次數

這對 CI/CD 自動化場景特別實用,確保 Agent 產出的程式碼符合品質要求。

8.2 多供應商支援

雖然是 MoonshotAI 的產品,但 Kimi Code CLI 支援配置多種 LLM 供應商:Moonshot AI(自家)、OpenAI(Legacy 和 Responses API)、Anthropic、Google GenAI / Gemini、Vertex AI。SDK 只是透傳設定,不介入供應商選擇。

8.3 MCP 原生整合

原生支援 Model Context Protocol 伺服器,設定透過 mcp.json 或程式碼傳入,支援 HTTP 和 stdio 兩種傳輸方式,並支援 OAuth 認證。Node.js SDK 另外提供 authMCP()testMCP()resetAuthMCP() 等管理函式。

8.4 子 Agent 與 KAOS 沙箱

透過 SubagentEvent 支援巢狀 Agent 任務,事件可以遞迴包裝。Python 範例中還展示了 KAOS(Kimi Agent OS)沙箱後端支援,包含 BoxLite(本地輕量沙箱)、E2B(雲端沙箱)和 Sprites 三種後端,同一套工具可在不同沙箱環境隔離執行。

8.5 Thinking 模式

支援模型思考/推理過程的暴露。ContentPartTypeThink 事件包含模型的內部推理,可在 Session 建立時啟用或停用,還支援加密思考內容(encrypted 欄位),兼顧了透明度與安全性。


九、限制與觀察

9.1 主要限制

  1. CLI 依賴:Go/Node.js 版本必須安裝 Kimi CLI,Python 版本則需要 kimi-clikosong 套件,對部署環境有明確要求。
  2. Python 版本鎖定:kimi-cli>=1.12.0,<1.13.0kosong>=0.42.0,<0.43.0,版本範圍極為狹窄,升級風險高。
  3. Python 版本限制:需要 Python >= 3.12,使用了 match/case 等較新語法。
  4. 無獨立 Agent 邏輯:SDK 不實作 Agent loop,無法脫離 CLI 獨立使用。
  5. 除錯困難:Go/Node.js 版本的跨進程通訊增加了除錯的複雜度。

9.2 與其他框架的比較

  • vs OpenAI Agents SDK:OpenAI 的 SDK 直接與 API 通訊,Kimi 透過 CLI 中介。OpenAI 版本更輕量,但 Kimi 版本繼承了 CLI 的全部能力。
  • vs LangGraph:LangGraph 提供圖狀態機編排,Kimi SDK 只暴露線性的 Session/Turn/Step 模型。
  • vs CrewAI:CrewAI 著重多 Agent 協作,Kimi SDK 透過 Task 工具和 SubagentEvent 提供類似但更受限的能力。
  • vs Claude Code SDK:架構理念最為接近(CLI 作為引擎),但 Kimi Agent SDK 是目前少數公開此架構的實作。

9.3 適用場景

Kimi Agent SDK 最適合以下場景:已有 Kimi Code 使用者希望程式化存取 Agent 能力、CI/CD 自動化(Ralph Loop 模式)、自訂 IDE 整合(Node.js SDK + VSCode Extension 提供完整範本)、以及需要沙箱隔離的執行環境(KAOS 後端)。


十、總結

Kimi Agent SDK 的「CLI-as-Engine」架構是一個大膽的設計選擇。它放棄了 SDK 層的自主性,換取了與 CLI 的完全一致性和低維護成本。Wire Protocol 的抽象讓多語言支援成為可能,而三種語言各自遵循語言慣例的實作(Go 的泛型反射、Node.js 的 Zod、Python 的 Pydantic)則展現了團隊對各語言生態的理解。

從工程角度看,Go SDK 的 JSON-RPC 2.0 雙向 Codec、外部工具的泛型 Schema 生成、以及 channel-based 的串流消費模式,都是值得學習的精巧設計。Python SDK 直接 import CLI 而非走子進程的架構差異,則是一個有趣的折衷。

對於已經在使用 Kimi Code CLI 的開發者而言,這個 SDK 提供了程式化存取 Agent 引擎的最短路徑。但對於希望從零搭建 Agent 系統的團隊來說,它的 CLI 依賴可能是一個需要考量的限制。無論如何,CLI-as-Engine 這個架構模式本身值得 Agent 框架設計者參考 — 在 Agent 能力快速迭代的當下,讓 SDK 保持「薄而穩定」,將複雜度集中在一處,或許是一種更務實的策略。