<!-- title: 用Python SDK编程造批量Agent流水线 -->

# 用 Python SDK 编程造批量 Agent 流水线：Claude Agent SDK 自动化编排实战

## 概述

本教程教你用 **Claude Agent SDK（Python 版）** 把 Claude Code 从"命令行工具"升级为"可编程的自动化引擎"。你将掌握 `query()` 异步查询、`ClaudeSDKClient` 双向会话、进程内自定义工具、Hooks 拦截、并行子代理编排与 Session 复用，最终亲手造一条**批量代码安全审计流水线**。适合已用过 Claude Code CLI、希望用代码精确控制 Agent 行为、跑无人值守批处理任务的后端工程师与 DevOps 自动化开发者。

## 前置条件

- **Python 3.10+**（SDK 硬性要求）
- 已安装 `pip` 包管理器
- 一份有效的 Claude Code 订阅或 API Key（环境变量 `ANTHROPIC_API_KEY`，或已通过 `claude` 登录）
- 对 `async/await` 异步编程有基本了解（SDK 全异步）
- 用过 Claude Code CLI 跑过至少一次任务（了解"Agent 循环"概念即可）

> 核心认知：Claude Agent SDK 是把 Claude Code 的整个 Agent 循环（调用模型→判断是否用工具→执行工具→回喂结果）封装成了 Python 库。CLI 自带、无需单独安装。

---

## 步骤一：安装 SDK 与跑通第一个 query()

### 操作说明

Claude Agent SDK 的 Python 包名为 `claude-agent-sdk`，安装时**自动捆绑 Claude Code CLI**，开箱即用：

```bash
pip install claude-agent-sdk
```

安装完成后，创建 `hello.py`，写入官方 Quick Start 的最小示例：

```python
import anyio
from claude_agent_sdk import query

async def main():
    # query() 返回一个异步消息迭代器，逐条 yield Agent 产生的消息
    async for message in query(prompt="What is 2 + 2?"):
        print(message)

anyio.run(main)
```

运行：

```bash
python hello.py
```

### 预期结果

终端会逐条打印 Agent 循环过程中产生的消息对象，包括 `SystemMessage`（初始化）、`AssistantMessage`（模型回复）、`ResultMessage`（含 token 用量与耗时统计的最终结果）。你会看到 `4` 这个答案被包含在某个 `AssistantMessage` 的 `TextBlock` 里。

### 常见问题与解决方案

| 问题 | 原因 | 解决 |
|------|------|------|
| `CLINotFoundError` | 找不到捆绑 CLI 或系统 CLI | 重装包；或手动 `curl -fsSL https://claude.ai/install.sh \| bash` 后用 `ClaudeAgentOptions(cli_path=...)` 指定 |
| `Weekly/Monthly Limit Exhausted` | 账户额度耗尽 | 切换 API Key 或等待额度重置 |
| 长时间无输出 | 首次启动 CLI 子进程较慢 | 等待 3-5 秒；后续调用会复用 |

> **为什么用 `anyio.run` 而不是 `asyncio.run`？** SDK 内部基于 `anyio` 抽象层，用 `anyio.run` 能保证事件循环兼容性。如果你坚持用 `asyncio.run(main())` 也能跑，但遇到 `Trio` 后端时会出问题。

---

## 步骤二：用 ClaudeAgentOptions 精确配置 Agent 行为

### 操作说明

`query()` 的第二参数 `options` 是一切配置的入口。它取代了旧版的 `ClaudeCodeOptions`（升级时注意改名）。核心字段如下：

```python
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, TextBlock

options = ClaudeAgentOptions(
    system_prompt="你是资深 Python 安全审计员，只读分析、不修改文件",  # 系统提示词
    max_turns=6,                    # Agent 循环最多迭代几轮，防止失控
    cwd="/home/me/myproject",       # Agent 的工作目录（影响 Read/Bash 的相对路径）
    allowed_tools=["Read", "Bash"], # 白名单：列出的工具自动放行，不再弹确认
    disallowed_tools=["Write", "Edit", "NotebookEdit"],  # 黑名单：彻底移除写操作能力
    permission_mode="acceptEdits",  # 兜底权限模式
)

async for message in query(prompt="列出项目里所有硬编码的密钥", options=options):
    if isinstance(message, AssistantMessage):
        for block in message.content:
            if isinstance(block, TextBlock):
                print(block.text)
```

### 预期结果

Agent 进入指定 `cwd`，仅用 `Read` 和 `Bash`（如 `grep`）扫描代码，不会修改任何文件，扫描完毕输出一份密钥清单。因为 `Write/Edit` 被加入 `disallowed_tools`，即使模型想改文件也无工具可用。

### 关键概念：白名单 vs 黑名单

这是新手最常踩的坑。**`allowed_tools` 是权限放行清单，不是工具清单**：

- **默认**：Claude 拥有完整 Claude Code 工具集（Read、Write、Edit、Bash、Glob、Grep 等）。
- **`allowed_tools=["Bash"]`**：Bash 自动放行（不弹确认）；其它工具并未消失，只是会走 `permission_mode` / `can_use_tool` 决策。
- **`disallowed_tools=["Write"]`**：真正从工具集中移除 Write，模型根本看不到它。

> 安全审计场景务必用 `disallowed_tools` 移除所有写工具，而不是依赖 `allowed_tools`——后者只是少弹确认，模型仍可能请求写操作。

---

## 步骤三：用 ClaudeSDKClient 跑双向交互式会话

### 操作说明

`query()` 是"一问一答"的快捷函数。若要多轮追问、随时插入新指令，需要用 `ClaudeSDKClient`：

```python
import anyio
from claude_agent_sdk import ClaudeAgentOptions, ClaudeSDKClient

async def main():
    options = ClaudeAgentOptions(
        allowed_tools=["Read", "Bash"],
        cwd=".",
    )
    # async with 自动管理 CLI 子进程的生命周期
    async with ClaudeSDKClient(options=options) as client:
        await client.query("读取 main.py 并总结它的职责")
        async for msg in client.receive_response():
            print("第一轮:", msg)

        # 同一会话内继续追问，Agent 记得上文
        await client.query("那它有哪些潜在的安全风险？")
        async for msg in client.receive_response():
            print("第二轮:", msg)

anyio.run(main)
```

### 预期结果

两轮对话共享同一上下文：第二轮的"那它有哪些风险"能正确指代第一轮读到的 `main.py`，无需重复喂文件。

### 常见问题

- **忘记 `async with`**：直接 `client = ClaudeSDKClient(...)` 后不进入上下文管理器，会导致 CLI 子进程泄漏、连接未初始化报 `CLIConnectionError`。务必用 `async with`。
- **`receive_response()` 提前结束**：它只在当前一轮响应完成后结束迭代；若想持续监听，需在每次 `client.query()` 后再次调用。

---

## 步骤四：用进程内自定义工具扩展 Agent（零额外进程）

### 操作说明

`ClaudeSDKClient` 相比 `query()` 的最大增值能力是**自定义工具**与 **Hooks**——两者都用普通 Python 函数定义。

自定义工具通过 `@tool` 装饰器 + `create_sdk_mcp_server` 实现，本质是一个**进程内 MCP 服务器**（无需像传统 MCP 那样起独立子进程）：

```python
import anyio
from claude_agent_sdk import (
    tool, create_sdk_mcp_server,
    ClaudeAgentOptions, ClaudeSDKClient,
)

# 定义一个工具：@tool(工具名, 描述, 参数类型签名)
@tool("count_secrets", "统计文件中疑似密钥的行数", {"file_path": str})
async def count_secrets(args):
    path = args["file_path"]
    try:
        with open(path, "r", encoding="utf-8") as f:
            lines = f.readlines()
        hits = sum(1 for ln in lines if any(k in ln.upper() for k in ("API_KEY", "SECRET", "TOKEN", "PASSWORD")))
        text = f"{path} 中发现 {hits} 行疑似密钥"
    except FileNotFoundError:
        text = f"文件不存在: {path}"
    # 返回 MCP 标准内容块
    return {"content": [{"type": "text", "text": text}]}

# 把函数打包成进程内 MCP 服务器
server = create_sdk_mcp_server(name="audit-tools", version="1.0.0", tools=[count_secrets])

options = ClaudeAgentOptions(
    mcp_servers={"t": server},
    allowed_tools=["mcp__t__count_secrets"],  # 命名规则：mcp__<服务器名>__<工具名>
)

async def main():
    async with ClaudeSDKClient(options=options) as client:
        await client.query("帮我统计 ./config.py 里有多少疑似密钥，调用 count_secrets 工具")
        async for msg in client.receive_response():
            print(msg)

anyio.run(main)
```

### 预期结果

Claude 会自主决定调用 `count_secrets` 工具，传入 `"./config.py"`，工具在**同一 Python 进程**内执行，返回命中行数，模型再据此组织最终回答。

### 为什么这比传统 MCP 强

| 维度 | 传统 MCP（stdio 子进程） | SDK 进程内工具 |
|------|--------------------------|----------------|
| 进程模型 | 每个服务器独立子进程 | 与你的应用同进程 |
| IPC 开销 | 每次调用走 stdin/stdout | 直接 Python 函数调用 |
| 调试 | 跨进程难断点 | 同进程可单步 |
| 部署 | 多进程编排 | 单进程 |

> **混合使用**：`mcp_servers` 字典里可同时放进程内 server（`create_sdk_mcp_server` 返回值）与外部 stdio server（`{"type":"stdio","command":"..."}` 字典），SDK 会分别处理。

---

## 步骤五：用 Hooks 拦截危险操作（确定性护栏）

### 操作说明

Hooks 与自定义工具不同：**Hook 由 Claude Code 应用层（不是模型）在 Agent 循环的特定节点调用**，用于注入确定性逻辑——比如无论模型怎么想，运行 `rm -rf` 前先拦截。

最经典的是 `PreToolUse` Hook，在工具执行前拦截：

```python
import anyio
from claude_agent_sdk import ClaudeAgentOptions, ClaudeSDKClient, HookMatcher

async def guard_bash(input_data, tool_use_id, context):
    """PreToolUse 钩子：在 Bash 执行前检查命令"""
    if input_data["tool_name"] != "Bash":
        return {}  # 非 Bash 工具，放行
    command = input_data["tool_input"].get("command", "")
    blocked = ["rm -rf", "DROP TABLE", "sudo ", "> /dev/sda", "mkfs"]
    for pattern in blocked:
        if pattern in command:
            return {
                "hookSpecificOutput": {
                    "hookEventName": "PreToolUse",
                    "permissionDecision": "deny",   # 拒绝执行
                    "permissionDecisionReason": f"命中危险模式: {pattern}",
                }
            }
    return {}  # 放行

options = ClaudeAgentOptions(
    allowed_tools=["Bash", "Read"],
    hooks={
        "PreToolUse": [
            HookMatcher(matcher="Bash", hooks=[guard_bash]),
        ],
    },
)

async def main():
    async with ClaudeSDKClient(options=options) as client:
        await client.query("用 rm -rf 删除 ./tmp 目录")
        async for msg in client.receive_response():
            print(msg)

anyio.run(main)
```

### 预期结果

模型虽然请求执行 `rm -rf`，但 Hook 在执行前返回 `deny`，Bash 工具被拒绝，模型收到拒绝原因后会改用更安全的方式（如 `rmdir` 或 `shutil`）或放弃。这就是"确定性护栏"——不依赖模型自觉，由代码硬性保证。

### Hook 与 disallowed_tools 的区别

- `disallowed_tools=["Bash"]`：彻底不让用 Bash（连只读命令 `ls` 也用不了）。
- `PreToolUse` Hook：仍允许用 Bash，但能**按命令内容动态放行/拒绝**（放行 `ls`、拒绝 `rm -rf`）。粒度更细，是生产环境首选。

---

## 步骤六：用并行子代理编排批量任务（核心实战）

### 操作说明

这是把 Claude Code 真正变成"批处理引擎"的关键。每个 `query()` 调用是**独立会话、独立 Agent 实例**，因此可以用 `asyncio.gather` 同时跑 N 个任务，互不干扰。

子代理（Subagent）的本质就是：一个拥有独立上下文窗口、独立工具权限的 Agent 实例，专注于一个子任务。在 SDK 里，你不需要"定义"它——**每次 `query()` 调用就是一个临时子代理**。

```python
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, AssistantMessage, TextBlock

def make_options():
    """每个子代理用相同的安全配置"""
    return ClaudeAgentOptions(
        system_prompt="你是代码安全审计员。只读分析，输出 JSON：{\"file\":..,\"risks\":[..],\"severity\":\"high|mid|low\"}",
        allowed_tools=["Read", "Bash"],
        disallowed_tools=["Write", "Edit"],
        max_turns=4,
        cwd=".",
    )

async def audit_one(file_path: str) -> dict:
    """一个子代理审一个文件，返回结构化结果"""
    options = make_options()
    collected = []
    prompt = f"审计文件 {file_path}，找出硬编码密钥、SQL 注入、命令注入风险，按约定 JSON 输出。"
    async for message in query(prompt=prompt, options=options):
        if isinstance(message, AssistantMessage):
            for block in message.content:
                if isinstance(block, TextBlock):
                    collected.append(block.text)
    return {"file": file_path, "report": "\n".join(collected)}

async def batch_audit(files: list[str]) -> list[dict]:
    # 并发扇出：所有文件同时审计
    return await asyncio.gather(*[audit_one(f) for f in files])

if __name__ == "__main__":
    targets = ["src/auth.py", "src/db.py", "src/utils.py", "src/api.py"]
    results = asyncio.run(batch_audit(targets))
    for r in results:
        print(f"=== {r['file']} ===")
        print(r["report"])
```

### 预期结果

4 个文件的审计**同时进行**（而非串行排队），总耗时接近"最慢的一个文件"，而非"四个文件之和"。每个子代理的上下文窗口只装着自己那个文件的内容，互不污染，输出互不串扰。

### 并发控制：别让 N 无限大

`asyncio.gather` 会同时启动所有任务，若文件有 500 个，会瞬间起 500 个 CLI 子进程，耗尽内存与 API 速率配额。务必加信号量限流：

```python
import asyncio

sem = asyncio.Semaphore(5)  # 最多 5 个子代理并发

async def audit_one(file_path: str) -> dict:
    async with sem:            # 拿到许可才跑
        options = make_options()
        collected = []
        async for message in query(prompt=f"审计 {file_path}", options=options):
            # ... 同上
            pass
        return {"file": file_path, "report": "\n".join(collected)}
```

> **经验值**：并发数建议设为 `min(CPU核心数-2, API速率限制/2)`。Anthropic API 有 RPM/TPM 限制，并发过高会触发 `429`。

### 常见问题

| 问题 | 解决 |
|------|------|
| 偶发 `429 Rate Limit` | 降低 `Semaphore` 值，或对 `query()` 包一层指数退避重试 |
| 某个子代理卡死不返回 | 设小 `max_turns`（如 4-6），防止模型无限探索 |
| 结果里混入工具调用日志 | 只收 `AssistantMessage` 的 `TextBlock`，过滤掉 `ToolUseBlock` |

---

## 步骤七：用 Session 实现可中断、可复用的长任务

### 操作说明

Session 是 SDK 在 Agent 工作期间累积的**完整对话历史**——你的 prompt、每一次工具调用、每一次工具结果、每一条回复。它让自动化任务具备三个能力：

- **continue**：在同一 session 上追加新指令，历史不丢。
- **resume**：进程重启后，用 session id 恢复之前的对话。
- **fork**：从某次运行的中间状态"分叉"出一条新会话，探索不同分支而不破坏主线。

这在批处理里价值巨大：一个跑了一半的大型重构任务，可以 fork 出一个"试验性改法"的分支，主线不受影响；崩了也能用 session id 续跑。

`ClaudeSDKClient` 在会话层面提供 session 管理能力。典型用法是在 `ClaudeAgentOptions` 中维持/传入 session 标识，使跨进程、跨时间的会话得以衔接：

```python
import anyio
from claude_agent_sdk import ClaudeAgentOptions, ClaudeSDKClient

async def long_task():
    options = ClaudeAgentOptions(
        allowed_tools=["Read", "Edit", "Bash"],
        cwd=".",
    )
    async with ClaudeSDKClient(options=options) as client:
        # 阶段一：分析
        await client.query("分析整个 src 目录的测试覆盖率缺口")
        async for _ in client.receive_response():
            pass

        # 阶段二：在阶段一的上下文基础上继续
        await client.query("针对最大的三个缺口，各写一个失败的单元测试")
        async for _ in client.receive_response():
            pass
        # 关键：这条 session 的历史在 client 存活期内始终累积
```

### 何时用 fork

当你想"基于一个已经分析完毕的会话，同时尝试两种不同的修复方案"时：fork 主 session 得到两条独立分支，各自演进，互不干扰，最后比较结果择优。这正是单线程 CLI 难以做到、而 SDK 编程模型天然擅长的场景。

> 具体的 session id 获取与 fork 调用细节，以官方 [Work with sessions](https://code.claude.com/docs/en/agent-sdk/sessions) 文档为准——不同 SDK 小版本字段可能调整。

---

## 完整实战案例：批量代码安全审计流水线

把上面七步串成一条真实可跑的流水线：扫描整个项目 → 并行审计 → 危险操作拦截 → 汇总报告。

```python
# audit_pipeline.py
import asyncio
import json
from pathlib import Path
from claude_agent_sdk import (
    query, ClaudeAgentOptions, ClaudeSDKClient, HookMatcher,
    AssistantMessage, TextBlock,
)

# ---- 1. 危险命令护栏 ----
async def guard_bash(input_data, tool_use_id, context):
    if input_data["tool_name"] != "Bash":
        return {}
    cmd = input_data["tool_input"].get("command", "")
    for bad in ["rm -rf", "DROP TABLE", "sudo ", ":(){:|:&};:"]:
        if bad in cmd:
            return {"hookSpecificOutput": {
                "hookEventName": "PreToolUse",
                "permissionDecision": "deny",
                "permissionDecisionReason": f"危险命令: {bad}",
            }}
    return {}

# ---- 2. 单文件审计子代理 ----
def audit_options():
    return ClaudeAgentOptions(
        system_prompt=(
            "你是安全审计员。审一个文件，输出严格 JSON："
            '{"file":str,"risks":[{"line":int,"type":str,"desc":str}],'
            '"severity":"high|mid|low|none"}。只读，禁止修改文件。'
        ),
        allowed_tools=["Read", "Bash"],
        disallowed_tools=["Write", "Edit", "NotebookEdit"],
        max_turns=5,
        cwd=".",
        hooks={"PreToolUse": [HookMatcher(matcher="Bash", hooks=[guard_bash])]},
    )

sem = asyncio.Semaphore(4)

async def audit_one(file_path):
    async with sem:
        opts = audit_options()
        text_parts = []
        async for msg in query(
            prompt=f"审计 {file_path} 的安全风险并输出 JSON。",
            options=opts,
        ):
            if isinstance(msg, AssistantMessage):
                for b in msg.content:
                    if isinstance(b, TextBlock):
                        text_parts.append(b.text)
        raw = "\n".join(text_parts)
        # 尝试从回复里抠出 JSON（容错）
        try:
            start, end = raw.index("{"), raw.rindex("}") + 1
            return json.loads(raw[start:end])
        except (ValueError, json.JSONDecodeError):
            return {"file": file_path, "risks": [], "severity": "none",
                    "raw": raw[:200]}

# ---- 3. 编排器 ----
async def run_pipeline(root="."):
    files = [str(p) for p in Path(root).rglob("*.py") if "venv" not in str(p)]
    print(f"发现 {len(files)} 个 Python 文件，开始并行审计（并发上限 4）...")
    results = await asyncio.gather(*[audit_one(f) for f in files])
    high = [r for r in results if r.get("severity") == "high"]
    print(json.dumps({"total": len(files), "high_risk": len(high),
                      "details": high}, ensure_ascii=False, indent=2))

if __name__ == "__main__":
    asyncio.run(run_pipeline("."))
```

运行：

```bash
python audit_pipeline.py
```

这条流水线综合运用了：**白名单+黑名单权限隔离**、**进程内 Hooks 护栏**、**信号量限流的并行子代理**、**结构化 JSON 输出**——这正是 SDK 编程模型相比 CLI 脚本的核心优势：每一条策略都是普通 Python 代码，可测试、可版本化、可纳入 CI。

---

## 工作流程图

```mermaid
flowchart TD
    A[Python 应用] -->|asyncio.run| B[编排器 batch_audit]
    B -->|asyncio.gather 并发扇出| C1[子代理1: query]
    B -->|asyncio.gather 并发扇出| C2[子代理2: query]
    B -->|asyncio.gather 并发扇出| C3[子代理N: query]
    B -.->|Semaphore=4 限流| C1

    C1 --> D1[ClaudeAgentOptions]
    D1 --> D1a[allowed_tools 白名单]
    D1 --> D1b[disallowed_tools 黑名单]
    D1 --> D1c[hooks PreToolUse]

    C1 -->|Agent 循环| E1[模型决策]
    E1 -->|调用工具前| F1[Hook guard_bash 拦截]
    F1 -->|deny 危险命令| G1[拒绝执行]
    F1 -->|allow 安全命令| H1[执行 Read/Bash]
    H1 --> E1
    E1 -->|完成| I1[结构化 JSON 结果]
    C2 --> I2[结果]
    C3 --> I3[结果]

    I1 --> J[汇总报告]
    I2 --> J
    I3 --> J
    J --> K[CI / PR 评论 / 告警]

    style F1 fill:#fde68a,stroke:#b45309
    style G1 fill:#fecaca,stroke:#b91c1c
    style B fill:#bfdbfe,stroke:#1d4ed8
```

---

## 延伸阅读

- **Claude Agent SDK 官方文档（Python）**：`platform.claude.com/docs/en/agent-sdk/python` —— 最权威的 API 参考
- **Subagents in the SDK**：`code.claude.com/docs/en/agent-sdk/subagents` —— 子代理隔离上下文与并行执行详解
- **Work with sessions**：`code.claude.com/docs/en/agent-sdk/sessions` —— continue / resume / fork 机制
- **Intercept and control agent behavior with hooks**：`platform.claude.com/docs/en/agent-sdk/hooks` —— Hook 全节点参考
- **GitHub 示例库**：`github.com/anthropics/claude-agent-sdk-python` 的 `examples/` 目录（`mcp_calculator.py`、`hooks.py`、`streaming_mode.py`）可直接运行学习

---

## 训练营适用性评估

| 维度 | 评估 |
|------|------|
| **适合的训练营类型** | Python 后端进阶营、DevOps/SRE 自动化营、AI 工程化实战营、企业级 LLM 应用开发营 |
| **难度** | 中高级（需异步编程基础 + 对 Agent 循环的理解） |
| **建议教学时长** | 4-6 课时（含实操）：1 课时安装与 query、1 课时配置与权限、1 课时自定义工具+Hooks、2 课时并行编排实战、1 课时 Session 与答疑 |
| **前置课程** | 建议先完成"读懂智能体循环，跑通首个项目"（CLI 基础）与本系列 MCP 服务器开发章节 |
| **可交付产出** | 学员独立交付一条可接入 CI 的代码审计/文档生成/测试批量生成流水线 |
| **延伸挑战** | 引入 `ccusage` 做 token 成本监控、把 fork 用于 A/B 重构方案对比、用 `ClaudeSDKClient` 造交互式运维排障助手 |

从 `query()` 一行代码起步，到 `asyncio.gather` 并行扇出、Hooks 硬性护栏、Session 可复用编排——Claude Agent SDK 把 Agent 循环彻底变成了可编程的库。当你能用 Python 精确控制"跑几个、怎么限流、拦什么、记什么"，批处理自动化就从"写一堆 shell 脚本"升级成了"写工程化的并发程序"。

## 参考来源

1. [claude-agent-sdk-python (GitHub README)](https://github.com/anthropics/claude-agent-sdk-python) — 官方代码库，本文所有 API 示例（query/ClaudeAgentOptions/ClaudeSDKClient/@tool/HookMatcher/错误处理）的权威来源
2. [Subagents in the SDK — Claude Code Docs](https://code.claude.com/docs/en/agent-sdk/subagents) — 子代理隔离上下文与并行执行的官方说明，支撑步骤六并行编排
3. [Work with sessions — Claude Code Docs](https://code.claude.com/docs/en/agent-sdk/sessions) — session 历史、continue/resume/fork 机制，支撑步骤七
4. [Claude Agent SDK Python Guide 2026 — AI Workflow Lab](https://aiworkflowlab.dev/article/how-to-build-production-ai-agents-claude-agent-sdk-custom-tools-hooks-subagents) — 生产级 Agent 构建（自定义工具、Hooks、子代理）的实战博客
5. [Claude Agent SDK (PyPI)](https://pypi.org/project/claude-agent-sdk/) — 包安装入口与版本/Python 3.10+ 依赖说明

> [!tip] 51学通信知识星球
> **星主：爱卫生**（微信：gprshome201101）——公众号「51学通信」创办者，19 年通信行业运维与讲师经验，著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书（各大电商平台有售）。
> **星球定位**：深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域，主打运维岗位与信令分析。
> **当前规模**：5200 篇主题 / 990 个回答，198 元/年。
> **加入福利**：加入星球后请加爱卫生微信，拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题，另有群内分享。
> **AI 加持**：51学通信知识星球已作为 skill 接入 WorkBuddy，使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例，做总结、找文档和 PPT 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
