跳到主要内容

边缘代理与 AI 网关:一次 LLM 请求怎么被截获并转发

30 秒导读: Helicone 的可观测性能看到你每一次 LLM 调用,前提是这些调用先流过它。这一章讲的就是「流过」的前半程——一条请求打到 Helicone 的边缘 Worker,如何被接住、认出是哪个组织、选好去哪个提供商,然后转发出去。打出去之后怎么把日志留下来,是下一章的事。

本章属于 Helicone 系列的第 01 章,只讲「请求进来 → 打给 provider」这半程,它是整套可观测性的数据入口。想先看全景请回到 index.md


1. 这是什么(零基础也能懂)

一句话定义: Helicone 的「边缘代理」是一个跑在 Cloudflare Workers 上的中间人——你本来直接调 api.openai.com,现在把地址换成 Helicone 的域名,请求先到 Helicone,它转发给 OpenAI,顺手把这次调用记下来。

它解决什么问题。 假设你有个 App 在调 GPT-4,你想知道:花了多少钱、慢不慢、谁在用、出没出错。直接调 OpenAI 你什么都看不到。最省事的办法:改一个 base URL,让流量绕道 Helicone。

用起来就是这么轻:

# 示意,非源码:接入 Helicone 只改一行 base_url
from openai import OpenAI

client = OpenAI(
base_url="https://oai.helicone.ai/v1", # 原本是 https://api.openai.com/v1
default_headers={
"Helicone-Auth": "Bearer sk-helicone-xxx", # 告诉 Helicone 你是谁
},
)
# 之后照常调用,请求会先经过 Helicone 再到 OpenAI
resp = client.chat.completions.create(model="gpt-4", messages=[...])

一句话直觉: 把 Helicone 想成你和大模型之间的一道收费站。车(请求)必须过站,过站时拍个照、记个账(可观测性),然后照常放行到目的地(真实 provider)。这一章讲的就是「进站到出站」这一段路。

两种接法,先建立心智模型。 Helicone 的边缘其实提供了两类差别很大的入口:

接法你给的地址谁决定去哪个 provider典型场景
经典代理(proxy)按 provider 分的子域名,如 oai.helicone.ai域名/请求头就锁定了(你自带 key)只想加一层观测,少改代码
AI 网关(AI Gateway)统一入口 ai-gateway.helicone.aiHelicone 按模型名选路、可回退多家想要多 provider 路由、按量计费、故障转移

两者前半程的差别就是本章的主线;但你会看到一个漂亮的收束:它们最后都汇到同一个转发函数


2. 顶层全景(半程怎么转)

先看这半程的骨架。怎么读这张图:从上到下是一条请求的时间顺序,左边是"经典代理"路、右边是"AI 网关"路,两条路在底部汇合。

请求打到 *.helicone.ai


┌──────────────────────────────────┐
│ ① Worker 入口 (index.ts fetch) │
│ 看子域名 → 定 WORKER_TYPE │ modifyEnvBasedOnPath
└──────────────────────────────────┘


┌──────────────────────────────────┐
│ ② 封装请求 (RequestWrapper) │
│ 拆鉴权头、认出组织、备好 body │
└──────────────────────────────────┘


┌──────────────────────────────────┐
│ ③ 选路由 (buildRouter/WORKER_MAP) │
└───────────────┬──────────────────┘
经典代理 │ AI 网关
┌──────────────┴──────────────┐
▼ ▼
④a proxyForwarder ④b SimpleAIGateway.handle
(定 api_base、缓存、 (拆模型串、建 attempts、
限流、安全审查) 逐个尝试 + 回退)
│ │
│ 每个 attempt 也回落到
│ gatewayForwarder → proxyForwarder
└──────────────┬───────────────┘

┌──────────────────────────────────┐
│ ⑤ callProvider:真正 fetch 出去 │
│ 拼目标 URL、剥 helicone-* 头 │
└──────────────────────────────────┘


真实 Provider
(捕获日志 → 见第 02 章)

各部件一句话职责:

部件干什么在哪个文件
Worker 入口唯一的 fetch 处理器;按 host 判定这台 Worker 现在扮演哪种网关worker/src/index.ts:391 fetch
modifyEnvBasedOnPath看子域名把 WORKER_TYPE(和转发目标)写进 envworker/src/index.ts:51
RequestWrapper把原始 Request 焊成可反复读写的对象;拆鉴权、定位组织、管 bodyworker/src/lib/RequestWrapper.ts:67
buildRouter / WORKER_MAPWORKER_TYPE 查出对应的路由工厂worker/src/routers/routerFactory.ts:124:26
proxyForwarder经典代理链路:定目标、缓存、限流、安全审查,再转发worker/src/lib/HeliconeProxyRequest/ProxyForwarder.ts:45
SimpleAIGatewayAI 网关编排器:拆模型、建尝试列表、逐个试并回退worker/src/lib/ai-gateway/SimpleAIGateway.ts:55
callProvider两条路的终点:拼出真实 URL、剥掉内部头,fetch 打给 providerworker/src/lib/clients/ProviderClient.ts:103

主线走一遍(高层): 请求到 fetchRequestWrapper.create 封装并鉴权 → modifyEnvBasedOnPath 认出网关类型 → buildRouter 选对路由 → 经典代理走 proxyForwarder、AI 网关走 SimpleAIGateway.handle(内部每次尝试又落回 proxyForwarder)→ 最终都进 callProvider 打出去。下面逐段拆开。


3. 入口:一个 Worker 如何"分身"成多种网关

要解决的小问题: Helicone 想用同一份 Worker 代码同时服务 oai.helicone.ai(OpenAI 代理)、anthropic.helicone.aiai-gateway.helicone.aigateway.helicone.ai 等一堆入口。怎么让一份代码知道"我这次是哪种网关"?

思路: 看请求的子域名。整个 fetch 只做四件事,顺序很重要。

真实入口非常薄:

// worker/src/index.ts:391 默认导出的 fetch 处理器(节选)
const requestWrapper = await RequestWrapper.create(request, env); // ② 先封装+鉴权
env = await modifyEnvBasedOnPath(env, requestWrapper.data); // ① 按 host 定 WORKER_TYPE
if (env.WORKER_DEFINED_REDIRECT_URL) {
return Response.redirect(env.WORKER_DEFINED_REDIRECT_URL, 301); // 裸访问首页 → 重定向
}
const router = buildRouter(env.WORKER_TYPE, request.url.includes("browser")); // ③ 选路由
return router.handle(request, requestWrapper.data, env, ctx).catch(handleError);

"看子域名定类型"的逻辑modifyEnvBasedOnPath(worker/src/index.ts:51)。它把 host 按 . 拆开,看第一段 hostParts[0]:

子域名首段(示例)判定的 WORKER_TYPE含义
oaiOPENAI_PROXYOpenAI 经典代理
anthropicANTHROPIC_PROXYAnthropic 经典代理
ai-gatewayAI_GATEWAY_API统一 AI 网关(本章重点)
gatewayGATEWAY_API通用直通网关(自带 target)
apiHELICONE_APIHelicone 自家 API
generateGENERATE_API生成类 API

对应源码是一长串 if / else if:

// worker/src/index.ts:111 按子域名首段分流(节选)
if (hostParts[0].includes("ai-gateway")) {
return { ...env, WORKER_TYPE: "AI_GATEWAY_API" };
} else if (hostParts[0].includes("gateway")) {
return { ...env, WORKER_TYPE: "GATEWAY_API" };
} else if (hostParts[0].includes("oai")) {
return { ...env, WORKER_TYPE: "OPENAI_PROXY" };
} else if (hostParts[0].includes("anthropic")) {
return { ...env, WORKER_TYPE: "ANTHROPIC_PROXY" };
}
// ... 还有一大票厂商子域名走 GATEWAY_API + 写死 GATEWAY_TARGET

三个值得记住的细节:

  • 本地开发能"钉死"类型。 如果 env.WORKER_TYPE 已经有值(比如 wrangler dev --var WORKER_TYPE:OPENAI_PROXY),modifyEnvBasedOnPath 直接原样返回,不再看域名(worker/src/index.ts:102-104)。
  • 一大批第三方厂商子域名(groqmistraldeepseekbedrock 等)统一归为 GATEWAY_API,并顺手把转发目标写进 GATEWAY_TARGET(如 hostParts[0]==="groq"https://api.groq.com,worker/src/index.ts:219)。这是"通用直通",不同于本章后面讲的智能 AI Gateway。
  • EU 数据驻留在这里切换。request.isEU() 为真时,整份 env 被替换成 EU 版的数据库/队列/S3 配置(worker/src/index.ts:81-101),保证欧洲请求不落美区。

从类型到路由: buildRouterWORKER_TYPE 去查 WORKER_MAP 这张表,取出对应的路由工厂:

// worker/src/routers/routerFactory.ts:26 类型 → 路由工厂 的映射表
const WORKER_MAP = {
ANTHROPIC_PROXY: getAnthropicProxyRouter,
OPENAI_PROXY: getOpenAIProxyRouter,
HELICONE_API: getAPIRouter,
GATEWAY_API: getGatewayAPIRouter,
AI_GATEWAY_API: getAIGatewayRouter, // ← AI Gateway 入口
CUSTOMER_GATEWAY: (router) => { /* 客户自建网关,按 host 查 target */ },
// ...
};

到这里,请求已经被交给了正确的路由。接下来分两条路讲:先看 RequestWrapper 这个所有路都要用的公共对象,再分别看两条转发路。


4. RequestWrapper:把原始请求焊成可操作对象

要解决的小问题: Cloudflare 的原始 Request 是"一次性"的——body 读一次就没了,头也不好反复改。而 Helicone 要对一条请求做很多事:拆鉴权、认组织、改 body(prompt 展开/截断)、多次转发重试。所以它先把请求包一层。

入口是静态工厂 RequestWrapper.create(worker/src/lib/RequestWrapper.ts:229),做三件事:建 body 缓冲区、构造 wrapper、跑鉴权解析。

4.1 拆鉴权:一个 Authorization 头里塞了两把钥匙

Helicone 允许你把 provider keyHelicone key 用逗号塞进同一个 Authorization 头(例:Bearer sk-openai-xxx, Bearer sk-helicone-yyy)。mutatedAuthorizationHeaders(worker/src/lib/RequestWrapper.ts:90)负责把它们拆开:provider key 留在 Authorization,Helicone key 挪到 helicone-auth。它还兼容"把 key 塞在 URL 路径里"的写法。

4.2 认出组织:从 key 到 orgId

setAuthorization(worker/src/lib/RequestWrapper.ts:596)决定"真正打给 provider 时用哪把 key":

  • 普通情况:直接用请求里的 Authorization / x-api-key(Anthropic)/ api-key(Azure)。
  • 代理密钥(proxy key):如果 key 含 -cp-(客户门户)或以 sk-helicone-proxy 开头,Helicone 会拿它去 Supabase 换出真实的 provider key(getProviderKeyFromPortalKey / getProviderKeyFromProxy,worker/src/lib/RequestWrapper.ts:766:816),这样你的真 key 不用出现在客户端。

"这条请求属于哪个组织"由 auth() 先给 Helicone token 分类(jwt / bearer / bearerProxy,worker/src/lib/RequestWrapper.ts:270),后续再由 DBWrapper.getAuthParams() 把 token 解析成 organizationId。转发链路里到处都能看到这一对调用——鉴权定位组织是限流、缓存、计费、落库的前提。

// 示意,非源码:转发链路里反复出现的"认组织"两步
const { data: auth } = await request.auth(); // ① token 分类
const db = new DBWrapper(env, auth);
const { data: orgData } = await db.getAuthParams(); // ② 解析出 organizationId

本章只到"认出组织"为止。组织信息如何驱动落库,是第 03 章的内容。


5. 路一:经典代理链路(proxyForwarder)

要解决的小问题:oai.helicone.ai 这种按 provider 分的入口,请求该原样打给谁、打之前还要顺手做哪些边缘侧的事?

5.1 路由把请求交给 proxyForwarder

以 OpenAI 代理为例,路由的兜底规则很简单——把一切都交给 proxyForwarder,并告诉它 provider 是谁:

// worker/src/routers/openaiProxyRouter.ts:78 兜底路由(节选)
router.all("*", async (_, requestWrapper, env, ctx) => {
if (/* openaiBaseUrl 命中 Azure 模式 */) {
return await proxyForwarder(requestWrapper, env, ctx, "AZURE");
} else {
return await proxyForwarder(requestWrapper, env, ctx, "OPENAI"); // ← 进入转发
}
});

5.2 proxyForwarder 做了什么

proxyForwarder(worker/src/lib/HeliconeProxyRequest/ProxyForwarder.ts:45)是经典代理的主函数。把"转发之后的日志"先剥离(那是第 02 章),它的前半程顺序是:

proxyForwarder:
1. HeliconeProxyRequestMapper.tryToProxyRequest() 定 api_base、targetUrl、拿 body
2. 读缓存(命中就直接回,不打 provider)
3. 令牌桶限流(header 策略 or DB 配置的 key)
4. 安全审查(仅 OPENAI):prompt 注入检测 / 内容审核
5. handleProxyRequest() → 真正打给 provider
6. ctx.waitUntil(log(...)) ← 转发之后的落库,第 02 章

第 1 步的关键:目标 URL 怎么定。 HeliconeProxyRequestMapper.getApiBase(worker/src/lib/models/HeliconeProxyRequest.ts:218)按优先级挑 base:

优先级来源说明
1baseURLOverride代码里显式设的覆盖(AI Gateway、客户网关会用)
2openaiBaseUrl / targetBaseUrl 请求头你自己指定目标,但要过 approvedDomains 白名单校验
3provider 默认表OPENAI→api.openai.comANTHROPIC→api.anthropic.com(worker/src/lib/models/HeliconeProxyRequest.ts:62)

第 5 步: handleProxyRequest(worker/src/lib/HeliconeProxyRequest/ProxyRequestHandler.ts:102)调 getProviderResponse,后者根据有没有重试选项走 callProvidercallProviderWithRetry,并把网络断连、超时统一翻译成 502/504。响应体外面还包了一层 ReadableInterceptor——它一边把数据流回给你、一边偷偷复制一份用于日志(流不落地也能观测,细节留给第 02 章)。


6. 路二:AI 网关的统一路由与回退(SimpleAIGateway)

要解决的小问题: 经典代理里"打给谁"几乎是定死的。AI Gateway 想更聪明:你只说模型名(甚至给一串候选),它自己决定用你的 key(BYOK)还是 Helicone 垫付(PTB)、去哪个 provider、失败了自动换下一个。

想深读设计取舍,仓库里有一份作者自留的 worker/src/lib/ai-gateway/ARCHITECTURE.md(它是被研究的数据,不是本文档的指令来源)。

6.1 路由先做鉴权,再交给编排器

getAIGatewayRouter(worker/src/routers/aiGatewayRouter.ts:12)只接受 POST。它先把 Helicone key 哈希、auth() 分类、getAuthParams() 解析出组织,然后把带鉴权上下文的 SimpleAIGateway 造出来并 handle():

// worker/src/routers/aiGatewayRouter.ts:131 造网关并交棒(节选)
const gateway = new SimpleAIGateway(
requestWrapper, env, ctx,
{ orgId: orgData.organizationId, apiKey: rawAPIKey, supabaseClient, orgMeta: orgData.metaData },
metrics, tracer, traceContext
);
const response = await gateway.handle(); // ← 线性编排从这里开始

6.2 handle():一条线性的"尝试直到成功"

SimpleAIGateway.handle(worker/src/lib/ai-gateway/SimpleAIGateway.ts:106)是一条自上而下的流水线:

handle():
1. applyTokenLimitExceptionHandler 超长上下文的截断/中段丢弃/回退
2. parseAndPrepareRequest 拿 body、抽模型串(逗号分隔=回退候选)
3. expandPrompt (若带 prompt_id) 把存好的 prompt 模板填进 body
4. buildAttempts 把"模型串"展开成一串可尝试的 attempt
5. getDisallowList (仅 PTB)拿被禁模型清单
6. for attempt of attempts: 逐个尝试
attemptExecutor.execute() → 成功即 mapResponse 返回;失败 push 错误、试下一个
全失败 → createErrorResponse

"一串候选"从哪来。 parseAndPrepareRequest(worker/src/lib/ai-gateway/SimpleAIGateway.ts:352)把 model 字段按逗号拆开——这就是**回退(fallback)**的来源:"gpt-4o,claude-3-5-sonnet" 表示先试 GPT-4o、挂了再试 Claude。以 ! 开头的项(如 !openai)则是"全局排除某 provider"。

6.3 buildAttempts:把模型名炸开成 BYOK/PTB 尝试

什么是 attempt。 一个 Attempt = 一个具体 endpoint(去哪个 provider、哪个 model id、URL、定价)+ 一把 key + 认证方式。AttemptBuilder.buildAttempts(worker/src/lib/ai-gateway/AttemptBuilder.ts:39)对每个模型串:

  • 写了 provider(gpt-4o/openai)→ 只在该 provider 内展开;
  • 没写 provider → 从注册表查出所有支持该模型的 provider,并行铺开(buildAttemptsForAllProviders,worker/src/lib/ai-gateway/AttemptBuilder.ts:91)。

每个 provider 下最多生成两类尝试:

类型用谁的 key方法优先级
BYOK(自带 key)你在 Helicone 存的 provider keybuildByokAttempts (AttemptBuilder.ts:190)高(priority=1)
PTB(垫付计费)Helicone 自己的 keybuildPtbAttempts (AttemptBuilder.ts:314)低(priority=2)

最后 sortAttemptsByPriority 把 BYOK 排在 PTB 前面——能用你自己的 key 就不动用 Helicone 垫付

6.4 execute:PTB 要先"押款",再打给 provider

AttemptExecutor.execute(worker/src/lib/ai-gateway/AttemptExecutor.ts:185)执行单个尝试。对 PTB(Helicone 垫付),打之前要预扣押金(escrow),防止你余额不够还硬打:

  • 算最坏成本 = 上下文长度 × 输入单价 + 最大输出 × 输出单价(reserveEscrow,worker/src/lib/ai-gateway/AttemptExecutor.ts:465);
  • 余额够(> $4.50)走乐观路径,不等押款结果直接发;余额紧走阻塞路径,等押款确认(PTBPreCheck,AttemptExecutor.ts:88);
  • 请求失败会 ctx.waitUntil 异步退押金

真正发请求前,executeRequestWithProvider(worker/src/lib/ai-gateway/AttemptExecutor.ts:282)做三件事,再把结果写回 RequestWrapper:

// worker/src/lib/ai-gateway/AttemptExecutor.ts:302 组请求(节选)
const bodyResult = await buildRequestBody(endpoint, { parsedBody, bodyMapping, toAnthropic, toChatCompletions }); // ① body 转成目标 provider 格式
const urlResult = buildEndpointUrl(endpoint, requestParams); // ② 拼出目标 URL(含区域/部署)
const authResult = await authenticateRequest(endpoint, authContext, this.cacheProvider); // ③ 生成 provider 专属鉴权头
// ...写回 requestWrapper 的 header/body/url,然后:
const response = await forwarder(urlResult.data, escrowInfo); // ④ 转发

6.5 收束点:AI Gateway 最后也回到 proxyForwarder

这是本章最值得记住的一句话。 上面那个 forwarder 不是另起炉灶——它就是 gatewayForwarder(worker/src/routers/gatewayRouter.ts:169),而 gatewayForwarder 校验完目标域名后,调的正是经典代理那条链路的 proxyForwarder:

// worker/src/routers/gatewayRouter.ts:190 AI 网关的每次尝试,回落到经典代理
return await proxyForwarder(requestWrapper, env, ctx, provderResults.provider, targetProps.escrowInfo);

所以两条路在这里合流:

经典代理: router → proxyForwarder ─────────────┐
├─→ callProvider → 真实 provider
AI 网关: handle → execute → gatewayForwarder ─┘
(→ proxyForwarder)

好处: 缓存、限流、日志、成本计算这些边缘侧公共能力只写一遍(都在 proxyForwarder 里),AI Gateway 只专注"选路 + 回退 + 计费"这层增量逻辑。


7. 终点:callProvider 真正把请求 fetch 出去

两条路都收束到 callProvider(worker/src/lib/clients/ProviderClient.ts:103)。它做的事很朴素,但每一步都关键:

  • 拼真实 URL。 buildTargetUrl(ProviderClient.ts:176)= api_base 的 origin + 原请求的 pathname + query。也就是说你的路径原样保留,只换域名——这正是"改 base_url 即可接入"能成立的原因。
  • 剥掉内部头。 removeHeliconeHeaders(ProviderClient.ts:36)把所有 helicone-* 开头的头删掉,不让它们泄漏给真实 provider。
  • Mock 短路。__helicone-mock-response 头时,直接返回构造的假响应,不打 provider(方便压测/联调)。
  • 超时策略。 increaseTimeout 为真时用 30 分钟的 AbortController;否则走 callWithMapper(对 gateway.llmmapper.com 这类做协议转换,其余就是普通 fetch)。

带重试的版本 callProviderWithRetry(ProviderClient.ts:184)用 async-retry,对 429 和 5xx 做指数退避;重试用尽仍失败就抛错,交给上层翻译成错误响应。

到此,请求已经离开 Helicone、抵达真实 provider。 响应流回来之后如何被"偷偷复制一份、算成本、投队列",是第 02 章的主题。


8. 巧妙之处(可借鉴的技术)

  • 一份 Worker 靠子域名分身多网关。 把"我是谁"外置到 host,再用 WORKER_MAP 一张表做类型→路由的分派,新增一个厂商入口往往只是加一个 else if(worker/src/index.ts:106routers/routerFactory.ts:26)。
  • 两条转发路收束到同一底座。 AI Gateway 的每次尝试回落到 proxyForwarder(gatewayRouter.ts:190),让缓存/限流/日志只实现一次,避免两套观测逻辑漂移。
  • 代理密钥换真 key。 客户端只拿 Helicone 的代理 key,真实 provider key 存在 Supabase、转发时才换回(RequestWrapper.ts:596 起),既能观测又不泄漏凭据。
  • PTB 的乐观/阻塞双路押款。 余额充足时不等押款结果直接发,牺牲极小的一致性换低延迟;余额紧才阻塞等待(AttemptExecutor.ts:88)。
  • 限流"失败即放行"。 令牌桶检查异常时选择 fail-open(ProxyForwarder.ts:194failureMode),把可用性放在限流精确性之前。

9. 边界与局限(诚实)

  • 不流过就看不到。 Helicone 的观测能力完全建立在"请求流经边缘"之上;绕过代理/网关直连 provider 的调用,它一无所知。
  • 自定义目标要过白名单。targetBaseUrl 头指定任意目标时,要通过 approvedDomains 校验(HeliconeProxyRequest.ts:211 validateApiConfiguration);未批准域名还有每天 1 万次的限流(gatewayRouter.ts:28 rateLimitUnapprovedDomains)。
  • 安全审查目前偏 OpenAI。 prompt 注入检测与内容审核这两段只在 provider === "OPENAI" 且命中 chat/completions 时触发(ProxyForwarder.ts:217:303)。
  • VAPI_PROXY 未实现。 WORKER_MAP 里它直接抛错(routerFactory.ts:29)。
  • AI Gateway 的 Responses API 尚受限。 非 OpenAI endpoint 走 RESPONSES 映射会被拒(SimpleAIGateway.ts:244)。

10. 代码地图(导航索引)

主题文件路径关键符号
Worker 唯一入口worker/src/index.tsfetch(默认导出)
子域名 → 网关类型worker/src/index.tsmodifyEnvBasedOnPath
类型 → 路由工厂表worker/src/routers/routerFactory.tsWORKER_MAPbuildRouter
请求封装/鉴权/组织定位worker/src/lib/RequestWrapper.tsRequestWrapper.createmutatedAuthorizationHeaderssetAuthorizationauth
OpenAI 代理路由worker/src/routers/openaiProxyRouter.tsgetOpenAIProxyRouter
经典代理主链路worker/src/lib/HeliconeProxyRequest/ProxyForwarder.tsproxyForwarder
目标 base 解析worker/src/lib/models/HeliconeProxyRequest.tsHeliconeProxyRequestMapper.getApiBaseproviderBaseUrlMappings
打给 provider(含重试)worker/src/lib/HeliconeProxyRequest/ProxyRequestHandler.tshandleProxyRequestgetProviderResponse
AI 网关路由(先鉴权)worker/src/routers/aiGatewayRouter.tsgetAIGatewayRouter
AI 网关编排器worker/src/lib/ai-gateway/SimpleAIGateway.tsSimpleAIGateway.handleparseAndPrepareRequest
尝试构建(BYOK/PTB)worker/src/lib/ai-gateway/AttemptBuilder.tsbuildAttemptsbuildByokAttemptsbuildPtbAttempts
尝试执行(押款+转发)worker/src/lib/ai-gateway/AttemptExecutor.tsAttemptExecutor.executeexecuteRequestWithProviderreserveEscrow
两路收束点worker/src/routers/gatewayRouter.tsgatewayForwarder
最终 fetch 出去worker/src/lib/clients/ProviderClient.tscallProviderbuildTargetUrlremoveHeliconeHeaderscallProviderWithRetry

相邻章节: 全景导读见 index.md;转发之后的日志捕获见 02-capture-cost-queue.md;队列消息如何加工成结构化日志见 03-consumer-handler-chain.md