ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

AX Task Runner 深度解析:任务容器 PID 1 的启动契约、生命周期与三种自定义方案

AX Task Runner 深度解析:任务容器 PID 1 的启动契约、生命周期与三种自定义方案 人工智能AI AgentAgent 框架自主智能体【免费下载链接】axGoogles open agentic orchestration runtime项目地址https://gitcode.com/GitHub_Trending/ax11/ax点击查看免费下载导读Runner 是 AX 在每个任务容器内以 PID 1 启动的桥接程序负责把控制面下发的Task与Workspace规格转化为就绪的工作区、一个受监督的代理命令以及一组可供外部探测沙箱的 HTTP 接口。本文以官方文档 docs/runner.md 为主体结合仓库源码与测试用例系统讲解 runner 的启动环境契约、六项核心职责、默认实现ax-task-runner的构建方式以及扩展镜像、嵌入 Go 包、从头编写三种替换路径。读完后你将能自定义 runner 镜像、通过runner.Run挂接命令退出钩子并在本地独立验证 runner 行为。Runner 在 AX 架构中的位置一个 runner 就是 AX 在每个任务容器内部以 PID 1 启动的程序。它是控制面与你的代理本体之间的桥梁控制面侧把Task和Workspace规格交给 runner通过环境变量注入容器内侧runner 负责把这些规格变成一个已准备好的工作区 一个正在运行的命令 一个供 AX 其余部分观察沙箱的小型 HTTP 服务面。AX 默认自带ax-task-runner并已烧录进默认任务镜像。但默认 runner 不是强制的任何遵守下述契约的二进制都可以被打包进容器镜像并在spec.image中指定控制面会把它与默认实现一视同仁。这意味着你可以完全掌控代理在沙箱里的启动方式。本篇聚焦runner 必须做什么至于默认 runner 起来之后你的命令在沙箱内部能看到什么元数据端点、guest 服务、环境变量见 docs/sandbox.md。Runner 是如何被启动的关键设计点是控制面并不把spec.command当作容器 entrypoint 运行。它总是用一条固定命令启动容器然后让 runner 接手后续一切。下表是 AX 在启动时设置的全部内容AX 设置的内容值容器镜像spec.image未设置时使用默认ax-task-runner镜像容器命令固定为/usr/local/bin/ax-task-runnerAX_TASK_YAMLTask启动配置的 YAML不含 status 字段AX_WORKSPACES_YAML每个绑定的Workspace资源按任务绑定顺序拼成的多文档 YAML 流spec.env各条目每条直接写入容器环境GEMINI_API_KEY当 atespace 配置了 Gemini 凭据时注入卷一个持久化目录挂载到/workspace就绪探针端口 80 上的GET /readyz由这张表可以推出两个直接后果你的镜像里必须存在/usr/local/bin/ax-task-runner这个可执行文件——哪怕是符号链接或包着其他程序的 shell 包装脚本。控制面只认这个固定路径。spec.command只能通过AX_TASK_YAML到达 runnerrunner 负责解析该环境变量并负责真正启动命令。为什么 command 不直接是 entrypoint把命令交给 runner 而非容器运行时是刻意为之runner 需要在命令启动之前先准备好工作区克隆、skills、bootstrap并且要在命令退出之后继续存活以维持元数据服务。若把spec.command设为 entrypoint容器会在命令结束时一起退出ax ssh也就随之失效。/workspace卷挂起与恢复的关键/workspace卷是跨 suspend/resume 存活的部分。Agent Substrate 在任务挂起时会对其做快照恢复时再把快照还原进一个全新容器。因此 runner 恢复后看到的是同样的文件、全新的进程树——这决定了工作区准备必须是只做一次的幂等操作见下文准备每个工作区。Runner 必须履行的六项职责官方文档将 runner 的契约归纳为若干硬性要求下面逐项结合源码展开。1. 在 80 端口提供 HTTP 服务Agent Substrate 和 AX server 都会探测容器 80 端口。需要关注的路径路径行为/healthzrunner 一存活就返回200/readyz工作区未准备好时返回503准备好后返回200。AX 轮询该端点以设置任务的WorkspaceReady条件ax watch会展示这一状态切换/metadata/v1alpha1/ax/task以application/yaml返回Task。可选但你的命令和ax工具链可能依赖它/metadata/v1alpha1/ax/workspaces以多文档 YAML 流返回每个绑定的Workspace。可选同上这些端点由 internal/metadata/server.go 实现/healthz恒返回 200ok\n/readyz在workspaceReady未置位时返回503 workspace initializing任务与工作区端点分别用 yaml 编码器输出Task与按声明顺序排列的Workspace流。同一端口还通过 HTTP/2 明文h2c与 gRPC 复用见职责 6。2. 每个工作区只准备一次任务通过spec.workspaces绑定工作区。对每个绑定在其路径上执行以下工作从spec.git克隆 Git 仓库创建 skills 路径写入任何 MCP 配置若绑定带goal运行其要求的环境 bootstrap。绑定未指定path时落在/workspace/name。必须把已初始化这一事实记录到持久卷或已知位置按工作区分别记录后续启动时跳过该工作——因为恢复会重启容器而向已还原的工作区重新克隆会摧毁代理的全部状态。默认 runner 会为每个工作区路径在/ax下写一个标记文件。源码佐证见 internal/workspace/setup.go标记文件名为initialized路径形如/ax/initialized-clean-pathMarkerName用清理后的路径生成文件名使同一容器内多个工作区互不干扰标记文件内容记录工作区名与初始化时间writeMarkergit 克隆失败时不写标记下次启动会重试克隆gitOK为 false 时直接返回git 操作带重试最多 5 次、间隔 2 秒gitRetries/gitRetryDelay克隆目标、分支、浅克隆深度均来自Workspace规格若绑定带goal则把目标交给 Antigravity 代理完成环境准备要求容器内有GEMINI_API_KEY默认超时 10 分钟defaultBootstrapTimeout可用环境变量AX_BOOTSTRAP_TIMEOUT以 Go duration 字符串覆盖如15m。bootstrap 脚本见 cmd/ax-task-runner/antigravity_bootstrap.py其 API key 只从环境读取、绝不经命令行参数传入避免出现在进程列表里。值得注意的是runner/runner.go 的Run对每个 mount 逐个调用workspace.SetupWorkspace某个工作区准备失败只记录日志、不中止运行——该任务只是永远不报告 ready/readyz持续返回 503。Run仅当元数据服务器或命令本身无法启动时才返回错误。3. 启动命令并监督它runner 要把spec.command作为子进程启动以第一个工作区为工作目录向其环境注入AX_METADATA_URL指向 runner 自己的 HTTP 服务每一条spec.env。命令还要放在独立的进程组中以便能向它派生出的所有进程统一发信号。runner/runner.go 的实现细节cmd : exec.Command(cmdArgs[0], cmdArgs[1:]...) cmd.Dir wsPath cmd.Stdin os.Stdin cmd.Stdout os.Stdout cmd.Stderr os.Stderr cmd.Env commandEnv(cfg.Task, port) // A dedicated process group lets shutdown signal the command together with // anything it spawned. cmd.SysProcAttr syscall.SysProcAttr{Setpgid: true}其中commandEnv依次拼接 runner 自身环境、AX_METADATA_URLhttp://127.0.0.1:port和spec.env条目。runner 包在Run一开始也会把AX_METADATA_URL与spec.env写入自身进程环境os.Setenv因此沙箱内的任何子进程都能看到。4. 命令退出后继续存活Runner 是 PID 1容器的寿命等于 runner 的寿命。如果 runner 随命令一起退出元数据服务器随之消失ax ssh也会失效。正确做法是记录命令的退出状态然后继续对外服务直到被告知停止。控制面目前不会从容器读回命令的退出码。runner/runner.go 中命令通过 goroutine 的cmd.Wait()返回退出信息PID、退出码、错误交给OnCommandExit钩子与日志select { case err : -exited: reportExit(cfg, cmd, err) // Keep the sandbox up and inspectable until told to stop. -ctx.Done() case -ctx.Done(): reportExit(cfg, cmd, stopCommand(cmd, exited)) }对应测试 runner/runner_test.go 中TestRun_KeepsServingAfterCommandExits验证了这一点命令执行true退出后/healthz仍返回 200且Run在上下文未取消前不返回。5. 收到SIGTERM后干净地关闭停止stop与挂起suspend都会向 PID 1 发送SIGTERM。Runner 应当把SIGTERM转发给命令的进程组等待一个有限的宽限期宽限期后对剩余进程SIGKILL退出前冲刷一切代理在恢复后需要的东西。默认实现的宽限期是10 秒stopGracePeriod 10 * time.Second。stopCommand用syscall.Kill(-pid, syscall.SIGTERM)向整个进程组发信号time.NewTimer(stopGracePeriod)计时超时则SIGKILL进程组并等待退出。测试TestRun_StopsRunningCommandOnCancel用sh -c sleep 30; sleep 30验证了整棵进程树都能被停止。6. 仅在要求时提供 guest 服务当spec.debug为 true 时runner 还应在 80 端口通过 h2c 与 HTTP 端点复用以 gRPC 提供 Agent Substrate guest 服务——这正是ax ssh连接的目标。当spec.debug为 false 时必须保持关闭这些服务允许在沙箱内任意执行进程与访问文件ax ssh拒绝连接未显式开启opt-in的任务。internal/metadata/server.go 的实现印证了该契约NewServer读取task.GetSpec().GetDebug()为 true 时用guest.NewServer(guestCfg)初始化 gRPC 服务guestCfg.Workspace指向工作区路径并把h2c的application/grpc流量路由给 gRPC server为 false 时记录guest services disabled。默认镜像的 examples/task.yaml 中debug: true # serve guest services so ax ssh works; off by default正是这种显式选择。默认 runnerax-task-runnerax-task-runner位于 cmd/ax-task-runner是runnerGo 包的薄包装入口 main.go 加载AX_TASK_YAML与AX_WORKSPACES_YAML或用--task-file/--workspace-file从文件读取随后调用 runner.Run其余一切交给该包完成。其镜像由 Dockerfile.task-runner 构建基础镜像为python:3.12-slim安装git、curl、ca-certificates、openssh-client、procps、bash通过 pip 安装google-antigravity包拷贝bin/linux_amd64/ax-task-runner到/usr/local/bin/ax-task-runner并拷贝antigravity_bootstrap.py到/usr/local/bin/ENTRYPOINT [/usr/local/bin/ax-task-runner]。因为 goal 型工作区 bootstrap 要把目标交给 Antigravity 代理所以镜像里才需要 Python 与 Antigravity。构建与推送命令见 Makefilemake build-task-runner # 交叉编译 linux/amd64 并构建镜像 make push-task-runner # 推送镜像设置 TASK_RUNNER_REPO 选择 registry细节build-task-runner先以GOOSlinux GOARCHamd64 CGO_ENABLED0交叉编译出bin/linux_amd64/ax-task-runner再用$(CONTAINER_CLI)优先 podman、回退 docker以--platform linux/amd64构建镜像$(TASK_RUNNER_REPO):latestTASK_RUNNER_REPO默认值为gcr.io/ax-substrate/ate-images/ax-task-runnerpush-task-runner依赖build-task-runner推送后还会用 gcloud 列出latest标签对应的 digest方便你在 Task 清单里按 digest 固定版本。三种替换默认 runner 的路径官方文档给出由浅入深的三级定制原则是选能解决问题的、改动最浅的那一级。路径一在默认镜像之上扩展若 runner 行为没问题、只是沙箱里需要更多工具直接在默认镜像之上构建并保留其 entrypoint 即可# Pin the same digest the examples use so the runner behavior is reproducible. FROM gcr.io/ax-substrate/ate-images/ax-task-runnersha256:69b764607ec7f1e433d83d2eca17dccfa04f663b43f071fd376e2dd716a57f8c RUN apt-get update apt-get install -y --no-install-recommends nodejs npm \ rm -rf /var/lib/apt/lists/* RUN npm install -g my-agentrunner 二进制仍位于/usr/local/bin/ax-task-runner其余一切不变。注意官方建议固定与示例一致的 digest以保证 runner 行为可复现examples/task.yaml 同样以sha256:...固定镜像。路径二在自己的二进制里嵌入 runner 包若你想要标准生命周期、但需要在它周围运行自己的代码可以导入github.com/google/ax/runner并自己调用runner.Run。这给你两个钩子命令退出的回调以及在元数据服务器启动前后做自有设置的位置。官方示例代码package main import ( context log/slog os os/signal strings syscall github.com/google/ax/pkg/apis/v1alpha1 github.com/google/ax/runner gopkg.in/yaml.v3 ) func main() { var task v1alpha1.Task _ yaml.Unmarshal([]byte(os.Getenv(AX_TASK_YAML)), task) // AX_WORKSPACES_YAML is a multi-document stream, one Workspace per document. var workspaces []*v1alpha1.Workspace dec : yaml.NewDecoder(strings.NewReader(os.Getenv(AX_WORKSPACES_YAML))) for { var ws v1alpha1.Workspace if err : dec.Decode(ws); err ! nil { break } workspaces append(workspaces, ws) } ctx, stop : signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer stop() err : runner.Run(ctx, runner.Config{ Task: task, Workspaces: workspaces, OnCommandExit: func(exit runner.CommandExit) { slog.Info(agent finished, exitCode, exit.ExitCode) // Upload artifacts, notify a webhook, and so on. }, }) if err ! nil { slog.Error(runner failed, error, err) os.Exit(1) } }编译注意事项结合 Makefile 的交叉编译参数用CGO_ENABLED0为linux/amd64交叉编译再把产物拷到镜像的/usr/local/bin/ax-task-runner。runner.Config的可配置项包括Port零值表示DefaultPort80即 Agent Substrate 探测的端口、Task、Workspaces按名字与spec.workspaces绑定匹配与OnCommandExitCommandExit携带命令 PID、退出码被信号杀死时为 -1与错误。路径三从零编写 runner若默认生命周期不适用——例如你的代理框架自己管理进程或你希望采用不同的工作区布局——可以用任何语言从零写一个读取AX_TASK_YAML与AX_WORKSPACES_YAML满足上一节全部契约HTTP 端口、只初始化一次、监督命令、SIGTERM 转发、debug 门控 guest 服务把产物安装到/usr/local/bin/ax-task-runner。ax.io/v1alpha1schema 定义于 pkg/apis/v1alpha1/ax.proto逐字段详解见 docs/manifests.md。类型层面对自定义 runner 的约束还可以参考 pkg/apis/v1alpha1/types.go默认镜像常量DefaultTaskImage、工作区挂载路径解析WorkspacePaths显式 path 优先否则/workspace/name以及ValidateTask对绑定名称唯一、路径绝对、路径唯一的校验。发布你的 runner无论走哪条路最终都要把镜像推送到集群可拉取的 registry并在任务里引用apiVersion: ax.io/v1alpha1 kind: Task metadata: name: custom-runner spec: image: ghcr.io/my-org/my-runnersha256:... command: [my-agent, --goal, fix the flaky test] debug: trueAX 会为每一种不同的镜像 环境组合预置一个独立的 Agent Substrate actor 模板因此不同任务可以在同一个 atespace 里并行运行不同的 runner。spec.image缺省时的默认值可在 pkg/apis/v1alpha1/types.go 中查到DefaultTaskImage gcr.io/ax-substrate/ate-images/ax-task-runner。在本地测试 runner默认 runner 除了从环境变量接收规格外也接受文件输入这让它在集群外也很容易运行。你自己的 runner 也应提供类似机制ax-task-runner --task-file task.yaml --workspace-file code.yaml --workspace-file tools.yaml --port 8080 # 在另一个终端里 curl -i http://127.0.0.1:8080/readyz curl -s http://127.0.0.1:8080/metadata/v1alpha1/ax/task命令行参数在 cmd/ax-task-runner/main.go 中定义--port默认runner.DefaultPort即 80、--task-file替代AX_TASK_YAML、--workspace-file可重复替代AX_WORKSPACES_YAML每个文件可含多个文档。本地测试时注意把--port指到未占用端口如 8080避免与容器内的 80 端口语义混淆。镜像构建好后最快的端到端验证是提交一个debug: true的任务然后ax ssh进去确认工作区、命令与环境都符合预期。交付前自检清单官方文档以清单收尾逐条对照可快速判断自定义 runner 是否达标/usr/local/bin/ax-task-runner存在可执行文件读取AX_TASK_YAML和AX_WORKSPACES_YAML在 80 端口服务/healthz与/readyz且/readyz在每个工作区就绪前返回503在重启与恢复之间每个工作区只在自己的路径上被初始化一次以第一个工作区为工作目录启动spec.command环境包含AX_METADATA_URL与spec.env命令退出后 runner 继续存活把SIGTERM转发给命令进程组宽限期后退出仅当spec.debug为 true 时在 80 端口提供 guest gRPC 服务上述每一项都能在 runner/runner_test.go 中找到对应的行为验证环境注入与工作目录、退出码上报、退出后继续服务、取消时停止整个进程组、无命令时等待、多工作区依次初始化与readyz/元数据顺序可作为自定义实现的参考基线。赞分享人工智能AI AgentAgent 框架自主智能体【免费下载链接】axGoogles open agentic orchestration runtime项目地址https://gitcode.com/GitHub_Trending/ax11/ax点击查看免费下载相关推荐QQ空间历史说说本地备份一次扫码全部动态和图片都拿回QQ空间历史说说本地备份一次扫码全部动态和图片都拿回 你翻 QQ 空间找一条三年前的老说说翻几页就断了照片散落在各个时段没法按时间检索。GetQzone网页爬虫数据分析Agent Zero 工具基类深度解析Tool 与 Response 契约、执行生命周期与自定义工具开发指南Agent Zero 工具基类深度解析Tool 与 Response 契约、执行生命周期与自定义工具开发指南 Agent Zero 是一个 AI Agent人工智能大模型AI AgentAgent 框架自主智能体多智能体工具调用MCP 服务浏览器控制Cherry Studio WindowManager 架构详解三种生命周期模式与事件时序契约Cherry Studio WindowManager 架构详解三种生命周期模式与事件时序契约 WindowManager 是 Cherry Studio 主人工智能大模型AI 应用交互助手本地部署创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表