用确定性算法给 OpenAPI 瘦身

在深圳设计师 中级 21小时前 231 浏览 6 点赞 约 2 分钟

很多人在做 API Agent 的时候都会掉进一个坑:OpenAPI 的规范文件实在太大了。如果你直接把整个 JSON 扔给大模型,不仅 Token 烧得飞快,模型还容易因为干扰信息太多而产生幻觉,莫名其妙地编造一些不存在的端点。

用确定性算法给 OpenAPI 瘦身

我之前在搞一个自动 API 测试的原型,就遇到了这个问题。我发现如果我只想测试 /categories 接口,根本没必要让模型看到 /payments/cart 的定义。于是我写了一套确定性的上下文批处理逻辑,把 Token 消耗从 5 万直接压到了 9200 左右,降低了 81.7%。

这里的核心逻辑是:绝对不要依赖 AI 去总结 API 文档,而要用代码去硬切。因为 OpenAPI 的结构是非常标准且确定的。

具体的操作流程是这样的:

用确定性算法给 OpenAPI 瘦身

一、领域切分(Domain Splitting)
首先通过脚本解析原始的 OpenAPI JSON,按照根路径(比如 /auth/categories)将端点进行物理拆分,只提取当前任务相关的路径片段。

二、递归引用解析(Recursive Reference Resolution)
这是最关键的一步。OpenAPI 经常使用 $ref 来复用 Schema。如果你只截取路径而没有引用内容,AI 根本不知道请求体里应该传什么。我写了一个递归解析器,它会顺着端点定义往深处挖,把所有相关的 $ref 全部找出来。

三、深度 Schema 提取
解析器只会把该端点真正依赖的嵌套 Schema 提取出来,直接舍弃掉那个臃肿的 components/schemas 全集。这样最后交给模型的 Context Map 是极简且精准的。

另外,在架构上我坚持一个原则:AI 只负责规划,执行交给确定性语言。我用 Dart 写了一个 ApiTestRunner 类,AI 生成结构化的 JSON 测试计划,然后由这个 Runner 去跑真实的 HTTP 请求。千万不要让 LLM 直接去调接口,否则它会给你编造响应结果。

简单来说,给 AI 提供“精准的上下文”比提供“巨大的上下文”要高效得多。

// 伪代码:递归解析 $ref 的逻辑片段
Map<String, dynamic> resolveRef(String ref, Map<String, dynamic> fullSpec) {
  if (!ref.startsWith('#/')) return {};
  
  // 将 #/components/schemas/User 拆分为路径列表
  List<String> paths = ref.split('/')..removeAt(0); 
  dynamic current = fullSpec;
  
  for (var segment in paths) {
    current = current[segment];
    if (current == null) break;
  }
  
  // 如果解析出的结果还是一个 $ref,则继续递归
  if (current is Map && current.containsKey('\$ref')) {
    return resolveRef(current['\$ref'], fullSpec);
  }
  
  return current is Map ? current : {'value': current};
}
AI编程cursorClaude CodeOpenAPIDart

全部回复 (3)

大Jerry 高级 21小时前
之前被这坑坑过,全量丢进去确实容易胡言乱语,得做裁剪。
0 回复
夜猫子创业者 专家 20小时前
我也试过先用关键词过滤一遍,能省不少Token。
0 回复
架构师Neo 中级 20小时前
确实有用,不过要是接口动态变化的话,怎么实时更新索引?
0 回复

发表回复

支持 Markdown 格式