技能 编程开发 内容策略与使用防护栏

内容策略与使用防护栏

v20260423
exa-policy-guardrails
本技能提供了一套全面的外部搜索API防护机制,用于增强系统的稳定性和安全性。它实现了内容审核(过滤不良内容)、领域白名单/黑名单限制、查询内容清洗、结果时效性检查,以及关键的按用户配额限制,确保API调用结果的可靠性和合规性。
获取技能
355 次下载
概览

Exa Policy Guardrails

Overview

Policy enforcement for Exa neural search integrations. Exa searches the open web, so results may include unreliable sources, competitor content, or inappropriate material. This skill covers domain allowlists/blocklists (via Exa's includeDomains/excludeDomains), content moderation, query sanitization, freshness policies, and per-user budget enforcement.

Prerequisites

  • exa-js installed and configured
  • Content policy requirements defined
  • Redis for per-user quota tracking (optional)

Instructions

Step 1: Domain Filtering (Built-in Exa Feature)

import Exa from "exa-js";

const exa = new Exa(process.env.EXA_API_KEY);

// Exa supports up to 1200 domains in includeDomains/excludeDomains
const TRUSTED_SOURCES = {
  medical: [
    "pubmed.ncbi.nlm.nih.gov", "who.int", "cdc.gov",
    "nejm.org", "nature.com", "thelancet.com",
  ],
  technical: [
    "github.com", "stackoverflow.com", "developer.mozilla.org",
    "docs.python.org", "nodejs.org", "arxiv.org",
  ],
  news: [
    "reuters.com", "apnews.com", "bbc.com",
    "techcrunch.com", "arstechnica.com",
  ],
};

const BLOCKED_DOMAINS = [
  "competitor1.com", "competitor2.io",
  "spam-farm.com", "content-mill.net",
];

async function policySearch(
  query: string,
  category: keyof typeof TRUSTED_SOURCES | "general"
) {
  const opts: any = {
    type: "auto",
    numResults: 10,
    text: { maxCharacters: 1000 },
    moderation: true,  // Exa's built-in content moderation
  };

  if (category !== "general" && TRUSTED_SOURCES[category]) {
    opts.includeDomains = TRUSTED_SOURCES[category];
  } else {
    opts.excludeDomains = BLOCKED_DOMAINS;
  }

  return exa.searchAndContents(query, opts);
}

Step 2: Query Content Policy

const BLOCKED_PATTERNS = [
  /how to (hack|exploit|attack|ddos)/i,
  /(buy|purchase|order)\s+(drugs|weapons|firearms)/i,
  /personal.*(address|phone|ssn|social security)/i,
  /generate.*(malware|ransomware|virus)/i,
];

function validateQuery(input: string): string {
  for (const pattern of BLOCKED_PATTERNS) {
    if (pattern.test(input)) {
      throw new PolicyViolation("Query blocked by content policy");
    }
  }

  // Sanitize
  return input
    .replace(/[<>{}]/g, "")     // strip HTML/template chars
    .replace(/\0/g, "")         // remove null bytes
    .trim()
    .substring(0, 500);         // cap query length
}

class PolicyViolation extends Error {
  constructor(message: string) {
    super(message);
    this.name = "PolicyViolation";
  }
}

Step 3: Freshness Policy

// Enforce minimum recency for time-sensitive use cases
function applyFreshnessPolicy(
  opts: any,
  maxAgeDays: number
): any {
  const cutoff = new Date(Date.now() - maxAgeDays * 24 * 60 * 60 * 1000);
  return {
    ...opts,
    startPublishedDate: cutoff.toISOString(),
  };
}

// Usage: only return results from the last 90 days
const results = await exa.searchAndContents("AI regulation updates",
  applyFreshnessPolicy(
    { type: "neural", numResults: 10, text: true },
    90  // max 90 days old
  )
);

Step 4: Per-User Budget Enforcement

class ExaUsagePolicy {
  private usage = new Map<string, { count: number; resetAt: number }>();
  private limits: Record<string, number>;

  constructor(limits: Record<string, number> = {
    "free": 10,
    "pro": 100,
    "enterprise": 1000,
  }) {
    this.limits = limits;
  }

  checkQuota(userId: string, tier: string): void {
    const limit = this.limits[tier] || this.limits["free"] || 10;
    const now = Date.now();
    const hourKey = `${userId}:${new Date().toISOString().substring(0, 13)}`;

    let entry = this.usage.get(hourKey);
    if (!entry || entry.resetAt < now) {
      entry = { count: 0, resetAt: now + 3600 * 1000 };
    }

    if (entry.count >= limit) {
      throw new PolicyViolation(
        `Hourly search quota exceeded: ${entry.count}/${limit}`
      );
    }

    entry.count++;
    this.usage.set(hourKey, entry);
  }
}

const usagePolicy = new ExaUsagePolicy();

Step 5: Combined Policy Enforcement

async function enforcedSearch(
  userId: string,
  userTier: string,
  rawQuery: string,
  category: keyof typeof TRUSTED_SOURCES | "general" = "general",
  maxAgeDays?: number
) {
  // 1. Check quota
  usagePolicy.checkQuota(userId, userTier);

  // 2. Validate and sanitize query
  const query = validateQuery(rawQuery);

  // 3. Build options with domain policy
  let opts: any = {
    type: "auto",
    numResults: 10,
    text: { maxCharacters: 1000 },
    moderation: true,
  };

  if (category !== "general" && TRUSTED_SOURCES[category]) {
    opts.includeDomains = TRUSTED_SOURCES[category];
  } else {
    opts.excludeDomains = BLOCKED_DOMAINS;
  }

  // 4. Apply freshness policy
  if (maxAgeDays) {
    opts = applyFreshnessPolicy(opts, maxAgeDays);
  }

  // 5. Execute search
  return exa.searchAndContents(query, opts);
}

Error Handling

Issue Cause Solution
Competitor content in results No domain filtering Apply excludeDomains blocklist
Harmful query accepted No content policy Validate queries against blocked patterns
Stale results displayed No freshness check Apply startPublishedDate filter
API cost overrun No usage limits Implement per-user/tier quotas
Blocked policy query False positive Review and adjust BLOCKED_PATTERNS

Resources

Next Steps

For architecture decisions, see exa-architecture-variants. For cost control, see exa-cost-tuning.

信息
Category 编程开发
Name exa-policy-guardrails
版本 v20260423
大小 6.2KB
更新时间 2026-04-28
语言