Metadata-Version: 2.4
Name: bkai-init
Version: 0.1.0rc10
Summary: Synchronize AIDEV agent packages through the application OpenAPI.
Author: Tencent BlueKing
License-Expression: MIT
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: Jinja2<4,>=3.1.6
Requires-Dist: PyYAML>=6.0
Requires-Dist: requests>=2.31
Provides-Extra: test
Requires-Dist: pytest>=8.0; extra == "test"

# bkai-init

将模块维护的 Agent Package 同步到 AIDEV。只调用平台 app 应用态接口，不回退 private。

[协议与接入目录](docs/protocol.md) · [主子智能体 Demo](demo/readme.md) · [支持范围](docs/protocol.md#支持范围)

静态规则和公共默认值统一维护在 [bkai_init/settings.py](bkai_init/settings.py)，包括变量白名单、code 命名、协议字段与范围校验。内部代码统一通过 `from bkai_init import settings` 获取；凭据仍由调用参数提供，不写入 settings。

包顶层公开 `BkaiInit`、四类异常（`BkaiCliError`、`APIError`、`ConfigurationError`、`ManifestError`）及 `__version__`。服务类、返回类型从 `bkai_init.services` 导入；`SyncClient` 从 `services.sync`，`InspectionClient` / `inspection_as_dict` 从 `services.inspection` 导入。

## pip 安装

在发布相应版本的包仓库安装：

```bash
python -m pip install "bkai-init==0.1.0"
bkai-init --help
```

使用内部包源时配置 `PIP_INDEX_URL`。此处版本仅作示例，需先确认目标包源已有该版本；本地构建不等于发布。

## 快速生成模板

```bash
# 默认单智能体；目标必须是新目录，无需凭据或 --space
bkai-init init ./bk-job --agent-code ai-job --name "作业助手"

# 按需生成并关联 Skill、知识库和带快捷指令的子智能体
bkai-init init ./bk-job-full --agent-code ai-job --name "作业助手" \
  --with-skill job_skill --with-knowledgebase job_docs --with-subagent ai-job-child

bkai-init validate -f ./bk-job/bkai.yaml --space system-bkaidev
```

不指定 code/name 时默认 ai-demo / 初始化助手。目录已存在（包括空目录、符号链接）直接报错，不覆盖；不调用平台、不加载凭据、不发布。写入前使用现有完整协议校验。写入异常可能留下部分文件，检查后改用新目录重试。

默认生成 bkai.yaml 和 agents/main.yaml；可选依赖同时加入资源清单及主智能体引用。知识库只生成 knowledgebase.yaml，不放示例 Markdown，避免误触发镜像删除；添加实际文档后同步前须确认删除范围。Skill 生成 skill.yaml / SKILL.md，自定义镜像按[协议](docs/protocol.md#文件白名单与变量替换)另行补充。MCP/角色不由 init 生成，按需手工配置。

Python 同样不需要凭据：

```python
from bkai_init import BkaiInit

manifest = BkaiInit.init("./bk-job", agent_code="ai-job", name="作业助手",
                        skill_code="job_skill", knowledgebase_code="job_docs",
                        subagent_code="ai-job-child")
BkaiInit(space="system-bkaidev").validate(manifest)
```

init 只是生成模板，不是完整业务实现；按用途完善 Prompt 和 Skill。新建主子智能体的同步仍遵循下文子智能体发布约束。

`model.context_window` 是会话轮次，只接受 1～30 的整数，默认模板填写 16；不是模型上下文 token 数，不能填写 256。所有资源在远程请求前校验，非法值不会自动截断。

开发提交前在包目录运行 `make check`，执行全量本地测试（包含模板、Demo 与字段边界校验），不加载环境凭据或同步线上资源。安装仓库提交钩子后，修改 bkai-init 文件时会自动执行此检查，包含 settings.py。

## CLI 调用

先按[协议](docs/protocol.md)准备 `bkai.yaml` 及资源。凭据放在本机 `.local/env_bkai_init` 或部署环境，CLI 不自动加载 env 文件：

```bash
set -a
source .local/env_bkai_init
set +a

# 仅本地校验，不需要凭据
bkai-init validate -f /bk-job/bkai.yaml --space system-bkaidev

# 查看、对比线上配置（不写入）
bkai-init show -f /bk-job/bkai.yaml --tenant-id system --space system-bkaidev --format json
bkai-init diff -f /bk-job/bkai.yaml --tenant-id system --space system-bkaidev

# 同步会创建或更新资源
bkai-init sync -f /bk-job/bkai.yaml --tenant-id system --space system-bkaidev --confirm
```

| 参数 / 环境变量 | 说明 |
| --- | --- |
| `--base-url` / `BKAI_BASE_URL` | 必需。未设置时使用 `BK_API_URL_TMPL`，仅将 `{api_name}` 替换为 `bk_aidev`；支持服务根地址、网关 stage 或完整 `/openapi/aidev/app/v1` 前缀 |
| `--app-code` / `BKAI_APP_CODE` | 必需。蓝鲸应用编码，未设置时使用 `BKPAAS_APP_ID` |
| `--app-secret` / `BKAI_APP_SECRET` | 必需。应用密钥，未设置时使用 `BKPAAS_APP_SECRET` |
| `--access-token` / `BKAI_ACCESS_TOKEN` | 可选，兼容 `ACCESS_TOKEN`；token 不替代应用空间授权 |
| `--username` / `BKAI_USERNAME` | 可选，传递调用用户名 |
| `--tenant-id` | 默认 `system`，只通过参数传入 |
| `--space` | 默认 `bkaidev`，显式传值时不可为空；多租户传完整空间 ID，如 `system-bkaidev`，不会自动拼接租户前缀。所有资源统一使用此空间，旧 `metadata.space` 不再生效 |
| `--timeout` | 单次请求超时，默认 60 秒 |
| `--var KEY=VALUE` | 可重复；仅替换协议 YAML 与已声明 Skill 根目录 Dockerfile，详见[变量规则](docs/protocol.md#文件白名单与变量替换) |

CLI 不自动读取 `BKAI_SPACE_ID`；需要时显式传 `--space "$BKAI_SPACE_ID"`。不在命令示例、镜像或 Git 中保存真实凭据。

以上三项逐项按 **命令行参数 > BKAI 环境变量 > PaaS 内置环境变量** 取值，可混合来源。空的 BKAI 环境变量视为未设置；命令行显式传空值会报缺参，不回退。`BK_API_URL_TMPL` 例如 `https://{api_name}.example.com/`，解析为 `https://bk_aidev.example.com/`；其余路径保持不变，不自动追加 Stage。Python 类仍显式接收调用参数，不自动加载环境变量。

PaaS 已注入 `BK_API_URL_TMPL`、`BKPAAS_APP_ID`、`BKPAAS_APP_SECRET` 时无需重复配置 BKAI 同名用途变量。容器或 Helm Job 需确保这三个变量实际注入进程；CLI 不会获取宿主机或其它容器的环境变量。

Python `BkaiInit()` 同样默认使用 `bkaidev`；多租户部署使用 `BkaiInit(space="system-bkaidev")`。下列显式传入 `system-bkaidev` 的示例适用于多租户部署。

### 传入部署变量

Demo Skill 的 Dockerfile 使用短镜像名 `bkdbm-aidev-skills-env:0.0.1-alpha.13`。平台构建时按 `SKILL_SANDBOX_BASE_IMAGE_PREFIX`（对齐 Helm `global.imageRegistry`）补仓库前缀；Helm Job 不必传 registry 变量。需要覆盖完整地址时，再在已有 Dockerfile 中使用变量：

在 Skill 根目录 Dockerfile 中引用基础镜像：

```dockerfile
FROM {{ SKILL_BASE_IMAGE }}
```

```bash
bkai-init sync -f /bk-job/bkai.yaml --space system-bkaidev --confirm \
  --var SKILL_BASE_IMAGE=registry.example.com/team/skill:1.0
```

Python 调用同样支持：

```python
from bkai_init import BkaiInit

initializer = BkaiInit(
    space="system-bkaidev",
    variables={"SKILL_BASE_IMAGE": "registry.example.com/team/skill:1.0"},
)
initializer.validate("/bk-job/bkai.yaml")
# 远程操作另需传入 base_url、app_code、app_secret，见下文。
```

也可以直接在变量引用处配置默认值（标准 Jinja 语法）：

```dockerfile
FROM {{ SKILL_BASE_IMAGE | default("registry.example.com/team/skill:1.0") }}
```

未传该变量时使用默认值；CLI `--var` 或 Python `variables` 显式传值时优先使用传入值，包括空字符串。YAML 示例：`name: "{{ SKILL_NAME | default('demo') }}"`。

仅允许简单变量及 `default("字符串")`，不支持第二个参数、其它过滤器或模板功能，替换结果不再次渲染。不改源文件，不处理 Markdown、脚本或嵌套 Dockerfile。不提供独立的 Skill 镜像字段。

### 查看指定资源

```bash
# 单个资源
bkai-init show -f /bk-job/bkai.yaml --resource agent/ai-job-assist --space system-bkaidev

# 多个资源，也可重复传入 --resource
bkai-init show -f /bk-job/bkai.yaml --space system-bkaidev \
  --resource skill/job_skill knowledgebase/job_docs --format json
```

资源必须在 Package 清单中；省略 `--resource` 查看全包，指定后只请求并输出所选资源，不额外查询其依赖。未知编码或空集合会在请求前报错，全包本地格式校验仍保留。无论选择哪些资源，均使用命令指定的空间。

`show` 不修改资源，不提供权限查询，也不支持 `--exclude-resource`。`diff` 同样支持 `--resource`，资源选择规则与 show 一致。

### 同步预览与 CI 检查

```bash
bkai-init plan -f /bk-job/bkai.yaml --resource agent/ai-job-assist --format json --space system-bkaidev
bkai-init sync -f /bk-job/bkai.yaml --space system-bkaidev
bkai-init diff -f /bk-job/bkai.yaml --resource agent/ai-job-assist --check --space system-bkaidev
```

`plan` / 不带 `--confirm` 的 `sync` 只读取线上配置，不上传、创建、更新或发布。输出实际租户、资源空间、create/update/skip、差异、依赖来源、执行顺序和阻塞原因；支持资源选择、黑名单和发布参数。未选或排除的资源不会同步，其依赖需在线上存在。`ready` 仅表示已执行的检查未发现阻塞，不保证写权限、全部服务端校验或执行成功；已有资源即使无可见差异仍显示 update，与 sync 的行为一致。确认执行时增加 `--confirm`；不再提供 `--dry-run`。

`diff --check`：0 无差异，1 执行错误，2 有差异，3 无法完整比较。不可比较优先于有差异；不加 `--check` 保持原行为。Skill 文件摘要、envs 值、角色标签等未回显内容明确标记未知，不将其视为一致。

### 同步并发布智能体

```bash
# 显式开启发布；默认只发布配置，先子后主
bkai-init sync -f /bk-job/bkai.yaml --confirm --publish --publish_config_only=1 --space system-bkaidev

# 只预览包括发布的计划，不执行
bkai-init plan -f /bk-job/bkai.yaml --publish --space system-bkaidev

# 明确改用平台常规发布流程
bkai-init sync -f /bk-job/bkai.yaml --confirm --publish --publish_config_only=0 --space system-bkaidev
```

确认执行后，未指定 `--publish` 时仅同步草稿。Agent 不接受配置版本；发布请求不传 version，由平台分配，计划中显示 `auto`。脚本检查发布成功状态，并通过详情回读确认接口返回的版本；不覆盖已发布版本，发布失败时停止。

主智能体仍只能引用已发布的子智能体和该版本中的指令。开启发布后，所选子智能体先同步并发布，再从发布版本展开主智能体的指令引用；未选或排除的子智能体不会发布。发布失败、非成功状态、缺少版本或回读不一致时立即停止，不继续写主智能体，也不自动回滚。常规发布可能异步完成，需到平台确认后再继续；脚本不自动重试发布。

### 指定同步资源

```bash
# 一个或多个资源；--resource 可重复传入
bkai-init sync -f /bk-job/bkai.yaml --space system-bkaidev --confirm \
  --resource skill/job_skill knowledgebase/job_docs

# 跳过手工修改的资源，避免覆盖
bkai-init sync -f /bk-job/bkai.yaml --space system-bkaidev --confirm \
  --exclude-resource agent/ai-job-assist
```

类型使用小写 `kind/code`。省略 `--resource` 表示全包同步，黑名单优先；未知资源或 Python 空集合会报错。不自动同步未选中的依赖，只选 Agent 时依赖需已存在。

**同步前先查看 plan / diff。** Agent 更新是整配置覆盖，失败不保证回滚。知识库目录含 Markdown 时会同步 ZIP，并移除线上目标目录中不在本地的内容。完整边界见[协议说明](docs/protocol.md#知识库文档同步)。

APIGW MCP 查询会携带已存在智能体的 `agent_code`，平台检查调用应用是否获授该智能体所在空间的权限。新建智能体预检查只查询公开 MCP；要引用非公开 MCP，需先创建智能体并完成网关授权。`plan` 不为查询授权提前创建资源。

### 同步知识库文档

```bash
bkai-init sync -f /bk-job/bkai.yaml --tenant-id system --space system-bkaidev \
  --resource knowledgebase/job_docs --confirm
```

目录内的 Markdown 和图片按原层级打包，不替换正文变量、不包含根目录 `knowledgebase.yaml`。无文档时仅更新知识库配置，不清空线上目录。

流程：app `upload/url` 获取临时授权 → PUT 直传 BKRepo → app `upload/status` 校验大小及 SHA256 → app `knowledges/archive/import` 提交。提交成功后继续同步后续资源，不等待导入任务完成。上传失败时停止，不回退 private 或网关文件上传。

申请临时地址只发送空间、模块和文件名，不发送 `file_size`、`sha256`。上传后仍使用本地大小和摘要对比平台返回的实际元数据，确认一致后才提交导入。

上传连接不携带应用凭据，不输出签名 URL。上传超时会先核对上传状态，不自动重复 PUT；导入接口若直接返回失败或部分成功则命令报错，不自动取消或重提，应先到平台确认。`show` 仍仅查看配置；`diff` 不比较文档内容，会标记无法完整比较。

## Python 调用

安装同一 pip 包后，统一从 `bkai_init` 导入：

```python
import logging
import os
import sys

from bkai_init import BkaiInit, BkaiCliError

logging.basicConfig(level=logging.INFO, stream=sys.stdout)

# 无凭据也可进行本地校验
BkaiInit().validate("bkai.yaml")

initializer = BkaiInit(
    base_url=os.environ["BKAI_BASE_URL"],
    app_code=os.environ["BKAI_APP_CODE"],
    app_secret=os.environ["BKAI_APP_SECRET"],
    access_token=os.environ.get("BKAI_ACCESS_TOKEN") or os.environ.get("ACCESS_TOKEN"),
    tenant_id="system",
    space="system-bkaidev",
)

try:
    online = initializer.show("bkai.yaml", resources={"agent/ai-job-assist"})
    differences = initializer.diff("bkai.yaml", resources={"agent/ai-job-assist"})
    preview = initializer.plan("bkai.yaml", publish=True, publish_config_only=True)
    # 显式选择要写入的资源；省略 resources 表示全包同步
    report = initializer.sync(
        "bkai.yaml",
        resources={"skill/job_skill", "knowledgebase/job_docs"},
        excludes={"agent/ai-job-assist"},
    )
    # 完整同步并发布主子智能体时，显式传 publish=True（默认只发布配置）
    # report = initializer.sync("bkai.yaml", publish=True, publish_config_only=True)
except BkaiCliError as exc:
    logging.error("初始化失败：%s", exc)
    raise
```

返回值分别为 `PackageInspection`、`tuple[ResourceDiff, ...]`、`PackagePlan` 和 `SyncReport`，均可从包级导入。同步资源必须与自己的清单编码一致。Python 的 `sync()` 本身就是显式执行，不需要 confirm 参数；只读预览调用 `plan()`。

过程日志统一使用 logging。CLI 默认 INFO 输出到 stdout；类调用由应用配置日志，不修改 root logger。show 的 JSON/YAML 和 diff 的结果不带日志前缀；CLI 执行错误（含接口 URL、请求参数、输出和处理说明）返回 1 并写入 stdout，参数错误由 argparse 输出到 stderr。

## Dockerfile 与镜像调用

[基础 Dockerfile](Dockerfile) 不复制源码，只通过 pip 安装指定版本。交付顺序为：**构建包 → 发布包 → 构建基础镜像 → 构建模块镜像**。

在本目录执行：

```bash
make build
make publish                  # 显式发布到配置的包仓库
make image PACKAGE_VERSION=0.1.0 IMAGE=bkai-init:local

# 模块镜像继承基础镜像，只增加初始化目录
podman build -f demo/Dockerfile \
  --build-arg BKAI_INIT_IMAGE=bkai-init:local \
  -t bkai-init-demo:local demo
```

基础镜像支持 `PYTHON_BASE_IMAGE`、`BKAI_INIT_PACKAGE`、`BKAI_INIT_VERSION` 和 pip 包源构建参数；可通过 `make image IMAGE_BUILD_ARGS="..."` 传入。不要通过构建参数传真实包源凭据。

[模块 Dockerfile](demo/Dockerfile) 将资源复制到 `/bk-job`，继承 `bkai-init` 入口。已有包源版本时，`make demo-test PACKAGE_VERSION=0.1.0 SPACE=system-bkaidev` 可构建镜像并仅执行 validate。

```bash
# 先本地验证，不访问平台
podman run --rm bkai-init-demo:local validate -f /bk-job/bkai.yaml --space system-bkaidev

# 环境变量需先加载到宿主环境；容器继承应用凭据
# 这里只同步依赖；完整初始化主子智能体时使用 --publish
podman run --rm \
  -e BKAI_BASE_URL -e BKAI_APP_CODE -e BKAI_APP_SECRET \
  -e BKAI_ACCESS_TOKEN -e ACCESS_TOKEN \
  bkai-init-demo:local sync -f /bk-job/bkai.yaml --confirm \
  --tenant-id system --space system-bkaidev \
  --resource skill/bkai_init_demo_skill knowledgebase/bkai_init_demo_docs
```

## Helm 接入

模块镜像需包含 `bkai-init` 和清单目录，并推送到集群可访问的镜像仓库。将[Job 模板](demo/helm/templates/bkai-init-job.yaml)合入业务 Chart，通过 values 控制：

```yaml
image:
  repository: example.invalid/modules/bk-job
  tag: "1.0.0"
  pullPolicy: IfNotPresent

bkai:
  enabled: true
  tenantId: system
  space: system-bkaidev
  packagePath: /bk-job/bkai.yaml
  excludeResources: []
```

- `enabled: false` 不生成 Job；启用时在 `post-install`、`post-upgrade` 执行 `sync --confirm`。模板固定携带确认参数，避免只预览而未同步。
- Job 复用模块的 `image`；`packagePath` 为镜像内路径，`excludeResources` 映射为多个 `--exclude-resource`。
- 业务 Chart 必须将现有环境变量注入逻辑接入 Job，提供 `BKAI_BASE_URL`、`BKAI_APP_CODE`、`BKAI_APP_SECRET`，或对应 PaaS 内置变量 `BK_API_URL_TMPL`、`BKPAAS_APP_ID`、`BKPAAS_APP_SECRET`，按需传 token。示例模板未内置这部分逻辑，不增加 `credentialsSecretName` 开关。
- 当前示例 Chart 执行全包同步但不发布；首次主子初始化时需由业务 Chart 显式给 Job 添加 `--publish`，默认只发布配置。示例不是部署成功证明。

```bash
helm lint demo/helm
helm template bkai-init-demo demo/helm
```

以上只检查或渲染模板，不部署到集群。独立 Job 参考 [k8s-job.yaml](demo/k8s-job.yaml)，其中 Secret 仅是向容器注入环境变量的一种示例，需按部署环境调整。

## 本地开发与验证

```bash
make init
make test                     # 单元测试，不调用线上接口
make local-test SPACE=system-bkaidev # Demo 本地校验，不加载凭据
make local-show ENV_FILE=/path/to/.local/env_bkai_init RESOURCES="agent/ai-bkai-demo"
make local-diff ENV_FILE=/path/to/.local/env_bkai_init
make local-diff ENV_FILE=/path/to/.local/env_bkai_init RESOURCES="agent/ai-bkai-demo" CHECK=true
make local-plan ENV_FILE=/path/to/.local/env_bkai_init PUBLISH=true
make local-sync ENV_FILE=/path/to/.local/env_bkai_init \
  RESOURCES="skill/bkai_init_demo_skill knowledgebase/bkai_init_demo_docs"
make clean                    # 清理虚拟环境与构建产物
```

Makefile 参数：`PACKAGE_FILE` 替换默认 Demo 清单，`TENANT_ID` 默认 system，`SPACE` 优先于 env 中的 `BKAI_SPACE_ID`，两者均未设置时使用 CLI 默认空间 bkaidev；多租户部署需设置完整空间 ID（如 system-bkaidev）；`RESOURCES` 用于 show/diff/plan/sync，`EXCLUDE_RESOURCES` 用于 plan/sync；`CHECK=true` 开启 diff 检查，Make 会将非零子命令退出码包装为自己的失败退出码。plan/sync 可传 `PUBLISH=true` 和 `PUBLISH_CONFIG_ONLY=0`，后者只接受 1 / 0、默认 1，转为 CLI 的 `--publish_config_only=1` / `0`；默认不发布，发布时默认只发布配置。远程入口显式加载 `ENV_FILE`，默认是业务仓库根目录的 `.local/env_bkai_init`。只有 `local-sync` 写入平台，该入口固定带 `--confirm`；预览使用 `local-plan`。
