Semantic Context Filtering Pattern

Nikola Balic (@nibzard)· emerging

问题

原始数据源过于冗长且充斥噪声,无法被LLM有效利用。完整的数据表示包含不可见元素、实现细节和无关信息,会造成context膨胀,干扰推理过程。

这会引发一系列问题:

  • Token膨胀:原始数据超出context限制,或使成本高得难以承受
  • 信噪比过低:LLM将推理能力浪费在无关细节上
  • 推理速度变慢:Token数量越多,生成速度越慢,成本也越高
  • 推理混乱:噪声导致生成幻觉或错误结论

该问题在多个领域均有体现:

  • 网页爬取:完整的HTML DOM包含脚本、样式、跟踪用iframe
  • API响应:包含嵌套元数据、内部字段、调试信息的JSON
  • 文档处理:页眉、页脚、导航栏、样板文本
  • 代码分析:注释、空白字符、样板代码

方案

从原始数据中仅提取语义、交互或相关元素,过滤掉噪声,为LLM提供能够捕获推理关键信息的干净数据表示。

核心原则

不要向LLM发送原始数据,要发送语义抽象结果。

示例1:浏览器无障碍树

不要发送完整的HTML DOM:

<!-- 原始HTML(10000+个token) -->
<html>
  <head>
    <script src="analytics.js"></script>
    <style>body { margin: 0; }</style>
  </head>
  <body>
    <div class="tracking-pixel" style="display:none"></div>
    <iframe src="ad-server.com"></iframe>
    <nav aria-label="Navigation">
      <a href="/">Home</a>
      <a href="/about">About</a>
    </nav>
    <main>
      <button id="login-button">Login</button>
      <input type="email" name="email" placeholder="Email" />
    </main>
    <footer>Copyright 2024</footer>
  </body>
</html>

提取无障碍树(100-200个token):

{
  "interactiveElements": [
    {
      "role": "link",
      "name": "Home",
      "xpath": "/html/body/nav/a[1]"
    },
    {
      "role": "link",
      "name": "About",
      "xpath": "/html/body/nav/a[2]"
    },
    {
      "role": "button",
      "name": "Login",
      "id": "login-button",
      "xpath": "/html/body/main/button"
    },
    {
      "role": "textbox",
      "name": "Email",
      "id": "email",
      "xpath": "/html/body/main/input"
    }
  ]
}

实现代码:

// 利用浏览器内置的无障碍树
const tree = await page.accessibility.snapshot({
  interestingOnly: true  // 仅保留交互元素
});

// 自动过滤以下内容:
// - 带有aria-hidden="true"的元素
// - display:none的元素
// - 广告/追踪类iframe(按域名过滤)
// - 无语义的div和span标签

示例2:API响应过滤

原始API响应通常包含内部元数据:

// 原始API响应(2000个token)
{
  "data": {
    "users": [
      {
        "id": "123",
        "name": "Alice",
        "email": "alice@example.com",
        "internalFlags": ["vip", "beta_tester"],
        "metadata": {
          "created_at": "2024-01-01",
          "updated_at": "2024-01-15",
          "version": 42
        }
      }
    ]
  },
  "_internal": {
    "requestId": "req-abc123",
    "latency": 45,
    "cache": "HIT",
    "debug": []
  },
  "_links": {
    "self": "/users",
    "next": "/users?page=2"
  }
}

仅保留语义字段:

function filterAPIResponse(response: any, schema: FieldSchema): any {
  const filtered = {};

  for (const field of schema.relevantFields) {
    if (response.data?.[field]) {
      filtered[field] = response.data[field];
    }
  }

  return filtered;
}

// 处理结果(200个token):
{
  "users": [
    {
      "name": "Alice",
      "email": "alice@example.com"
    }
  ]
}

示例3:文档章节提取

完整文档包含大量模板化内容:

完整文档:
====================
公司机密 [每页重复显示的页眉]
版权所有 2024 Acme公司 保留所有权利。

[长达3页的法律免责声明]

执行摘要
====================
Q4营收增长15%...

[导航菜单]
- 目录
- 索引
- 术语表

实际内容开始
====================
市场趋势分析显示...

[后续50页内容]

附录
========
[技术规格]
[法律免责声明]
[重复显示的联系信息]

提取语义章节:

def extract_semantic_content(document: str) -> dict:
    # 跳过页眉、页脚、导航内容
    sections = {
        "executive_summary": extract_section(document, "EXECUTIVE SUMMARY"),
        "analysis": extract_section(document, "ANALYSIS"),
        "conclusions": extract_section(document, "CONCLUSIONS"),
    }

    # 移除模板化内容
    for section in sections.values():
        section = remove_legal_disclaimers(section)
        section = remove_navigation(section)

    return sections

# 处理结果:仅保留实际内容,大小约为原始的20%

架构

graph LR
    A[原始数据源] --> B[语义过滤器]
    B --> C[干净的Context]

    subgraph "过滤层"
        B --> D[交互元素]
        B --> E[相关字段]
        B --> F[语义章节]
    end

    C --> G[LLM处理]

    A -. "移除噪声" .-> B
    B -. "缩减10-100倍" .-> C

    style C fill:#9f9,stroke:#333
    style A fill:#f99,stroke:#333

核心优势

| 维度 | 原始数据 | 语义过滤 | 提升效果 | |--------------|----------------|------------------------|------------------------| | Token数量 | 10,000 | 100-1,000 | 缩减10-100倍 | | LLM推理能力 | 受噪声干扰易混淆 | 专注于有效信息 | 决策更准确 | | 成本 | 高昂 | 低廉 | 成本降低10-100倍 | | 延迟 | 缓慢 | 快速 | 速度提升2-5倍 | | 准确性 | 易出错 | 更可靠 | 成功率更高 |

如何使用

1. 识别语义元素

针对你的业务领域,明确真正有价值的内容:

// 网页爬取:仅保留交互元素
const semanticElements = [
  'button', 'link', 'textbox', 'checkbox',
  'radio', 'combobox', 'slider'
];

// API 响应:仅保留业务数据
const relevantFields = [
  'name', 'email', 'status', 'amount'
];

// 文档:仅保留内容章节
const contentSections = [
  'executive_summary', 'analysis', 'conclusions'
];

2. 构建过滤层

class SemanticFilter {
  filter(data: any, domain: string): any {
    switch (domain) {
      case 'web':
        return this.filterAccessibilityTree(data);
      case 'api':
        return this.filterAPIResponse(data);
      case 'document':
        return this.filterDocumentSections(data);
    }
  }

  private filterAccessibilityTree(dom: any): any {
    // 仅保留带有ARIA角色的交互元素
    return dom
      .filter(el => el.interactive) // 筛选交互元素
      .filter(el => !el.isHidden) // 剔除隐藏元素
      .filter(el => !this.isAdIframe(el)) // 剔除广告iframe
      .map(el => ({
        role: el.role,
        name: el.name,
        xpath: el.xpath
      }));
  }
}

3. 在调用LLM前应用过滤

// 错误做法:直接发送原始数据
const response = await llm.generate({
  prompt: `Analyze this page: ${rawHTML}`
});

// 正确做法:先执行过滤
const filtered = semanticFilter.filter(rawHTML, 'web');
const response = await llm.generate({
  prompt: `Analyze this page: ${JSON.stringify(filtered)}`
});

4. 维护引用映射

为执行环节记录过滤后元素与原始元素的映射关系:

interface FilteredElement {
  semanticId: string;    // 语义ID,示例:"login-button"
  originalRef: string;   // 原始引用标识,示例:"frameIndex-backendNodeId"
  xpath: string;         // 元素的XPath路径
}

// 过滤后的上下文使用语义ID
const filteredContext = [
  { id: "btn-1", name: "Login", role: "button" }
];

// 执行层将语义ID映射回原始引用
const element = mapToOriginal(filteredContext[0].id);
await page.click(element.xpath);

权衡

优点:

  • 大幅减少token用量:context规模缩小10-100倍
  • 提升LLM推理能力:聚焦有效信号,而非冗余噪声
  • 降低成本:token用量越少,成本越低
  • 加快推理速度:context越小,生成速度越快
  • 提高可靠性:减少混淆和幻觉

缺点:

  • 过滤逻辑复杂度高:需要构建并维护过滤逻辑
  • 存在信息丢失风险:可能移除关键context
  • 领域特异性强:过滤规则需根据具体用例定制
  • 映射开销大:需要跟踪过滤后内容与原始内容的关联关系
  • 潜在bug风险:过滤逻辑可能移除重要元素

缓解策略:

  • 初始采用保守策略:过滤明显的噪声,纳入边界案例
  • 添加过滤绕过机制用于调试
  • 监控LLM性能:若准确率下降则扩展过滤规则
  • 为过滤规则与数据schema同步做版本管理
  • 向LLM提供提示信息:“Context已按相关性过滤”

参考文献

关键词

涵盖HyperAgent项目的原始可访问性树实现仓库、WAI-ARIA可访问性树对应的浏览器无障碍API规范,以及上下文窗口焦虑管理、精选上下文窗口两种相关设计模式。

直译

来源摘要

正在获取来源并生成中文摘要…

来源: https://github.com/hyperbrowserai/HyperAgent

← 返回社区