OmniRoute Devin Claude Bridge 技术解析:让 Claude Code 前端驱动 Devin ACP 模型回复的安全桥接方案
【免费下载链接】OmniRouteNever stop coding. Free MIT AI gateway: one endpoint, 352 providers (150+ free), 1200+ models Kimi, Claude, GPT, Gemini, GLM, DeepSeek, MiniMax. Works with Claude Code, Codex, Cursor, OpenCode, Cline & Copilot. Quota-aware auto-fallback, RTK+Caveman compression saves 15-95% tokens, MCP/A2A, Desktop/PWA. Built by 550+ contributors项目地址: https://gitcode.com/GitHub_Trending/om/OmniRoute
docs/DEVIN_CLAUDE_BRIDGE.md描述了 OmniRoute 仓库中一个被称为Devin Claude Bridge(桥接代号devin-cli-agentic)的容器化集成方案:它允许真实的 Claude Code 运行时通过 OmniRoute 本地 Anthropic Messages 端点工作,而由官方 Devin CLI 经 ACP(Agent Client Protocol)stdio 提供模型回复。本文围绕该文档展开,并结合仓库源码深入讲解其架构、工具信封契约、隔离威胁模型、安装使用与验证清理流程,帮助读者理解如何在不触碰宿主 Claude 账号、不产生 Claude 出站流量的前提下,用 Claude Code 的体验去消费 Devin 模型。
解决的问题与定位
devin-cli-agentic的设计目标是让 Claude Code 充当“客户端外壳”,而把每次模型推理真正交给 Devin 账号侧的模型(仓库 live 验证使用的模型为swe-1-7-lightning)。具体链路为:
Claude Code 2.1.220(隔离的非 root Linux 容器) -> http://omniroute:20128/v1/messages -> devin-cli-agentic(Claude 格式、无鉴权 provider) -> devin acp --agent-type summarizer(官方 ACP stdio,不带 Devin 工具) -> 专用 devin-auth 卷中的 Devin 账号关键定位是不侵入既有能力:它不修改已有的 Anthropic、Claude OAuth、Claude Web 或devin-cli等 provider,也不改动官方 Devin CLI 本身。从源码看,它只是 OmniRoute 众多 executor 之外新增的一个 executor 类型(见 open-sse/executors/devin-cli-agentic.ts),通过 provider 注册表以独立 id 暴露(open-sse/config/providers/registry/devin-cli-agentic/index.ts)。该 provider 的format为claude、baseUrl为devin://acp/stdio、authType与authHeader均为none——因为认证完全由隔离卷内的官方 Devin CLI 自持,OmniRoute 既不导入也不持久化宿主凭据。
为什么必须用summarizer角色:无工具 ACP 代理的选择
文档强调了一个反直觉的设计:官方 Devin CLI 的默认 ACP agent 可以自行执行工具,因此桥接器不使用它,而是固定启动summarizerACP agent——该角色的官方 CLI 模式不携带任何工具——再把序列化后的 Anthropic 请求“伪装”成一段执行轨迹(execution trace)交给它。这样 Deven 侧永远不能真正执行工具,全部工具动作的所有权都归于 Claude Code 客户端。
这一点可以在执行器的子进程调用中得到印证:open-sse/executors/devin-cli-agentic.ts 处spawn(devinBin, ["acp", "--agent-type", "summarizer"], ...),子进程环境会被收敛到DEVIN_AGENTIC_HOME(必须是以/home/bridge或/.sandbox/开头的绝对路径,否则直接抛unsafe_devin_home错误)。同时,执行器逐帧解析 ACP 的 stdout JSON-RPC 消息:一旦收到session/update或$/update中的tool_call/tool_call_update事件,会立即判定为“Devin 尝试在内部执行工具”,以devin_internal_tool_execution错误终止本轮——因为所有工具执行必须由 Claude Code 客户端拥有。
为了保证summarizer的输出行为可控,执行器在把提示词交给 ACP 前会拼装一个[Devin Summarizer Bridge]前缀提示:要求把内容当作执行轨迹、若还需要客户端动作则必须返回恰好一个<tool>JSON 信封且不得夹带散文、明确“客户端工作区是 /workspace、/home/bridge 仅是隔离的 Devin 进程家目录”、禁止用 Markdown 围栏包裹响应。这些规则与工具解析器的强制校验共同构成了双保险。
请求/响应转换:序列化器与工具信封解析器
桥接的核心是两段可独立测试的转换逻辑:把 Anthropic Messages 请求序列化成纯文本轨迹,以及把模型文本回复解析回 Anthropictool_use。
序列化器:Anthropic 请求 → Devin 执行轨迹
open-sse/executors/devin-agentic/serializer.ts 的serializeAnthropicForDevin会保留以下内容块与元数据:
system:文本型系统块被规整为[System]段,其它类型的系统块直接报unsupported_system_block;- 角色消息
text:按User/Assistant标签折叠为[User]/[Assistant]段; tool_use/tool_result:历史消息中的工具调用被记录进knownToolUses集合,重复的tool_use id、引用未声明工具、孤立的tool_result都会显式失败;thinking与redacted_thinking:分别序列化为[Thinking]与[Redacted Thinking];tool_choice:auto/any/none/ 显式tool会被翻译成对应的[Tool Choice]指令行;- Claude Code 提供的工具目录:以
[Available Tools]+ 每个工具的[Tool](含 description 与完整 JSON Schema 的input_schema)呈现,并要求模型用<tool>{"name":"ToolName","arguments":{}}</tool>的 XML 信封请求工具; - 图片块与未知块会显式抛错(
unsupported_image_block/unsupported_content_block),这正是文档所述“图像不支持”的底层原因。
大工具结果存在可见截断:MAX_TOOL_RESULT_CHARS为 65536 字符,超出部分以[TRUNCATED n CHARACTERS BY OMNIROUTE]标记结尾(serializer.ts),保证上下文进入 Devin 前体积可控。
工具信封解析器:模型回复 → Anthropic tool_use
open-sse/executors/devin-agentic/toolParser.ts 的parseDevinToolRequest对每个模型回合只接受一个独立<tool>{...}</tool>信封:
- 零个匹配 → 纯文本完结响应;
- 多于一个匹配 → 抛
multiple_tool_requests(并行工具调用不支持); - 匹配但整段文本不严格等于信封本身 → 抛
mixed_tool_narrative(拒绝“叙事夹带动作”); - 信封内不是合法 JSON →
invalid_tool_json; - 缺少
name/ 名字不在请求工具列表 →missing_tool_name/unknown_tool; - 参数用该工具的 JSON Schema 逐字段递归校验(支持
type、enum、required、properties、additionalProperties: false、数组items),不通过 →invalid_tool_arguments。
通过后,工具调用的 id 由sha256(idSeed:name:stableJson(arguments))的前 16 位十六进制派生,格式为tool_devin_<digest>,保证同一请求下确定性且可追踪。这套校验对应文档所述“performs one bounded repair”:执行器定义了REPAIRABLE_TOOL_ERRORS(见 open-sse/executors/devin-cli-agentic.ts),对invalid_tool_json、missing_tool_name、unknown_tool、invalid_tool_arguments、multiple_tool_requests、mixed_tool_narrative、unexecuted_tool_intent等可修复错误,会追加一段[Single Repair Attempt]提示再给 Devin 一次机会;若修复轮仍违反“必须返回信封”的约束(如继续叙述式动作),则以显式错误结束,绝不放行。
值得一提的启发式是describesUnexecutedToolIntent:当文本中出现I'll / let me / next steps / planned actions / still need to等未来动作措辞却又没有真正发出信封时,执行器会把它当作“叙述了未执行的工具意图”拒绝掉,这正是文档“补偿 summarize 形状的中间响应”一句的源码体现。
响应装配:Claude 生命周期事件的重构
得到 Devin 文本或解析出的工具后,执行器不再增量转发 ACP 分块,而是把完整 ACP 回合收集完之后,再按 Anthropic 规范重新拼装响应(open-sse/executors/devin-agentic/anthropicResponse.ts):
- 文本结局:
content: [{ type: "text" }]+stop_reason: "end_turn"; - 工具结局:
content: [{ type: "tool_use" }]+stop_reason: "tool_use",随后 Claude Code 在本地执行该tool_use,并把tool_result通过 OmniRoute 传回,形成下一轮输入; - 流式场景:
buildClaudeSseFrames会按message_start → content_block_start/delta/stop(每个块)→ message_delta → message_stop的顺序发出合法的 Anthropic SSE 事件(anthropicResponse.ts),因此对 Claude Code 而言上游完全是一个标准 Anthropic 端点; - token 统计方面,
usage.input_tokens来自序列化文本的估算值(estimateTokens),output_tokens来自回复文本估算,字段齐全以兼容 Claude Code 的用量处理。
Provider 与模型命名约束
Provider 注册信息(open-sse/config/providers/registry/devin-cli-agentic/index.ts)显示它默认contextLength为 200000,并把 Devin 模型目录中的所有模型标注为toolCalling: true、supportsReasoning: false、supportsVision: false——这与文档中“vision、thinking 输出、effort 控制不被宣传”的 Limits 一致。
模型目录来自 open-sse/config/providers/registry/devin/catalog.ts,包含swe-1-7-lightning/swe-1-7等 SWE 系、以及 Devin 平台上可用的若干第三方模型 id。所有配置的模型都必须保留devin-cli-agentic/前缀,例如devin-cli-agentic/swe-1-7-lightning。compose 文件中的默认环境变量即以此形式出现(docker/devin-bridge/compose.yml):
DEVIN_BRIDGE_MODEL(同时映射到ANTHROPIC_MODEL),默认devin-cli-agentic/swe-1-7;DEVIN_BRIDGE_SONNET_MODEL→ANTHROPIC_DEFAULT_SONNET_MODEL;DEVIN_BRIDGE_OPUS_MODEL→ANTHROPIC_DEFAULT_OPUS_MODEL;DEVIN_BRIDGE_HAIKU_MODEL→ANTHROPIC_DEFAULT_HAIKU_MODEL;DEVIN_BRIDGE_SUBAGENT_MODEL→CLAUDE_CODE_SUBAGENT_MODEL(子代理模型)。
这些别名都可在.env.devin-bridge中覆盖,且任何配置值都必须保持devin-cli-agentic/前缀,否则会在assertKnownDevinModel(open-sse/executors/devin-cli-agentic.ts)处因“不在当前 Devin catalog”而被拒绝。
隔离与威胁模型
文档反复强调:宿主机的 Claude 安装、账号与配置一律视为禁区。Compose 清单(docker/devin-bridge/compose.yml)逐条落实了这些约束:
- 服务以
user: "10001:10001"、只读根文件系统(read_only: true)、cap_drop: [ALL]、security_opt: no-new-privileges运行;可写路径仅限 tmpfs(/tmp与/opt/omniroute/.source两个临时卷); - 私有
HOME=/home/bridge、专用CLAUDE_CONFIG_DIR=/home/bridge/.claude-devin-isolated、隔离的 OmniRoute 数据目录与 SQLite 文件、独立devin-auth卷; - 只挂载一次性
.sandbox工作区与证据目录,不挂载宿主 home、Keychain、SSH、云凭据或 Docker socket; - 显式构造子进程环境并删除Anthropic 相关变量——源码中
CLAUDE_ENV_BLOCKLIST会剔除ANTHROPIC_API_KEY、CLAUDE_CODE_OAUTH_TOKEN、ANTHROPIC_BEDROCK_BASE_URL、ANTHROPIC_VERTEX_BASE_URL、CLAUDE_CODE_USE_BEDROCK、CLAUDE_CODE_USE_VERTEX、CLAUDE_CODE_USE_FOUNDRY等(open-sse/executors/devin-cli-agentic.ts); - Claude Code 的推理只指向
http://omniroute:20128,携带本地专用 keysk-local-devin-gateway(OMNIROUTE_API_KEY与ANTHROPIC_AUTH_TOKEN一致),并开启REQUIRE_API_KEY; - 服务之间通过内部网络通信:offline profile 使用
bridge-internal(internal: true)。
双向出站守卫
网络层面的控制分为两套 guard:
- Claude 侧(deny-all 策略):
claude-egress-guard服务对所有 profile 生效,GUARD_POLICY: deny-all,并把代理审计写入.sandbox/guard-audit/claude/egress.jsonl。claude 容器通过HTTP_PROXY/HTTPS_PROXY指向该 guard,NO_PROXY: omniroute仅放行本地 OmniRoute,因此api.anthropic.com、claude.ai等一律被拒; - Devin 侧(devin 白名单策略,仅 live profile):
network-guard的GUARD_POLICY: devin只允许.devin.ai/.cognition.ai后缀及server.codeium.com/unleash.codeium.com两个精确主机(docker/devin-bridge/network-guard/policy.mjs)。guard 会解析 TLS ClientHello 中的 SNI 来做域名判定(同文件parseTlsClientHelloSni),并对逐跳头与host头做清洗后转发。OmniRoute live 容器只有在代理 URL 精确等于受信任值http://network-guard:8080时才会把HTTP_PROXY/HTTPS_PROXY注入 Devin 子进程环境(open-sse/executors/devin-cli-agentic.ts),其余情况一概不注入代理。
guard 审计文件只由其对应的 guard 进程挂载,防止篡改;脚本在导出 token-free 证据前会校验文件属主、权限模式、链接数与每一条决策记录。
独立隔离验证
隔离证明可以独立运行:
./scripts/devin-bridge/verify-anthropic-isolation它验证:拓扑与命名挂载、非 root 与只读设置、显式本地路由、敏感环境变量缺失、Docker socket 不可达、api.anthropic.com与claude.ai被阻断、仅选中 Devin provider,以及ACP 后端不可用时显式失败(fail-closed,绝不回退到别的 provider)。
首次安装与日常使用
构建钉定镜像
./scripts/devin-bridge/build镜像基于node:26.0.0-bookworm-slim,Dockerfile(docker/devin-bridge/Dockerfile)把 Node、Claude Code 与 Devin CLI 全部钉在构建参数里:CLAUDE_CODE_VERSION=2.1.220、DEVIN_CLI_VERSION=3000.2.17。Devin CLI 从官方静态地址下载,并分别校验 amd64 / arm64 两套架构的sha256sum后才安装;构建期还会执行一次 OmniRoute 的npm run build(把DATA_DIR临时指向/tmp),确保镜像内产物可用。Claude Code 通过npm install --global安装,同样固定版本。
仅对隔离的 Devin 卷做登录
ENABLE_LIVE_DEVIN_TESTS=1 ./scripts/devin-bridge/login-devin该命令走的是官方为远程/容器环境设计的 manual-token 流程。文档特别说明:令牌值直接输入到 CLI 提示符中,不会被当作进程参数传递、不会被写入 Git,也不会从宿主复制。认证只落在隔离的devin-auth卷中。
启动隔离的 Claude Code 运行时
./scripts/devin-bridge/launch从 scripts/devin-bridge/launch 源码可见,launch会依次完成:清理既有 compose 资源、准备.sandbox工作区、运行verify-anthropic-isolation复核隔离、重置 Claude 与 Devin 的 egress 审计、启动network-guard与claude-egress-guard、用bridge_run_devin models list做模型发现并借助scripts/devin-bridge/select-live-model.mjs从 live 目录中挑选模型,再以devin-cli-agentic/<model>形式导出全部DEVIN_BRIDGE_*_MODEL变量,最后拉起omniroute-live并运行claude-live服务。它永远不会执行宿主的 Claude 可执行文件——Claude 只在容器内运行。
验证命令:离线可复现 + 在线实况
离线路径(无需 Devin 账号、无运行时联网)
./scripts/devin-bridge/test-unit ./scripts/devin-bridge/test-contract ./scripts/devin-bridge/test-e2e-mock ./scripts/devin-bridge/verify-anthropic-isolation离线 profile 中 OmniRoute 的 Devin 子进程被替换为仓库内的确定性 mock(CLI_DEVIN_AGENTIC_BIN指向 docker/devin-bridge/mock-devin.mjs),mock 动作记录到.sandbox/evidence/mock-acp.jsonl;contract服务通过 docker/devin-bridge/run-contract.mjs 验证桥接契约。
在线实况路径(需认证、可选)
ENABLE_LIVE_DEVIN_TESTS=1 ./scripts/devin-bridge/test-live-devinlive runner 会在三个场景之间等待间隔,避免瞬间爆发式打开 ACP 会话;它校验的是结构化的 Claude 流式事件而非文本声明(docker/devin-bridge/run-claude-live-e2e.sh)。三个场景分别证明:
- 直接的项目读取与缺陷分析;
- 一次真实的
Edit、一次客户端拥有的Bash执行npm test并返回终端结果; /bridge-check加bridge-proof技能发现、项目读取、又一次成功的客户端npm test,并在无遗留工作时正常收尾。
文档给出的当前状态为offline 与 live 均已验证:钉定的 Claude Code2.1.220通过 Devin CLI3000.2.17与模型swe-1-7-lightning完成了上述三个隔离场景,最终 live 运行证实了客户端拥有的Read/Edit/Bash轮次、成功的npm test结果、项目命令与技能发现、仅走 Devin 的路由,以及Devin 侧审计非空而 Claude 出站审计必须保持为空的最终门禁。
诊断与证据文件
- 查看本地路由与消毒后的 executor 错误:
docker compose -f docker/devin-bridge/compose.yml --profile offline logs omniroute.sandbox/evidence/mock-acp.jsonl:确定性 mock ACP 动作记录;.sandbox/evidence/claude-stream.jsonl:真实 Claude Code 离线运行记录;.sandbox/evidence/live-*.jsonl:三个已通过的 live 流记录;.sandbox/evidence/egress.jsonl与.sandbox/evidence/claude-egress.jsonl:两份经过校验、token-free 的 guard 审计副本。
清理
仅停掉桥接自有的容器与网络(保留登录/配置卷):
./scripts/devin-bridge/clean连带删除命名卷的彻底清理:
./scripts/devin-bridge/clean --all升级钉定组件
镜像把 Node、Claude Code、Devin CLI 全部钉在 docker/devin-bridge/Dockerfile 中。升级步骤文档给出五个阶段:
- 修改显式版本号;
- 把两个架构(amd64/arm64)的 Devin 官方归档校验和替换为官方新产物对应的值;
- 重建镜像并运行全部离线验证命令(
test-unit、test-contract、test-e2e-mock、verify-anthropic-isolation); - 确认镜像内实际版本与预期一致;
- 重新跑一遍认证的三场景 live 套件。
注意两条红线:不要把任一 CLI 全局安装到宿主,不要用未验证来源的下载替代校验和验证。
明确的边界与限制
桥接的取舍在文档中写得很直白,理解这些有助于避免误用:
- 依赖固定无工具的
summarizer角色,因为 Devin CLI3000.2.17未暴露中性的无工具 ACP agent;适配器会补偿 summary 形状的中间响应,但仅有一次受限修复,失败即显式报错; - live ACP 调用可能偶发
502/504,harness 会拉开场景间隔;持续失败保持 fail-closed,永不选择其它 provider; - ACP 上下文由每次 Anthropic 请求重新构建,没有进程/会话亲和性;
- 每个模型响应只支持一个工具调用,并行调用被拒绝;
- 图片显式不支持;不宣传 vision、thinking 输出、effort 控制与 1M 上下文窗口——尽管模型目录中的部分条目带有大 context 标注,桥接仍按实际能力保守配置;
- SSE 使用合法的 Anthropic 生命周期事件,但在有界的 ACP 回合完整收集后才整体发出,ACP 分块不做增量转发。
小结
Devin Claude Bridge(devin-cli-agentic)在 OmniRoute 中是一个“把客户端能力与推理后端解耦”的范例:Claude Code 负责读代码、编辑、跑测试等客户端工具,Devin 账号只负责在summarizer无工具角色下产出文本或单个<tool>信封,所有穿越边界的内容都经过严格序列化、Schema 校验与网络守卫审计。从 compose.yml 的容器隔离、serializer.ts 的轨迹折叠、toolParser.ts 的信封解析到 open-sse/executors/devin-cli-agentic.ts 的 ACP 回合管理,每一条安全与契约约束都能在源码中找到对应实现,并配有一整套离线可复现与在线实况的验证脚本(scripts/devin-bridge/)。对于需要“不同账号体系模型 + 现有 Claude Code 工作流”的团队,这是一个可直接参照的、可验证的桥接与隔离范式。
【免费下载链接】OmniRouteNever stop coding. Free MIT AI gateway: one endpoint, 352 providers (150+ free), 1200+ models Kimi, Claude, GPT, Gemini, GLM, DeepSeek, MiniMax. Works with Claude Code, Codex, Cursor, OpenCode, Cline & Copilot. Quota-aware auto-fallback, RTK+Caveman compression saves 15-95% tokens, MCP/A2A, Desktop/PWA. Built by 550+ contributors项目地址: https://gitcode.com/GitHub_Trending/om/OmniRoute
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考