超越 Copilot Chat:用 SDK 打造专属 AI Agent(实战四场景)

超越 Copilot Chat:用 SDK 打造专属 AI Agent(实战四场景)

前两篇我们从入门到架构,把 GitHub Copilot SDK 拆透了。这篇换个角度——直接上代码,用四个真实场景展示 SDK 能做什么,以及为什么这比 " 直接调 LLM" 更有意思。

为什么需要 Demo?

理论讲了很多:Client/Session 的职责分离、事件驱动模型、39 种事件类型……

但真正让你 感受到 SDK 价值的,是看到它在实际场景里怎么运作。

这四个场景来自我的 copilot-sdk-demo 仓库,从简单到复杂,每个场景都聚焦一个核心能力:

场景核心能力一句话描述
场景一工具调用PR Code Review Bot
场景二多工具 + 输出保存自动文档生成器
场景三交互式对话循环kubectl 自然语言助手
场景四Skills 机制带团队规范的 PR Review

环境准备

# 克隆 demo 仓库
git clone https://github.com/addozhang/copilot-sdk-demo
cd copilot-sdk-demo

# 安装依赖
pip install -r requirements.txt

前置条件:已安装并登录 gh-copilot CLI(详见 入门篇)。


场景一:PR Code Review Bot

核心能力:工具调用(Tool Calling)

这是最经典的 Agent 模式:让 LLM " 决定 " 什么时候去调用外部函数,而不是在代码里硬编码调用顺序。

from copilot.tools import define_tool
from pydantic import BaseModel, Field

class GetPrDiffParams(BaseModel):
    repo: str = Field(description="Repository in owner/repo format")
    pr_number: int = Field(description="Pull request number")

@define_tool(description="Fetch the diff for a pull request")
def get_pr_diff(params: GetPrDiffParams) -> str:
    # 实际项目里调用 GitHub API;demo 用硬编码 diff
    return SAMPLE_DIFF

把工具注册到 Session:

session = await client.create_session(
    model="claude-sonnet-4.6",
    streaming=True,
    tools=[get_pr_diff],
    on_permission_request=lambda req, inv: PermissionRequestResult(kind="approved"),
)

await session.send_and_wait(
    "Please review PR #42 in 'acme/backend'. "
    "Use the get_pr_diff tool to fetch the diff, then provide a thorough code review."
)

发生了什么?

你没有告诉 LLM " 先调用 get_pr_diff,拿到结果再分析 “。你只是说 " 帮我 review PR #42”。LLM 根据工具描述自主判断需要先获取 diff,调用工具,拿到结果,再完成 review。

这就是工具调用的核心价值:LLM 做规划,你定义能力边界


场景二:自动文档生成器

核心能力:工具调用 + 结果持久化

给定一个 Python 源文件,自动生成 README 文档并保存到磁盘。

@define_tool(description="Read the contents of a file from disk")
def read_file(params: ReadFileParams) -> str:
    path = Path(params.filename)
    if not path.exists():
        return f"Error: file '{params.filename}' not found."
    return path.read_text()

关键点在于流式输出的收集:

generated_chunks = []

def handle_event(event):
    t = str(event.type)
    if "ASSISTANT_MESSAGE_DELTA" in t:
        if hasattr(event.data, "delta_content") and event.data.delta_content:
            sys.stdout.write(event.data.delta_content)
            sys.stdout.flush()
            generated_chunks.append(event.data.delta_content)  # 同时收集

session.on(handle_event)

await session.send_and_wait(
    f"Read the file '{target_file}' using the read_file tool, "
    "then generate a comprehensive README.md. Output only the markdown content."
)

# 流式输出完成后,拼接并保存
Path("README_generated.md").write_text("".join(generated_chunks))

一边流式输出给用户看,一边收集内容保存到文件——这是事件驱动模型带来的自然优势。用同步的请求 - 响应模式,你要等全部内容返回才能显示或保存。


场景三:kubectl 自然语言助手

核心能力:交互式对话循环 + System Prompt

这个场景最接近 " 真正的 AI 助手 “:多轮对话,每轮都能调用工具操作真实系统。

@define_tool(description="Execute a kubectl command and return the output")
def run_kubectl(params: RunKubectlParams) -> str:
    result = subprocess.run(
        f"kubectl {params.command}",
        shell=True, capture_output=True, text=True, timeout=30,
    )
    return result.stdout or result.stderr or "(no output)"

注入系统提示词,塑造助手角色:

SYSTEM_PROMPT = """You are a Kubernetes assistant. Translate natural language 
to kubectl commands, execute them, and explain the output. 
If the request is potentially dangerous (deleting resources), ask for confirmation first."""

session = await client.create_session(
    model="claude-sonnet-4.6",
    streaming=True,
    tools=[run_kubectl],
    system_message={"mode": "append", "content": SYSTEM_PROMPT},
    on_permission_request=lambda req, inv: PermissionRequestResult(kind="approved"),
)

交互循环:

while True:
    user_input = input("\n> ").strip()
    if user_input.lower() in ("exit", "quit"):
        break
    await session.send_and_wait(user_input)

运行效果:

> Show me all running pods in the default namespace

[Tool] Running: kubectl get pods --field-selector=status.phase=Running -n default

NAME                    READY   STATUS    RESTARTS   AGE
nginx-7d9b8c4f9-xk2p9   1/1     Running   0          3d
api-server-6f8b9-mnp4   1/1     Running   2          5d

以上是 default namespace 中当前运行的 2 个 Pod...

这里有个设计细节值得注意system_messagemode: "append" 意味着你的 System Prompt 是附加到 Copilot 内置 System Prompt 之后,而不是替换它。这样你既能保留 Copilot 的基础行为,又能叠加自己的角色定义。


场景四:带团队规范的 PR Review(Skills)

核心能力:Skills 机制

场景一和场景四 用的是完全一样的代码,只有一行区别:

# Scenario 1
session = await client.create_session(
    model="claude-sonnet-4.6",
    tools=[get_pr_diff],
    ...
)

# Scenario 4:增加 skill_directories
session = await client.create_session(
    model="claude-sonnet-4.6",
    tools=[get_pr_diff],
    skill_directories=["./skills/code-review"],  # 👈 这一行
    ...
)

skills/code-review/SKILL.md 的内容:

# Code Review Standards

When reviewing code, always check for:
1. Missing error handling (try/except for I/O, network, parsing)
2. Type hints on all function parameters and return values
3. Docstrings on all public functions and classes
4. No hardcoded credentials or secrets
5. Functions should be < 30 lines; suggest refactoring if longer

Output format:
- Start with a summary: APPROVED / NEEDS CHANGES / MAJOR ISSUES
- List each issue with: [SEVERITY: HIGH/MEDIUM/LOW] description
- End with specific suggestions

这个 Markdown 文件被 SDK 自动注入到 Session 上下文中,LLM 的输出格式会因此发生显著变化——不再是自由风格的 review,而是严格遵循团队规范的结构化报告。

Skills 的本质:一种轻量的 Prompt 工程封装机制。把团队规范、领域知识、输出模板写进 Markdown,通过目录路径注入,让不同的 Session 拥有不同的 " 专业背景 “。

对比 场景一和场景四 的输出就会发现:同样的代码 diff,同样的用户提问,加了 Skills 之后的 review 立刻变得更专业、更一致,而且更贴近你团队的实际需求。


四个场景的共同模式

回顾这四个 demo,你会发现一个固定的代码骨架:

# 1. 初始化客户端
client = CopilotClient()
await client.start()

# 2. 创建会话(注册工具、配置模型)
session = await client.create_session(
    model="claude-sonnet-4.6",
    tools=[your_tools],
    ...
)

# 3. 注册事件处理器(流式输出)
def handle_event(event):
    if "ASSISTANT_MESSAGE_DELTA" in str(event.type):
        sys.stdout.write(event.data.delta_content or "")

session.on(handle_event)

# 4. 发送消息
await session.send_and_wait("your prompt")

# 5. 清理
await client.stop()

这个骨架不变,变的是:

  • 工具定义:你想给 LLM 哪些 " 手脚 “?
  • System Prompt / Skills:你想给 LLM 什么 " 性格 " 和 " 背景 “?
  • 对话模式:单轮还是多轮?

从 Demo 到生产

这四个场景是 " 最小可运行 " 版本,但核心模式已经够用了。走向生产时,你主要需要补充:

  1. 真实的工具实现:把 get_pr_diff 里的硬编码 diff 换成真正的 GitHub API 调用
  2. 错误处理:工具调用失败时的重试和降级
  3. 权限控制on_permission_request 里加真实的审批逻辑,而不是一律 approved
  4. 日志和可观测性:利用 TOOL_EXECUTION_* 系列事件记录工具调用链路

总结

三篇文章,完整的学习路径:

  • 入门篇:5 行代码启动 Agent,理解 SDK 与 CLI 的关系
  • 架构篇:Client/Session 职责分离,事件驱动模型,39 种事件类型
  • 本文(实战篇):四个场景,工具调用、交互循环、Skills 机制,从 demo 到生产

GitHub Copilot SDK 的价值不在于 " 它帮你调 LLM”,而在于它提供了一个生产验证过的 Agent 运行时——你只需要告诉它 " 能做什么 “(工具定义)和 " 怎么思考 “(系统提示词/Skills),剩下的规划和执行交给 Copilot。

完整代码:https://github.com/addozhang/copilot-sdk-demo

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

comments powered by Disqus