简介
AI 模型通常会在应用每次调用时生成新的响应。即使请求与模型刚刚处理过的请求完全相同,这项工作仍然需要时间并消耗模型用量。缓存会在有限时间内保存可复用的响应,因此完全相同的请求无需再次调用模型即可得到回答。
只有在重复使用安全的情况下,缓存才有价值。公开且固定的常见问题适合缓存,因为所有调用者都可以收到相同的回答。个性化的支持提示则不适合:不能为了提高速度,就仅凭共享缓存键把两位客户的请求归到一起。AI Gateway 的默认缓存键包含提供商、端点、模型、提供商凭据和完整请求正文,可以保护本实验。正文的任何变化都会创建不同的缓存条目。
你将创建一个临时的、需要身份验证的网关,并将缓存生命周期设置为 5 分钟。你会发送一个简短的公开 Workers AI 问题并观察缓存 MISS,然后重复完全相同的请求并证明出现 HIT,接着修改问题并再次看到 MISS。最后,在需要最新结果时绕过现有缓存的回答,并通过网关日志确认请求确实到达了模型。
如果你是直接进入本课程的,请先完成将 LabEx 连接到你的 Cloudflare 账户。该实验会介绍 LabEx VM 终端、Wrangler 设备授权、学习账户确认以及显式账户 ID。还请先完成 通过网关路由推理请求,因为本实验会复用其中独立的网关和上游授权边界。
本实验使用 Cloudflare 托管的 @cf/meta/llama-3.3-70b-instruct-fp8-fast 模型,并采用 Standard Workers AI 计费。无需使用 Workers Paid、Unified Billing 或外部提供商账户。只有 3 个请求应到达模型;完全相同的重复请求应来自缓存。如果共享的每日 Workers AI 配额不可用,请停止操作,不要反复重试。
准备环境会在 /home/labex/project/ai-gateway-cache 中安装 Node.js 22.22.0 和项目本地的 Wrangler 4.132.0。它会准备独立的只读评估,但不会授权 Wrangler、创建云资源或发送模型流量。实验结束时,LabEx 会销毁临时 VM;但你仍需在退出登录前删除网关和令牌,因为仅销毁 VM 无法删除云资源。
授权 VM 并命名缓存实验
在本步骤中,你将把全新的 VM 连接到学习账户,并为一个临时网关和令牌生成名称。
缓存属于共享基础设施,因此必须有意识地确定其范围。本实验使用一个唯一命名的网关,并且只使用虚构的公开问题。随机后缀可以避免你的实验与同一学习账户中的其他网关发生冲突。
进入已准备好的项目,确认固定版本的 CLI,并授权此 VM:
cd /home/labex/project/ai-gateway-cache
npx wrangler --version
npx wrangler login --device --browser=false --scopes account:read user:read ai:write
打开显示的链接,输入代码,并授权指定的学习账户。确认结构化身份信息:
npx wrangler whoami --json
预期显示 Wrangler 4.132.0 和 loggedIn: true。将下面的 YOUR_ACCOUNT_ID 替换为指定账户显示的实际 32 位 ID:
GATEWAY_ID="labex-c09-g03-$(openssl rand -hex 6)"
TOKEN_NAME="$GATEWAY_ID-token"
cat > .labex/state.json <<JSON
{
"accountId": "YOUR_ACCOUNT_ID",
"gatewayId": "$GATEWAY_ID",
"tokenName": "$TOKEN_NAME"
}
JSON
cat .labex/state.json
这些非机密标识符会保存在本地清单中,以确保后续的读取、验证和清理操作只针对本实验创建的资源。
创建带有短缓存时间的身份验证网关
在本步骤中,你将创建网关,并将缓存回答的 生存时间(time to live,TTL)设置为 5 分钟。TTL 表示缓存条目在过期并必须从模型刷新之前,最多可以被重复使用的时间。
打开 Cloudflare Dashboard,然后选择 AI → AI Gateway → Create gateway → Custom gateway。使用已保存的 gatewayId 作为网关名称。保持请求日志记录和网关身份验证处于开启状态,启用 Cache responses,并将 TTL 精确设置为 300 秒。关闭速率限制、支出限制和重试,并将 Workers AI billing 保持为 Standard。

创建后,确认面包屑导航中显示的是唯一的网关 ID。较短的 TTL 足以让你重复本实验,同时可以避免示例回答不必要地长期保留。
选择 Create an AI Gateway authentication token。使用已保存的 tokenName,仅包含指定的学习账户,并精确设置以下权限:
- AI Gateway — Run:用于进入需要身份验证的网关;
- AI Gateway — Edit:用于读取缓存证据并删除这个临时网关。
不要添加 Workers AI 权限。Wrangler 会提供单独的短期上游凭据。确认账户和权限后创建令牌,然后在不回显令牌值的情况下保存一次性令牌:
bash -c '
while :; do
read -ersp "Paste the AI Gateway token: " GATEWAY_TOKEN
printf "\n"
[ -n "$GATEWAY_TOKEN" ] && break
printf "Token cannot be empty; paste it again.\n" >&2
done
umask 077
printf "%s" "$GATEWAY_TOKEN" > .labex/gateway-token
unset GATEWAY_TOKEN
chmod 600 .labex/gateway-token
'
通过需要身份验证的管理 API,验证准确的缓存配置:
ACCOUNT_ID=$(node -p 'JSON.parse(require("fs").readFileSync(".labex/state.json")).accountId')
GATEWAY_ID=$(node -p 'JSON.parse(require("fs").readFileSync(".labex/state.json")).gatewayId')
GATEWAY_TOKEN=$(cat .labex/gateway-token)
curl --http1.1 -fsS \
-H "Authorization: Bearer $GATEWAY_TOKEN" \
"https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-gateway/gateways/$GATEWAY_ID" \
| node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{const b=JSON.parse(s),g=b.result||{};console.log(JSON.stringify({success:b.success,id:g.id,collect_logs:g.collect_logs,authentication:g.authentication,cache_ttl:g.cache_ttl},null,2))})'
unset GATEWAY_TOKEN
预期显示已保存的 ID、collect_logs: true、authentication: true 和 cache_ttl: 300。
发送第一个公开常见问题请求
在本步骤中,你将发送一个适合所有学习者重复使用的简短公开问题。新的网关下不可能已经存在第一个符合条件的请求条目,因此它应该显示缓存 MISS。MISS 表示 AI Gateway 将请求转发给 Workers AI,然后保存成功的响应。
默认缓存键包含上游提供商凭据。私下保存此 VM 当前的 Wrangler 凭据,确保 4 个请求都使用同一个受控缓存键。这只是一个短期实验文件,并不是生产环境中的机密信息管理方案:
umask 077
npx wrangler auth token --json \
| node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>process.stdout.write(JSON.parse(s).token))' \
> .labex/upstream-token
chmod 600 .labex/upstream-token
发送第一个请求,并保存响应头、响应正文和 HTTP 状态,同时不打印任一凭据:
ACCOUNT_ID=$(node -p 'JSON.parse(require("fs").readFileSync(".labex/state.json")).accountId')
GATEWAY_ID=$(node -p 'JSON.parse(require("fs").readFileSync(".labex/state.json")).gatewayId')
MODEL='@cf/meta/llama-3.3-70b-instruct-fp8-fast'
METADATA='{"lab":"g03-cache","case":"public-faq","synthetic":true}'
GATEWAY_TOKEN=$(cat .labex/gateway-token)
UPSTREAM_TOKEN=$(cat .labex/upstream-token)
STATUS=$(curl --http1.1 -sS -D .labex/first-headers.txt \
-o .labex/first-response.json -w '%{http_code}' \
-H "cf-aig-authorization: Bearer $GATEWAY_TOKEN" \
-H "Authorization: Bearer $UPSTREAM_TOKEN" \
-H "cf-aig-metadata: $METADATA" \
-H 'Content-Type: application/json' \
--data '{"prompt":"In one short sentence, what does an AI gateway do?","max_tokens":32}' \
"https://gateway.ai.cloudflare.com/v1/$ACCOUNT_ID/$GATEWAY_ID/workers-ai/$MODEL")
unset GATEWAY_TOKEN UPSTREAM_TOKEN METADATA
printf '%s\n' "$STATUS" | tee .labex/first-status.txt
awk 'BEGIN{IGNORECASE=1} /^cf-aig-cache-status:/ {gsub("\r","",$2); print toupper($2)}' .labex/first-headers.txt \
| tail -1 | tee .labex/first-cache-status.txt
node -e 'const b=require("./.labex/first-response.json"); console.log(b.result?.response ?? b.result)'
预期 HTTP 200、缓存状态为 MISS,并显示一段简短的生成回答。请求正文不包含客户数据,因此暂时重复使用此回答是安全的。
重复完全相同的请求并证明缓存命中
在本步骤中,你将使用完全相同的提供商、端点、模型、凭据和请求正文发送请求。因此,AI Gateway 可以复用上一步创建的条目。缓存 HIT 表示回答来自网关缓存,没有进行新的模型生成。
缓存存储是异步的,因此第一次请求成功后等待几秒,让缓存完成写入,再重复请求:
sleep 5
GATEWAY_TOKEN=$(cat .labex/gateway-token)
UPSTREAM_TOKEN=$(cat .labex/upstream-token)
METADATA='{"lab":"g03-cache","case":"public-faq","synthetic":true}'
STATUS=$(curl --http1.1 -sS -D .labex/repeat-headers.txt \
-o .labex/repeat-response.json -w '%{http_code}' \
-H "cf-aig-authorization: Bearer $GATEWAY_TOKEN" \
-H "Authorization: Bearer $UPSTREAM_TOKEN" \
-H "cf-aig-metadata: $METADATA" \
-H 'Content-Type: application/json' \
--data '{"prompt":"In one short sentence, what does an AI gateway do?","max_tokens":32}' \
"https://gateway.ai.cloudflare.com/v1/$ACCOUNT_ID/$GATEWAY_ID/workers-ai/$MODEL")
unset GATEWAY_TOKEN UPSTREAM_TOKEN METADATA
printf '%s\n' "$STATUS" | tee .labex/repeat-status.txt
awk 'BEGIN{IGNORECASE=1} /^cf-aig-cache-status:/ {gsub("\r","",$2); print toupper($2)}' .labex/repeat-headers.txt \
| tail -1 | tee .labex/repeat-cache-status.txt
cmp -s .labex/first-response.json .labex/repeat-response.json \
&& echo 'response bytes match the cached source'
预期 HTTP 200 和 HIT。响应字节相同是一个有帮助的额外观察结果,但权威证据是 HIT 响应头和 Dashboard 中的缓存日志。AI Gateway 的缓存存储是异步且易失的,因此不要同时发送这两个请求。如果按顺序重复后仍然是 miss,请等待几秒,然后完整地重新运行此代码块一次。
打开 Dashboard 中网关的 Logs 视图。找到两个 public-faq 请求,并比较它们的缓存指标、耗时和令牌用量。其中一行应显示由模型处理的 miss,另一行应显示缓存命中。

修改问题并观察新的缓存未命中
在本步骤中,你只修改提示内容。完整请求正文会参与默认缓存键的计算,因此这个新问题不能得到之前的回答。
GATEWAY_TOKEN=$(cat .labex/gateway-token)
UPSTREAM_TOKEN=$(cat .labex/upstream-token)
METADATA='{"lab":"g03-cache","case":"changed-question","synthetic":true}'
STATUS=$(curl --http1.1 -sS -D .labex/changed-headers.txt \
-o .labex/changed-response.json -w '%{http_code}' \
-H "cf-aig-authorization: Bearer $GATEWAY_TOKEN" \
-H "Authorization: Bearer $UPSTREAM_TOKEN" \
-H "cf-aig-metadata: $METADATA" \
-H 'Content-Type: application/json' \
--data '{"prompt":"In one short sentence, name one benefit of an AI gateway.","max_tokens":32}' \
"https://gateway.ai.cloudflare.com/v1/$ACCOUNT_ID/$GATEWAY_ID/workers-ai/$MODEL")
unset GATEWAY_TOKEN UPSTREAM_TOKEN METADATA
printf '%s\n' "$STATUS" | tee .labex/changed-status.txt
awk 'BEGIN{IGNORECASE=1} /^cf-aig-cache-status:/ {gsub("\r","",$2); print toupper($2)}' .labex/changed-headers.txt \
| tail -1 | tee .labex/changed-cache-status.txt
node -e 'const b=require("./.labex/changed-response.json"); console.log(b.result?.response ?? b.result)'
预期 HTTP 200 和 MISS。这种完全匹配行为有意比语义相似更严格:两个听起来相关的问题仍然具有不同的正文和不同的缓存条目。
不要将默认键替换为诸如 support-answer 这样的共享键来处理个性化提示。只有在归入该键的每个请求都有权接收相同回答时,自定义键才是安全的。
在需要最新结果时绕过缓存
在本步骤中,你将返回原来的问题,但显式跳过已缓存的回答。绕过缓存表示「现在向提供商发起请求」,即使有效的缓存条目已经存在。这适用于应用需要为某个特定请求获取最新输出的情况。
cf-aig-skip-cache: true 请求头只控制当前请求,不会为其他调用者禁用网关缓存:
GATEWAY_TOKEN=$(cat .labex/gateway-token)
UPSTREAM_TOKEN=$(cat .labex/upstream-token)
METADATA='{"lab":"g03-cache","case":"fresh-bypass","synthetic":true}'
STATUS=$(curl --http1.1 -sS -D .labex/bypass-headers.txt \
-o .labex/bypass-response.json -w '%{http_code}' \
-H "cf-aig-authorization: Bearer $GATEWAY_TOKEN" \
-H "Authorization: Bearer $UPSTREAM_TOKEN" \
-H "cf-aig-metadata: $METADATA" \
-H 'cf-aig-skip-cache: true' \
-H 'Content-Type: application/json' \
--data '{"prompt":"In one short sentence, what does an AI gateway do?","max_tokens":32}' \
"https://gateway.ai.cloudflare.com/v1/$ACCOUNT_ID/$GATEWAY_ID/workers-ai/$MODEL")
unset GATEWAY_TOKEN UPSTREAM_TOKEN METADATA
printf '%s\n' "$STATUS" | tee .labex/bypass-status.txt
awk 'BEGIN{IGNORECASE=1} /^cf-aig-cache-status:/ {gsub("\r","",$2); print toupper($2)}' .labex/bypass-headers.txt \
| tail -1 | tee .labex/bypass-cache-status.txt
node -e 'const b=require("./.labex/bypass-response.json"); console.log(b.result?.response ?? b.result)'
预期 HTTP 200,并且不应出现 HIT。根据当前网关响应,响应头可能会明确说明发生了绕过,也可能只是保持为非命中状态;权威的网关日志必须在 fresh-bypass 中显示 cached: false。
返回 Dashboard 中的 Logs,打开 fresh-bypass 请求。将它与已缓存的 public-faq 行进行比较。由于本次请求级绕过覆盖了网关默认行为,同一个问题再次到达了 Workers AI。

删除临时网关
在本步骤中,你将删除网关,同时管理凭据仍可用于证明它已不存在。删除这个由你创建的网关也会删除其短期缓存命名空间和日志。
ACCOUNT_ID=$(node -p 'JSON.parse(require("fs").readFileSync(".labex/state.json")).accountId')
GATEWAY_ID=$(node -p 'JSON.parse(require("fs").readFileSync(".labex/state.json")).gatewayId')
GATEWAY_TOKEN=$(cat .labex/gateway-token)
curl --http1.1 -fsS -X DELETE \
-H "Authorization: Bearer $GATEWAY_TOKEN" \
"https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-gateway/gateways/$GATEWAY_ID" \
| node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{const b=JSON.parse(s);if(!b.success)process.exit(1);console.log("gateway deletion accepted")})'
unset GATEWAY_TOKEN
GATEWAY_TOKEN=$(cat .labex/gateway-token)
curl --http1.1 -fsS \
-H "Authorization: Bearer $GATEWAY_TOKEN" \
"https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/ai-gateway/gateways" \
> .labex/gateways-after-delete.json
unset GATEWAY_TOKEN
node -e 'const b=require("./.labex/gateways-after-delete.json"),id=process.argv[1],found=(b.result||[]).some(g=>g.id===id);console.log("gateway absent:",!found);if(found)process.exit(1)' "$GATEWAY_ID"
预期显示 gateway absent: true。通过身份验证的清单可以区分真正删除成功与因退出登录或网络故障导致的页面缺失。
删除令牌并退出登录
在本步骤中,你将撤销剩余的云凭据,删除两个临时令牌副本,并断开 VM。
在 Cloudflare Dashboard 中打开 My Profile → API Tokens。找到准确的已保存 tokenName,打开 Actions,选择 Delete,检查确认信息,并且只删除该令牌。由于已经证明网关删除成功,现在可以安全地撤销此令牌。
删除网关令牌和上游令牌文件,然后结束 Wrangler 的独立授权:
shred -u .labex/gateway-token .labex/upstream-token
npx wrangler logout
npx wrangler whoami --json || true
test ! -e .labex/gateway-token -a ! -e .labex/upstream-token \
&& echo "local token files removed"
预期显示 loggedIn: false 和 local token files removed。Dashboard 会话是独立的,仍会保持登录状态。实验结束时,LabEx 会销毁这个临时 VM,而不是保存它。
总结
你为一个安全的公开问题配置了短时的 AI Gateway 响应缓存。第一次请求产生了 MISS,完全相同的重复请求变成了 HIT,修改输入则创建了单独的条目。随后,在需要最新结果时,你使用请求级绕过,并通过日志确认由模型而不是缓存副本处理了该请求。
下一项实验将加入流量控制。你将学习限制请求到达频率与限制网关可消耗的模型用量之间的区别,同时让测试流量和成本保持在较低水平。



