danswer Craft:Docker 沙箱从逐消息 ACP 执行迁移到 opencode serve 长驻 HTTP 传输

发布时间:2026/9/10 0:56:02

danswer Craft:Docker 沙箱从逐消息 ACP 执行迁移到 opencode serve 长驻 HTTP 传输 danswer CraftDocker 沙箱从逐消息 ACP 执行迁移到 opencode serve 长驻 HTTP 传输【免费下载链接】danswerOpen Source AI Platform - AI Chat with advanced features that works with every LLM项目地址: https://gitcode.com/GitHub_Trending/da/danswer在 danswer 的 Craft智能体沙箱功能中自托管 docker-compose 部署的 Docker 沙箱管理器需要与 Kubernetes 后端对齐从每条用户消息临时 exec 一次opencode acp子进程切换为容器内长驻的opencode serveHTTP 传输端口 4096。读完本文你将理解这次迁移要解决的五个 Docker 专属阻塞点、serve 传输的密码/配置供给模型、OPENCODE_CONFIG_CONTENT单配置每容器的设计权衡以及共享 serve 管道在仓库源码中的落地位置与验证方式。本文基于设计文档 Docker sandbox → opencode-serve 展开该文档是 drop-acp-layer彻底删除 ACP 传输层的前置工作。结合当前仓库源码可以确认该迁移计划中的核心改动已经在代码中落地——入口脚本无条件拉起opencode serveDocker 管理器的容器创建参数已携带 serve 密码与配置共享 serve 管道也已抽出为独立模块下文逐一给出证据。一、背景逐消息 ACP 执行路径的问题迁移前DockerSandboxManager 的send_message会为每条用户消息生成一个 ACP exec 客户端通过docker exec在沙箱容器内临时执行opencode acp。这条路径有三个结构性缺陷逐进程的启动成本每条消息都要冷启动一次 opencode 子进程会话生命周期绑定单轮opencode acp进程的存活时间只覆盖一轮对话无法承载长会话状态上游 bug 无法规避opencode 1.15.7 存在丢失终结符的 bugACP 传输在该版本上不可靠。而 Kubernetes 后端早已迁移到长驻opencode serve的 HTTP 传输自托管 docker-compose 用户则仍停留在这条有 bug 的路径上。迁移目标就是让 Docker 后端复用同一套 serve 传输机制。二、五个 Docker 专属阻塞点设计文档把阻塞点全部归纳为 Docker 侧的缺口这也是本次 PR 的工作边界1. Docker 管理器没有 serve 客户端接线迁移前send_message虽然接收opencode_session_id/agent_provider/agent_model参数但标注noqa: ARG002 — serve-only后直接忽略没有_send_message_via_serve、没有ensure_opencode_session覆写、没有prompt_slot实现、没有事件总线。2. 没有密码供给机制K8s 管理器会为每个 Pod 创建V1Secret承载OPENCODE_SERVER_PASSWORD与OPENCODE_CONFIG_CONTENT。Docker 侧没有等价物build_container_create_kwargs原来只是一个{ONYX_PAT, ONYX_SERVER_URL}的环境变量白名单且有专门的测试锁定该白名单。当前仓库中该函数的签名已经扩展见 docker_sandbox_manager.py显式接收opencode_password与opencode_config_json两个参数docstring 明确记录了供给模型def build_container_create_kwargs( *, sandbox_id: UUID, user_id: UUID, tenant_id: str, image: str, onyx_pat: str, api_server_url: str, network: str, volume_name: str, memory_limit: str, cpu_limit: float, opencode_password: str, # 每次 provision 生成注入容器 env opencode_config_json: str, # OPENCODE_CONFIG_CONTENT 内容 provisioning_attempt_number: int, ...其 docstring 还点出了持久化策略密码由管理器在 provision 时生成并作为容器环境变量注入api_server 通过docker inspect读回而不落盘存储——这是 Docker 场景下与 K8s Secret 对等的唯一持久存储。3. provision 时机没有OPENCODE_CONFIG_CONTENT关键点来自 opencode_config.py 的模块文档opencode-serve loads config once at startup and does not hot-reload。即 opencode-serve 只在启动时加载一次配置不会热重载。迁移前 Docker 路径是在setup_session_workspace里逐会话写opencode.json文件这条路径对 serve 无效——它启动时读不到后来写入的 provider 列表。因此配置必须在容器创建时以OPENCODE_CONFIG_CONTENT注入。4. 镜像入口点已有 serve 分支但管理器从不触发沙箱镜像的 entrypoint.sh 本身就支持运行opencode serve。迁移前它由AGENT_TRANSPORTserve环境变量门控而 Docker 管理器从不设置该变量于是入口点落入tail -f /dev/null空闲分支靠逐消息 exec 驱动。当前仓库中该门控已经消失入口点直接进入带指数退避的重启循环无条件运行 serve见 entrypoint.shwhile true; do echo [entrypoint] starting opencode serve on 0.0.0.0:$OPENCODE_PORT ... opencode serve --hostname 0.0.0.0 --port $OPENCODE_PORT --print-logs child_pid$! wait $child_pid # 失败后指数退避重启上限 30s入口点同时处理了两件事OPENCODE_SERVER_PASSWORD为空时打告警serve 将无鉴权运行以及把 egress 代理的 MITM CA 导入 Chromium 的 NSS 数据库Chromium 只信任 NSS db 中的证书浏览器 HTTPS 才能工作。5. 网络可达性K8s 通过每 Pod 的 ClusterIP Serviceservice_name.namespace.svc.cluster.local:4096访问 opencode-serveDocker 则必须走onyx_craft_sandbox专用 bridge 网络、以容器名解析 4096 端口。不做宿主机端口映射——那会破坏沙箱隔离。api_server 必须与沙箱容器处于同一 bridgepush daemon 的 8731 端口已经是这个模式文档要求 PR 描述里声称无需改 compose之前先实际核对 compose 文件与既有 push-daemon 代码路径。三、关键设计决策3.1 为什么与删除 ACP 层的 PR 分离两种风险性质不同本 PR 是在原本单路径的管理器里新增一条代码路径爆炸半径是自托管用户而 drop-acp-layer 是删除已经跑过 soak 的代码爆炸半径是是否漏掉了某个分支。捆绑提交会让评审者无法区分失败的测试是Docker serve 有 bug还是漏删 ACP 分支。3.2 复用 K8s 的 serve 管道而非复制K8s 管理器拥有六块可直接映射到 Docker 的 serve 管道设计文档给出了逐条复用策略K8sDocker 等价物复用策略_get_service_name→ DNS 名bridge 上的容器名实现不同、接口相同各自保留_get_opencode_secret_name_provision_opencode_secretV1Secret创建容器时注入的 env 变量实现不同、接口相同密码生成secrets.token_urlsafe(32)可抽成小助手_read_opencode_password容器 env 的 dict 查找Docker inspect 读回实现不同_serve_base_urlfhttp://{container_name}:{OPENCODE_SERVE_PORT}平凡_wait_for_opencode_serve_ready对新 base_url 的相同逻辑上移到基类接收base_urlpassword_get_or_create_event_bus_build_serve_client_event_buses缓存相同逻辑上移到基类为 mixin 或默认实现这一条在仓库中已有明确落点共享管道现在位于 serve_transport.py其模块 docstring 说明了独立成文件的原因——Lives outsidebase.pyso the abstract supertype doesnt import concreteopencode.*modules (would invert the dependency arrow).SandboxManagercomposes_ServeMixinin.该 mixin 持有文档要求的_event_buses缓存、_event_buses_lock、_wait_for_opencode_serve_ready、_get_or_create_event_bus、_build_serve_client和_send_message_via_serve见 serve_transport.py。基类 SandboxManager.send_message 现在直接yield from self._send_message_via_serve(...)接口签名保留了agent_provider/agent_model作为每轮模型覆盖参数。3.3 环境变量白名单不变式test_docker_manager_config.py测试文件把build_container_create_kwargs锁定在固定 env 白名单上。迁移新增OPENCODE_SERVER_PASSWORD、OPENCODE_CONFIG_CONTENT、OPENCODE_SERVE_PORT、AGENT_TRANSPORTserve过渡期四个变量时必须同一变更里同时更新函数与测试并守住不变式容器 env 中不含 S3/MinIO/Postgres/Redis 凭据、不含 compose 服务主机名。密码的生命周期有严格约束在provision()期间生成、在build_container_create_kwargs被调用之前完成以参数形式透传api_server 磁盘上不存。唯一的持久存储是容器 env 本身_read_opencode_password通过client.containers.get(name).attrs[Config][Env]解析OPENCODE_SERVER_PASSWORD...行读回。当前源码的 docstring 印证了这一设计docker_sandbox_manager.pyThe api_server reads it back viadocker inspectrather than persisting it on disk.此外该函数对ONYX_SERVER_URL还有一道防御检测到看起来像 compose 内部主机名时打警告因为沙箱只加入 craft bridge 网络default 网络上的 DNS 会解析失败见 docker_sandbox_manager.py。3.4 单配置每容器而非每会话由于 opencode-serve 启动时加载 providerDocker 管理器必须删除setup_session_workspace与_regenerate_session_config中逐会话写opencode.json的分支K8s 在AGENT_TRANSPORTserve时已删除同样分支在 provision 时构建一次配置 JSON以OPENCODE_CONFIG_CONTENT注入每轮 provider 切换改走 prompt 正文里的 model overrideOpencodeServeClient.send_message已支持。逐会话的opencode.json文件成为死代码serve 从不读它还会污染快照——设计上选择干脆不写。opencode_config.py 同时展示了权限模型bash 工具层禁用了rm/ssh/scp/telnet/nc/base64等命令opencode.json与start-webapp.sh在 edit/write/read/grep/glob 层面全部 deny——这些保护规则正是随容器级配置下发、启动时一次性生效的。3.5 provision 时刻的 LLM provider 列表K8s 在 provision 时从数据库拉取所有已配置 provider 的LLMProviderConfigbuild_multi_provider_opencode_config使每轮模型覆盖可以跨 provider 切换而无需重启 opencode。Docker 的provision()原本只接收单个LLMProviderConfig。设计决策是本 PR 选保持单 provider 签名用build_opencode_config构建单 provider 的OPENCODE_CONFIG_CONTENT——行为与今天逐会话文件一致只是改在启动时注入。跨 provider 切换留给后续 PR一旦多 provider 配置接通即免费获得。3.6 Docker 快照今天不保留 opencode 历史普通工作区快照只捕获每会话的outputs/与attachments/不含 opencode 的沙箱全局数据目录。K8s 通过独立的/opencode-history/*sidecar 端点持久化历史Docker 暂无等价路径。迁移到 serve 后会话历史应通过独立的沙箱级归档保存而不是塞进每会话工作区快照且由于活跃的opencode serve进程可能存在 WAL 状态归档必须基于一致的 SQLite backup 生成而不是中途写入时的裸文件拷贝。四、Docker 管理器的接线步骤设计文档给出的实现顺序前四步完成即让新路径从约 500 行缩到约 50 行密码生命周期provision()中生成secrets.token_urlsafe(32)构建单 provider 的OPENCODE_CONFIG_CONTENT两者一起传入build_container_create_kwargs——当前源码中该函数签名已如第二节所示包含这两个参数。env 白名单扩展新增两个参数 OPENCODE_SERVE_PORT4096过渡期还有AGENT_TRANSPORTserve同步更新test_docker_manager_config.py并断言旧白名单不再匹配以捕捉双向回归。_serve_base_url返回fhttp://{_sandbox_container_name(sandbox_id)}:{OPENCODE_SERVE_PORT}。_read_opencode_password经self._docker.containers.get(name).attrs[Config][Env]读容器 env 并解析。send_message整个函数体除 packet 日志替换为调用基类_send_message_via_serve。ensure_opencode_session构建 serve 客户端并调client.ensure_session(None, cwdsession_path, title...)基类重构后直接继承即可。会话初始化删除setup_session_workspace中printf %s {opencode_json} {session_path}/opencode.json的行与_regenerate_session_config的写入分支AGENTS.md 保留。terminate 清理容器删除时事件总线与缓存的 opencode 密码一并回收基类的_terminated_sandboxes机制通用处理需在 Docker 的terminate()中接上并验证。参数命名边界OpencodeServeClient.send_message使用model_provider/model_id而管理器接口使用agent_provider/agent_model见 base.py。重命名发生在调用点随函数体一起上移Docker 的send_message保持管理器侧命名——不允许出现第三种命名约定。镜像侧无需改动现有镜像在收到AGENT_TRANSPORTserve时已会走 serve 分支因此本 PR 落地后只需在容器创建时注入该变量即可。AGENT_TRANSPORT是过渡变量随 drop-acp-layer 删除后入口点变为无条件——当前仓库的 entrypoint.sh 已经处于这个终态无条件拉起opencode serve并带重启循环。五、测试矩阵设计文档要求的验证分层external-dependency-unit保留对 Docker provision 出的沙箱容器的直连传输/事件矩阵测试K8s 侧的部署后 API/Celery 回合交接改由集成测试覆盖。unittest_docker_manager_config.py——env 白名单断言扩展到包含新增 serve 变量且断言旧白名单不再匹配新增 provision 密码测试——断言密码逐次 provision 重新生成、OPENCODE_CONFIG_CONTENT是合法的build_opencode_configJSON。仓库中现存的 test_docker_serve_plumbing.py 覆盖了 Docker serve 管道层面的单元测试。integrationtest_messages_api.py已泛化覆盖 serve 路径需验证其能在SANDBOX_BACKENDdocker下于 CI 运行否则参数化。不得回归test_docker_acp_exec_client.py验证的是由 drop-acp-layer 删除的路径本 PR 内必须继续通过ACP 代码当时仍在树中。从当前仓库源码检索DockerACPExecClient已无任何命中可以确认 ACP 层已按后续 PR 删除完毕。六、范围外事项Docker 的多 providerOPENCODE_CONFIG_CONTENT单 provider 与现状等价需要时再做删除 Docker ACP exec 客户端属于 drop-acp-layer修改网络策略/compose 把 4096 暴露到宿主机——沙箱到宿主机的端口映射是明确的隔离违规bridge 网络是唯一路径Docker 上的跨 provider 每轮切换多 provider 配置接通后免费获得独立 PR。小结这次迁移的本质是让自托管 docker-compose 用户获得与 Kubernetes 部署完全对等的流式传输能力长驻opencode serve 密码与配置随容器创建注入 bridge 网络按容器名寻址。工程上的三个关键取舍值得记录——共享管道上移到基类 mixin子类只需实现_serve_base_url与_read_opencode_password、密码只存在于容器 env 且经docker inspect读回不落盘、容器级单配置替代逐会话配置因为 serve 不热重载。仓库当前状态显示这些决策已全部落地入口点无条件运行 serve 并带退避重启build_container_create_kwargs携带 serve 凭据SandboxManager.send_message统一经由 serve_transport.py 的共享管道ACP 逐消息 exec 路径已从代码树中消失。【免费下载链接】danswerOpen Source AI Platform - AI Chat with advanced features that works with every LLM项目地址: https://gitcode.com/GitHub_Trending/da/danswer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/10 0:56:02

Linux 内核 Ramoops 持久化 Oops/Panic 日志机制完全指南

Linux 内核 Ramoops 持久化 Oops/Panic 日志机制完全指南 【免费下载链接】linux Linux kernel source tree 项目地址: https://gitcode.com/GitHub_Trending/li/linux 本指南系统讲解 Linux 内核中 ramoops(RAM Oops/Panic logger)的完整原理与实…

2026/9/10 1:46:08

CANN/ge ATC工具输出类型参数说明

--output_type 【免费下载链接】ge GE(Graph Engine)是面向昇腾的图编译器和执行器,提供了计算图优化、多流并行、内存复用和模型下沉等技术手段,加速模型执行效率,减少模型内存占用。 GE 提供对 PyTorch、TensorFlow …

2026/9/10 1:46:08

GE ATC工具输出参数说明

--output 产品支持情况 全量芯片支持 功能说明 如果是开源框架的网络模型: 存放转换后的离线模型的路径以及文件名,例如:$HOME/module/out/tf_resnet50,转换后的模型文件名以指定的为准,自动以.om后缀结尾&#xff…

2026/9/10 1:46:08

CANN/ge ACL模型执行OM描述

aclmdlExeOMDesc 【免费下载链接】ge GE(Graph Engine)是面向昇腾的图编译器和执行器,提供了计算图优化、多流并行、内存复用和模型下沉等技术手段,加速模型执行效率,减少模型内存占用。 GE 提供对 PyTorch、TensorFlo…

2026/9/9 13:11:35

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/8 7:15:15

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/9 16:31:09

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/10 0:00:55

目录对比去重实战:用哈希算法精准清理重复文件

我电脑里现在还有一块换了三次机的“数据墓地”硬盘,里面存着2016年以前所有旧笔记本的完整备份。平时不觉得有什么,直到前阵子想把它整理归档,发现同一个安装包、同一批照片、同一份论文草稿,在几个不同的备份目录里反复出现。更…

2026/9/10 0:00:55

Leaflet离线地图完整Demo合集:内网部署与坐标纠偏实战

简介:这是一份面向Web GIS开发者的LeafLet离线地图示例合集,帮助开发者快速掌握离线地图从搭建到交互的完整流程。压缩包共723个文件,大小14.06MB,以319个js脚本、175个html页面和29个css样式文件为主体,配合png/svg图…

2026/9/10 0:00:55

MATLAB读取Rinex 3.02观测文件:多系统GNSS数据解析实战

简介:基于MATLAB开发的Rinex3.02版观测文件(o文件)读取代码包,面向卫星定位导航方向的学习者与研究人员,用于解决新版观测文件的数据解析、历元提取与时间转换问题。压缩包共4个文件,包含两个m脚本、一个19…

2026/9/7 16:23:03

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/7 22:46:00

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/9 10:21:54

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码