Logo
热心市民王先生

Agent 集成方案

Agent 集成 Cider Claude 自动化

Claude/CodeBuddy CLI 作为 Cider Agent 与 Firecracker 沙箱的集成方案设计

本章详细设计如何将 Claude 或 CodeBuddy CLI 作为 Cider Agent 集成到 Firecracker 本地开发沙箱中,实现自动化开发工作流。

架构设计

系统架构总览

flowchart TB
    subgraph "Cider Platform"
        A[Claude/CodeBuddy CLI] -->|Agent Protocol| B[Cider Agent Runtime]
    end
    
    subgraph "macOS Host"
        B -->|API Calls| C[Agent Controller]
        C -->|gRPC/HTTP| D[Lima VM]
    end
    
    subgraph "Lima VM 内部"
        D --> E[Orchestrator Service]
        E --> F[Firecracker API]
    end
    
    subgraph "Firecracker MicroVMs"
        F --> G[MicroVM 1<br/>Node.js App]
        F --> H[MicroVM 2<br/>Go App]
        F --> I[MicroVM N<br/>Python App]
        
        G --> J[Agent Sidecar]
        H --> J
        I --> J
    end
    
    subgraph "服务暴露"
        J -->|HTTP/gRPC| K[API Gateway]
        K -->|Port Forward| L[macOS Host Ports]
    end
    
    style A fill:#e1f5e1
    style C fill:#fff3cd
    style E fill:#f8d7da
    style J fill:#d1ecf1
    style K fill:#d4edda

核心组件说明

组件职责技术栈部署位置
Agent Controller接收 Cider Agent 指令,转换为 Firecracker 操作Node.js/GomacOS Host
Orchestrator Service管理 MicroVM 生命周期、资源调度Go/RustLima VM
Firecracker API与 Firecracker 进程通信REST APILima VM
Agent Sidecar在 MicroVM 内执行 Agent 指令Node.jsMicroVM
API Gateway统一服务暴露和路由Nginx/EnvoyLima VM

Claude/CodeBuddy CLI 集成

Cider Agent 协议适配

Cider Agent 通常通过标准化协议与外部环境交互。我们需要在 Firecracker 沙箱中实现适配层。

协议转换层设计

sequenceDiagram
    participant Agent as Cider Agent<br/>(Claude/CodeBuddy)
    participant Adapter as Protocol Adapter
    participant Controller as Agent Controller
    participant Orchestrator as Orchestrator
    participant MicroVM as Firecracker MicroVM
    
    Agent->>Adapter: 1. Agent Action<br/>(如: run_tests)
    Adapter->>Adapter: 2. 解析并验证
    Adapter->>Controller: 3. gRPC Call<br/>(ExecuteAction)
    Controller->>Controller: 4. 权限检查
    Controller->>Orchestrator: 5. HTTP API<br/>(POST /vms/action)
    Orchestrator->>MicroVM: 6. vsock/HTTP<br/>(执行命令)
    MicroVM-->>Orchestrator: 7. 执行结果
    Orchestrator-->>Controller: 8. 结果聚合
    Controller-->>Adapter: 9. gRPC Response
    Adapter-->>Agent: 10. Agent Result<br/>(标准化格式)

适配器实现方案

方案 A:Sidecar 模式(推荐)

在 MicroVM 内运行轻量级 Agent Sidecar:

# MicroVM 启动时自动启动 Sidecar
#!/bin/sh
# /etc/init.d/agent-sidecar

# 启动 Sidecar 服务
/usr/local/bin/agent-sidecar \
  --vsock-cid 3 \
  --vsock-port 8080 \
  --workspace /app \
  --runtime nodejs

Sidecar 代码示例(Node.js):

// agent-sidecar.js
const http = require('http');
const { exec } = require('child_process');
const net = require('net');

class AgentSidecar {
  constructor(config) {
    this.workspace = config.workspace;
    this.runtime = config.runtime;
    this.port = config.port || 8080;
  }

  async start() {
    // 创建 HTTP 服务器
    const server = http.createServer((req, res) => {
      this.handleRequest(req, res);
    });

    // 监听 vsock(如果在 Firecracker 内)
    // 或标准 TCP
    server.listen(this.port, () => {
      console.log(`Agent Sidecar listening on port ${this.port}`);
    });
  }

  async handleRequest(req, res) {
    const { method, url } = req;
    
    if (method === 'POST' && url === '/execute') {
      let body = '';
      req.on('data', chunk => body += chunk);
      req.on('end', async () => {
        const action = JSON.parse(body);
        const result = await this.executeAction(action);
        res.writeHead(200, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify(result));
      });
    }
  }

  async executeAction(action) {
    switch (action.type) {
      case 'run_command':
        return this.runCommand(action.command, action.cwd);
      case 'read_file':
        return this.readFile(action.path);
      case 'write_file':
        return this.writeFile(action.path, action.content);
      case 'list_files':
        return this.listFiles(action.path);
      case 'run_tests':
        return this.runTests(action.testCommand);
      default:
        throw new Error(`Unknown action: ${action.type}`);
    }
  }

  runCommand(command, cwd = this.workspace) {
    return new Promise((resolve, reject) => {
      exec(command, { cwd }, (error, stdout, stderr) => {
        resolve({
          success: !error,
          exitCode: error ? error.code : 0,
          stdout: stdout.toString(),
          stderr: stderr.toString()
        });
      });
    });
  }

  // 其他方法实现...
}

// 启动 Sidecar
const sidecar = new AgentSidecar({
  workspace: process.env.WORKSPACE || '/app',
  runtime: process.env.RUNTIME || 'nodejs',
  port: process.env.PORT || 8080
});

sidecar.start().catch(console.error);

方案 B:API Gateway 模式

通过统一的 API Gateway 暴露所有 MicroVM 服务:

# nginx-agent-gateway.conf
upstream microvms {
    server 172.20.0.2:8080;  # MicroVM 1
    server 172.20.0.3:8080;  # MicroVM 2
    # 动态添加更多
}

server {
    listen 9000;
    
    location /agent/ {
        proxy_pass http://microvms/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        
        # 超时设置
        proxy_connect_timeout 30s;
        proxy_send_timeout 30s;
        proxy_read_timeout 300s;
    }
    
    location /agent/stream {
        proxy_pass http://microvms/stream;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

与 Claude CLI 集成

Claude CLI 配置

Claude CLI 可以通过 --tool 参数扩展能力。我们需要创建自定义工具:

// claude-firecracker-tool.json
{
  "name": "firecracker-sandbox",
  "description": "Manage Firecracker MicroVMs for development",
  "tools": [
    {
      "name": "create_vm",
      "description": "Create a new Firecracker MicroVM",
      "parameters": {
        "type": "object",
        "properties": {
          "runtime": {
            "type": "string",
            "enum": ["nodejs", "go", "python", "rust"]
          },
          "workspace": {
            "type": "string",
            "description": "Path to workspace"
          },
          "resources": {
            "type": "object",
            "properties": {
              "cpu": { "type": "number", "default": 2 },
              "memory": { "type": "string", "default": "512MiB" }
            }
          }
        },
        "required": ["runtime", "workspace"]
      }
    },
    {
      "name": "execute_command",
      "description": "Execute a command in the MicroVM",
      "parameters": {
        "type": "object",
        "properties": {
          "vm_id": { "type": "string" },
          "command": { "type": "string" },
          "cwd": { "type": "string" }
        },
        "required": ["vm_id", "command"]
      }
    },
    {
      "name": "read_file",
      "description": "Read a file from the MicroVM",
      "parameters": {
        "type": "object",
        "properties": {
          "vm_id": { "type": "string" },
          "path": { "type": "string" }
        },
        "required": ["vm_id", "path"]
      }
    },
    {
      "name": "run_tests",
      "description": "Run tests in the MicroVM",
      "parameters": {
        "type": "object",
        "properties": {
          "vm_id": { "type": "string" },
          "test_command": { "type": "string", "default": "npm test" }
        },
        "required": ["vm_id"]
      }
    }
  ]
}

Claude CLI 使用示例

# 启动 Claude CLI 并加载 Firecracker 工具
claude --tool ./claude-firecracker-tool.json

# 在 Claude 会话中使用
[Claude] > Create a Node.js MicroVM for my project at ~/workspace/my-app

[Tool Call] create_vm
{
  "runtime": "nodejs",
  "workspace": "~/workspace/my-app",
  "resources": {
    "cpu": 2,
    "memory": "1GiB"
  }
}

[Tool Result] 
 MicroVM created: vm-nodejs-abc123
 Runtime: Node.js 20.x
 Resources: 2 vCPU, 1GiB RAM
 API Endpoint: http://localhost:9000/agent/vm-nodejs-abc123

[Claude] > Run npm install in the VM

[Tool Call] execute_command
{
  "vm_id": "vm-nodejs-abc123",
  "command": "npm install",
  "cwd": "/app"
}

[Tool Result]
 Command executed successfully
Exit Code: 0
Duration: 45s
Output: added 234 packages in 45s

与 CodeBuddy CLI 集成

CodeBuddy 配置文件

# .codebuddy/firecracker.yaml
version: "1.0"

sandbox:
  provider: firecracker
  config:
    lima_instance: firecracker
    network_bridge: br0
    tap_prefix: tap
    
  runtimes:
    nodejs:
      image: alpine-nodejs-dev:latest
      kernel: vmlinux-5.10.209
      default_resources:
        vcpu: 2
        memory: 512MiB
      ports:
        - 3000:3000
        - 9229:9229  # Debug
        
    go:
      image: alpine-go-dev:latest
      kernel: vmlinux-5.10.209
      default_resources:
        vcpu: 2
        memory: 512MiB
      ports:
        - 8080:8080
        - 2345:2345  # Delve debug

agent:
  sidecar:
    enabled: true
    port: 8080
    commands:
      test: "npm test"
      build: "npm run build"
      lint: "npm run lint"
      
  permissions:
    - read: "/app"
    - write: "/app/src"
    - execute: "/app/scripts"

hooks:
  pre_start: "./scripts/setup-vm.sh"
  post_start: "./scripts/health-check.sh"
  on_change: "./scripts/sync-changes.sh"

CodeBuddy 命令集成

# 初始化 Firecracker 沙箱
codebuddy sandbox init --provider firecracker

# 创建新的开发环境
codebuddy sandbox create --runtime nodejs --workspace ./my-app

# 在沙箱中执行命令
codebuddy sandbox exec -- npm install
codebuddy sandbox exec -- npm run dev

# 运行测试
codebuddy sandbox test

# 查看沙箱状态
codebuddy sandbox status

# 进入沙箱 shell
codebuddy sandbox shell

# 销毁沙箱
codebuddy sandbox destroy

准备工作清单

Agent Controller 部署

步骤 1:安装 Controller

# 在 macOS Host 上安装 Agent Controller
npm install -g @cider/firecracker-controller

# 或从源码构建
git clone https://github.com/cider/firecracker-controller.git
cd firecracker-controller
npm install
npm run build
npm link

步骤 2:配置 Controller

# ~/.cider/firecracker-controller.yaml
server:
  host: localhost
  port: 9090
  grpc_port: 9091

lima:
  instance: firecracker
  socket_path: /Users/$USER/.lima/firecracker/sock/lima.sock

firecracker:
  binary_path: /usr/local/bin/firecracker
  kernel_path: /Users/$USER/firecracker-images/vmlinux-5.10.209
  images_path: /Users/$USER/firecracker-images
  vms_path: /Users/$USER/firecracker-vms
  
network:
  bridge: br0
  subnet: 172.20.0.0/24
  gateway: 172.20.0.1
  tap_prefix: tap

agent:
  sidecar_image: cider/agent-sidecar:latest
  default_port: 8080
  
logging:
  level: info
  file: ~/.cider/logs/controller.log

步骤 3:启动 Controller

# 前台启动(调试)
firecracker-controller --config ~/.cider/firecracker-controller.yaml

# 后台启动
firecracker-controller --daemon

# 验证状态
curl http://localhost:9090/health

MicroVM 模板准备

创建 Agent Sidecar 镜像

# Dockerfile.agent-sidecar
FROM alpine:3.19

# 安装 Node.js
RUN apk add --no-cache nodejs npm curl

# 安装 Sidecar
WORKDIR /opt/sidecar
COPY agent-sidecar.js ./
COPY package.json ./
RUN npm install --production

# 安装常用工具
RUN apk add --no-cache \
    git \
    openssh-client \
    rsync \
    htop

# 配置环境
ENV WORKSPACE=/app
ENV RUNTIME=nodejs
ENV PORT=8080

EXPOSE 8080

CMD ["node", "agent-sidecar.js"]

构建并转换:

# 构建 Docker 镜像
docker build -t agent-sidecar:latest -f Dockerfile.agent-sidecar .

# 转换为 ext4 根文件系统
docker create --name sidecar-export agent-sidecar:latest
docker export sidecar-export | tar -C /tmp/sidecar-rootfs -xvf -
docker rm sidecar-export

# 创建 ext4 镜像
dd if=/dev/zero of=agent-sidecar-rootfs.ext4 bs=1M count=1024
mkfs.ext4 agent-sidecar-rootfs.ext4
mkdir -p /mnt/sidecar
mount -o loop agent-sidecar-rootfs.ext4 /mnt/sidecar
cp -a /tmp/sidecar-rootfs/* /mnt/sidecar/
umount /mnt/sidecar

通信协议设计

协议栈架构

flowchart TB
    subgraph "应用层"
        A[Agent Actions]
    end
    
    subgraph "协议层"
        B[JSON-RPC 2.0]
        C[gRPC]
        D[HTTP/REST]
    end
    
    subgraph "传输层"
        E[vsock]
        F[TCP/IP]
        G[Unix Socket]
    end
    
    subgraph "适配层"
        H[Host Transport]
        I[VM Transport]
    end
    
    A --> B
    A --> C
    A --> D
    B --> E
    C --> F
    D --> G
    E --> H
    F --> H
    G --> I

推荐协议:JSON-RPC over vsock

优势:

  • 简单:易于实现和调试
  • 高效:vsock bypass 网络栈,性能接近本地通信
  • 安全:基于 CID(Context ID)隔离,天然多租户

协议定义:

// agent-protocol.ts

interface AgentRequest {
  jsonrpc: "2.0";
  id: string;
  method: string;
  params: unknown;
}

interface AgentResponse {
  jsonrpc: "2.0";
  id: string;
  result?: unknown;
  error?: {
    code: number;
    message: string;
    data?: unknown;
  };
}

// 支持的 Method
enum AgentMethods {
  // 文件操作
  FILE_READ = "file/read",
  FILE_WRITE = "file/write",
  FILE_DELETE = "file/delete",
  FILE_LIST = "file/list",
  FILE_STAT = "file/stat",
  
  // 命令执行
  EXEC_RUN = "exec/run",
  EXEC_START = "exec/start",
  EXEC_STOP = "exec/stop",
  EXEC_STATUS = "exec/status",
  
  // 进程管理
  PROCESS_LIST = "process/list",
  PROCESS_KILL = "process/kill",
  
  // 测试和构建
  TEST_RUN = "test/run",
  BUILD_START = "build/start",
  BUILD_STATUS = "build/status",
  
  // 环境信息
  ENV_GET = "env/get",
  ENV_SET = "env/set",
  INFO_SYSTEM = "info/system",
  INFO_RUNTIME = "info/runtime"
}

// 示例请求
const exampleRequest: AgentRequest = {
  jsonrpc: "2.0",
  id: "req-123",
  method: "exec/run",
  params: {
    command: "npm test",
    cwd: "/app",
    timeout: 120000,
    env: {
      NODE_ENV: "test"
    }
  }
};

vsock 通信实现

宿主机端(Lima VM):

// vsock-server.go
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"net"

	"github.com/mdlayher/vsock"
)

type AgentServer struct {
	microVMs map[string]*MicroVM
}

type MicroVM struct {
	CID  uint32
	Port uint32
}

func (s *AgentServer) handleConnection(conn net.Conn) {
	defer conn.Close()
	
	decoder := json.NewDecoder(conn)
	encoder := json.NewEncoder(conn)
	
	var req AgentRequest
	if err := decoder.Decode(&req); err != nil {
		log.Printf("Decode error: %v", err)
		return
	}
	
	resp := s.processRequest(&req)
	if err := encoder.Encode(resp); err != nil {
		log.Printf("Encode error: %v", err)
	}
}

func (s *AgentServer) processRequest(req *AgentRequest) *AgentResponse {
	// 路由到对应的 MicroVM
	vm, ok := s.microVMs[req.Params.VMID]
	if !ok {
		return &AgentResponse{
			JSONRPC: "2.0",
			ID:      req.ID,
			Error: &AgentError{
				Code:    -32602,
				Message: "MicroVM not found",
			},
		}
	}
	
	// 转发到 MicroVM
	return s.forwardToVM(vm, req)
}

func (s *AgentServer) forwardToVM(vm *MicroVM, req *AgentRequest) *AgentResponse {
	ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
	defer cancel()
	
	// 建立 vsock 连接
	conn, err := vsock.DialContext(ctx, vm.CID, vm.Port, nil)
	if err != nil {
		return &AgentResponse{
			JSONRPC: "2.0",
			ID:      req.ID,
			Error: &AgentError{
				Code:    -32000,
				Message: fmt.Sprintf("Failed to connect to VM: %v", err),
			},
		}
	}
	defer conn.Close()
	
	// 发送请求
	encoder := json.NewEncoder(conn)
	if err := encoder.Encode(req); err != nil {
		return &AgentResponse{
			JSONRPC: "2.0",
			ID:      req.ID,
			Error: &AgentError{
				Code:    -32001,
				Message: fmt.Sprintf("Failed to send request: %v", err),
			},
		}
	}
	
	// 接收响应
	decoder := json.NewDecoder(conn)
	var resp AgentResponse
	if err := decoder.Decode(&resp); err != nil {
		return &AgentResponse{
			JSONRPC: "2.0",
			ID:      req.ID,
			Error: &AgentError{
				Code:    -32002,
				Message: fmt.Sprintf("Failed to receive response: %v", err),
			},
		}
	}
	
	return &resp
}

func main() {
	server := &AgentServer{
		microVMs: make(map[string]*MicroVM),
	}
	
	// 监听 vsock
	listener, err := vsock.Listen(8080, nil)
	if err != nil {
		log.Fatalf("Failed to listen: %v", err)
	}
	defer listener.Close()
	
	log.Println("Agent Server listening on vsock:8080")
	
	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Printf("Accept error: %v", err)
			continue
		}
		
		go server.handleConnection(conn)
	}
}

MicroVM 端(Sidecar):

// vsock-client.js
const net = require('net');
const { exec } = require('child_process');

class VsockServer {
  constructor(port = 8080) {
    this.port = port;
    this.handlers = new Map();
    this.setupHandlers();
  }

  setupHandlers() {
    this.handlers.set('file/read', this.handleFileRead.bind(this));
    this.handlers.set('file/write', this.handleFileWrite.bind(this));
    this.handlers.set('exec/run', this.handleExecRun.bind(this));
    // 更多 handler...
  }

  start() {
    // 在 Firecracker MicroVM 内,vsock 表现为特殊的 socket
    const server = net.createServer((socket) => {
      this.handleConnection(socket);
    });

    // Firecracker vsock 在 guest 内表现为 /dev/vsock
    server.listen({
      port: this.port,
      host: '0.0.0.0'
    }, () => {
      console.log(`Vsock server listening on port ${this.port}`);
    });
  }

  handleConnection(socket) {
    let buffer = '';
    
    socket.on('data', (data) => {
      buffer += data.toString();
      
      // 尝试解析 JSON-RPC
      try {
        const lines = buffer.split('\n');
        for (let i = 0; i < lines.length - 1; i++) {
          const line = lines[i].trim();
          if (line) {
            const request = JSON.parse(line);
            this.processRequest(socket, request);
          }
        }
        buffer = lines[lines.length - 1];
      } catch (err) {
        // 等待更多数据
      }
    });
  }

  async processRequest(socket, request) {
    const handler = this.handlers.get(request.method);
    
    let response;
    if (handler) {
      try {
        const result = await handler(request.params);
        response = {
          jsonrpc: '2.0',
          id: request.id,
          result
        };
      } catch (err) {
        response = {
          jsonrpc: '2.0',
          id: request.id,
          error: {
            code: -32603,
            message: err.message
          }
        };
      }
    } else {
      response = {
        jsonrpc: '2.0',
        id: request.id,
        error: {
          code: -32601,
          message: `Method not found: ${request.method}`
        }
      };
    }
    
    socket.write(JSON.stringify(response) + '\n');
  }

  async handleFileRead(params) {
    const fs = require('fs').promises;
    const content = await fs.readFile(params.path, 'utf8');
    return { content };
  }

  async handleExecRun(params) {
    return new Promise((resolve, reject) => {
      const options = {
        cwd: params.cwd,
        env: { ...process.env, ...params.env },
        timeout: params.timeout || 60000
      };
      
      exec(params.command, options, (error, stdout, stderr) => {
        resolve({
          success: !error,
          exitCode: error ? error.code : 0,
          stdout: stdout.toString(),
          stderr: stderr.toString()
        });
      });
    });
  }
}

// 启动 vsock 服务器
const server = new VsockServer(8080);
server.start();

自动化集成流程

CI/CD 集成

# .github/workflows/firecracker-ci.yaml
name: Firecracker CI

on: [push, pull_request]

jobs:
  test:
    runs-on: macos-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Firecracker
        uses: cider/setup-firecracker@v1
        with:
          lima-version: '1.0.0'
          firecracker-version: '1.7.0'
      
      - name: Start Development Environment
        run: |
          codebuddy sandbox create \
            --runtime nodejs \
            --workspace . \
            --name ci-test
      
      - name: Install Dependencies
        run: codebuddy sandbox exec -- npm ci
      
      - name: Run Linter
        run: codebuddy sandbox exec -- npm run lint
      
      - name: Run Tests
        run: codebuddy sandbox test --coverage
      
      - name: Build
        run: codebuddy sandbox exec -- npm run build
      
      - name: Cleanup
        if: always()
        run: codebuddy sandbox destroy --name ci-test

本地开发自动化

#!/bin/bash
# dev-automation.sh

# 项目根目录钩子脚本

start_dev_env() {
    echo "🚀 启动 Firecracker 开发环境..."
    
    # 检查是否存在现有环境
    if codebuddy sandbox status | grep -q "Running"; then
        echo "✓ 开发环境已在运行"
        return 0
    fi
    
    # 创建新环境
    codebuddy sandbox create \
        --runtime nodejs \
        --workspace . \
        --name "$(basename $PWD)-dev"
    
    # 安装依赖
    echo "📦 安装依赖..."
    codebuddy sandbox exec -- npm install
    
    # 启动开发服务器
    echo "🌐 启动开发服务器..."
    codebuddy sandbox exec -- npm run dev &
    
    echo "✓ 开发环境准备完成"
    echo "访问: http://localhost:3000"
}

sync_changes() {
    echo "🔄 同步变更到沙箱..."
    
    # 使用 rsync 快速同步
    rsync -avz --delete \
        --exclude node_modules \
        --exclude .git \
        ./ lima-firecracker:/app/
    
    # 触发沙箱内重载(如需要)
    codebuddy sandbox exec -- kill -HUP 1  # 发送 SIGHUP 触发重载
}

run_tests() {
    echo "🧪 在沙箱中运行测试..."
    codebuddy sandbox test
}

# 根据参数执行
 case "$1" in
    start)
        start_dev_env
        ;;
    sync)
        sync_changes
        ;;
    test)
        run_tests
        ;;
    *)
        echo "Usage: $0 {start|sync|test}"
        exit 1
        ;;
esac

总结

集成架构关键点

  1. 多层抽象:从 Cider Agent 到 MicroVM 需要多层协议转换,但每层职责清晰
  2. Sidecar 模式:在 MicroVM 内运行轻量级 Agent 是最灵活的方案
  3. vsock 通信:利用 Firecracker 的 vsock 特性实现高效宿主机-客户机通信
  4. 标准化协议:JSON-RPC 2.0 提供简单、通用的交互方式

准备工作要点

组件优先级预计工作量依赖
Agent Controller1-2 周Lima VM
Orchestrator Service1 周Firecracker
Agent Sidecar1 周Node.js 运行时
vsock 通信层3-5 天vsock 内核模块
Claude CLI 工具3-5 天Agent Controller
CodeBuddy 插件1 周CodeBuddy SDK

下一步

完成 Agent 集成设计后,需要关注:


本章参考资料: