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

picture.image

TL;DR

The core value of the GitHub Copilot SDK is not the convenience of "calling an LLM" (that's already been solved by the OpenAI SDK, LangChain, etc.), but rather providing a production-proven Agent runtime.

The problems it actually solves are:

  • Orchestration complexity: Planner, tool routing, and state management are built-in
  • Stability: Reliability guaranteed by millions of developers using it daily
  • Evolvability: New models and tool capabilities are automatically updated by the CLI

When you start building your next AI application, ask yourself two questions:

  1. Where is my core value? If it's in business logic and tool definitions, use the SDK; if it's in low-level orchestration innovation, build your own framework.
  2. How fast do you need to reach production? The SDK lets you skip 80% of the infrastructure work and focus on the last 20% of differentiated capability.

The barrier to Agent development has dropped, but the real challenge is: defining valuable tools, designing smooth interactions, and solving real problems. Technology is no longer the bottleneck — imagination is.

Introduction: Why Agent Development Is No Longer Just for Experts

In January 2026, GitHub released the Copilot SDK, marking a pivotal shift in AI Agent development from "expert territory" to "mainstream tooling."

Before this, building an AI Agent capable of autonomous planning, tool invocation, and file editing required you to:

  • Choose and integrate an LLM service (OpenAI, Anthropic, Azure...)
  • Build your own Agent orchestrator (planner, tool routing, state management)
  • Handle streaming output, error retries, and context management
  • Implement tool definition standards (function calling schema)

This process was complex and fragile. Open-source frameworks (LangChain, AutoGPT) lowered the barrier, but still required deep understanding of Agent runtime mechanics. The real turning point: GitHub opened up the production-grade Agent runtime from Copilot CLI as an SDK.

What does this mean? You can launch a complete Agent runtime with just 5 lines of code:

python
 体验AI代码助手
 代码解读
复制代码
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": "Explain quantum entanglement"})
    print(response.data.content)

asyncio.run(main())

No need to worry about model integration, prompt engineering, or response parsing — all of this has been battle-tested by Copilot CLI across millions of developers. You only need to define business logic; the SDK handles everything else.

Goal of this article: Through a complete weather assistant example, help you understand:

  1. How the SDK communicates with the CLI (the architectural essence)
  2. How the tool invocation mechanism works (how the LLM "decides" to call your code)
  3. The key leap from toy to tool (streaming responses, event listening, state management)

Whether you want to quickly validate an AI application idea or build a customized Agent for your enterprise, this article is the starting point.

Prerequisites: Setting Up Your Environment

Before writing any code, make sure your development environment meets the following requirements.

Prerequisites Checklist

1. Install the GitHub Copilot CLI

The SDK itself does not contain AI inference capabilities — it communicates with the Copilot CLI via JSON-RPC. The CLI is the real "engine"; the SDK is the "steering wheel."

bash
 体验AI代码助手
 代码解读
复制代码
# macOS/Linux
brew install copilot-cli

# Verify installation
copilot --version

2. Authenticate Your GitHub Account

bash
 体验AI代码助手
 代码解读
复制代码
copilot login

You need a GitHub Copilot subscription (individual or enterprise). If using BYOK (Bring Your Own Key) mode, you can skip this step.

Verify the Environment

Run the following command to confirm the CLI is working:

bash
 体验AI代码助手
 代码解读
复制代码
copilot -p "Explain recursion in one sentence"

If you see an AI response, the environment is ready.

Step 1: Send Your First Message

Install the SDK

Create a project directory and install the Python SDK:

bash
 体验AI代码助手
 代码解读
复制代码
mkdir copilot-demo && cd copilot-demo
# working in virtual env
python -m venv venv && source venv/bin/activate
pip install github-copilot-sdk

Minimal Code Example

Create main.py:

python
 体验AI代码助手
 代码解读
复制代码
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": "What is quantum entanglement?"})
    
    print(response.data.content)
    
    await client.stop()

asyncio.run(main())

Run it:

bash
 体验AI代码助手
 代码解读
复制代码
python main.py

You'll see the AI's complete response. With just 9 lines of code, a complete AI conversation is done.

Execution Flow Breakdown

What happens behind this code?

scss
 体验AI代码助手
 代码解读
复制代码
1. client.start()     → SDK launches the Copilot CLI process (runs in the background)
2. create_session()   → Requests the CLI to create a session via JSON-RPC
3. send_and_wait()    → Sends the prompt; the CLI forwards it to the LLM
4. LLM inference      → Response is returned to the SDK through the CLI
5. response.data      → SDK parses the JSON response and extracts the content

The Architectural Essence: The SDK Is the CLI's "Remote Control"

GitHub's design philosophy is separation of concerns:

ComponentResponsibility
Copilot CLIAgent runtime (planning, tool invocation, LLM communication)
SDKProcess management, JSON-RPC wrapper, event listening
Your codeBusiness logic and tool definitions

Advantages of this architecture:

  • Independent CLI upgrades: New models and tool capabilities don't require SDK changes
  • Low multi-language support cost: Each language SDK only needs to implement a JSON-RPC client
  • Debug-friendly: The CLI can run independently, making it easy to observe logs and troubleshoot

JSON-RPC Communication Example

When you call send_and_wait(), the actual request the SDK sends:

json
 体验AI代码助手
 代码解读
复制代码
{
  "jsonrpc": "2.0",
  "method": "session.send",
  "params": {
    "sessionId": "abc123",
    "prompt": "What is quantum entanglement?"
  },
  "id": 1
}

CLI response:

json
 体验AI代码助手
 代码解读
复制代码
{
  "jsonrpc": "2.0",
  "result": {
    "data": {
      "content": "Quantum entanglement refers to a phenomenon where two or more quantum systems..."
    }
  },
  "id": 1
}

Understanding this is important: The SDK is not "calling an LLM" — it's "calling the CLI." The CLI has already encapsulated all the complexity.

Step 2: Real-Time AI Responses — Streaming Output

Why Streaming Responses Are Needed

When using send_and_wait(), you must wait for the LLM to generate a complete response before seeing any output. For long-form generation (such as code explanations or documentation), users might stare at a blank screen for 10–30 seconds.

Streaming responses let the AI output text word by word, like a typewriter — improving user experience while also allowing you to catch early signs that the model is going off track.

Event Listening Mechanism

Modify main.py to enable streaming output:

python
 体验AI代码助手
 代码解读
复制代码
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())

After running it, you'll see results gradually "stream in" rather than appearing all at once.

The Design Philosophy of the Event-Driven Model

The SDK uses the Observer pattern to handle the asynchronous event stream from the CLI:

scss
 体验AI代码助手
 代码解读
复制代码
CLI generates events → SDK parses → Dispatches to listeners → Your handle_event() executes

Main event types:

EventTriggered WhenTypical Use
ASSISTANT_MESSAGE_DELTAAI generates partial contentReal-time display
ASSISTANT_MESSAGEAI completes a full messageGet final content
SESSION_IDLESession enters idle stateMark task complete
TOOL_CALLAI decides to invoke a toolLogging, auth check

Code Comparison: Synchronous vs. Streaming

Synchronous mode — suitable for short responses:

python
 体验AI代码助手
 代码解读
复制代码
response = await session.send_and_wait({"prompt": "1+1=?"})
print(response.data.content)  # Wait and print all at once

Streaming mode — suitable for long-form content:

python
 体验AI代码助手
 代码解读
复制代码
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"})

Technical Details Under the Hood

Streaming responses are based on Server-Sent Events (SSE) or WebSocket:

  1. The CLI receives a token stream from the LLM
  2. For each token received, the CLI sends a message_delta event to the SDK
  3. The SDK triggers your event listener
  4. The user immediately sees new content

This design lets your application perceive the AI's "thinking process" , not just the final result.

Step 3: Giving the AI Capabilities — Custom Tools

The Essence of Tools: Letting the LLM Call Your Code

Up to now, the AI can only "talk" — it cannot interact with the outside world. Tools are the core capability of an Agent: you define functions, and the AI decides when to call them.

For example:

  1. User: "What's the weather in Beijing today?"
  2. AI thinks: I need weather data → call get_weather("Beijing")
  3. Your code: returns {"temperature": "15°C", "condition": "sunny"}
  4. AI synthesizes: "Beijing is sunny today, 15°C."

Key point: The AI autonomously decides whether to call a tool and what parameters to pass.

Three Elements of a Tool Definition

A tool contains:

  1. Description: Tells the AI what this tool does
  2. Parameter schema: Defines the structure of input parameters (using Pydantic)
  3. Handler: The Python function that actually executes

Complete Weather Assistant Example

Create weather_assistant.py:

python
 体验AI代码助手
 代码解读
复制代码
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())

Run:

bash
 体验AI代码助手
 代码解读
复制代码
python weather_assistant.py

Execution Flow Explained

When you ask "What's the weather in Beijing and Shanghai":

  1. AI analyzes the question → weather data needed
  2. AI checks available tools → finds the get_weather function
  3. AI decides to call → get_weather(city="Beijing")
  4. SDK triggers the handler → your function returns {"temperature": "22°C", ...}
  5. AI receives the result → calls get_weather(city="Shanghai") again
  6. AI synthesizes the answer → "Beijing is sunny at 22°C; Shanghai is overcast at 18°C..."

The AI will automatically call the tool multiple times (once for Beijing, once for Shanghai) — you don't need to write any loop logic.

Why the Parameter Schema Matters

Why define parameters with Pydantic?

python
 体验AI代码助手
 代码解读
复制代码
class GetWeatherParams(BaseModel):
    city: str = Field(description="City name")
    unit: str = Field(default="celsius", description="Temperature unit: celsius or fahrenheit")

The SDK converts this schema to JSON Schema and passes it to the LLM:

json
 体验AI代码助手
 代码解读
复制代码
{
  "type": "object",
  "properties": {
    "city": {"type": "string", "description": "City name"},
    "unit": {"type": "string", "description": "Temperature unit"}
  },
  "required": ["city"]
}

The LLM extracts parameters based on this schema. Therefore, the clearer the description, the more accurately the AI will invoke the tool.

Step 4: Building an Interactive Assistant

Now let's combine all the capabilities: streaming output + tool invocation + command-line interaction.

Complete Runnable Code

Create interactive_assistant.py: https://588ku.com/search-sucai?word=关于北京159.1415.8529北京开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于天津159.1415.8529天津开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于上海159.1415.8529上海开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于重庆159.1415.8529重庆开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于太原159.1415.8529太原开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于石家庄159.1415.8529石家庄开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于呼和浩特159.1415.8529呼和浩特开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于沈阳159.1415.8529沈阳开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于吉林159.1415.8529吉林开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于长春159.1415.8529长春开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于哈尔滨159.1415.8529哈尔滨开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于南京159.1415.8529南京开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于苏州159.1415.8529苏州开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于杭州159.1415.8529杭州开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于合肥159.1415.8529合肥开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于厦门159.1415.8529厦门开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于福州159.1415.8529福州开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于南昌159.1415.8529南昌开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于济南159.1415.8529济南开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于青岛159.1415.8529青岛开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于郑州159.1415.8529郑州开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于武汉159.1415.8529武汉开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于长沙159.1415.8529长沙开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于深圳159.1415.8529深圳开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于广州159.1415.8529广州开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于东莞159.1415.8529东莞开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于南宁159.1415.8529南宁开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于海口159.1415.8529海口开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于三亚159.1415.8529三亚开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于成都159.1415.8529成都开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于贵阳159.1415.8529贵阳开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于昆明159.1415.8529昆明开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于拉萨159.1415.8529拉萨开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于西安159.1415.8529西安开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于兰州开具施工费住宿餐饮发票兰州开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于西宁159.1415.8529西宁开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于银川159.1415.8529银川开具机械设备发票‖第一财经.html https://588ku.com/search-sucai?word=关于乌鲁木齐159.1415.8529乌鲁木齐开具机械设备发票‖第一财经.html

python
 体验AI代码助手
 代码解读
复制代码
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())

Sample Output

bash
 体验AI代码助手
 代码解读
复制代码
python interactive_assistant.py

Example conversation:

vbnet
 体验AI代码助手
 代码解读
复制代码
🌤️  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!

Key Design Considerations

1. Session Persistence

Notice that we create the session only once, and reuse it throughout the entire conversation loop. This means:

  • The AI remembers previous conversation content
  • Follow-up questions like "What about tomorrow?" work (the AI knows which city you mean)
  • Tool call history is also retained

2. Async I/O Done Right

python
 体验AI代码助手
 代码解读
复制代码
# 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})

Why is input() blocking acceptable here? Because we're waiting for user input, not an I/O operation. The real async behavior happens when communicating with the CLI.

3. Graceful Exit

python
 体验AI代码助手
 代码解读
复制代码
try:
    user_input = input("You: ")
except EOFError:  # Catch Ctrl+D
    break

Handling EOFError and common exit commands (exit, quit) ensures a smooth user experience.

Extension Ideas

Based on this framework, you can quickly extend functionality:

Add more tools:

python
 体验AI代码助手
 代码解读
复制代码
@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],
})

The AI will automatically select the appropriate tool based on the user's question.

Add a system prompt:

python
 体验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."
    }
})

Log tool calls:

python
 体验AI代码助手
 代码解读
复制代码
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")

Debugging Tips

During development, observing CLI logs is crucial for understanding Agent behavior.

Start a standalone CLI server:

bash
 体验AI代码助手
 代码解读
复制代码
# Start CLI server in debug mode
copilot --headless --log-level debug --port 9999

# Optional: specify log directory
copilot --headless --log-level debug --port 9999 --log-dir ./logs

Connect from your code:

python
 体验AI代码助手
 代码解读
复制代码
client = CopilotClient({
    'cli_url': 'http://localhost:9999',
})
await client.start()  # Connects directly without starting a new process

View logs:

By default, logs are saved in ~/.copilot/logs/, with an independent log file for each server process. Use tail -f to monitor in real time:

bash
 体验AI代码助手
 代码解读
复制代码
tail -f ~/.copilot/logs/process-<timestamp>-<pid>.log

Debug tool calls:

python
 体验AI代码助手
 代码解读
复制代码
def handle_event(event):
    # Tool call starts
    if event.type == SessionEventType.TOOL_USER_REQUESTED:
        print(f"[Tool Call] {event.data.tool_name}")
        print(f"Arguments: {event.data.arguments}")
    
    # Tool execution result
    if event.type == SessionEventType.TOOL_EXECUTION_COMPLETE:
        print(f"[Tool Result] {event.data.tool_name}")
        print(f"Result: {event.data.result}")
    
    # AI's final response
    if event.type == SessionEventType.ASSISTANT_MESSAGE:
        print(f"[Assistant] {event.data.content[:100]}...")

session.on(handle_event)

This pattern gives you a clear view of the entire tool call chain: AI decision → tool execution → result return → final response.

0
0
0
0
评论
未登录
暂无评论