rlm 开发与扩展指南:编写 LM 客户端、REPL 环境以及环境 ↔ LM Handler 的通信协议
rlm 开发与扩展指南编写 LM 客户端、REPL 环境以及环境 ↔ LM Handler 的通信协议【免费下载链接】rlmGeneral plug-and-play inference library for Recursive Language Models (RLMs), supporting various sandboxes.项目地址: https://gitcode.com/GitHub_Trending/rlm/rlm本文基于 rlm 仓库根目录下的贡献者指南 AGENTS.md 展开系统讲解递归语言模型Recursive Language Models, RLM推理库rlm的工程规范、两类扩展点rlm/clients/中的 LM 客户端与rlm/environments/中的 REPL 环境的完整实现模式以及环境与 LM Handler 之间基于 TCP socket 与 HTTP Broker 的通信架构。读完后你能够按仓库标准独立编写并注册新的模型客户端或沙箱环境并理解llm_query()/rlm_query()在代码执行期间如何跨进程/跨机器路由回宿主机的 LM 服务。一、开发环境搭建仓库指南明确使用 uv 作为开发工具链Python 版本建议 3.12README 中声明运行时最低要求为 Python 3.11# 安装 uv首次 curl -LsSf https://astral.sh/uv/install.sh | sh # 如需初始化空白项目 uv init uv venv --python 3.12 source .venv/bin/activate # 以可编辑模式安装 uv pip install -e . # 启用 Modal 沙箱支持 uv pip install -e .[modal] # 启用 Prime 沙箱支持 uv pip install -e .[prime]核心开发工作流在 AGENTS.md 中给出的标准命令为# 常规开发同步 uv sync # 安装 dev test 依赖组 uv sync --group dev --group test # 安装 pre-commit 钩子 uv run pre-commit install仓库同时附带 Makefile提供make install安装基础依赖与make check运行 linter、formatter 与测试两个常用入口与 AGENTS.md 的 PR 前检查清单相互呼应。二、通用工程规范2.1 代码风格与类型标注AGENTS.md 对风格与类型化给出三条硬性约束格式化严格使用ruff所有 PR 必须通过ruff check --fix .类型标注优先显式类型。可接受cast(...)、assert ...做类型收窄简单的参数场景如 prompt 处理器可以接受无类型参数不接受没有充分理由的# type: ignore命名约定方法与变量用 snake_case类用 PascalCase如LocalREPL、PortkeyClient常量用 UPPER_CASE如_SAFE_BUILTINS、RLM_SYSTEM_PROMPT除非明确要求不要给私有方法加_前缀。2.2 错误处理哲学指南的核心立场是fail fast, fail loud快速失败、大声失败不做防御性编程不做静默回退最小化分支优先单一代码路径每个if/try都需要正当理由典型例子缺少 API key 时应立即抛出ValueError而不是优雅降级。这一点在源码中可以得到印证get_client() 遇到未知 backend 时直接raise ValueErrorget_environment() 同样以ValueError拒绝未知环境名二者都没有任何兜底路径。2.3 依赖、测试、文档与改动范围维度要求依赖避免新增核心依赖非必需功能走 optional extras如modalextra例外是体积很小且能显著简化常用代码的依赖测试uv run pytest用例放在tests/下写简单、确定性的单元测试功能变更必须同步更新测试涉及隔离环境的测试要 mock 外部服务文档保持简洁可执行行为变化时同步更新 README避免内容重复改动范围小而聚焦的 diff一个 PR 只做一件事仅在不引入过多维护负担时才做向后兼容删除死代码而不是保留保护逻辑PR 提交前的完整检查清单# 风格 lint 检查 uv run ruff check --fix . uv run ruff format . uv run pre-commit run --all-files # 运行测试 uv run pytest同时确保文档与测试已按需更新、死代码已删除追求最小外科手术式 diff。三、开发 LM 客户端LM 客户端实现位于rlm/clients/所有客户端必须继承 BaseLM。3.1 基类接口BaseLM定义了四个必须实现的抽象方法见 rlm/clients/base_lm.py抽象方法职责completion(prompt)同步单次补全返回字符串acompletion(prompt)异步单次补全批量并发路径使用get_usage_summary()返回全部调用的聚合用量UsageSummaryget_last_usage()返回最近一次调用的模型级用量ModelUsageSummary基类构造函数签名提供了几个值得注意的默认行为timeout默认 300 秒模块级常量DEFAULT_TIMEOUTsampling_argstemperature、top_p、max_tokens、seed 等会作为**self.sampling_args转发给底层补全 API。3.2 实现要求与结构示例AGENTS.md 列出的硬性要求继承 rlm/clients/base_lm.py 中的BaseLM实现全部四个抽象方法按模型追踪用量调用次数、输入/输出 token同时支持字符串与消息列表两种 prompt 形态在 rlm/clients/__init__.py 中注册新客户端。仓库给出的标准骨架from rlm.clients.base_lm import BaseLM from rlm.core.types import ModelUsageSummary, UsageSummary class MyClient(BaseLM): def __init__(self, api_key: str, model_name: str, **kwargs): super().__init__(model_namemodel_name, **kwargs) # 初始化你的客户端 def completion(self, prompt: str | list[dict[str, Any]], model: str | None None) - str: # 同时处理 str 与消息列表两种格式 # 用 _track_cost() 记录用量 # 返回响应字符串 def get_usage_summary(self) - UsageSummary: # 返回跨全部调用的聚合用量3.3 配置规范环境变量只用于 API key并在 README 中说明硬编码默认 base URL 与合理默认值构造参数必要的定制项通过__init__()传入。3.4 客户端如何被路由与调用从 get_client() 的实现可以看到当前的路由表openai、vllm、portkey、openrouter、vercel、anthropic、gemini、azure_openai。其中vllm复用OpenAIClient并断言必须传入base_url本地 vLLM 服务地址openrouter与vercel则分别用setdefault注入默认网关地址——这正是上文硬编码合理默认值规范的直接体现。在运行时客户端并非被主进程直接调用而是被 LMHandler 包装成多线程 TCP 服务get_client(model, depth) 的路由逻辑是——显式指定了已注册的model时优先按模型名取客户端覆盖 depth 路由depth0走默认客户端主 backenddepth1走other_backend_client若存在否则回落到默认客户端。批量请求则经由 LMRequestHandler._handle_batched() 用asyncio.gather并发执行、以Semaphore(batch_max_concurrent)默认 16限流且单个 prompt 失败不会拖垮整个批次。四、开发 REPL 环境环境实现位于rlm/environments/需要先选择正确的基类模式基类适用场景抽象方法非隔离Non-isolatedNonIsolatedEnv本机执行、与 RLM 同机setup、load_context、execute_code隔离IsolatedIsolatedEnv云沙箱Modal、Prime 等setup、load_context、execute_code两个基类都继承自 BaseEnv其构造函数默认参数为persistentFalse、depth1、max_concurrent_subcalls4。4.1 实现要求继承 rlm/environments/base_env.py 中的NonIsolatedEnv或IsolatedEnv实现全部抽象方法setup、load_context、execute_codeexecute_code()必须返回REPLResult定义在 rlm/core/types.py处理lm_handler_address使llm_query()与rlm_query()能跨进程调用 LM实现cleanup()做资源管理在 rlm/environments/__init__.py 中注册环境。关键实现细节的语义setup()初始化全局/局部命名空间与辅助函数load_context()把上下文载荷作为context变量暴露给被执行的代码execute_code()执行代码并捕获 stdout/stderr返回REPLResult环境全局中必须始终提供llm_query、llm_query_batched、rlm_query、rlm_query_batched四个函数。4.2 状态管理执行代码可用的保留全局量AGENTS.md 规定每个环境必须向被执行的代码提供以下全局变量其中保留名集合在 RESERVED_TOOL_NAMES 中有精确对应全局名语义context已加载的上下文载荷llm_query(prompt, modelNone)纯单次 LM 补全不进 REPL、不迭代llm_query_batched(prompts, modelNone)批量纯 LM 补全rlm_query(prompt, modelNone)递归子 RLM 调用拥有独立 REPL 与迭代达到最大深度时回落到llm_queryrlm_query_batched(prompts, modelNone)批量递归子 RLM 调用answer字典{content: , ready: False}模型写入answer[content]并置answer[ready] True后环境将内容挂到REPLResult.final_answer上SHOW_VARS()列出当前可用变量这些名字是保留的自定义工具不允许覆盖它们且每次代码执行结束后会被恢复防止命名空间被污染。这一机制由 validate_custom_tools() 在入口处强制校验冲突即抛ValueError。4.3 结构示例与环境检查清单from rlm.environments.base_env import NonIsolatedEnv from rlm.core.types import REPLResult class MyEnvironment(NonIsolatedEnv): def __init__(self, lm_handler_address: tuple[str, int] | None None, context_payload: dict | list | str | None None, **kwargs): super().__init__(**kwargs) self.lm_handler_address lm_handler_address self.setup() if context_payload: self.load_context(context_payload) def setup(self): # 初始化执行命名空间 def load_context(self, context_payload: dict | list | str): # 让 context 对执行代码可见 def execute_code(self, code: str) - REPLResult: # 执行代码并返回 REPLResult def cleanup(self): # 清理资源交付前的环境检查清单源自 AGENTS.md遵循上述指南环境能配合基础 RLM 补全调用正常工作cleanup()正确释放所有资源子 LM 调用通过llm_query()与rlm_query()可用保留名llm_query、rlm_query、context、history、answer、SHOW_VARS在每次执行后被恢复。当前get_environment()已支持local、ipython、modal、docker、daytona、prime、e2b七种环境新环境加入后在此处分发即可被RLM(environment...)使用。五、架构环境 ↔ LM Handler 的通信理解环境与 LM Handler 的通信方式是开发新环境的前提。AGENTS.md 给出了总体拓扑宿主进程中的 RLM 主循环与LMHandler一个ThreadingTCPServer互连非隔离环境如LocalREPL再通过同一 TCP socket 协议把llm_query()/rlm_query()转发给 LMHandler。5.1 Socket 协议非隔离环境协议格式4 字节大端长度前缀 UTF-8 JSON 载荷。发送侧实现见 socket_send()def socket_send(sock: socket.socket, data: dict) - None: payload json.dumps(data).encode(utf-8) sock.sendall(struct.pack(I, len(payload)) payload)接收侧 socket_recv() 先读 4 字节长度再按长度循环recv收满整个消息体若在消息收全前连接断开抛出ConnectionError。请求流程以llm_query为例代码执行期间环境内的llm_query(prompt)或rlm_query(prompt)被调用llm_query构造LMRequest并调用send_lm_request(address, request)rlm_query则通过subcall_fn派生一个子 RLM达到最大深度时回落到llm_query客户端向(host, port)建立新的 TCP 连接发送带长度前缀的 JSON 请求LMHandler由 LMRequestHandler.handle() 处理批量/单条分流、异常时以LMResponse.error_response回传而不是崩溃返回携带RLMChatCompletion或 error 字段的LMResponse。关键组件LMHandler多线程 TCP 服务器包装 LM 客户端支持上下文管理器start()/stop()LMRequest / LMResponse类型化的请求/响应 dataclass同时支持单条prompt与批量prompts两种形态send_lm_request() / send_lm_request_batched()socket 通信的 typed 辅助函数异常统一收敛为 error 响应而非抛出。5.2 HTTP Broker 模式隔离环境隔离环境Modal、Prime运行在云端机器上无法直连宿主机的 socket 服务因此采用 HTTP broker 中转。拓扑为宿主机上的ModalREPL轮询器通过隧道访问沙箱内的 Flask brokerbroker 内由执行脚本经localhost与之通信。工作流程沙箱创建环境创建云沙箱并在沙箱内部启动一个 HTTP broker 服务隧道暴露broker 通过供应商的加密隧道对外暴露如 Modal 的encrypted_ports代码执行沙箱内调用llm_query()时 POST 到http://localhost:8080/enqueue沙箱内 broker 基地址形如http://127.0.0.1:{broker_port}见 BROKER_URL请求入队broker 把请求排队并阻塞等待响应宿主轮询宿主侧ModalREPL后台线程轮询{tunnel_url}/pending获取新请求_poll_broker() 中轮询间隔为time.sleep(0.1)即 100msLM 转发宿主把请求经 socket 转发给LMHandler并取得响应响应回传宿主 POST 到{tunnel_url}/respond解除阻塞broker 用响应解除原始/enqueue调用的阻塞。Broker 端点表Flask 路由在 rlm/environments/modal_repl.py 中可逐一对应端点方法用途/enqueuePOST沙箱代码提交 LLM 请求阻塞直至响应/pendingGET获取待处理请求列表由宿主轮询器调用/respondPOST为某个 request ID 提交响应由宿主轮询器调用/healthGET健康检查关键实现细节broker 是运行在沙箱内部的 Flask 服务使用threading.Event做请求/响应的同步respond端点中entry[event].set()即解除阻塞点宿主侧 poller 线程在后台运行轮询间隔 100ms执行状态通过dill序列化持久化到/tmp/rlm_state.dillSTATE_FILE在代码块之间做 state 的存取。5.3 实现一个新的隔离环境AGENTS.md 给出的五步清单以新云厂商为例创建 broker 服务——实现/enqueue、/pending、/respond端点的 Flask/HTTP 服务暴露隧道——用供应商的隧道/端口转发把 broker 暴露给宿主实现 poller——宿主上的后台线程负责轮询并转发请求编写执行脚本——在沙箱内运行、其中llm_query()调用 broker 的脚本处理状态——在代码块之间序列化/反序列化执行状态。指南明确指定参考实现rlm/environments/modal_repl.py 是隔离环境的 canonical reference配合 tests/test_docker_repl_robustness.py、tests/test_local_repl_persistent.py 等测试用例可以对照验证行为。六、小结AGENTS.md 把 rlm 的扩展体系归纳为两条对称的路径新增模型后端只需继承BaseLM并实现四个抽象方法、接入get_client()路由新增执行环境则按隔离级别选择NonIsolatedEnv/IsolatedEnv补齐setup/load_context/execute_code/cleanup与六个保留全局量再接入get_environment()。而贯穿两类扩展的底层骨架是长度前缀 JSON over TCP的 socket 协议与面向云沙箱的 HTTP broker 中转模式——前者由 rlm/core/comms_utils.py 与 rlm/core/lm_handler.py 实现后者以 rlm/environments/modal_repl.py 为范本。遵循仓库fail fast、最小分支、死代码即删的工程哲学完成上述检查清单即可交付一个符合仓库标准的扩展 PR。【免费下载链接】rlmGeneral plug-and-play inference library for Recursive Language Models (RLMs), supporting various sandboxes.项目地址: https://gitcode.com/GitHub_Trending/rlm/rlm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →