GitHub Copilot SDK 入门:五分钟构建你的第一个 AI Agent

GitHub Copilot SDK 入门:五分钟构建你的第一个 AI Agent

TL;DR

GitHub Copilot SDK 的核心价值不是“调用 LLM”的便利性(这已经被 OpenAI SDK、LangChain 等解决),而是提供了一个经过生产验证的 Agent 运行时

它解决的真正问题是:

  • 编排复杂性:规划器、工具路由、状态管理已内置
  • 稳定性:数百万开发者日常使用的可靠性保障
  • 演进能力:新模型、新工具能力由 CLI 自动更新

当你开始构建下一个 AI 应用时,问自己两个问题:

  1. 我的核心价值在哪里? 如果是业务逻辑和工具定义,使用 SDK;如果是底层编排创新,自建框架。
  2. 多久能到生产环境? SDK 让你跳过 80% 的基础设施工作,专注于最后 20% 的差异化能力。

Agent 开发的门槛已经降低,但真正的挑战在于:定义有价值的工具,设计流畅的交互,解决真实的问题。技术不再是壁垒,想象力才是。

引言:为什么 Agent 开发不再是少数人的游戏

2026 年 1 月,GitHub 发布了 Copilot SDK,这标志着 AI Agent 开发从“专家领域”走向“大众工具”的关键转折。

在此之前,构建一个能够自主规划、调用工具、编辑文件的 AI Agent,你需要:

  • 选择并集成 LLM 服务(OpenAI、Anthropic、Azure…)
  • 自建 Agent 编排器(规划器、工具路由、状态管理)
  • 处理流式输出、错误重试、上下文管理
  • 实现工具定义标准(function calling schema)

这套流程复杂且脆弱,开源框架(LangChain、AutoGPT)虽降低门槛,但仍需深入理解 Agent 运行机制。真正的转折点在于:GitHub 将 Copilot CLI 的生产级 Agent 运行时开放为 SDK

这意味着什么?你可以用 5 行代码启动一个完整的 Agent 运行时

import asyncio
from copilot import CopilotClient

async def main():
    client = CopilotClient()
    await client.start()
    session = await client.create_session({"model": "gpt-4.1"})
    response = await session.send_and_wait({"prompt": "解释下量子纠缠"})
    print(response.data.content)

asyncio.run(main())

无需关心模型接入、Prompt 工程、响应解析——这些 Copilot CLI 已在数百万开发者的实战中验证过。你只需定义业务逻辑,SDK 处理剩下的一切。

本文目标:通过一个完整的天气助手案例,带你理解:

  1. SDK 如何与 CLI 通信(架构本质)
  2. 工具调用机制如何工作(LLM 如何 " 决定 " 调用你的代码)
  3. 从玩具到工具的关键跃迁点(流式响应、事件监听、状态管理)

无论你是想快速验证 AI 应用 idea,还是为企业构建定制化 Agent,这篇文章都是起点。

准备工作:环境搭建

在开始编码前,确保你的开发环境满足以下条件。

前置条件清单

1. 安装 GitHub Copilot CLI

SDK 本身不包含 AI 推理能力,它通过 JSON-RPC 与 Copilot CLI 通信。CLI 是真正的 " 引擎 “,SDK 是 " 方向盘 “。

# macOS/Linux
brew install copilot-cli

# Verify installation
copilot --version

2. 认证 GitHub 账号

copilot login

你需要一个 GitHub Copilot 订阅(个人版或企业版)。如果使用 BYOK(Bring Your Own Key)模式,可跳过此步骤。

验证环境

运行以下命令确认 CLI 工作正常:

copilot -p "Explain recursion in one sentence"

如果看到 AI 的回答,说明环境就绪。

第一步:发送你的第一条消息

安装 SDK

创建项目目录并安装 Python SDK:

mkdir copilot-demo && cd copilot-demo
# working in virtual env
python -m venv venv && source venv/bin/activate
pip install github-copilot-sdk

最简代码示例

创建 main.py

import asyncio
from copilot import CopilotClient

async def main():
    client = CopilotClient()
    await client.start()
    
    session = await client.create_session({"model": "gpt-4.1"})
    response = await session.send_and_wait({"prompt": "量子纠缠是什么?"})
    
    print(response.data.content)
    
    await client.stop()

asyncio.run(main())

运行:

python main.py

你会看到 AI 的完整回答。仅用 9 行代码,一个完整的 AI 对话就完成了。

执行流程解析

这段代码背后发生了什么?

1. client.start()     → SDK 启动 Copilot CLI 进程(在后台运行)
2. create_session()   → 通过 JSON-RPC 请求 CLI 创建会话
3. send_and_wait()    → 发送提示词,CLI 转发给 LLM
4. LLM 推理          → 响应通过 CLI 返回给 SDK
5. response.data      → SDK 解析 JSON 响应,提取内容

架构本质:SDK 是 CLI 的“遥控器”

GitHub 的设计哲学是关注点分离

组件职责
Copilot CLI智能体运行时 (规划、工具调用、LLM 通信)
SDK进程管理、JSON-RPC 包装器、事件监听
你的代码进程管理、JSON-RPC 包装器、事件监听

这种架构的优势:

  • CLI 可独立升级:新的模型、工具能力无需修改 SDK
  • 多语言支持成本低:各语言 SDK 只需实现 JSON-RPC 客户端
  • 调试友好:CLI 可独立运行,便于观察日志和排查问题

JSON-RPC 通信示例

当你调用 send_and_wait() 时,SDK 实际发送的请求:

{
  "jsonrpc": "2.0",
  "method": "session.send",
  "params": {
    "sessionId": "abc123",
    "prompt": "What is quantum entanglement?"
  },
  "id": 1
}

CLI 响应:

{
  "jsonrpc": "2.0",
  "result": {
    "data": {
      "content": "Quantum entanglement refers to a phenomenon where two or more quantum systems..."
    }
  },
  "id": 1
}

理解这一点很重要:SDK 不是“调用 LLM”,而是“调用 CLI”。CLI 已经封装了所有复杂性。

第二步:让 AI 实时响应——流式输出

为什么需要流式响应

使用 send_and_wait() 时,你必须等待 LLM 生成完整回答后才能看到任何输出。对于长文本生成(如代码解释、文档编写),用户可能等待 10-30 秒看到空白屏幕。

流式响应让 AI 像打字机一样逐字输出,提升用户体验的同时,也能提前发现模型是否“跑偏”。

事件监听机制

修改 main.py,启用流式输出:

import asyncio
import sys
from copilot import CopilotClient
from copilot.generated.session_events import SessionEventType

async def main():
    client = CopilotClient()
    await client.start()
    
    session = await client.create_session({
        "model": "gpt-4.1",
        "streaming": True,  # Enable streaming mode
    })
    
    # Listen for response deltas
    def handle_event(event):
        if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA:
            sys.stdout.write(event.data.delta_content)
            sys.stdout.flush()
        if event.type == SessionEventType.SESSION_IDLE:
            print()  # Newline when complete
    
    session.on(handle_event)
    
    await session.send_and_wait({"prompt": "Write a code example of quicksort"})

    await client.stop()

asyncio.run(main())

运行后,你会看到结果逐渐“流出”,而不是一次性显示。

事件驱动模型的设计哲学

SDK 使用观察者模式处理 CLI 的异步事件流:

CLI generates events → SDK parses → Dispatches to listeners → Your handle_event() executes

主要事件类型:

事件触发时机典型用途
ASSISTANT_MESSAGE_DELTA人工智能生成部分内容实时显示
ASSISTANT_MESSAGEAI 完成完整信息获取最终内容
SESSION_IDLE会话进入空闲状态标记任务完成
TOOL_CALL人工智能决定调用工具日志记录、权限检查

代码对比:同步 vs 流式

同步模式,适合短回答的场景:

response = await session.send_and_wait({"prompt": "1+1=?"})
print(response.data.content)  # Wait and print all at once

流式模式,适合长文本场景:

session.on(lambda event: 
    print(event.data.delta_content, end="") 
    if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA 
    else None
)
await session.send_and_wait({"prompt": "Write an article"})

背后的技术细节

流式响应基于 Server-Sent Events (SSE)WebSocket

  1. CLI 从 LLM 接收 token 流
  2. 每收到一个 token,CLI 向 SDK 发送一个 message_delta 事件
  3. SDK 触发你的事件监听器
  4. 用户立即看到新内容

这种设计让你的应用感知 AI 的“思考过程”,而不仅仅是最终结果。

第三步:赋予 AI 能力——自定义工具

工具的本质:让 LLM 调用你的代码

到目前为止,AI 只能 “说话”,无法与外部世界交互。工具(Tools) 是 Agent 的核心能力:你定义函数,AI 决定何时调用。

举个例子:

  1. 用户:“北京今天天气怎么样?”
  2. AI 思考:我需要天气数据 → 调用 get_weather(“Beijing”)
  3. 你的代码:返回 {“temperature”: “15°C”, “condition”: “sunny”}
  4. AI 合成:“北京今天晴朗,15°C。”

关键点:AI 自主决定是否调用工具,以及传递什么参数

工具定义三要素

一个工具包含:

  1. 描述(description):告诉 AI 这个工具的用途
  2. 参数模式(parameters):定义输入参数的结构(使用 Pydantic)
  3. 处理器(handler):实际执行的 Python 函数

完整天气助手示例

创建 weather_assistant.py

import asyncio
import random
import sys
from copilot import CopilotClient
from copilot.tools import define_tool
from copilot.generated.session_events import SessionEventType
from pydantic import BaseModel, Field

# 1. Define parameter schema
class GetWeatherParams(BaseModel):
    city: str = Field(description="City name, e.g., Beijing, Shanghai")

# 2. Define tool (description + handler)
@define_tool(description="Get current weather for a specified city")
async def get_weather(params: GetWeatherParams) -> dict:
    city = params.city
    
    # In production, call a real weather API here
    # Using mock data for demonstration
    conditions = ["sunny", "cloudy", "rainy", "overcast"]
    temp = random.randint(10, 30)
    condition = random.choice(conditions)
    
    return {
        "city": city,
        "temperature": f"{temp}°C",
        "condition": condition
    }

async def main():
    client = CopilotClient()
    await client.start()
    
    # 3. Pass tools to session
    session = await client.create_session({
        "model": "gpt-4.1",
        "streaming": True,
        "tools": [get_weather],  # Register tool
    })
    
    # Listen for streaming responses
    def handle_event(event):
        if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA:
            sys.stdout.write(event.data.delta_content)
            sys.stdout.flush()
        if event.type == SessionEventType.SESSION_IDLE:
            print("\n")
    
    session.on(handle_event)
    
    # Send a prompt that requires tool calls
    await session.send_and_wait({
        "prompt": "What's the weather like in Beijing and Shanghai? Compare them."
    })
    
    await client.stop()

asyncio.run(main())

运行:

python weather_assistant.py

执行流程详解

当你问“What’s the weather in Beijing and Shanghai”时:

  1. AI 分析问题 → 需要天气数据
  2. AI 检查可用工具 → 找到 get_weather 函数
  3. AI 决定调用 → get_weather(city=“Beijing”)
  4. SDK 触发处理程序 → 您的函数返回 {“temperature”: “22°C”, …}
  5. AI 收到结果 → 再次调用 get_weather(city=“Shanghai”)
  6. AI 合成答案 → " 北京晴,气温 22°C;上海阴,气温 18°C…”

AI 会自动调用工具多次(北京一次、上海一次),你无需编写任何循环逻辑。

参数模式的重要性

为什么用 Pydantic 定义参数?

class GetWeatherParams(BaseModel):
    city: str = Field(description="City name")
    unit: str = Field(default="celsius", description="Temperature unit: celsius or fahrenheit")

SDK 会将这个模式转换为 JSON Schema,传递给 LLM:

{
  "type": "object",
  "properties": {
    "city": {"type": "string", "description": "City name"},
    "unit": {"type": "string", "description": "Temperature unit"}
  },
  "required": ["city"]
}

LLM 根据这个 schema 提取参数。因此,描述越清晰,AI 调用越准确。

第四步:构建交互式助手

现在把所有能力组合起来:流式输出 + 工具调用 + 命令行交互。

完整可运行代码

创建 interactive_assistant.py

import asyncio
import random
import sys
from copilot import CopilotClient
from copilot.tools import define_tool
from copilot.generated.session_events import SessionEventType
from pydantic import BaseModel, Field

# Define tools
class GetWeatherParams(BaseModel):
    city: str = Field(description="City name, e.g., Beijing, Shanghai, Guangzhou")

@define_tool(description="Get current weather for a specified city")
async def get_weather(params: GetWeatherParams) -> dict:
    city = params.city
    conditions = ["sunny", "cloudy", "rainy", "overcast", "hazy"]
    temp = random.randint(5, 35)
    condition = random.choice(conditions)
    humidity = random.randint(30, 90)
    
    return {
        "city": city,
        "temperature": f"{temp}°C",
        "condition": condition,
        "humidity": f"{humidity}%"
    }

async def main():
    client = CopilotClient()
    await client.start()
    
    session = await client.create_session({
        "model": "gpt-4.1",
        "streaming": True,
        "tools": [get_weather],
    })
    
    # Event listeners
    def handle_event(event):
        if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA:
            sys.stdout.write(event.data.delta_content)
            sys.stdout.flush()
        if event.type == SessionEventType.SESSION_IDLE:
            print()  # Newline when complete
    
    session.on(handle_event)
    
    # Interactive conversation loop
    print("🌤️  Weather Assistant (type 'exit' to quit)")
    print("Try: 'What's the weather in Beijing?' or 'Compare weather in Guangzhou and Shenzhen'\n")
    
    while True:
        try:
            user_input = input("You: ")
        except EOFError:
            break
        
        if user_input.lower() in ["exit", "quit"]:
            break
        
        if not user_input.strip():
            continue
        
        sys.stdout.write("Assistant: ")
        await session.send_and_wait({"prompt": user_input})
        print()  # Extra newline
    
    await client.stop()
    print("Goodbye!")

asyncio.run(main())

运行效果

python interactive_assistant.py

示例对话:

🌤️  Weather Assistant (type 'exit' to quit)
Try: 'What's the weather in Beijing?' or 'Compare weather in Guangzhou and Shenzhen'

You: Compare weather in Guangzhou and Shenzhen
Assistant: Guangzhou: 21°C, sunny, 84% humidity.
Shenzhen: 33°C, hazy, 77% humidity.
Shenzhen is significantly warmer and hazier, while Guangzhou is cooler and sunnier with slightly higher humidity.

You: What's the weather in Shenzhen
Assistant: The weather in Shenzhen is 8°C, overcast, with 47% humidity.

You: quit
Goodbye!

关键设计点

1. 会话持久化

注意我们只创建了一次 session,整个对话循环中持续复用。这意味着:

  • AI 记住之前的对话内容
  • 可以追问 “What about tomorrow?"(AI 知道你指的是哪个城市)
  • 工具调用历史也被保留

2. 异步 I/O 的正确姿势

# Using input() in while True loop
user_input = input("You: ")  # Synchronous blocking, but acceptable here

# send_and_wait() is async
await session.send_and_wait({"prompt": user_input})

为什么 input() 的阻塞可以接受?因为我们在等待用户输入,而不是等待 I/O 操作。真正的异步发生在与 CLI 通信时。

3. 优雅退出

try:
    user_input = input("You: ")
except EOFError:  # Catch Ctrl+D
    break

处理 EOFError 和常见退出命令(exitquit),确保用户体验流畅。

扩展思路

基于这个框架,你可以快速扩展功能:

添加更多工具

@define_tool(description="Query real-time stock price")
async def get_stock_price(params): ...

@define_tool(description="Search information on the web")
async def web_search(params): ...

session = await client.create_session({
    "tools": [get_weather, get_stock_price, web_search],
})

AI 会根据用户问题自动选择合适的工具。

添加系统提示词

session = await client.create_session({
    "model": "gpt-4.1",
    "tools": [get_weather],
    "system_message": {
        "content": "You are a professional weather assistant. Keep answers concise but informative."
    }
})

记录工具调用日志

def handle_event(event):
    if event.type == SessionEventType.TOOL_CALL:
        print(f"\n[Debug] AI called tool: {event.data.tool_name}")
        print(f"[Debug] Arguments: {event.data.arguments}\n")

调试技巧

开发过程中,观察 CLI 的日志对于理解 Agent 的行为非常重要。

启动独立的 CLI Server

# 启动调试模式的 CLI Server
copilot --headless --log-level debug --port 9999

# 可选:指定日志目录
copilot --headless --log-level debug --port 9999 --log-dir ./logs

在代码中连接到 Server

client = CopilotClient({
    'cli_url': 'http://localhost:9999',
})
await client.start()  # 不会启动新进程,直接连接到已有 Server

查看日志

默认情况下,日志保存在 ~/.copilot/logs/ 目录中,每个 Server 进程都有独立的日志文件。使用 tail -f 实时观察:

tail -f ~/.copilot/logs/process-<timestamp>-<pid>.log

调试工具调用

def handle_event(event):
    # 工具调用开始
    if event.type == SessionEventType.TOOL_USER_REQUESTED:
        print(f"[Tool Call] {event.data.tool_name}")
        print(f"Arguments: {event.data.arguments}")
    
    # 工具执行结果
    if event.type == SessionEventType.TOOL_EXECUTION_COMPLETE:
        print(f"[Tool Result] {event.data.tool_name}")
        print(f"Result: {event.data.result}")
    
    # AI 的最终响应
    if event.type == SessionEventType.ASSISTANT_MESSAGE:
        print(f"[Assistant] {event.data.content[:100]}...")

session.on(handle_event)

这种模式让你清晰地看到整个工具调用链路:AI 决策 → 工具执行 → 结果返回 → 最终响应。

(转载本站文章请注明作者和出处乱世浮生,请勿用于任何商业用途)

comments powered by Disqus