./avatar.jpg

调用大模型 API 的几个最佳实践:重试、降级与审计

刚开始用大模型 API 时,大家一般关心的是:

这个模型效果好不好?回答聪不聪明?

但当你真的把它作为「线上能力」接入产品时,会发现很多工程问题:

  • API 偶尔超时 / 报错怎么办?
  • 费用怎么控?
  • 返回内容里如果有敏感信息怎么办?
  • 出了问题,怎么排查某次请求?

这篇文章就是从这些「落地问题」出发,整理几条接入大模型 API 的最佳实践。


1. 超时与重试:别把前端一直挂着

1.1 超时要有明确上限

大模型生成本身需要时间,如果不设超时,前端可能会一直转圈。
我们一般做法:

  • 在 BFF / 中间层对每个调用设定超时时间(例如 10–15 秒);
  • 超时后直接返回「超时错误」,由前端给出友好提示;
  • 前端也设置请求超时,避免被挂死。

1.2 重试策略

常见错误类型:

  • 临时网络抖动;
  • API 限流(429);
  • 服务端偶发异常。

幂等请求(比如生成建议文案),我们采用:

  • 指数退避重试:例如最多重试 2 次,间隔 500ms / 1000ms;
  • 遇到某些错误码(如 4xx 中的参数错误)不重试。

伪代码:

async function callLLMWithRetry(payload) {
  const maxRetry = 2;
  let attempt = 0;

  while (true) {
    try {
      return await callLLM(payload);
    } catch (e: any) {
      if (!isRetryableError(e) || attempt >= maxRetry) {
        throw e;
      }
      attempt++;
      await sleep(500 * attempt);
    }
  }
}

2. 降级:模型不可用时要有「备胎」

大模型 API 作为外部依赖,不可能 100% 可用。
因此建议设计「降级方案」:

前端视角下的 A/B Test:埋点、分流与验证

「做个 A/B 实验看看」这句话听起来很轻松,真正落地时会发现:

  • 指标怎么算?
  • 实验人群怎么分?
  • 前端要怎么接入实验配置?
  • 实验结束之后,代码怎么收口?

这篇文章从前端的角度,讲一次相对完整的 A/B 实验闭环。


1. 先确定:为什么要做这个实验?

我们当时的实验目标是:提高某投资流程的转化率

假设是:

把原本一个超长的单页表单拆成多步引导,是否能提高「填写完成率」。

因此:

  • 实验对象:访问该流程的真实用户;
  • 对照组:看到旧版「单页表单」;
  • 实验组:看到新版「多步引导」;
  • 观测指标:完成率、平均耗时、中途流失位置等。

只有先把这些定义清楚,后续埋点和分流才有方向。


2. 埋点设计:先有指标,再谈实现

这次我们重点关心几个指标:

  1. flow_enter:进入流程的人数;
  2. flow_complete:完成流程的人数;
  3. step_view:在多步表单中,每一步的曝光;
  4. step_error:每一步表单提交失败次数。

在前端埋点上做了几件事:

  • 所有埋点带上 experiment_idvariant(A / B);
  • 关键事件使用统一的 event_name + event_params 格式;
  • 尽量避免把业务逻辑硬编码到埋点名里。

示例:

track('flow_enter', {
  experimentId: 'exp_split_form_v1',
  variant: currentVariant, // 'A' 或 'B'
});

track('step_view', {
  experimentId: 'exp_split_form_v1',
  variant: currentVariant,
  step: 1,
});

track('flow_complete', {
  experimentId: 'exp_split_form_v1',
  variant: currentVariant,
});

3. 分流:让实验「公平」且可控

3.1 基本原则

  1. 同一用户体验稳定

    • 被分到 A 组,就一直看 A;
    • 避免一次访问是 A,刷新变 B。
  2. 分配近似均匀

微前端尝试记:什么时候值得用,又该怎么用

微前端这个词已经火了很多年,但真要落地时,经常会遇到几个灵魂拷问:

  • 我们的项目有必要用吗?
  • 是不是换个 iframe 也能解决?
  • 上了之后会不会更复杂?

这篇文章是一次真实的尝试记录:我们为什么考虑微前端、最后怎么落地、踩过哪些坑。


1. 我们为什么会想到微前端?

当时的背景:

  • 已经有一个比较大的运营中台,使用 Vue2;
  • 新业务线希望独立迭代,想用 Vue3/Vite;
  • 不同团队节奏不一致,但最终还是要在一个「统一入口」下为业务方服务。

简单说就是:

不同团队、不同技术栈、不同发布节奏,但对外要看起来像「一个系统」。

这是典型的微前端适用场景之一。

我们也认真讨论过:

  • 方案 A:继续在原系统里加路由

    • 所有业务都塞进一个 Vue2 大仓库;
    • 优点是简单,缺点是历史包袱越来越重。
  • 方案 B:子系统独立域名

    • 完全分开,运营自己维护入口;
    • 用户在不同系统间跳转,体验割裂。

最终,我们选了一个折中方案:

用微前端把多个独立应用挂在一个统一壳子下,保证用户体验一致,同时保留各团队的技术栈和发布节奏。


2. 选型:为什么最后选了「框架型」方案

有几种路线:

  1. 自己基于 iframe + postMessage 搞一套协议;
  2. 使用成熟微前端框架(如 single-spa、qiankun、Module Federation 等);
  3. 基于构建时集成的模块联邦方案(Webpack Module Federation 等)。

我们最后选的是类似 qiankun 的「运行时加载子应用」方案,原因:

  • 接入成本相对低,不用大改原有项目;
  • 不强绑某个构建工具;
  • 社区实践比较多。

整体架构是:

主应用(Shell,负责导航/布局/统一登录)
  ├── 子应用 A(Vue2,老中台)
  ├── 子应用 B(Vue3,新模块)
  └── 子应用 C(React,小实验项目)

3. 路由接入:谁管 URL?

微前端绕不开的一个问题是:路由由谁控制?

把多个前端项目合进一个 Monorepo 是怎样的体验

之前团队的前端项目是这样管理的:

  • 每个系统一个 Git 仓库;
  • 各自有一套构建脚本、一份依赖清单;
  • 公共组件、工具要么复制粘贴,要么用「半私有」的 NPM 包。

结果就是:

  • 组件 bug 修一份,要手动同步 N 个仓库;
  • 依赖升级经常有「版本错乱」;
  • CI 流水线一堆重复配置。

于是我们决定把几个核心前端项目合到一个 Monorepo 里。
这篇文章就是那次迁移的实战记录:工具选择、包划分、依赖管理、CI 改造,全流程。


1. 为什么要搞 Monorepo?

总结下来就是三点:

  1. 公共代码复用

    • 组件库、Hooks、工具函数不用再复制粘贴;
    • bug 修一次,多项目同步升级。
  2. 一致的工程规范

    • 统一 ESLint/Prettier/Commit 规范;
    • 统一构建脚手架和发布流程。
  3. 协同开发体验好

    • 打开一个仓库就能看到相关项目;
    • 修改组件立刻能在多个应用里验证。

当然,Monorepo 不是银弹,仓库会变大、CI 要更精细。
所以「是否值得」的标准是:

项目之间有明显共享,且会长期共同演进。


2. 工具选型:pnpm + Turborepo

我们调研了一圈:Lerna、Nx、Yarn Workspaces、pnpm Workspace、Turborepo……
最后选的是:pnpm Workspace + Turborepo,原因:

  • pnpm:硬链接节省磁盘空间、依赖管理清晰、速度快;
  • Turborepo:任务编排 + 缓存好用,学习成本相对较低。

2.1 仓库结构

repo-root
├── apps
│   ├── manager-web        # 管理端
│   ├── investor-web       # 投资人 Web
│   └── mini-program       # 小程序(uni-app 等)
├── packages
│   ├── ui-components      # 通用 UI 组件库
│   ├── shared-utils       # 工具函数
│   └── eslint-config      # 统一 ESLint 配置
├── package.json
├── pnpm-workspace.yaml
└── turbo.json

pnpm-workspace.yaml

第一次用 AI 写前端代码:哪些好用,哪些坑

2022 年我开始把 AI 编码助手真正用在日常开发里,体验可以用一句话概括:

它不是银弹,但如果用得好,很像一个永远不喊累的实习生。

这篇文章主要记录几个方面:

  • 它在哪些场景明显好用;
  • 哪些地方容易翻车;
  • 我最后形成的一些使用习惯。

1. 几个「惊艳到」的使用场景

1.1 写样板代码(boilerplate)

比如:

  • 新建一个 Vue 3 组合式组件;
  • 写一个标准的 Axios 封装;
  • 初始化一个简单的 React 页面结构。

以前要么复制项目里旧代码改,要么从 CLI 模板改;
现在直接丢一句:

「写一个 Vue3 + TypeScript 的表格组件,支持分页、排序,props 包含 xxx。」

然后拿到一个 70% 可用的版本,再按自己习惯调整。

1.2 写简单的工具函数

例如:

  • 将后端分页结果转换为前端表格数据;
  • 格式化日期/金额;
  • 从树结构中找到某个节点路径。

用自然语言描述需求 + 给一两个例子,AI 通常能给出还不错的实现。
有时候还能顺带推荐几种写法,帮你对比。

1.3 帮忙「翻译」老代码

面对一些古早 jQuery/复杂 if-else 的老代码,直接让 AI:

「帮我解释这段代码在干嘛,并用更现代的写法重构。」

得到的是:

  • 一段自然语言解释(方便确认理解);
  • 一版初步的重构实现(不一定完美,但至少是个起点)。

2. 重构和单测:AI 很适合当助手

2.1 帮忙拆函数 / 命名

长函数重构时,让 AI:

「这段函数太长了,帮我按逻辑拆成几个小函数,顺带起一些更好的函数名。」

用大模型做一个简单「文档问答助手」

2022 年开始,大模型 API 越来越好用,大家最直觉的一个想法就是:

能不能把项目文档「喂给」模型,然后直接用自然语言问问题?

这篇文章记录的是一个从 0 到 1 的小 Demo:用大模型 API + 向量检索,实现一个能回答「我们自己文档」问题的小助手。


1. 目标和约束

目标很简单:

  • 把部分项目文档(接口说明、架构设计、规范)导入系统;
  • 在一个聊天界面里,可以问:
    • 「用户登录接口的必填参数有哪些?」
    • 「私募投资人小程序的技术栈是什么?」
  • 助手能基于文档给出相对准确的回答。

约束:

  • 不追求「完美答案」;
  • 优先做到:能指向正确文档,并给出简洁总结

2. 总体架构

整体流程可以概括成:

  1. 文档导入 & 切分;
  2. 向量化(Embedding)并存入向量库;
  3. 用户提问 → 向量检索相关片段;
  4. 把相关片段 + 问题一起发给大模型 → 生成回答;
  5. 前端展示答案 + 引用片段。

用一张简图就是所谓的:RAG(检索增强生成)


3. 文档预处理:从 Markdown 到「小段文本」

我们选用的文档源主要是:

  • Git 仓库里的 Markdown(README、设计文档);
  • 部分导出的接口文档(JSON/YAML 转文本)。

预处理步骤:

  1. 把 Markdown 转成纯文本(保留标题层级);
  2. 按一定策略切分成「小段文本」,比如每段 300–500 字;
  3. 每段附带元信息:文档名、段落标题、在文档中的位置。

简单示例:

{
  "docId": "fund-investor-miniapp",
  "title": "技术栈与整体架构",
  "content": "前端采用 Vue3 + uni-app...",
  "section": "2.1 技术栈",
  "index": 5
}

切分策略的小经验: