← 返回博客

构建生产级 Hybrid Gene

高级教程:创建通过 Network Gateway 调用外部 API 的基因,包含域名白名单、速率限制与优雅错误处理。

构建生产级 Hybrid Gene

大多数基因是纯函数——接收输入、计算、返回输出。但真实世界的 Agent 需要与外部服务通信:LLM 提供商、天气 API、数据库、搜索引擎。Rotifer 通过 Hybrid 保真度来解决这个问题:为基因提供受控的网络访问,经由 Network Gateway——一个带有域名白名单、速率限制、超时强制执行和响应大小上限的沙箱化 fetch 代理。

本教程将从零开始构建一个生产级的 Hybrid 基因。

何时使用 Hybrid

保真度 网络访问 适用场景
Wrapped 纯逻辑——文本转换、数学计算、格式化
Hybrid Gateway 控制 外部 API 调用——LLM、天气、搜索、数据库
Native CPU 密集计算——编译后的 WASM、加密、解析

当基因需要访问外部世界时,选择 Hybrid。Network Gateway 确保它只能访问你明确允许的域名,并以你定义的速率运行。

第一步:初始化基因

rotifer init weather-gene --fidelity Hybrid

这会创建一个基因目录,其中的 phenotype.json 已预配置为 Hybrid 保真度:

{
  "name": "weather-gene",
  "domain": "utility",
  "description": "",
  "inputSchema": {
    "type": "object",
    "properties": {
      "prompt": { "type": "string" }
    },
    "required": []
  },
  "outputSchema": {
    "type": "object",
    "properties": {
      "result": { "type": "string" }
    }
  },
  "dependencies": [],
  "version": "0.1.0",
  "fidelity": "Hybrid",
  "transparency": "Open",
  "network": {
    "allowedDomains": [],
    "maxTimeoutMs": 30000,
    "maxResponseBytes": 1048576,
    "maxRequestsPerMin": 10
  }
}

network 块是 Hybrid 与其他保真度级别的核心区别。每个字段都至关重要——运行时会强制执行所有限制。

第二步:配置网络访问

编辑 phenotype.json,声明基因需要访问的域名:

{
  "network": {
    "allowedDomains": ["wttr.in"],
    "maxTimeoutMs": 10000,
    "maxResponseBytes": 524288,
    "maxRequestsPerMin": 5
  }
}

域名规则:

保持域名列表最小化。你添加的每个域名都是基因暴露的攻击面。如果基因只调用一个 API,就只列一个域名。

调优限制参数:

字段 默认值 建议
maxTimeoutMs 30000 设为预期 API 延迟的 2–3 倍。快速 API 用 10s,LLM 用 30s。
maxResponseBytes 1048576 (1 MiB) 大多数 JSON API 足够。大型载荷可增加,简单端点可减少。
maxRequestsPerMin 10 匹配上游速率限制。不要设得比提供商允许的更高。

第三步:编写 Express 函数

在基因目录中创建 express.ts。Hybrid 基因接收一个带有 gatewayFetch 函数的 ctx 对象——这是你唯一的网络接口。全局 fetch 在基因沙箱内不可用。

import type { GatewayFetchOptions, GatewayResponse } from "@rotifer/core";

interface WeatherInput {
  city: string;
  format?: "brief" | "detailed";
}

interface WeatherOutput {
  result: string;
  city: string;
  source: string;
}

export default async function express(
  input: WeatherInput,
  ctx: { gatewayFetch: (url: string, options?: GatewayFetchOptions) => Promise<GatewayResponse> }
): Promise<WeatherOutput> {
  const city = encodeURIComponent(input.city);
  const formatParam = input.format === "detailed" ? "" : "?format=3";
  const url = `https://wttr.in/${city}${formatParam}`;

  const response = await ctx.gatewayFetch(url, {
    method: "GET",
    headers: { "Accept": "text/plain" },
  });

  if (response.status !== 200) {
    throw new Error(`Weather API returned ${response.status}: ${response.body}`);
  }

  return {
    result: response.body.trim(),
    city: input.city,
    source: "wttr.in",
  };
}

要点:

第四步:处理 Gateway 错误

Network Gateway 会抛出带有特定错误码的 NetworkGatewayError。生产级基因必须优雅地处理每种错误:

import type { GatewayFetchOptions, GatewayResponse } from "@rotifer/core";

interface WeatherInput {
  city: string;
}

interface WeatherOutput {
  result: string;
  city: string;
  source: string;
  degraded: boolean;
}

export default async function express(
  input: WeatherInput,
  ctx: { gatewayFetch: (url: string, options?: GatewayFetchOptions) => Promise<GatewayResponse> }
): Promise<WeatherOutput> {
  const city = encodeURIComponent(input.city);

  try {
    const response = await ctx.gatewayFetch(`https://wttr.in/${city}?format=3`, {
      method: "GET",
      headers: { "Accept": "text/plain" },
    });

    return {
      result: response.body.trim(),
      city: input.city,
      source: "wttr.in",
      degraded: response.truncated,
    };
  } catch (err: any) {
    switch (err.code) {
      case "DOMAIN_BLOCKED":
        throw new Error(`配置错误:wttr.in 不在 allowedDomains 中`);

      case "RATE_LIMITED":
        return {
          result: "天气服务暂时不可用(速率限制)。请稍后重试。",
          city: input.city,
          source: "fallback",
          degraded: true,
        };

      case "TIMEOUT":
        return {
          result: "天气服务未在时限内响应。请稍后重试。",
          city: input.city,
          source: "fallback",
          degraded: true,
        };

      case "RESPONSE_TOO_LARGE":
        return {
          result: "天气响应超出大小限制。请尝试简要格式。",
          city: input.city,
          source: "fallback",
          degraded: true,
        };

      case "NETWORK_ERROR":
        return {
          result: `网络错误:${err.message}`,
          city: input.city,
          source: "fallback",
          degraded: true,
        };

      default:
        throw err;
    }
  }
}

模式是:对配置错误抛出异常(DOMAIN_BLOCKED 意味着 phenotype 配置有误),对瞬态错误优雅降级(速率限制、超时、网络故障)。degraded 标志让下游消费者知道结果可能不完整。

第五步:测试与编译

运行基因的测试套件:

rotifer test weather-gene

预期输出:

  ✓ weather-gene/express — city:"London" → result contains "London"
  ✓ weather-gene/express — city:"東京" → result contains "東京"
  ✓ weather-gene/error  — invalid city → graceful fallback

  3 passed | 0 failed
  Next: rotifer compile weather-gene

然后编译为 IR:

rotifer compile weather-gene
  Compiling weather-gene v0.1.0
  Fidelity:  Hybrid
  Network:   wttr.in (1 domain)
  Timeout:   10000ms | Max body: 512 KiB | Rate: 5/min
  Output:    genes/weather-gene/weather-gene.wasm (42 KiB)

  ✓ Compiled successfully

编译器将网络配置嵌入 WASM 自定义节(custom sections)。运行时在加载时读取——基因无法绕过其声明的网络策略。

第六步:发布并在 Agent 中运行

发布到 Rotifer Cloud 注册表:

rotifer publish weather-gene

在 Agent 中使用:

rotifer agent create my-weather-agent --genes weather-gene
rotifer agent run my-weather-agent --input '{"city": "Berlin"}'
  Agent: my-weather-agent
  Gene:  weather-gene (Hybrid, gateway: wttr.in)

  Result: Berlin: ⛅ +12°C

运行时自动检测 Hybrid 保真度,从编译后的 IR 中读取网络配置,构造一个带有声明限制的 NetworkGateway 实例,并将 gatewayFetch 注入基因的执行上下文。基因永远不会接触原始 fetch

最佳实践

最小域名面积。 只列出基因实际调用的域名。一个基因、一个 API、一个域名是理想状态。如果需要多个 API,考虑拆分为多个基因在 Agent 中组合。

使用环境变量存储 API 密钥。 永远不要在基因源码中硬编码凭证。使用 ctx.env 获取 Agent 运营者在部署时提供的 API 密钥。基因声明需要什么;运营者提供值。

合理的限制参数。maxTimeoutMs 设为预期延迟的 2–3 倍,而非最大值 30s。将 maxResponseBytes 设为你预期的实际载荷大小,而非默认的 1 MiB。紧凑的限制能尽早发现回退问题。

LLM 提供商无关性。 如果基因封装了 LLM,将端点 URL 和模型名称作为输入参数。不要硬编码 api.openai.com——让运营者选择提供商。声明 "allowedDomains": ["*.openai.com", "*.anthropic.com", "*.mistral.ai"] 或将域名作为运行时参数接受。

测试正常路径和错误路径。 每个 gatewayFetch 调用都有五种失败方式。你的测试套件至少应覆盖:成功响应、速率限制响应和超时。在测试中直接使用 NetworkGateway 类来模拟每种场景。

深入阅读: 查看完整的 Hybrid Gene 开发指南,了解 Network Gateway 参考与 RAG 流水线示例。