GitHub Copilot SDK 进程内运行(In-Process Runtime):把原生 Copilot 运行时装进你的应用进程

原创2026-09-15 22:04:381,850 阅读
文章标签:人工智能AI AgentAgent 框架工具调用代码智能体开发工具

GitHub Copilot SDK 进程内运行(In-Process Runtime):把原生 Copilot 运行时装进你的应用进程

本指南系统讲解 GitHub Copilot SDK 的 in-process 托管模式:它不是另起一个 Copilot CLI 子进程,而是把原生 Copilot 运行时以动态库形式直接加载进你的应用程序进程,在内存连接上继续使用原有的 JSON-RPC 协议,从而保留同样的会话(session)、流式事件(streaming events)、工具(tools)、钩子(hooks)与权限行为。读完本文,你将掌握六种语言 SDK(TypeScript、Python、Go、.NET、Rust、Java)各自的 in-process 接入方式、构建/打包前置条件、运行时解析规则与生命周期行为,以及该模式当前的局限性。

什么是 in-process 托管

进程内托管(in-process hosting)是 GitHub Copilot SDK 提供的一种运行模式:SDK 将 Copilot 原生运行时(native runtime)加载到应用程序自己的进程里,而不是启动一个独立的 Copilot CLI 进程。它消除了子进程管理的负担,同时 SDK 的会话、事件、工具、钩子和 JSON-RPC 行为与其它传输方式保持一致——从架构上看,它只是换了一条传输通道,协议本身没有变化。

[!WARNING] in-process 托管在每一种语言 SDK 中都属于实验性(experimental)能力。请务必在你实际部署的每个操作系统和 CPU 架构上测试启动、模型对话轮次(model turns)和关闭(shutdown)行为。

何时选择 in-process 托管

根据官方设置文档,in-process 托管适合以下场景:

  • 你的应用必须运行在没有任何独立运行时进程的环境中;
  • 你希望 SDK 自己拥有运行时的完整生命周期;
  • 你能够为每个部署平台(操作系统 + 架构 + Linux C 库)发布对应的原生库;
  • 进程级的环境变量与工作目录设置可以接受(即应用不依赖"每个客户端一套独立环境")。

相反,如果进程隔离和经过最成熟验证的部署路径更重要,应使用默认的 bundled CLI 方案;如果多个应用实例需要通过网络连接到共享运行时,则应使用backend service(TCP)方案。三种方案的分工可参考 Choosing a setup path。

工作原理:固定 C ABI + 内存中的 JSON-RPC

从源码可以清晰还原其实现机制。SDK 加载 Copilot 运行时的原生动态库,并绑定其固定的 C ABI;所有 SDK 方法继续通过既有的 Content-Length 帧格式 JSON-RPC 协议,在一条内存(in-memory)连接上通信。其架构可以用下面的流程图概括:

flowchart LR
    App["Your application"]
    SDK["Copilot SDK client"]
    FFI["C ABI connection"]
    Runtime["Native Copilot runtime"]

    App --> SDK
    SDK -- "JSON-RPC frames" --> FFI
    FFI --> Runtime
    Runtime -- "Events and callbacks" --> FFI
    FFI --> SDK

以 Python 和 Node.js 的实现为例,加载的是名为 runtime.node 的 Rust cdylib(见 python/copilot/_ffi_runtime_host.py 与 nodejs/src/ffiRuntimeHost.ts)。SDK 只负责在 C ABI 边界上泵送不透明的 LSP Content-Length: 帧 JSON-RPC 字节,帧解析继续由现有的 JSON-RPC 客户端完成——这是"传输层替换"而非"新协议"。这套 C ABI 在 .NET、Node.js、Python、Rust 四个 SDK 间共享,核心导出函数包括:

uint32 copilot_runtime_host_start(uint8 *argv, size_t argv_len, uint8 *env, size_t env_len);
bool   copilot_runtime_host_shutdown(uint32 server_id);
uint32 copilot_runtime_connection_open(uint32 server_id, outbound cb, void *user_data, ...);
bool   copilot_runtime_connection_write(uint32 conn_id, uint8 *bytes, size_t len);
bool   copilot_runtime_connection_close(uint32 conn_id);
void   outbound(void *user_data, uint8 *bytes, size_t len); // 原生回调

其中 copilot_runtime_host_start 会在当前进程内同步构造 Rust 服务器(server),出站回调则从原生 worker 线程向 SDK 投递事件。

运行时在进程内运行时有以下特点:

  • 运行在应用程序进程中,不需要 Node.js、子进程、TCP 端口或 connection token;
  • 支持与其它传输完全相同的会话、流式事件、工具、钩子、权限以及 server-to-client 请求;
  • 可以从原生 worker 线程调用 SDK 回调,线程编组(thread marshalling)与回调生命周期由 SDK 负责;
  • 加载的原生库及其 worker 池在应用进程存活期间一直可用。

各语言 SDK 的接入要求

所有语言 SDK 都暴露了显式的 in-process 连接选项,部分语言还需要额外的构建或包配置。官方文档给出了完整的对照表:

SDK 连接选项 额外要求
TypeScript RuntimeConnection.forInProcess() 包内已包含兼容运行时 bundle 时无需额外配置
Python RuntimeConnection.for_inprocess() 启动时无法自动下载运行时,需先执行 python -m copilot download-runtime --in-process 预下载
Go copilot.InProcessConnection{} 必须用 -tags copilot_inprocess 构建
.NET RuntimeConnection.ForInProcess() 需放行 GHCP001 实验性 API 诊断
Rust Transport::InProcess 需启用 bundled-in-process Cargo feature
Java RuntimeConnection.forInProcess() 需要 JNA、平台运行时 classifier,以及实验性 API 的显式 opt-in

原生运行时 bundle 必须匹配宿主机的操作系统、CPU 架构,以及在 Linux 上还要匹配 C 库(glibc/musl)。不支持的宿主会在运行时解析或启动阶段直接失败,而不会回退到子进程方式。

源码佐证

  • Go:构建标签强制在编译期约束能力。NewClient 识别 InProcessConnection 后设置 useInProcess = true(见 go/client.go);若未用 -tags copilot_inprocess 构建,Start 会直接返回 "in-process transport unavailable: rebuild with -tags copilot_inprocess" 错误(见 go/client.go)。对应测试见 go/client_test.go。Go 实现基于纯 Go 的 FFI 库 purego,因此 CGO_ENABLED=0 与交叉编译仍然可用,不需要 C 工具链(见 go/README.md)。
  • Rust:Transport::InProcess 定义在 rust/src/lib.rs,其可用性由 bundled-in-process feature 决定;未启用该 feature 时会报 "requires the bundled-in-process Cargo feature"(见 rust/src/lib.rs)。
  • .NET:GHCP001 是一个通过 <a href="https://link.gitcode.com/i/858cac8c915f2f088b58bec92837fb5b" target="_blank">Obsolete(..., DiagnosticId = "GHCP001")] 实现的实验性 API 诊断(见 [dotnet/src/Generated/Rpc.cs),在调用处需要 #pragma warning disable GHCP001。

配置一个 in-process 连接

在创建客户端时传入对应语言的连接选项即可。以下是官方文档给出的六种语言完整示例。

TypeScript

import { CopilotClient, RuntimeConnection } from "@github/copilot-sdk";

const client = new CopilotClient({
  connection: RuntimeConnection.forInProcess(),
});

await client.start();

Python

from copilot import CopilotClient, RuntimeConnection

client = CopilotClient(
    connection=RuntimeConnection.for_inprocess(),
)

await client.start()

Go

client := copilot.NewClient(&copilot.ClientOptions{
    Connection: copilot.InProcessConnection{},
})

if err := client.Start(context.Background()); err != nil {
    log.Fatal(err)
}
defer client.Stop()

构建命令:go build -tags copilot_inprocess。

.NET

#pragma warning disable GHCP001

var client = new CopilotClient(new CopilotClientOptions
{
    Connection = RuntimeConnection.ForInProcess(),
});

await client.StartAsync();

Rust

let options = ClientOptions::default()
    .with_transport(Transport::InProcess);

let client = Client::start(options).await?;

启用 Cargo feature 后即可使用:

[dependencies]
github-copilot-sdk = { version = "...", features = ["bundled-in-process"] }

Java

import com.github.copilot.AllowCopilotExperimental;

@AllowCopilotExperimental
public class Example {
    public void run() throws Exception {
        CopilotClientOptions options = new CopilotClientOptions()
            .setConnection(RuntimeConnection.forInProcess());

        CopilotClient client = new CopilotClient(options);
        client.start().join();
    }
}

RuntimeConnection.forInProcess() 被标记为 @CopilotExperimental,因此使用它的类或方法必须用 @AllowCopilotExperimental 显式 opt-in(或在编译时传 -Acopilot.experimental.allowed=true)。该检查由注解处理器 CopilotExperimentalProcessor 在编译期强制执行,见 AllowCopilotExperimental.java 与 CopilotExperimentalProcessor.java,更完整的说明见 java/README.md。

用环境变量切换传输方式(免改代码)

除了显式配置,你还可以在启动应用前设置:

export COPILOT_SDK_DEFAULT_CONNECTION=inprocess

SDK 只在该客户端没有显式指定 connection 时才使用这个值;传入非法值会导致启动失败。该逻辑在多个 SDK 中均有实现,例如 Node.js 的 resolveDefaultConnection(见 nodejs/src/client.ts)与 Python 的 python/copilot/client.py 都只接受 inprocess、stdio 或未设置三种情况。

最佳实践:应用代码里优先显式配置连接;仅在部署配置需要不改代码地选择传输方式时,才使用环境变量。

配置运行时参数

SDK 会把受支持的、类型化的客户端选项转换为原生运行时参数(native runtime arguments)和宿主范围内的环境值。不同语言 SDK 支持的选项略有差异,通常包括:

  • 认证 token 与 logged-in-user 回退;
  • Copilot 基础目录(base directory);
  • 日志级别(log level);
  • 会话空闲超时(session idle timeout);
  • 远程会话模式(remote session mode)。

in-process 运行时收到的是宿主环境的一份快照加上 SDK 管理的覆盖项(overrides),它不会修改宿主环境。因此,凡是未被类型化客户端选项覆盖的进程级值——包括其它环境变量和应用的当前工作目录——必须在创建第一个 in-process 客户端之前设置好。

这一点在源码中体现得非常直接:Node.js、Go、Python 的 in-process 传输都会拒绝那些无法在共享进程内兑现的客户端选项,例如 workingDirectory、env 和 telemetry(会抛出错误或 panic),因为宿主进程只有一个环境块(environment block)、一个工作目录和一套共享的遥测状态(见 nodejs/src/client.ts、go/client.go、python/copilot/client.py)。这类选项应该改为:在宿主进程上配置进程级全局值,或改用子进程类传输(stdio/TCP)。

运行时库解析规则

每个 SDK 首先查找兼容的 bundled(内置)或缓存的原生运行时库。当你需要单独提供运行时(例如部署包不含内置 bundle)时,可以设置:

export COPILOT_CLI_PATH=/path/to/compatible/copilot/runtime/package

让 COPILOT_CLI_PATH 指向一个兼容的 Copilot 运行时包。以 Python 为例,启动时无法联网下载运行时的情况下,需要预先执行:

python -m copilot download-runtime --in-process

(见 python/copilot/_cli_download.py 与 python/copilot/_cli_download.py。)

另一个关键限制是:一个进程通常只能加载一个原生运行时库路径与版本。同一加载库上再启动一个客户端是支持的,但尝试加载不同的运行时库会失败——Node.js 的 loadLibrary 会直接拒绝不同路径的第二次加载(见 nodejs/src/ffiRuntimeHost.ts),Go 的 bundler 在 Linux 上会把 glibc 与 musl 两套运行时都打进包里并在启动时自动选择匹配的一套(见 go/README.md)。

生产部署检查清单

按官方文档,生产部署应完成以下步骤:

  1. 针对每个目标平台构建并测试应用;
  2. 确保匹配的原生运行时产物包含在部署包中,或可通过 SDK 的运行时下载机制获取;
  3. 在部署冒烟测试中至少启动一个会话并完成一次模型对话轮次;
  4. 在应用退出前优雅地停止所有客户端。

生命周期行为

启动一个 in-process 客户端时,SDK 依次完成:加载原生库 → 创建运行时宿主(runtime host)→ 打开内存连接 → 执行常规的 SDK 协议版本握手(protocol-version handshake)。

优雅关闭(graceful shutdown)时,SDK 按序执行:

  1. 关闭所有活动会话;
  2. 通过 JSON-RPC 请求正常关闭运行时;
  3. 关闭 JSON-RPC 连接与原生连接;
  4. 释放运行时宿主。

关闭完成后,原生库可以保持加载状态直到应用进程退出——不要依赖"首次使用后卸载并替换运行时库"的行为。Go SDK 的 inProcessHost 接口(Start / Writer / Reader / Dispose)正是对这一生命周期的抽象封装(见 go/inprocess.go),Node.js 侧则用了一个长周期 keep-alive timer 保证内存连接打开期间事件循环不被退出(见 nodejs/src/ffiRuntimeHost.ts)。

当前限制

in-process 托管目前存在以下约束,选型时必须逐条评估:

  • 实验性 API:行为与打包要求可能随版本发布而变化;
  • 共享进程状态:所有客户端共享宿主进程的环境变量、当前工作目录、原生库与运行时 worker 池;
  • 受限的进程选项:SDK 中针对任意环境、工作目录、遥测配置、可执行文件路径或 CLI 参数的选项在适用的地方会被拒绝,应改为在宿主进程上配置进程全局值,并用受支持的类型化选项配置运行时设置;
  • 无按客户端工作目录:运行时使用宿主进程的工作目录;
  • 每进程仅一个运行时版本:不支持加载另一个原生库路径或版本;
  • 平台成熟度不一:部分 SDK 与平台组合的模型对话轮次或关闭流程覆盖度较低,务必对你实际部署的组合做完整验证。

延伸阅读

登录后查看全文
copilot-sdk