Что вы создадите:
You: What's the weather like in Seattle?
Copilot: Let me check the weather for Seattle...
Currently 62°F and cloudy with a chance of rain.
Typical Seattle weather!
You: How about Tokyo?
Copilot: In Tokyo it's 75°F and sunny. Great day to be outside!
Необходимые условия
Прежде чем начать, убедитесь, что у вас есть:
- GitHub Copilot CLI установлен и аутентифицирован (Руководство по установке)
- Ваш предпочтительный языковой режим выполнения:
- Node.js 20+ или Python 3.11+ или Go 1.24+ или Rust 1.94+ или Java 17+ или .NET 8.0+
Убедитесь, что CLI работает:
copilot --version
Шаг 1: установка SDK
Сначала создайте новый каталог и инициализуйте проект:
mkdir copilot-demo && cd copilot-demo
npm init -y --init-type module
Затем установите SDK и TypeScript runner:
npm install @github/copilot-sdk tsx
pip install github-copilot-sdk
Во-первых, создайте новую директорию и инициализуйте модуль:
mkdir copilot-demo && cd copilot-demo
go mod init copilot-demo
Затем установите SDK:
go get github.com/github/copilot-sdk/go
Сначала создайте новый двоичный ящик:
cargo new copilot-demo && cd copilot-demo
Затем установите SDK и прямые зависимости, используемые в примерах:
cargo add github-copilot-sdk --features derive
# Used by #[tokio::main] and tokio::spawn
cargo add tokio --features rt-multi-thread,macros
# Used by custom-tool parameter derives later in this guide
cargo add serde --features derive
cargo add schemars
Во-первых, создайте новый консольный проект:
dotnet new console -n CopilotDemo && cd CopilotDemo
Затем добавьте SDK:
dotnet add package GitHub.Copilot.SDK
Сначала создайте новый каталог и инициализуйте свой проект.
Maven — добавьте к вашему pom.xml:
<dependency>
<groupId>com.github</groupId>
<artifactId>copilot-sdk-java</artifactId>
<version>${copilot.sdk.version}</version>
</dependency>
Gradle — добавьте к вашему build.gradle:
implementation 'com.github:copilot-sdk-java:${copilotSdkVersion}'
Шаг 2: отправьте первое сообщение
Создайте новый файл и добавьте следующий код. Это самый простой способ использовать SDK — около 5 строк кода.
Создайте index.ts:
import { CopilotClient } from "@github/copilot-sdk";
const client = new CopilotClient();
const session = await client.createSession({ model: "gpt-4.1" });
const response = await session.sendAndWait({ prompt: "What is 2 + 2?" });
console.log(response?.data.content);
await client.stop();
process.exit(0);
Запустите его:
npx tsx index.ts
Создайте main.py:
import asyncio
from copilot import CopilotClient
from copilot.session import PermissionHandler
async def main():
client = CopilotClient()
await client.start()
session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model="gpt-4.1")
response = await session.send_and_wait("What is 2 + 2?")
print(response.data.content)
await client.stop()
asyncio.run(main())
Запустите его:
python main.py
Создайте main.go:
package main
import (
"context"
"fmt"
"log"
"os"
copilot "github.com/github/copilot-sdk/go"
)
func main() {
ctx := context.Background()
client := copilot.NewClient(nil)
if err := client.Start(ctx); err != nil {
log.Fatal(err)
}
defer client.Stop()
session, err := client.CreateSession(ctx, &copilot.SessionConfig{Model: "gpt-4.1"})
if err != nil {
log.Fatal(err)
}
response, err := session.SendAndWait(ctx, copilot.MessageOptions{Prompt: "What is 2 + 2?"})
if err != nil {
log.Fatal(err)
}
if d, ok := response.Data.(*copilot.AssistantMessageData); ok {
fmt.Println(d.Content)
}
os.Exit(0)
}
Запустите его:
go run main.go
Создайте src/main.rs:
use std::sync::Arc;
use std::time::Duration;
use github_copilot_sdk::handler::ApproveAllHandler;
use github_copilot_sdk::{Client, ClientOptions, MessageOptions, SessionConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::start(ClientOptions::default()).await?;
let session = client
.create_session(SessionConfig::default().with_handler(Arc::new(ApproveAllHandler)))
.await?;
let response = session
.send_and_wait(
MessageOptions::new("What is 2 + 2?").with_wait_timeout(Duration::from_secs(120)),
)
.await?;
if let Some(event) = response {
if let Some(content) = event.data.get("content").and_then(|value| value.as_str()) {
println!("{content}");
}
}
session.disconnect().await?;
client.stop().await?;
Ok(())
}
Запустите его:
cargo run
Создайте новый консольный проект и добавьте его в Program.cs:
using GitHub.Copilot;
await using var client = new CopilotClient();
await using var session = await client.CreateSessionAsync(new SessionConfig
{
Model = "gpt-4.1",
OnPermissionRequest = PermissionHandler.ApproveAll
});
var response = await session.SendAndWaitAsync(new MessageOptions { Prompt = "What is 2 + 2?" });
Console.WriteLine(response?.Data.Content);
Запустите его:
dotnet run
Создайте HelloCopilot.java:
import com.github.copilot.sdk.CopilotClient;
import com.github.copilot.sdk.events.*;
import com.github.copilot.sdk.json.*;
public class HelloCopilot {
public static void main(String[] args) throws Exception {
try (var client = new CopilotClient()) {
client.start().get();
var session = client.createSession(
new SessionConfig()
.setModel("gpt-4.1")
.setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
).get();
var response = session.sendAndWait(
new MessageOptions().setPrompt("What is 2 + 2?")
).get();
System.out.println(response.getData().content());
client.stop().get();
}
}
}
Запустите его:
javac -cp copilot-sdk.jar HelloCopilot.java && java -cp .:copilot-sdk.jar HelloCopilot
Вам стоит увидеть:
4
Поздравляем! Вы только что создали своё первое приложение на базе Copilot.
Шаг 3: добавляйте ответы на поток
Сейчас вы ждёте полного ответа, прежде чем что-либо увидеть. Давайте сделаем это интерактивным, транслируя ответы по мере их генерации.
Обновление index.ts:
import { CopilotClient } from "@github/copilot-sdk";
const client = new CopilotClient();
const session = await client.createSession({
model: "gpt-4.1",
streaming: true,
});
// Listen for response chunks
session.on("assistant.message_delta", (event) => {
process.stdout.write(event.data.deltaContent);
});
session.on("session.idle", () => {
console.log(); // New line when done
});
await session.sendAndWait({ prompt: "Tell me a short joke" });
await client.stop();
process.exit(0);
Обновление main.py:
import asyncio
import sys
from copilot import CopilotClient
from copilot.session import PermissionHandler
from copilot.generated.session_events import SessionEventType
async def main():
client = CopilotClient()
await client.start()
session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model="gpt-4.1", streaming=True)
# Listen for response chunks
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() # New line when done
session.on(handle_event)
await session.send_and_wait("Tell me a short joke")
await client.stop()
asyncio.run(main())
Обновление main.go:
package main
import (
"context"
"fmt"
"log"
"os"
copilot "github.com/github/copilot-sdk/go"
)
func main() {
ctx := context.Background()
client := copilot.NewClient(nil)
if err := client.Start(ctx); err != nil {
log.Fatal(err)
}
defer client.Stop()
session, err := client.CreateSession(ctx, &copilot.SessionConfig{
Model: "gpt-4.1",
Streaming: copilot.Bool(true),
})
if err != nil {
log.Fatal(err)
}
// Listen for response chunks
session.On(func(event copilot.SessionEvent) {
switch d := event.Data.(type) {
case *copilot.AssistantMessageDeltaData:
fmt.Print(d.DeltaContent)
case *copilot.SessionIdleData:
_ = d
fmt.Println()
}
})
_, err = session.SendAndWait(ctx, copilot.MessageOptions{Prompt: "Tell me a short joke"})
if err != nil {
log.Fatal(err)
}
os.Exit(0)
}
Обновление src/main.rs:
use std::io::{self, Write};
use std::sync::Arc;
use std::time::Duration;
use github_copilot_sdk::handler::ApproveAllHandler;
use github_copilot_sdk::{Client, ClientOptions, MessageOptions, SessionConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::start(ClientOptions::default()).await?;
let mut config = SessionConfig::default();
config.streaming = Some(true);
let session = client
.create_session(config.with_handler(Arc::new(ApproveAllHandler)))
.await?;
// Listen for response chunks
let mut events = session.subscribe();
tokio::spawn(async move {
while let Ok(event) = events.recv().await {
match event.event_type.as_str() {
"assistant.message_delta" => {
if let Some(text) =
event.data.get("deltaContent").and_then(|value| value.as_str())
{
print!("{text}");
io::stdout().flush().ok();
}
}
"assistant.message" => println!(),
_ => {}
}
}
});
session
.send_and_wait(
MessageOptions::new("Tell me a short joke")
.with_wait_timeout(Duration::from_secs(120)),
)
.await?;
session.disconnect().await?;
client.stop().await?;
Ok(())
}
Обновление Program.cs:
using GitHub.Copilot;
await using var client = new CopilotClient();
await using var session = await client.CreateSessionAsync(new SessionConfig
{
Model = "gpt-4.1",
OnPermissionRequest = PermissionHandler.ApproveAll,
Streaming = true,
});
// Listen for response chunks
session.On<SessionEvent>(ev =>
{
if (ev is AssistantMessageDeltaEvent deltaEvent)
{
Console.Write(deltaEvent.Data.DeltaContent);
}
if (ev is SessionIdleEvent)
{
Console.WriteLine();
}
});
await session.SendAndWaitAsync(new MessageOptions { Prompt = "Tell me a short joke" });
Обновление HelloCopilot.java:
import com.github.copilot.sdk.CopilotClient;
import com.github.copilot.sdk.events.*;
import com.github.copilot.sdk.json.*;
public class HelloCopilot {
public static void main(String[] args) throws Exception {
try (var client = new CopilotClient()) {
client.start().get();
var session = client.createSession(
new SessionConfig()
.setModel("gpt-4.1")
.setStreaming(true)
.setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
).get();
// Listen for response chunks
session.on(AssistantMessageDeltaEvent.class, delta -> {
System.out.print(delta.getData().deltaContent());
});
session.on(SessionIdleEvent.class, idle -> {
System.out.println(); // New line when done
});
session.sendAndWait(
new MessageOptions().setPrompt("Tell me a short joke")
).get();
client.stop().get();
}
}
}
Запусти код ещё раз. Вы увидите, как ответ будет появляться слово за словом.
Методы подписки на события
SDK предоставляет методы подписки на события сессии:
| Метод | Description |
|---|---|
on(handler) | Подписывайтесь на все события; Возвращает функцию отписки |
on(eventType, handler) | Оформите подписку на конкретный тип события (Node.js/TypeScript); Возвращает функцию отписки |
subscribe() | Подписывайтесь на все события (Rust); Фильтр по event_type |
// Subscribe to all events
const unsubscribeAll = session.on((event) => {
console.log("Event:", event.type);
});
// Subscribe to specific event type
const unsubscribeIdle = session.on("session.idle", (event) => {
console.log("Session is idle");
});
// Later, to unsubscribe:
unsubscribeAll();
unsubscribeIdle();
from copilot import CopilotClient, PermissionDecisionApproveOnce
from copilot.generated.session_events import SessionEvent, SessionEventType
client = CopilotClient()
session = await client.create_session(on_permission_request=lambda req, inv: PermissionDecisionApproveOnce())
# Subscribe to all events
unsubscribe = session.on(lambda event: print(f"Event: {event.type}"))
# Filter by event type in your handler
def handle_event(event: SessionEvent) -> None:
if event.type == SessionEventType.SESSION_IDLE:
print("Session is idle")
elif event.type == SessionEventType.ASSISTANT_MESSAGE:
print(f"Message: {event.data.content}")
unsubscribe = session.on(handle_event)
# Later, to unsubscribe:
unsubscribe()
# Subscribe to all events
unsubscribe = session.on(lambda event: print(f"Event: {event.type}"))
# Filter by event type in your handler
def handle_event(event):
if event.type == SessionEventType.SESSION_IDLE:
print("Session is idle")
elif event.type == SessionEventType.ASSISTANT_MESSAGE:
print(f"Message: {event.data.content}")
unsubscribe = session.on(handle_event)
# Later, to unsubscribe:
unsubscribe()
package main
import (
"fmt"
copilot "github.com/github/copilot-sdk/go"
)
func main() {
session := &copilot.Session{}
// Subscribe to all events
unsubscribe := session.On(func(event copilot.SessionEvent) {
fmt.Println("Event:", event.Type)
})
// Filter by event type in your handler
session.On(func(event copilot.SessionEvent) {
switch d := event.Data.(type) {
case *copilot.SessionIdleData:
_ = d
fmt.Println("Session is idle")
case *copilot.AssistantMessageData:
fmt.Println("Message:", d.Content)
}
})
// Later, to unsubscribe:
unsubscribe()
}
// Subscribe to all events
unsubscribe := session.On(func(event copilot.SessionEvent) {
fmt.Println("Event:", event.Type)
})
// Filter by event type in your handler
session.On(func(event copilot.SessionEvent) {
switch d := event.Data.(type) {
case *copilot.SessionIdleData:
_ = d
fmt.Println("Session is idle")
case *copilot.AssistantMessageData:
fmt.Println("Message:", d.Content)
}
})
// Later, to unsubscribe:
unsubscribe()
let mut events = session.subscribe();
tokio::spawn(async move {
while let Ok(event) = events.recv().await {
println!("Event: {}", event.event_type);
match event.event_type.as_str() {
"session.idle" => println!("Session is idle"),
"assistant.message" => {
if let Some(content) = event.data.get("content").and_then(|value| value.as_str()) {
println!("Message: {content}");
}
}
_ => {}
}
}
});
using GitHub.Copilot;
public static class EventSubscriptionExample
{
public static void Example(CopilotSession session)
{
// Subscribe to all events
var unsubscribe = session.On<SessionEvent>(ev => Console.WriteLine($"Event: {ev.Type}"));
// Filter by event type using pattern matching
session.On<SessionEvent>(ev =>
{
switch (ev)
{
case SessionIdleEvent:
Console.WriteLine("Session is idle");
break;
case AssistantMessageEvent msg:
Console.WriteLine($"Message: {msg.Data.Content}");
break;
}
});
// Later, to unsubscribe:
unsubscribe.Dispose();
}
}
// Subscribe to all events
var unsubscribe = session.On<SessionEvent>(ev => Console.WriteLine($"Event: {ev.Type}"));
// Filter by event type using pattern matching
session.On<SessionEvent>(ev =>
{
switch (ev)
{
case SessionIdleEvent:
Console.WriteLine("Session is idle");
break;
case AssistantMessageEvent msg:
Console.WriteLine($"Message: {msg.Data.Content}");
break;
}
});
// Later, to unsubscribe:
unsubscribe.Dispose();
// Subscribe to all events
var unsubscribe = session.on(event -> {
System.out.println("Event: " + event.getType());
});
// Subscribe to a specific event type
session.on(AssistantMessageEvent.class, msg -> {
System.out.println("Message: " + msg.getData().content());
});
session.on(SessionIdleEvent.class, idle -> {
System.out.println("Session is idle");
});
// Later, to unsubscribe:
unsubscribe.close();
Шаг 4: добавить кастомный инструмент
А теперь о сильной части. Давайте дадим Copilot возможность вызывать ваш код, определив кастомный инструмент. Мы создадим простой инструмент для поиска погоды.
Обновление index.ts:
import { CopilotClient, defineTool } from "@github/copilot-sdk";
// Define a tool that Copilot can call
const getWeather = defineTool("get_weather", {
description: "Get the current weather for a city",
parameters: {
type: "object",
properties: {
city: { type: "string", description: "The city name" },
},
required: ["city"],
},
handler: async (args: { city: string }) => {
const { city } = args;
// In a real app, you'd call a weather API here
const conditions = ["sunny", "cloudy", "rainy", "partly cloudy"];
const temp = Math.floor(Math.random() * 30) + 50;
const condition = conditions[Math.floor(Math.random() * conditions.length)];
return { city, temperature: `${temp}°F`, condition };
},
});
const client = new CopilotClient();
const session = await client.createSession({
model: "gpt-4.1",
streaming: true,
tools: [getWeather],
});
session.on("assistant.message_delta", (event) => {
process.stdout.write(event.data.deltaContent);
});
session.on("session.idle", () => {
console.log(); // New line when done
});
await session.sendAndWait({
prompt: "What's the weather like in Seattle and Tokyo?",
});
await client.stop();
process.exit(0);
Обновление main.py:
import asyncio
import random
import sys
from copilot import CopilotClient
from copilot.session import PermissionHandler
from copilot.tools import define_tool
from copilot.generated.session_events import SessionEventType
from pydantic import BaseModel, Field
# Define the parameters for the tool using Pydantic
class GetWeatherParams(BaseModel):
city: str = Field(description="The name of the city to get weather for")
# Define a tool that Copilot can call
@define_tool(description="Get the current weather for a city")
async def get_weather(params: GetWeatherParams) -> dict:
city = params.city
# In a real app, you'd call a weather API here
conditions = ["sunny", "cloudy", "rainy", "partly cloudy"]
temp = random.randint(50, 80)
condition = random.choice(conditions)
return {"city": city, "temperature": f"{temp}°F", "condition": condition}
async def main():
client = CopilotClient()
await client.start()
session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model="gpt-4.1", streaming=True, tools=[get_weather])
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()
session.on(handle_event)
await session.send_and_wait("What's the weather like in Seattle and Tokyo?")
await client.stop()
asyncio.run(main())
Обновление main.go:
package main
import (
"context"
"fmt"
"log"
"math/rand"
"os"
copilot "github.com/github/copilot-sdk/go"
)
// Define the parameter type
type WeatherParams struct {
City string `json:"city" jsonschema:"The city name"`
}
// Define the return type
type WeatherResult struct {
City string `json:"city"`
Temperature string `json:"temperature"`
Condition string `json:"condition"`
}
func main() {
ctx := context.Background()
// Define a tool that Copilot can call
getWeather := copilot.DefineTool(
"get_weather",
"Get the current weather for a city",
func(params WeatherParams, inv copilot.ToolInvocation) (WeatherResult, error) {
// In a real app, you'd call a weather API here
conditions := []string{"sunny", "cloudy", "rainy", "partly cloudy"}
temp := rand.Intn(30) + 50
condition := conditions[rand.Intn(len(conditions))]
return WeatherResult{
City: params.City,
Temperature: fmt.Sprintf("%d°F", temp),
Condition: condition,
}, nil
},
)
client := copilot.NewClient(nil)
if err := client.Start(ctx); err != nil {
log.Fatal(err)
}
defer client.Stop()
session, err := client.CreateSession(ctx, &copilot.SessionConfig{
Model: "gpt-4.1",
Streaming: copilot.Bool(true),
Tools: []copilot.Tool{getWeather},
})
if err != nil {
log.Fatal(err)
}
session.On(func(event copilot.SessionEvent) {
switch d := event.Data.(type) {
case *copilot.AssistantMessageDeltaData:
fmt.Print(d.DeltaContent)
case *copilot.SessionIdleData:
_ = d
fmt.Println()
}
})
_, err = session.SendAndWait(ctx, copilot.MessageOptions{
Prompt: "What's the weather like in Seattle and Tokyo?",
})
if err != nil {
log.Fatal(err)
}
os.Exit(0)
}
Обновление src/main.rs:
use std::io::{self, Write};
use std::sync::Arc;
use std::time::Duration;
use github_copilot_sdk::handler::ApproveAllHandler;
use github_copilot_sdk::tool::{JsonSchema, ToolHandlerRouter, define_tool};
use github_copilot_sdk::{Client, ClientOptions, MessageOptions, SessionConfig, ToolResult};
use serde::Deserialize;
#[derive(Deserialize, JsonSchema)]
struct GetWeatherParams {
city: String,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Define a tool that Copilot can call
let router = ToolHandlerRouter::new(
vec![define_tool(
"get_weather",
"Get the current weather for a city",
|_inv, params: GetWeatherParams| async move {
Ok(ToolResult::Text(format!(
"{}: 62°F and sunny",
params.city
)))
},
)],
Arc::new(ApproveAllHandler),
);
let tools = router.tools();
let client = Client::start(ClientOptions::default()).await?;
let mut config = SessionConfig::default();
config.streaming = Some(true);
config.tools = Some(tools);
let session = client.create_session(config.with_handler(Arc::new(router))).await?;
let mut events = session.subscribe();
tokio::spawn(async move {
while let Ok(event) = events.recv().await {
match event.event_type.as_str() {
"assistant.message_delta" => {
if let Some(text) =
event.data.get("deltaContent").and_then(|value| value.as_str())
{
print!("{text}");
io::stdout().flush().ok();
}
}
"assistant.message" => println!(),
_ => {}
}
}
});
session
.send_and_wait(
MessageOptions::new("What's the weather like in Seattle and Tokyo?")
.with_wait_timeout(Duration::from_secs(120)),
)
.await?;
session.disconnect().await?;
client.stop().await?;
Ok(())
}
Обновление Program.cs:
using GitHub.Copilot;
using Microsoft.Extensions.AI;
using System.ComponentModel;
await using var client = new CopilotClient();
// Define a tool that Copilot can call
var getWeather = CopilotTool.DefineTool(
([Description("The city name")] string city) =>
{
// In a real app, you'd call a weather API here
var conditions = new[] { "sunny", "cloudy", "rainy", "partly cloudy" };
var temp = Random.Shared.Next(50, 80);
var condition = conditions[Random.Shared.Next(conditions.Length)];
return new { city, temperature = $"{temp}°F", condition };
},
factoryOptions: new AIFunctionFactoryOptions
{
Name = "get_weather",
Description = "Get the current weather for a city",
}
);
await using var session = await client.CreateSessionAsync(new SessionConfig
{
Model = "gpt-4.1",
OnPermissionRequest = PermissionHandler.ApproveAll,
Streaming = true,
Tools = [getWeather],
});
session.On<SessionEvent>(ev =>
{
if (ev is AssistantMessageDeltaEvent deltaEvent)
{
Console.Write(deltaEvent.Data.DeltaContent);
}
if (ev is SessionIdleEvent)
{
Console.WriteLine();
}
});
await session.SendAndWaitAsync(new MessageOptions
{
Prompt = "What's the weather like in Seattle and Tokyo?",
});
Обновление HelloCopilot.java:
import com.github.copilot.sdk.CopilotClient;
import com.github.copilot.sdk.events.*;
import com.github.copilot.sdk.json.*;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
public class HelloCopilot {
public static void main(String[] args) throws Exception {
var random = new Random();
var conditions = List.of("sunny", "cloudy", "rainy", "partly cloudy");
// Define a tool that Copilot can call
var getWeather = ToolDefinition.create(
"get_weather",
"Get the current weather for a city",
Map.of(
"type", "object",
"properties", Map.of(
"city", Map.of("type", "string", "description", "The city name")
),
"required", List.of("city")
),
invocation -> {
var city = (String) invocation.getArguments().get("city");
var temp = random.nextInt(30) + 50;
var condition = conditions.get(random.nextInt(conditions.size()));
return CompletableFuture.completedFuture(Map.of(
"city", city,
"temperature", temp + "°F",
"condition", condition
));
}
);
try (var client = new CopilotClient()) {
client.start().get();
var session = client.createSession(
new SessionConfig()
.setModel("gpt-4.1")
.setStreaming(true)
.setTools(List.of(getWeather))
.setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
).get();
session.on(AssistantMessageDeltaEvent.class, delta -> {
System.out.print(delta.getData().deltaContent());
});
session.on(SessionIdleEvent.class, idle -> {
System.out.println();
});
session.sendAndWait(
new MessageOptions().setPrompt("What's the weather like in Seattle and Tokyo?")
).get();
client.stop().get();
}
}
}
Запустите его, и вы увидите, как Copilot позвонит в ваш инструмент для получения погодных данных, а затем ответит с результатами!
Шаг 5: создайте интерактивного помощника
Давайте объединим всё это в полезного интерактивного помощника:
import { CopilotClient, defineTool } from "@github/copilot-sdk";
import * as readline from "readline";
const getWeather = defineTool("get_weather", {
description: "Get the current weather for a city",
parameters: {
type: "object",
properties: {
city: { type: "string", description: "The city name" },
},
required: ["city"],
},
handler: async ({ city }) => {
const conditions = ["sunny", "cloudy", "rainy", "partly cloudy"];
const temp = Math.floor(Math.random() * 30) + 50;
const condition = conditions[Math.floor(Math.random() * conditions.length)];
return { city, temperature: `${temp}°F`, condition };
},
});
const client = new CopilotClient();
const session = await client.createSession({
model: "gpt-4.1",
streaming: true,
tools: [getWeather],
});
session.on("assistant.message_delta", (event) => {
process.stdout.write(event.data.deltaContent);
});
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
console.log("🌤️ Weather Assistant (type 'exit' to quit)");
console.log(" Try: 'What's the weather in Paris?'\n");
const prompt = () => {
rl.question("You: ", async (input) => {
if (input.toLowerCase() === "exit") {
await client.stop();
rl.close();
return;
}
process.stdout.write("Assistant: ");
await session.sendAndWait({ prompt: input });
console.log("\n");
prompt();
});
};
prompt();
Проведите с:
npx tsx weather-assistant.ts
Создайте weather_assistant.py:
import asyncio
import random
import sys
from copilot import CopilotClient
from copilot.session import PermissionHandler
from copilot.tools import define_tool
from copilot.generated.session_events import SessionEventType
from pydantic import BaseModel, Field
class GetWeatherParams(BaseModel):
city: str = Field(description="The name of the city to get weather for")
@define_tool(description="Get the current weather for a city")
async def get_weather(params: GetWeatherParams) -> dict:
city = params.city
conditions = ["sunny", "cloudy", "rainy", "partly cloudy"]
temp = random.randint(50, 80)
condition = random.choice(conditions)
return {"city": city, "temperature": f"{temp}°F", "condition": condition}
async def main():
client = CopilotClient()
await client.start()
session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model="gpt-4.1", streaming=True, tools=[get_weather])
def handle_event(event):
if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA:
sys.stdout.write(event.data.delta_content)
sys.stdout.flush()
session.on(handle_event)
print("🌤️ Weather Assistant (type 'exit' to quit)")
print(" Try: 'What's the weather in Paris?' or 'Compare weather in NYC and LA'\n")
while True:
try:
user_input = input("You: ")
except EOFError:
break
if user_input.lower() == "exit":
break
sys.stdout.write("Assistant: ")
await session.send_and_wait(user_input)
print("\n")
await client.stop()
asyncio.run(main())
Проведите с:
python weather_assistant.py
Создайте weather-assistant.go:
package main
import (
"bufio"
"context"
"fmt"
"log"
"math/rand"
"os"
"strings"
copilot "github.com/github/copilot-sdk/go"
)
type WeatherParams struct {
City string `json:"city" jsonschema:"The city name"`
}
type WeatherResult struct {
City string `json:"city"`
Temperature string `json:"temperature"`
Condition string `json:"condition"`
}
func main() {
ctx := context.Background()
getWeather := copilot.DefineTool(
"get_weather",
"Get the current weather for a city",
func(params WeatherParams, inv copilot.ToolInvocation) (WeatherResult, error) {
conditions := []string{"sunny", "cloudy", "rainy", "partly cloudy"}
temp := rand.Intn(30) + 50
condition := conditions[rand.Intn(len(conditions))]
return WeatherResult{
City: params.City,
Temperature: fmt.Sprintf("%d°F", temp),
Condition: condition,
}, nil
},
)
client := copilot.NewClient(nil)
if err := client.Start(ctx); err != nil {
log.Fatal(err)
}
defer client.Stop()
session, err := client.CreateSession(ctx, &copilot.SessionConfig{
Model: "gpt-4.1",
Streaming: copilot.Bool(true),
Tools: []copilot.Tool{getWeather},
})
if err != nil {
log.Fatal(err)
}
session.On(func(event copilot.SessionEvent) {
switch d := event.Data.(type) {
case *copilot.AssistantMessageDeltaData:
fmt.Print(d.DeltaContent)
case *copilot.SessionIdleData:
_ = d
fmt.Println()
}
})
fmt.Println("🌤️ Weather Assistant (type 'exit' to quit)")
fmt.Println(" Try: 'What's the weather in Paris?' or 'Compare weather in NYC and LA'\n")
scanner := bufio.NewScanner(os.Stdin)
for {
fmt.Print("You: ")
if !scanner.Scan() {
break
}
input := scanner.Text()
if strings.ToLower(input) == "exit" {
break
}
fmt.Print("Assistant: ")
_, err = session.SendAndWait(ctx, copilot.MessageOptions{Prompt: input})
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
break
}
fmt.Println()
}
if err := scanner.Err(); err != nil {
fmt.Fprintf(os.Stderr, "Input error: %v\n", err)
}
}
Проведите с:
go run weather-assistant.go
Создайте src/main.rs:
use std::io::{self, BufRead, Write};
use std::sync::Arc;
use std::time::Duration;
use github_copilot_sdk::handler::ApproveAllHandler;
use github_copilot_sdk::tool::{JsonSchema, ToolHandlerRouter, define_tool};
use github_copilot_sdk::{Client, ClientOptions, MessageOptions, SessionConfig, ToolResult};
use serde::Deserialize;
#[derive(Deserialize, JsonSchema)]
struct GetWeatherParams {
city: String,
}
fn read_line() -> Option<String> {
let stdin = io::stdin();
let mut line = String::new();
stdin.lock().read_line(&mut line).ok()?;
if line.is_empty() {
return None;
}
Some(line.trim_end_matches(&['\n', '\r'][..]).to_string())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let router = ToolHandlerRouter::new(
vec![define_tool(
"get_weather",
"Get the current weather for a city",
|_inv, params: GetWeatherParams| async move {
Ok(ToolResult::Text(format!(
"{}: 62°F and sunny",
params.city
)))
},
)],
Arc::new(ApproveAllHandler),
);
let tools = router.tools();
let client = Client::start(ClientOptions::default()).await?;
let mut config = SessionConfig::default();
config.streaming = Some(true);
config.tools = Some(tools);
let session = client.create_session(config.with_handler(Arc::new(router))).await?;
let mut events = session.subscribe();
tokio::spawn(async move {
while let Ok(event) = events.recv().await {
match event.event_type.as_str() {
"assistant.message_delta" => {
if let Some(text) =
event.data.get("deltaContent").and_then(|value| value.as_str())
{
print!("{text}");
io::stdout().flush().ok();
}
}
"assistant.message" => println!(),
_ => {}
}
}
});
println!("Weather Assistant (type 'exit' to quit)");
println!("Try: 'What's the weather in Paris?' or 'Compare weather in NYC and LA'\n");
loop {
print!("You: ");
io::stdout().flush().ok();
let Some(input) = read_line() else { break };
if input.eq_ignore_ascii_case("exit") {
break;
}
print!("Assistant: ");
io::stdout().flush().ok();
session
.send_and_wait(MessageOptions::new(input).with_wait_timeout(Duration::from_secs(120)))
.await?;
println!();
}
session.disconnect().await?;
client.stop().await?;
Ok(())
}
Проведите с:
cargo run
Создайте новый консольный проект и обновите Program.csего:
using GitHub.Copilot;
using Microsoft.Extensions.AI;
using System.ComponentModel;
// Define the weather tool
var getWeather = CopilotTool.DefineTool(
([Description("The city name")] string city) =>
{
var conditions = new[] { "sunny", "cloudy", "rainy", "partly cloudy" };
var temp = Random.Shared.Next(50, 80);
var condition = conditions[Random.Shared.Next(conditions.Length)];
return new { city, temperature = $"{temp}°F", condition };
},
factoryOptions: new AIFunctionFactoryOptions
{
Name = "get_weather",
Description = "Get the current weather for a city",
});
await using var client = new CopilotClient();
await using var session = await client.CreateSessionAsync(new SessionConfig
{
Model = "gpt-4.1",
OnPermissionRequest = PermissionHandler.ApproveAll,
Streaming = true,
Tools = [getWeather]
});
// Listen for response chunks
session.On<SessionEvent>(ev =>
{
if (ev is AssistantMessageDeltaEvent deltaEvent)
{
Console.Write(deltaEvent.Data.DeltaContent);
}
if (ev is SessionIdleEvent)
{
Console.WriteLine();
}
});
Console.WriteLine("🌤️ Weather Assistant (type 'exit' to quit)");
Console.WriteLine(" Try: 'What's the weather in Paris?' or 'Compare weather in NYC and LA'\n");
while (true)
{
Console.Write("You: ");
var input = Console.ReadLine();
if (string.IsNullOrEmpty(input) || input.Equals("exit", StringComparison.OrdinalIgnoreCase))
{
break;
}
Console.Write("Assistant: ");
await session.SendAndWaitAsync(new MessageOptions { Prompt = input });
Console.WriteLine("\n");
}
Проведите с:
dotnet run
Создайте WeatherAssistant.java:
import com.github.copilot.sdk.CopilotClient;
import com.github.copilot.sdk.events.*;
import com.github.copilot.sdk.json.*;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Scanner;
import java.util.concurrent.CompletableFuture;
public class WeatherAssistant {
public static void main(String[] args) throws Exception {
var random = new Random();
var conditions = List.of("sunny", "cloudy", "rainy", "partly cloudy");
var getWeather = ToolDefinition.create(
"get_weather",
"Get the current weather for a city",
Map.of(
"type", "object",
"properties", Map.of(
"city", Map.of("type", "string", "description", "The city name")
),
"required", List.of("city")
),
invocation -> {
var city = (String) invocation.getArguments().get("city");
var temp = random.nextInt(30) + 50;
var condition = conditions.get(random.nextInt(conditions.size()));
return CompletableFuture.completedFuture(Map.of(
"city", city,
"temperature", temp + "°F",
"condition", condition
));
}
);
try (var client = new CopilotClient()) {
client.start().get();
var session = client.createSession(
new SessionConfig()
.setModel("gpt-4.1")
.setStreaming(true)
.setOnPermissionRequest(request ->
CompletableFuture.completedFuture(PermissionDecision.allow())
)
.setTools(List.of(getWeather))
).get();
session.on(AssistantMessageDeltaEvent.class, delta -> {
System.out.print(delta.getData().deltaContent());
});
session.on(SessionIdleEvent.class, idle -> {
System.out.println();
});
System.out.println("🌤️ Weather Assistant (type 'exit' to quit)");
System.out.println(" Try: 'What's the weather in Paris?' or 'Compare weather in NYC and LA'\n");
var scanner = new Scanner(System.in);
while (true) {
System.out.print("You: ");
if (!scanner.hasNextLine()) break;
var input = scanner.nextLine();
if (input.equalsIgnoreCase("exit")) break;
System.out.print("Assistant: ");
session.sendAndWait(
new MessageOptions().setPrompt(input)
).get();
System.out.println("\n");
}
client.stop().get();
}
}
}
Проведите с:
javac -cp copilot-sdk.jar WeatherAssistant.java && java -cp .:copilot-sdk.jar WeatherAssistant
Пример сеанса:
🌤️ Weather Assistant (type 'exit' to quit)
Try: 'What's the weather in Paris?' or 'Compare weather in NYC and LA'
You: What's the weather in Seattle?
Assistant: Let me check the weather for Seattle...
It's currently 62°F and cloudy in Seattle.
You: How about Tokyo and London?
Assistant: I'll check both cities for you:
- Tokyo: 75°F and sunny
- London: 58°F and rainy
You: exit
Вы создали ассистента с помощью кастомного инструмента, который Copilot может вызвать!
Как работают средства
Когда вы определяете инструмент, вы говорите Copilot:
- Что делает инструмент (описание)
- Какие параметры нужны (схема)
- Какой код запускать (обработчик)
Copilot решает, когда вызвать ваш инструмент, исходя из вопроса пользователя. Когда это происходит:
- Copilot отправляет запрос на вызов инструмента с параметрами
- SDK запускает функцию обработчика
- Результат отправляется обратно в Copilot
- Copilot включает результат в свой ответ
Что дальше?
Теперь, когда у вас есть основы, вот более мощные возможности для изучения:
Подключение к серверам MCP
Серверы MCP (Model Context Protocol) предоставляют готовые инструменты. Подключитесь к MCP-серверу GitHub, чтобы дать Copilot доступ к репозиториям, проблемам и pull requests:
const session = await client.createSession({
mcpServers: {
github: {
type: "http",
url: "https://api.githubcopilot.com/mcp/",
},
},
});
📖 ** Using MCP servers with the GitHub Copilot SDK** — Узнайте о локальных и удалённых серверах, всех настройках и решении неполадок.
Создание настраиваемых агентов
Определите специализированные ИИ-персоны для конкретных задач:
const session = await client.createSession({
customAgents: [{
name: "pr-reviewer",
displayName: "PR Reviewer",
description: "Reviews pull requests for best practices",
prompt: "You are an expert code reviewer. Focus on security, performance, and maintainability.",
}],
});
Совет
Вы также можете настроить agent: "pr-reviewer" в конфигурации сессии предвыбор этого агента с самого начала. Подробности смотрите в АВТОЗАГОЛОВКЕ .
Настройка системного сообщения
Контролируйте поведение и личность ИИ, добавляя инструкции:
const session = await client.createSession({
systemMessage: {
content: "You are a helpful assistant for our engineering team. Always be concise.",
},
});
Для более тонкого управления используйте mode: "customize" переобозначение отдельных разделов системного запроса, сохраняя остальные:
const session = await client.createSession({
systemMessage: {
mode: "customize",
sections: {
tone: { action: "replace", content: "Respond in a warm, professional tone. Be thorough in explanations." },
code_change_rules: { action: "remove" },
guidelines: { action: "append", content: "\n* Always cite data sources" },
},
content: "Focus on financial analysis and reporting.",
},
});
Доступные идентификаторы разделов: identity, tone, tool_efficiency, environment_context, code_change_rules, , guidelines, tool_instructions``last_instructions``safety``custom_instructions``runtime_instructions.
Каждое переопределение поддерживает четыре действия: replace, remove, appendи prepend. Неизвестные идентификаторы разделов обрабатываются изящно — к дополнительным инструкциям добавляется содержимое и появляется предупреждение; remove на неизвестных участках молча игнорируется.
См. языковые READMEs SDK для примеров в TypeScript, Python, Go, Rust, Java и C#.
Подключение к внешнему CLI-серверу
По умолчанию SDK автоматически управляет жизненным циклом процесса Copilot CLI, запуская и останавливая CLI по мере необходимости. Однако можно отдельно запустить CLI в серверном режиме и подключить к нему SDK. Это может быть полезно для:
- Отладка: Оставляйте CLI включённым между перезагрузками SDK для проверки логов
- Совместное использование ресурсов: несколько SDK-клиентов могут подключаться к одному и тому же CLI-серверу
- Разработка: запускайте CLI с пользовательскими настройками или в другой среде
Запуск CLI в серверном режиме
Запустите CLI в серверном режиме с помощью --headless флага и, по желанию, укажите порт:
copilot --headless --port 4321
Если вы не указываете порт, CLI выберет случайный доступный порт.
По умолчанию безголовый сервер принимает соединения только через loopback (127.0.0.1), поэтому SDK должен работать на той же машине. Чтобы принять соединения от других хостов (например, при запуске CLI в контейнере или на отдельном сервере), привяжьте к адресу без обратной связи с --host:
# Listen on all interfaces
copilot --headless --host 0.0.0.0 --port 4321
Предупреждение
Открытие безголового сервера на не-лупбэк-адресе делает его доступным для любого, кто может маршрутизироваться по этому адресу. Сочетайте его с сетевыми контролями (файрвол, частная сеть, обратный прокси) и аутентификацией, подходящей для вашей среды.
Подключение SDK к внешнему серверу
Когда CLI заработает в серверном режиме, настройте SDK-клиент для подключения к нему через опцию «cli url»:
import { CopilotClient, approveAll } from "@github/copilot-sdk";
const client = new CopilotClient({
cliUrl: "localhost:4321"
});
// Use the client normally
const session = await client.createSession({ onPermissionRequest: approveAll });
// ...
from copilot import CopilotClient, RuntimeConnection
from copilot.session import PermissionHandler
client = CopilotClient(connection=RuntimeConnection.for_uri("localhost:4321"))
await client.start()
# Use the client normally
session = await client.create_session(on_permission_request=PermissionHandler.approve_all)
# ...
package main
import (
"context"
"log"
copilot "github.com/github/copilot-sdk/go"
)
func main() {
ctx := context.Background()
client := copilot.NewClient(&copilot.ClientOptions{
Connection: copilot.UriConnection{URL: "localhost:4321"},
})
if err := client.Start(ctx); err != nil {
log.Fatal(err)
}
defer client.Stop()
// Use the client normally
_, _ = client.CreateSession(ctx, &copilot.SessionConfig{
OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
})
}
import copilot "github.com/github/copilot-sdk/go"
client := copilot.NewClient(&copilot.ClientOptions{
Connection: copilot.UriConnection{URL: "localhost:4321"},
})
if err := client.Start(ctx); err != nil {
log.Fatal(err)
}
defer client.Stop()
// Use the client normally
session, err := client.CreateSession(ctx, &copilot.SessionConfig{
OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
})
// ...
use std::sync::Arc;
use github_copilot_sdk::handler::ApproveAllHandler;
use github_copilot_sdk::{Client, ClientOptions, SessionConfig, Transport};
let mut options = ClientOptions::default();
options.transport = Transport::External {
host: "localhost".to_string(),
port: 4321,
};
let client = Client::start(options).await?;
// Use the client normally
let session = client
.create_session(SessionConfig::default().with_handler(Arc::new(ApproveAllHandler)))
.await?;
// ...
using GitHub.Copilot;
using var client = new CopilotClient(new CopilotClientOptions
{
Connection = RuntimeConnection.ForUri("localhost:4321"),
});
// Use the client normally
await using var session = await client.CreateSessionAsync(new()
{
OnPermissionRequest = PermissionHandler.ApproveAll
});
// ...
import com.github.copilot.sdk.CopilotClient;
import com.github.copilot.sdk.json.*;
var client = new CopilotClient(
new CopilotClientOptions().setCliUrl("localhost:4321")
);
client.start().get();
// Use the client normally
var session = client.createSession(
new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
).get();
// ...
Примечание: Когда cli_url / cliUrl предоставляется UriConnection / Go, или использует Rust Transport::External, SDK не создаёт и не управляет CLI-процессом — он подключается только к существующему серверу по указанному URL.
Телеметрия и наблюдаемость
SDK Copilot поддерживает OpenTelemetry для распределённого трассировки. Предоставить клиенту telemetry конфигурацию для включения экспорта трасс из процесса CLI и автоматического распространения контекста W3C Trace между SDK и CLI.
Включение телеметрии
Передайте конфигурацию telemetry (или Telemetry) при создании клиента. Это опция для включения — отдельный флаг «включено» не требуется.
import { CopilotClient } from "@github/copilot-sdk";
const client = new CopilotClient({
telemetry: {
otlpEndpoint: "http://localhost:4318",
},
});
Опциональная зависимость от пиров: @opentelemetry/api
from copilot import CopilotClient, CopilotClientOptions
client = CopilotClient(CopilotClientOptions(
telemetry={
"otlp_endpoint": "http://localhost:4318",
},
))
Установка с дополнительными элементами телеметрии: pip install copilot-sdk[telemetry] (предоставляет opentelemetry-api)
client, err := copilot.NewClient(copilot.ClientOptions{
Telemetry: &copilot.TelemetryConfig{
OTLPEndpoint: "http://localhost:4318",
},
})
Зависимость: go.opentelemetry.io/otel
use github_copilot_sdk::{Client, ClientOptions, OtelExporterType, TelemetryConfig};
let mut options = ClientOptions::default();
options.telemetry = Some(
TelemetryConfig::new()
.with_exporter_type(OtelExporterType::OtlpHttp)
.with_otlp_endpoint("http://localhost:4318"),
);
let client = Client::start(options).await?;
Никаких дополнительных зависимостей — SDK вводит переменные среды телеметрии для сгенерированного CLI-процесса.
var client = new CopilotClient(new CopilotClientOptions
{
Telemetry = new TelemetryConfig
{
OtlpEndpoint = "http://localhost:4318",
},
});
Нет дополнительных зависимостей — использует встроенные System.Diagnostics.Activity.
import com.github.copilot.sdk.CopilotClient;
import com.github.copilot.sdk.json.*;
var client = new CopilotClient(new CopilotClientOptions()
.setTelemetry(new TelemetryConfig()
.setOtlpEndpoint("http://localhost:4318")));
Зависимость: io.opentelemetry:opentelemetry-api
Опции TelemetryConfig
| Опция | Node.js | Python | Go | Rust | Java | .NET | Description |
|---|---|---|---|---|---|---|---|
| Конечная точка OTLP | otlpEndpoint | otlp_endpoint | OTLPEndpoint | otlp_endpoint | otlpEndpoint | OtlpEndpoint | URL конечной точки OTLP HTTP |
| Путь к файлу | filePath | file_path | FilePath | file_path | filePath | FilePath | Путь файла для вывода JSON-lines trace |
| Тип экспортера | exporterType | exporter_type | ExporterType | exporter_type | exporterType | ExporterType | |
"otlp-http" или "file" | |||||||
| Имя источника | sourceName | source_name | SourceName | source_name | sourceName | SourceName | Название области приборов |
| Захват контента | captureContent | capture_content | CaptureContent | capture_content | captureContent | CaptureContent | Нужно ли фиксировать содержимое сообщений |
Экспорт файлов
Чтобы записать трассы в локальный файл вместо конечной точки OTLP:
const client = new CopilotClient({
telemetry: {
filePath: "./traces.jsonl",
exporterType: "file",
},
});
Распространение контекста следов
Trace context распространяется автоматически — не требуется ручное инструментирование:
- SDK → CLI:
traceparentиtracestateзаголовки текущего диапазона/активности включены вsession.create,session.resumeиsession.sendRPC-вызовы. - CLI → SDK: Когда CLI вызывает обработчики инструментов, контекст трассировки из span-а CLI распространяется, чтобы ваш инструмент работал под правильным родительским span.
📖 ** Инструментация OpenTelemetry для Copilot SDK**—Опции TelemetryConfig, распространение контекста трассировки и зависимости по языкам.
Узнать больше
- Проверка подлинности - GitHub OAuth, переменные окружения и BYOK
- BYOK (bring your own key) — Используйте свои собственные API-ключи от Azure AI Foundry, OpenAI и т.д.
- Node.js Справочник SDK
- Python SDK Reference
- Справочник Go SDK
- Rust SDK Reference
- .NET Ссылка на SDK
- Java Ссылка на SDK
- Using MCP servers with the GitHub Copilot SDK — Интеграция внешних инструментов через протокол Model Context Protocol
- GitHub Документация MCP сервера
- Каталог серверов MCP — Изучите больше серверов MCP
- Инструментация OpenTelemetry для Copilot SDK — TelemetryConfig, распространение контекста трассировки и зависимости по языкам
Ты сделал это! Вы изучили основные концепции SDK GitHub Copilot:
- ✅ Создание клиента и сессии
- ✅ Отправка сообщений и получение ответов
- ✅ Стриминг для выхода в реальном времени
- ✅ Определение пользовательских инструментов, которые Copilot может вызывать
А теперь иди и построй что-то потрясающее! 🚀