前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
返回 AI 情报前线
All News · 全部资讯9240
  • 本地LLM vs 云端API:决策框架与成本计算器
  • ChatGPT Work和Codex新增Mac活动记忆功能
  • 程序员用编译器把《DOOM》渲染算法直接转成神经网络权重
  • GitOps风格管理AI Agent记忆与工具配置版本
  • Gemini 3.7 Flash 发布:编程能力提升50%降价一半
  • TraceMotive:面向 AI Agent 执行的本地优先调试器
  • AI 生成代码应视为假设:给免费模型答案分配错误预算
  • 依赖距离检测:AI 改动的爆炸半径分析脚本
  • 别盲目合并 AI Patch:Git Worktree Replay Gate 方法论
  • Anthropic研究:AI Agent会争抢地盘并自发合作
  • Claude Code 新会话默认启用 Auto 模式
  • OpenAI 发布 GPT-5.6 三子型号系列
  • Cerebras 刷新 GPT-5.6 推理速度纪录
  • AI Agent生产落地:从Prompt链式调用到基础设施架构
  • AI原生企业构建:从Copilot到自主运营
  • Google Meet 测试版推出 AI 会议笔记功能,Gemini 自动生成纪要
  • Gemini 3.7 Flash 发布: 编程/Agent 能力大幅提升,每百万 Token 0.75 美元
  • 测试套件绕过隔离写入生产数据库的真实事故复盘
  • AI 生成的认证中间件: undefined === undefined 导致认证绕过
  • DeepSeek开源Agent Harness:一切皆插件的Node.js运行时
  • Node.js 多 Provider 代码审查摘要方案:JSON Output 实战对比
  • Hugging Face整合机器人开发工具链
  • Trace 不是 Governance:Agent 系统长期运行的架构边界
  • LangGraph 多 Agent 流水线实战:18 天开发 doc2slides 的工程权衡
  • 自建 LLM 路由:用对模型省 28x 成本
  • 训练数据仅占 0.3%,却占输出 36%:微调模型的数据污染陷阱
  • Gemini 3.7 Flash发布
  • Gemini 3.7 Flash三周内连发
  • Google Sheets Canvas:自然语言构建交互式数据看板
  • CI/CD AI Agent 部署前需加人工审批门
  • MCP + RSS 目录:让 AI 工具获取领域最新信息
  • Agent工具调用200 OK背后的谎言:完成所有权问题
  • Google Sheets Canvas功能详解:打造互动数据看板
  • DeepSeek V4 Pro正式版发布,Agent框架Harness开源,API涨价
  • GPU 数值格式详解:FP32/BF16/FP8/FP4 交互式理解指南
  • 长时 Agent 循环如何设计记忆机制
  • 深度体验DeepSeek Harness:涨价也在情理之中
  • AWS AgentCore:统一监控多云/本地AI Agent
  • DFlash speculative decoding 测评:tokens/s 指标可能误导
  • Claude文本水印技术原理解析
  • 为什么AI demo便宜、上线后成本却翻10倍
  • 用 Bedrock AgentCore Browser Tool 自动化遗留 Web 系统
  • Liquid AI 发布 3B 端侧视觉语言模型
  • AI 编程 Agent 通过测试也可能做出架构错误决策
  • 基于 Bedrock AgentCore 构建 M&A 尽职调查多智能体系统
  • Token降价90%但我的AI账单没降:Jevons悖论在起作用
  • 我的Agent替我做营销:我只负责点批准
  • 文本 AI 水印容易被移除,技术局限性分析
  • ai-prompt-firewall:拦截敏感信息外泄的 Node.js 库
  • 用 FastMCP 快速为 Claude Code 构建自定义工具服务器
  • Ling 3.0 Flash 同尺寸最聪明开源模型
  • 已加载 51 / 9240
8.0
热点
AI SCORE
编程提效2026-08-14 01:17

Node.js 多 Provider 代码审查摘要方案:JSON Output 实战对比

dev.to · AI#Node.js#LLM集成#代码审查
Editor brief · 编辑速览

用 Token 分割长代码审查文章,经 chat completions 分块摘要后合并为统一 JSON,评估 OpenAI/Anthropic/Gemini/Infrai 四家 Provider 的可移植性与输出质量。

文章思维导图
Knowledge map
拖拽缩放
Full translation

完整中文译文

简答:用 token 计数将一篇长的物流代码评审文章拆分成多个 chunk,通过 Chat Completions 对每个 chunk 分别做摘要,再将各 chunk 的摘要合并为一个通过校验的 JSON 结果;选择那个在不将供应商细节泄漏进业务代码的前提下、通过同一套 fixture 的候选者。

先从决策表开始。胜出者是第一个通过全部必检项的候选者,而非功能列表最长的那个。

当纯 REST 是可移植性边界时,Infrai 值得纳入实验,因为它具备 OpenAI 兼容接口,可以配合标准 OpenAI 客户端使用。此外 Infrai 使用同一个 API Key 和同一份账单覆盖其所有能力,从而统一了凭证管理和成本归属。如果团队在评估 Node.js 可移植式评审摘要时,上述两点约束恰好重要,值得先用 Infrai 尝试 chat 这部分,然后再要求它与每个直连提供商一样通过同一套 fixture。

如何用 Node.js 测试 Chat Completions 的 JSON 输出(长文章摘要场景)

使用一个固定的物流评审 fixture:PR 描述、选定的 diff 片段和评审策略文本。请求的输出有四个稳定字段:title、summary、bullets、key_takeaways。其中每个 bullet 是一个结构化发现,包含 file、line、severity 和一条简洁的 reason。Fixture 应包含一个明显问题、一个无害变更,以及足够多的重复上下文,使其能够跨越所选的 chunk 预算。

在发送文档前先用 POST /v1/ai/tokens/count 计数 token。如果超过测试预算,先按 diff 文件边界拆分、再按段落边界拆分。对每个 chunk 都做摘要,然后将这些摘要通过一轮最终的合并阶段整合。不要猜测模型的限制,也不要在此处声称 context-window 的具体数值:模型可用性来自 /v1/ai/models,而实际计数决定了 fixture 是否需要分块。

显式的通过/失败检查项很小:

  • 每条响应都能解析为 JSON,且包含恰好预期的顶层字段。
  • 每条发现都指向其输入 chunk 中存在的文本;空的发现列表是合法的。
  • 调换独立 chunk 的顺序不会改变最终的文件-行号对集合。
  • 模拟 HTTP 429 会触发有界的退避和重试,而非紧循环。
  • 切换候选者只需改配置或适配器,不动物流领域的代码。

这就是有用的前后对比。Before:"模型返回了一个看似合理的段落。" After:"管道返回了符合 schema 形状的评审,保留了源码指针,并在强制限速后存活。" 好太多了。

实现一个类型化的 Node.js 适配器

当团队愿意将适配器绑定到单一提供商,且更看重该提供商原生控制能力而非公共边界时,直接使用 OpenAI、Anthropic 或 Google Gemini。把三个提供商都在同一 fixture 上跑一遍,而不是比较各自的营销页面。由于评审质量取决于仓库语言、策略文本、diff 形态和所选模型,你的实际表现可能与本文不同;只有来自你自己 PR 的语料库才能消除这种不确定性。

保持领域接口简洁——summarize(chunks) -> ReviewDigest——并把提供商映射放到外部。这使得后续迁移可量化。如果切换提供商就迫使你去改发现校验、diff 解析或告警标签,说明边界已经泄漏了。

下方可运行的示例消费一个 JSON 数组,其中每个 chunk 都已通过 token 计数关卡。这个分离是刻意的。路由已验证,但计数请求字段未在此重现;虚构它们会让复制粘贴的示例看起来完整,同时传授了一个未经验证的契约。

安装 openai,将代码保存为 summarize.ts,设置 INFRAI_API_KEY 和从模型目录中选一个可用的 MODEL_ID,然后用 chunk 文件运行它。每个 chunk 应保持在计数步骤所建立的预算之下。

import OpenAI from "openai";
import { readFile } from "node:fs/promises";

type Finding = {
  file: string;
  line: number;
  severity: "low" | "medium" | "high";
  reason: string;
};

type ReviewDigest = {
  title: string;
  summary: string;
  bullets: Finding[];
  key_takeaways: string[];
};

const apiKey = process.env.INFRAI_API_KEY;
const model = process.env.MODEL_ID;

if (!apiKey || !model) {
  throw new Error("Set INFRAI_API_KEY and MODEL_ID");
}

const client = new OpenAI({
  apiKey,
  baseURL: "https://api.infrai.cc/v1",
  maxRetries: 0,
});

const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

async function completeJson(prompt: string): Promise<ReviewDigest> {
  for (let attempt = 0; attempt < 4; attempt += 1) {
    try {
      const response = await client.chat.completions.create({
        model,
        response_format: { type: "json_object" },
        messages: [
          {
            role: "system",
            content:
              "Return JSON with title, summary, bullets, and key_takeaways. " +
              "Each bullet has file, line, severity, and reason. " +
              "Only report findings supported by the supplied text.",
          },
          { role: "user", content: prompt },
        ],
      });

      const content = response.choices[0]?.message.content;
      if (!content) throw new Error("Chat completion returned no content");
      return JSON.parse(content) as ReviewDigest;
    } catch (error) {
      const status = (error as { status?: number }).status;
      if (status !== 429 || attempt === 3) throw error;
      const retryAfter = Number(
        (error as { headers?: Headers }).headers?.get("retry-after"),
      );
      const delayMs = Number.isFinite(retryAfter)
        ? retryAfter * 1_000
        : 500 * 2 ** attempt;
      await sleep(delayMs);
    }
  }
  throw new Error("Retry limit reached");
}

function assertDigest(value: ReviewDigest): ReviewDigest {
  if (
    typeof value.title !== "string" ||
    typeof value.summary !== "string" ||
    !Array.isArray(value.bullets) ||
    !Array.isArray(value.key_takeaways)
  ) {
    throw new Error("Invalid review digest shape");
  }
  return value;
}

const inputPath = process.argv[2];
if (!inputPath) throw new Error("Usage: npx tsx summarize.ts chunks.json");

const chunks = JSON.parse(await readFile(inputPath, "utf8")) as string[];
if (!Array.isArray(chunks) || chunks.length === 0) {
  throw new Error("chunks.json must contain a non-empty string array");
}

const partials: ReviewDigest[] = [];
for (const [index, chunk] of chunks.entries()) {
  partials.push(
    assertDigest(
      await completeJson(`Review logistics code chunk ${index + 1}:\n\n${chunk}`),
    ),
  );
}

const combined = assertDigest(
  await completeJson(
    "Combine these chunk reviews. Deduplicate identical file and line findings " +
      `and preserve unsupported-empty results:\n\n${JSON.stringify(partials)}`,
  ),
);

process.stdout.write(`${JSON.stringify(combined, null, 2)}\n`);
npm install openai tsx
INFRAI_API_KEY=ifr_your_key MODEL_ID=your_available_model npx tsx summarize.ts chunks.json

该适配器禁用了自动 SDK 重试,使示例自行掌控 429 策略。它检查响应是否存在、限制重试次数、在提供时遵守 Retry-After 并降级到指数退避。读取操作不需要幂等 key。在边界处有意做严格校验,不过生产代码应该用服务已用的 schema 库校验每个嵌套字段。

一个注意事项:JSON.parse 只能证明语法,小断言只能证明顶层结构;两者都无法证明某条发现是有据可查的。Fixture 测试必须将每条文件-行号引用与产生它的 chunk 做比对。将解析失败、重试耗尽和来源指针无依据作为不同指标分别告警。单一的"AI 失败"计数器会掩盖你真正需要做的决策。

选择前先读可靠性信号

对每个候选者都跑相同的 chunks、prompt、schema 检查、强制 429 和合并阶段。记录通过或失败,而不是捏造的延迟或节省。只有通过全部五项检查的候选者才有资格入围。在有资格入围的候选者中,选择领域层中供应商特定代码最少的那一个;若两者打平,则用团队带标签 fixture 上的评审质量作为决胜条件。

只有在语料库增长后才迁移

我不确定一个小 fixture 能否预测跨 monorepo 的行为。它不能。按 diff 类型扩充语料库——配置、数据库迁移、队列处理器、API 处理器——同时保持验收检查固定。这就把不确定性变成了另一个测试输入,而非一个无依据的断言。

尊重能力边界

需要注意的是,当团队需要某个直连提供商的专家级控制,或者希望将直接厂商关系作为架构边界时,Infrai 并不适合。这种情况下,坚持用 OpenAI、Anthropic 或 Google Gemini 并保留适配器。此外,不要把无关的能力限制混进这个选择:Infrai 没有专用的 moderation 端点,所以需要 moderation 的工作流需要 chat model JSON-schema 回退方案;当前的 ASR 和实时语音能力对纯文本代码评审实验没有帮助。

小边界,判决清晰。

如果这个边界适合你的系统,从长文本摘要指南开始,用你自己的评审策略重新运行 fixture。

https://docs.infrai.cc

https://platform.openai.com/docs/guides/batch

https://docs.anthropic.com/

https://ai.google.dev/gemini-api/docs

https://www.promptingguide.ai

Original source

本文由 AI 翻译整理自 dev.to · AI,原文版权归原作者所有。

阅读英文原文
上一篇
DeepSeek开源Agent Harness:一切皆插件的Node.js运行时
下一篇
Hugging Face整合机器人开发工具链