Agent Lightning Controller 配置实践:用 Hydra 配置 k8s 与 local 两种 rollout 执行后端
2026/9/13 12:07:33 网站建设 项目流程

Agent Lightning Controller 配置实践:用 Hydra 配置 k8s 与 local 两种 rollout 执行后端

【免费下载链接】agent-lightningThe absolute trainer to light up AI agents.项目地址: https://gitcode.com/GitHub_Trending/ag/agent-lightning

Agent Lightning 中的 Controller 负责把声明式的 rollout 翻译成真实的 Agent 执行:它从 API Gateway 领取QUEUING状态的 rollout,在 Kubernetes Job 或本地子进程中运行 Agent,并把执行结果回写状态。本文基于docs/6-controller-configuration.md,结合agentlightning/controller/下的入口、K8s 协调器与本地协调器源码,完整讲解 Controller 的启动方式、Hydra 默认配置、命令行覆盖、两种 runner 的连接与限流参数,以及 Agent 进程的最终注入机制,帮助读者掌握可复制、可运行的 Controller 部署与调优方案。

Controller 是什么、如何启动

Controller 通过agl-controller命令启动。这个命令入口在 pyproject.toml 中注册:

[project.scripts] agl-server = "agentlightning.server.__main__:main" agl-controller = "agentlightning.controller.__main__:main"

入口函数定义在 agentlightning/controller/main.py,它是一个标准的 Hydra 应用:

@hydra.main(version_base=None, config_path="../config", config_name="controller") def main(config: DictConfig) -> None: asyncio.run(_run_controller(config))

从源码结构看,启动流程分为三步:

  1. Hydra 加载完整默认配置(配置组目录为../config,即 agentlightning/config/controller.yaml);
  2. _run_controllerconfig.agl_server.urlconfig.agl_server.key创建AgentLightningAsyncClient,作为访问 API Gateway 的通道;
  3. 根据config.runner_type分支:k8s时导入K8sReconciler(该路径在main.py 中是延迟导入,若缺少kr8s会抛出提示安装 controller 依赖的RuntimeError),local时导入LocalReconciler;其它取值直接抛ValueError

此外入口还为SIGTERM/SIGINT注册了信号处理器(main.py),收到信号后调用reconciler.stop()优雅退出——对本地模式而言,这意味着正在运行的 Agent 子进程会被终止并标记为 FAILED(详见下文本地协调器部分)。项目要求 Python ≥ 3.12,并固定使用hydra-core==1.3.2omegaconf==2.3.0(见 pyproject.toml 的dependencies),因此下述配置语法均以 Hydra 1.3 为准。

完整默认配置

Controller 的完整默认配置来自 agentlightning/config/controller.yaml,全文如下:

runner_type: k8s agl_server: url: http://localhost:8080 agent_url: null key: "" k8s_runner: namespace: default ttl_after_finished: 1200 max_jobs_per_minute: 100 poll_interval: 5 local_runner: maximum_size: 50 poll_interval: 10

其中runner_type是全局开关,决定后四组参数中哪一组生效:k8s模式读取k8s_runner.*local模式读取local_runner.*。其余配置对两种模式都生效(尤其是agl_server.*)。

用 Hydra 命令行覆盖配置

启动时可以用任意 Hydra 命令行参数覆盖配置中的任意一项,无需修改 YAML 文件。例如切换到本地模式、指定 API Gateway 地址与鉴权 key、缩小进程池:

agl-controller \ runner_type=local \ agl_server.url=http://localhost:8080 \ agl_server.key="$AGL_KEY" \ local_runner.maximum_size=32

这种覆盖方式与项目其它组件(Trainer、API Gateway)保持一致:agl_server.key必须与 API Gateway 的 key 一致,否则请求会被拒绝。

runner_type:k8s 与 local 二选一

默认值说明
runner_typek8s本 Controller 实例使用的唯一执行后端:k8slocal

一个 Controller 实例同一时间只能运行一种模式,不能混用。两种模式的执行语义为:

  • k8s 模式:每个 rollout 运行为一个 Kubernetes Job。Controller 使用本机~/.kube/config的默认 Kubernetes 配置访问集群——从源码看,协调器通过kr8s.asyncio.api()惰性初始化集群客户端(见 k8s_reconciler.py),这正是依赖默认 kubeconfig 的原因;
  • local 模式:每个 rollout 运行在 Controller 所在机器上的一个本地子进程中,多个 rollout 由进程池统一调度。

连接 API Gateway:两个 URL、一个 key

Controller 配置中包含两个 API Gateway URL,对应两条不同的网络路径:

默认值说明
agl_server.urlhttp://localhost:8080Controller 自身使用的 API Gateway 地址
agl_server.agent_urlnullAgent 使用的 API Gateway 地址;为null时回退到agl_server.url
agl_server.key""Controller 与 Agent 共用的 Bearer key

两者可达性的要求不同:

  • agl_server.url必须能从 Controller 进程访问,因为 Controller 用它查询和打补丁/api/rollouts
  • agl_server.agent_url必须能从 Agent 进程(或 Pod)访问,因为注入到 Agent 的 Gateway 代理地址和事件上报地址就是基于它拼出来的。

大多数场景下agent_url保持null即可,Agent 自动复用agl_server.url。只有当 Agent 无法通过agl_server.url到达 Gateway 时才需要单独设置——典型场景是 Minikube Docker driver:Controller 跑在宿主机上,Agent 跑在 Minikube 内部,两者网络不通。此时 Controller 侧可用http://localhost:8080,而 Agent 侧需要http://host.minikube.internal:8080访问同一个 Gateway(controller.yaml 中的注释也给出了这个示例)。

源码印证了这个回退逻辑:构造 Job 时,k8s_reconciler.py 以agent_url优先、url兜底的方式确定 Agent 侧基址:

agent_base_url = str( controller_config.agl_server.get("agent_url", None) or controller_config.agl_server.url ).rstrip("/")

本地模式同样如此:local_reconciler.py 用self._config.agl_server.url拼接注入的环境变量。

k8s_runner:Job 创建限流与完成清理

默认值说明
k8s_runner.namespacedefaultJob 创建的命名空间
k8s_runner.max_jobs_per_minute100Controller 每分钟最多创建的 Kubernetes Job 数
k8s_runner.ttl_after_finished1200完成的 Job 被 Kubernetes 自动删除前保留的秒数
k8s_runner.poll_interval5周期性对账循环的间隔秒数

max_jobs_per_minute防止 Controller 在短时间内创建过多 Job,ttl_after_finished防止已完成的 Job 堆积、压垮 Kubernetes API server。

从源码看,这两个参数的落地机制很明确:

限流采用 60 秒滑动窗口。K8sReconciler用一个deque记录每次成功创建 Job 的时间戳(k8s_reconciler.py):每次创建前,先丢弃 60 秒(常量JOB_CREATION_WINDOW_SECONDS = 60)之前的时间戳,若窗口内计数达到max_jobs_per_minute,则记录日志并延迟本轮创建——rollout 保持QUEUING状态,等下一轮对账再来,不会失败。若创建时遇到 422/Unprocessable/invalid等非法规格错误,rollout 会被直接标记为FAILED并附带错误信息,避免反复重试坏模板。

TTL 直接写入 Job spec。build_job_spec 把ttl_after_finished写进spec.ttlSecondsAfterFinished,同时强制backoffLimit: 0restartPolicy: Never,保证每个 rollout 只执行一次、失败不重试、完成后由集群自动清理。若 rollout 配置了timeout_seconds,还会写入spec.activeDeadlineSeconds作为 K8s 侧的超时兜底。

双循环架构。K8sReconciler.run()并发运行两个任务(k8s_reconciler.py):

  1. _periodic_reconcile_loop——按poll_interval轮询QUEUING/RUNNING状态的 rollout,与集群中带app.kubernetes.io/managed-by=agentlightning标签的 Job 对账:为缺失的 QUEUING rollout 创建 Job,发现 RUNNING rollout 对应的 Job 消失时标记为FAILED("Job disappeared");
  2. _watch_jobs_loop——用kr8s.asyncio.watch监听 Job 的ADDED/MODIFIED事件,Job 出现Complete/Failedcondition 时立即把 rollout 更新为SUCCEEDED/FAILED,watch 中断后自动 5 秒重连。

也就是说,poll_interval影响的是"新 rollout 何时被领取"的延迟,而完成事件主要由 watch 循环低延迟处理。

local_runner:本地进程池的并发与同步

默认值说明
local_runner.maximum_size50Controller 机器上并发管理的 Agent 子进程数上限
local_runner.poll_interval10本地进程与 rollout 状态自动同步检查的间隔秒数

当进程池达到maximum_size时,排队的 rollout 会等待容量释放。源码层面(local_reconciler.py)每轮_reconcile_once会:

  1. 拉取QUEUING/RUNNING状态、limit 50 的 rollout 列表;
  2. 统计存活子进程数(returncode is None),仅为QUEUING且未达maximum_size的 rollout 派生子进程;
  3. 发现RUNNING状态但本地无对应进程时(例如 Controller 重启过),标记FAILED("local subprocess is not running"),保证状态不会悬挂;
  4. 对已超时的子进程发送SIGKILL整个进程组,并标记FAILED("local subprocess timed out")。

子进程本身以sys.executable -c方式启动一个 worker,start_new_session=True使每个 Agent 处于独立进程组,超时和 Controller 关闭时都能用os.killpg干净地连根终止(local_reconciler.py)。退出码为 0 则 rollout 标记SUCCEEDED,否则FAILED并附带退出码;Controller 收到停止信号时,存活子进程会被终止并标记为FAILED("local controller shutdown"),最后做一次收尾对账。

两种模式下 Agent 是如何被拉起的

k8s 模式:渲染 Jinja Job 模板。Trainer 配置中的agentlightning.k8s.job_template_path指定模板文件(模板写法与示例见 docs/4-trainer-configuration.md 的 "Rollout execution" 小节,仓库中 examples/calc_x/job-template.yaml 是一个可直接参考的完整示例)。模板文本被 Trainer 读入并写入每个 rollout,Controller 侧的build_job_spec(k8s_reconciler.py)负责最终渲染与装配:

  • 用 rollout 的input和确定性job_nameagl-rollout-{rollout_id})渲染 Jinja 模板,模板中可用的过滤器包括yaml_escape(用于把输入安全地写进 YAML 字符串);渲染结果必须是且仅是一个kind: Job的 YAML 文档,否则报错;
  • 补全metadata.name/metadata.namespace(来自k8s_runner.namespace)和管理标签app.kubernetes.io/managed-byagentlightning/rollout-idagentlightning/attempt-id,供 watch 与对账反查;
  • 每个容器注入 rollout 专属的三个环境变量:AGL_OPENAI_BASE_URL(指向 Gateway 的该 rollout 代理路径,含 train/val mode)、AGL_EVENT_URL(该 rollout 的事件上报端点)和AGL_KEY。若模板里已定义同名变量,则以 Controller 注入值覆盖。

tests/controller/test_k8s_reconciler.py 在无集群环境下验证了上述行为:断言标签集合、AGL_KEY注入,以及AGL_OPENAI_BASE_URL中包含/rollout/test-id/attempt/0/mode/train/路径,可以作为模板正确性的参照。

local 模式:agent_class + env_map。Controller 从 rollout 中读取config.local.agent_classconfig.local.env_map:动态导入 Agent 类(支持module.Classmodule:Class两种写法),在子进程中实例化并调用run()(兼容同步与协程,见 local_reconciler.py)。env_map把环境变量名映射到 rolloutinput中的字段路径(local_reconciler.py):

  • 支持点路径如input.question,以及列表索引如input.answers.0
  • 值为字符串时原样注入,非字符串(如列表、字典)自动json.dumps后注入,不可序列化时直接报错;
  • 路径在input中找不到会抛出local.env_map path not found错误。

例如 Trainer 侧配置:

agentlightning: local: agent_class: examples.search_r1.agents.search_r1_agent.SearchR1Agent env_map: QUESTION: input.question GOLDEN_ANSWERS: input.golden_answers

Controller 就会为每个 rollout 导入SearchR1Agent、启动一个本地子进程,并从该 rollout 的input中取出QUESTIONGOLDEN_ANSWERS环境变量。与 k8s 模式一样,rollout 专属的AGL_OPENAI_BASE_URLAGL_EVENT_URLAGL_KEY会自动注入。

适用前提与配置要点小结

  • 两种模式共用一套agl_server连接配置;key需要与 API Gateway(agentlightning/config/server.yaml 中的key字段)以及 Trainer 的agentlightning.agl_key保持完全一致;
  • k8s 模式要求 Controller 机器可读取~/.kube/config且对k8s_runner.namespace有 Job 操作权限;local 模式则要求 Controller 机器上能直接导入并运行agent_class,即 Agent 代码及其依赖必须安装在 Controller 环境里;
  • rollout 的timeout_seconds(Trainer 配置中的agentlightning.rollout_timeout_seconds,默认 1800)在两种模式下都由 Controller 负责执行:k8s 侧体现为activeDeadlineSeconds,local 侧体现为进程组SIGKILL
  • 默认值(Job 限流 100/分钟、TTL 20 分钟、本地并发 50、本地轮询 10 秒)是通用起点,实际训练中可按集群 API server 负载与 Controller 机器 CPU/内存容量,通过 Hydra 命令行参数逐项调整,无需改动 YAML。

参考文件:docs/6-controller-configuration.md、agentlightning/config/controller.yaml、agentlightning/controller/main.py、agentlightning/controller/k8s_reconciler.py、agentlightning/controller/local_reconciler.py、tests/controller/test_k8s_reconciler.py。

【免费下载链接】agent-lightningThe absolute trainer to light up AI agents.项目地址: https://gitcode.com/GitHub_Trending/ag/agent-lightning

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询