在 Cloudflare 上跑 Claude Agent:边缘托管、内网穿透、全链路可观测

2026-05-28 13 预计阅读时间:1 分钟
来源:infoq.com AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:11 分钟

Anthropic 的 Claude 不只是聊天模型——它正在变成一个能自主调用工具、编排多步骤任务的 Agent。但 Agent 要落地,最难的不是模型本身,而是运行环境:它需要访问你的内部 API、需要稳定的执行沙箱、还需要有人盯着它到底干了什么。Cloudflare 最近上线了 Claude Managed Agents 支持,把这三件事一起搬到了边缘网络上。

Agent 托管,为什么要在 Cloudflare 上做?

传统做法是自己在 VPS 或 Kubernetes 里跑一个 Agent 服务:写一个长驻进程,调 Anthropic API,处理工具调用循环,再挂上日志和监控。这套东西能跑,但维护成本不低——尤其是 Agent 本质上是"有状态的长连接服务",和典型无状态 Web 服务的运维模式不一样。

Cloudflare 的方案把 Agent 的生命周期管理塞进了 Workers 运行时:

  • 冷启动快:Workers 的启动延迟在毫秒级,Agent 不用等一个笨重的容器拉起来。
  • 全球分布:Agent 可以跑在离用户或离数据源最近的节点,减少往返延迟。
  • 天然集成 Cloudflare 生态:D1 数据库、R2 存储、KV 缓存、Queue 消息队列,Agent 的工具调用可以直接用这些绑定,不需要自己搭基础设施。

连接私有系统:Agent 不再是"外网孤岛"

Agent 最常见的落地障碍是——它够不到你的内部服务。数据库在内网、ERP 系统在 VPN 后面、CI/CD 管道只在公司机房里暴露。让 Agent 直接访问这些资源,要么开公网端口(安全团队摇头),要么搞反向隧道(运维团队叹气)。

Cloudflare 的做法是利用已有的 Tunnel 和 Zero Trust 能力:

  1. 在内网机器上跑 cloudflared tunnel,把内部服务暴露到 Cloudflare 边缘。
  2. Agent 通过 Workers 环境内的私有域名(不经过公网 DNS)访问这些服务。
  3. 访问策略由 Cloudflare Access 控制,可以按身份、设备、IP 限流。

这意味着 Agent 的工具调用可以直达你的 PostgreSQL、Jira API、内部 Git 服务——而这些服务完全不需要对互联网开放。

运行时选择:Workers 还是 Containers?

不是所有 Agent 都能塞进 Workers。有些任务需要长时间运行(比如批量处理十万条记录),有些需要装自定义二进制(比如调用本地 OCR 引擎)。Cloudflare 提供了两种运行时:

运行时 适合场景 限制
Workers 短任务、工具调用少、响应快 CPU 时间 30s(付费版可更长),无本地文件系统
Containers(Cloudflare Containers) 长任务、需要自定义环境 启动稍慢,但可持久运行

对于典型的 Claude Agent——接收用户指令、调几个 API、返回结果——Workers 绝大多数情况下够用。如果你要跑"持续监控 + 定时巡检"类 Agent,Containers 更合适。

实战:用 Workers 跑一个 Claude Agent

下面是一个最小可运行的示例:一个 Cloudflare Worker 接收用户消息,调用 Claude 的 Agent 模式(带工具使用),并把工具调用结果返回给用户。

1. 项目结构

claude-agent-worker/
├── wrangler.toml
├── src/
   └── index.ts
└── package.json

2. Wrangler 配置

name = "claude-agent-worker"
main = "src/index.ts"
compatibility_date = "2024-12-01"

# Anthropic API 密钥,用 secret 管理——不要硬编码
# 部署后执行: wrangler secret put ANTHROPIC_API_KEY

[vars]
CLAUDE_MODEL = "claude-sonnet-4-20250514"

3. Worker 代码

// src/index.ts
export interface Env {
  ANTHROPIC_API_KEY: string;
  CLAUDE_MODEL: string;
}

// 定义 Agent 可用的工具——这里给一个查天气的示例
const tools = [
  {
    name: "get_weather",
    description: "获取指定城市的当前天气信息",
    input_schema: {
      type: "object",
      properties: {
        city: { type: "string", description: "城市名称,如 Beijing" },
      },
      required: ["city"],
    },
  },
];

// 工具执行函数——实际项目中可替换为内部 API 调用
async function executeTool(name: string, input: Record<string, unknown>): Promise<string> {
  if (name === "get_weather") {
    // 示例:调用公开天气 API
    // 生产环境可替换为通过 Tunnel 访问的内部气象服务
    const city = input.city as string;
    const resp = await fetch(
      `https://wttr.in/${encodeURIComponent(city)}?format=j1`
    );
    const data = await resp.json() as any;
    const current = data.current_condition?.[0];
    return `${city} 当前温度 ${current?.temp_C}°C,湿度 ${current?.humidity}%,天气 ${current?.weatherDesc?.[0]?.value}`;
  }
  return `未知工具: ${name}`;
}

// Agent 循环:消息 → Claude → 工具调用 → 结果 → 再送回 Claude → 最终回复
async function runAgent(userMessage: string, env: Env): Promise<string> {
  const messages: any[] = [{ role: "user", content: userMessage }];

  // 最多循环 5 次,防止无限工具调用
  for (let i = 0; i < 5; i++) {
    const response = await fetch("https://api.anthropic.com/v1/messages", {
      method: "POST",
      headers: {
        "x-api-key": env.ANTHROPIC_API_KEY,
        "anthropic-version": "2023-06-01",
        "content-type": "application/json",
      },
      body: JSON.stringify({
        model: env.CLAUDE_MODEL,
        max_tokens: 4096,
        tools,
        messages,
      }),
    });

    const result = await response.json() as any;

    // 收集 Claude 的输出内容块
    const contentBlocks = result.content as any[];
    messages.push({ role: "assistant", content: contentBlocks });

    // 检查是否包含工具调用
    const toolUseBlocks = contentBlocks.filter((b: any) => b.type === "tool_use");

    if (toolUseBlocks.length === 0) {
      // 没有工具调用,提取文本回复并返回
      const textBlocks = contentBlocks.filter((b: any) => b.type === "text");
      return textBlocks.map((b: any) => b.text).join("\n");
    }

    // 执行所有工具调用,构造 tool_result 消息
    const toolResults = await Promise.all(
      toolUseBlocks.map(async (block: any) => {
        const output = await executeTool(block.name, block.input);
        return {
          type: "tool_result",
          tool_use_id: block.id,
          content: output,
        };
      })
    );

    messages.push({ role: "user", content: toolResults });
  }

  return "Agent 达到最大循环次数,请简化你的请求。";
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    if (request.method !== "POST") {
      return new Response("Send POST with { message: '...' }", { status: 405 });
    }

    const body = await request.json() as { message?: string };
    if (!body.message) {
      return new Response("Missing 'message' field", { status: 400 });
    }

    try {
      const reply = await runAgent(body.message, env);
      return Response.json({ reply });
    } catch (err: any) {
      return Response.json({ error: err.message }, { status: 500 });
    }
  },
};

4. 部署与测试

# 安装依赖
npm install -g wrangler
npm init -y
npm install @cloudflare/workers-types

# 登录 Cloudflare
wrangler login

# 设置 Anthropic API Key(不会出现在代码或 wrangler.toml 里)
wrangler secret put ANTHROPIC_API_KEY
# 粘贴你的密钥,回车

# 部署
wrangler deploy

# 测试
curl -X POST https://claude-agent-worker.<your-subdomain>.workers.dev \
  -H "Content-Type: application/json" \
  -d '{"message": "北京今天天气怎么样?"}'

预期返回类似:

{
  "reply": "根据查询结果,北京当前温度 28°C,湿度 45%,天气晴。适合户外活动。"
}

5. 接入内部服务(进阶)

如果你想让 Agent 调用内网 API,只需在目标机器上跑一条 Tunnel:

# 在内网服务器上安装并启动 cloudflared
cloudflared tunnel create my-agent-tunnel
cloudflared tunnel route dns internal-api.your-team.com my-agent-tunnel

cloudflared tunnel --url http://localhost:8080 run my-agent-tunnel

然后在 Worker 的 executeTool 里,把 fetch 地址换成 https://internal-api.your-team.com/...——这个域名在 Cloudflare 边缘网络内可解析,外部完全看不到。

可观测:盯着 Agent 别跑偏

Agent 自主执行任务,意味着它可能调错 API、传错参数、甚至陷入死循环。Cloudflare 提供了几层监控手段:

  • Workers Logswrangler tail 实时看 Agent 每次请求的日志输出。
  • Workers Analytics:按时间维度看调用次数、错误率、CPU 耗时。
  • Cloudflare Observability(Logpush):把 Workers 日志推到你的 SIEM 或数据平台,做长期审计。

建议在 Agent 循环里加结构化日志,记录每次工具调用的输入输出:

// 在 executeTool 函数内加入日志
console.log(JSON.stringify({
  event: "tool_call",
  tool: name,
  input: input,
  output_preview: output.slice(0, 200),
  timestamp: Date.now(),
}));

部署后用 wrangler tail --format json 实时消费这些日志,或者通过 Logpush 写到 R2 / 外部分析平台。

上线前的 Checklist

检查项 说明
API Key 用 Secret 管理 不要写在 wrangler.toml 或代码里
工具调用加循环上限 防止 Agent 陷入无限工具调用
内网服务走 Tunnel + Access 不开公网端口,按身份限流
结构化日志 每次工具调用记录输入输出摘要
错误兜底 executeTool 捕获异常,返回友好错误而非让 Agent 崩溃
模型选择 简单任务用 Haiku 省成本,复杂编排用 Sonnet 或 Opus

Cloudflare 把 Agent 的运行、联网、观测打包进同一套边缘基础设施,省掉了自建运维的不少麻烦。但 Agent 的可靠性仍然取决于你的工具定义质量和错误处理——基础设施托底,逻辑层还得自己把关。


相关推荐