LLM 调用从 60s 卡死降到 3s!彻底绕过 tiktoken 网络阻塞(LangChain.js 必看)

前言(痛点暴击)

你是不是也遇到过:

  • LLM 接口本身明明很快(2~3s)
  • 一用上 LangChain + OpenAI 兼容接口
  • 国内环境直接30s~60s+ 超时
  • 服务一重启就集体卡死
  • 日志疯狂报 tiktoken 连接超时?

导致用户体验极差,接口响应时间从预期的 3-5 秒膨胀到 30-60 秒。

现象:重启即炸,超时拉满

典型症状

  1. 服务首次启动后,LLM 调用正常(2-3 秒返回)
  2. 服务重启后:所有 LLM 调用集体超时
  3. 响应时间从预期 3~5s → 暴冲到 30~60s+
  4. 控制台疯狂报错:(出现大量 tiktoken 连接超时错误)
text 复制代码
Failed to calculate number of tokens, falling back to approximate count TypeError: fetch failed
  [cause]: ConnectTimeoutError: Connect Timeout Error
    (attempted addresses: ***.**.**.**:443, ***.**.**.***:443, timeout: 10000ms)
    code: 'UND_ERR_CONNECT_TIMEOUT'

如果你用的是:

  • @langchain/openai
  • ChatOpenAI
  • 国内网络 / 无代理 / 代理不稳定

那这篇文章就是为你写的

根因:90% 的人都不知道的 LangChain 暗坑

谁在偷偷拖慢你?------ tiktoken

tiktoken 是 OpenAI 官方分词器,用来精确计算 token 数量

问题出在这里:LangChain.js 在每次 llm.invoke() 时,都会强制调用 tiktoken 计算 token!

超时链路(一图看懂)

rust 复制代码
llm.invoke(prompt)
  -> LangChain 内部调用 getNumTokens()
    -> js-tiktoken 需要加载编码数据
      -> 从 https://tiktoken.pages.dev/js/{encoding}.json 下载
        -> 该域名托管在 Cloudflare,国内网络不稳定/不可达
          -> p-retry 库重试 3-4 次,每次 10s 超时
            -> 总计阻塞 30-40 秒
              -> 最终 fallback 到近似计算,但时间已浪费

为什么重启才炸?

  • tiktoken 编码数据首次下载成功后,缓存在 Node.js 进程内存
  • 服务运行期间,后续调用直接使用内存缓存,不再下载
  • 服务重启后,内存缓存清空,tiktoken 重新尝试下载
  • 如果此时网络不通(Cloudflare 在国内不稳定),就会反复超时

时间线示例(来自实际日志)

时间 事件 结果
16:43:20 LLM 意图分析 成功(tiktoken 已缓存)
16:45:41 LLM 意图分析 成功(tiktoken 已缓存)
16:49:50 LLM 意图分析 成功(tiktoken 已缓存)
17:09:10 服务重启 tiktoken 缓存丢失
17:09:17 LLM 意图分析 超时(tiktoken 下载失败)
17:16:15 LLM 意图分析 超时
17:19:14 LLM 意图分析 超时
... 后续所有调用 全部超时

这就是典型的:LLM 本身不慢,tiktoken 慢到你怀疑人生。

方案:直接绕开!不依赖代理、不改环境

核心思路

抛弃 LangChain 调用层,直接用 fetch 调用 LLM API。

  • 绕开 tiktoken
  • 绕开 LangChain 内部逻辑
  • 只保留最核心的 chat completions
  • 响应时间直接回到 2~3s

最终代码(可直接复制使用)

为方便大家快速理解,当前已关闭流式输出(SSE) 。若需使用该功能,可在现有基础上调整实现。

src/services/llm.service.ts 中新增 invokeLLM 函数:

ts 复制代码
import { config } from '../config';

/**
 * 直接调用 LLM(绕过 LangChain & tiktoken 阻塞)
 * @param prompt 用户提示词
 * @param options 温度、超时等配置
 * @returns 模型返回内容
 */
export async function invokeLLM(
  prompt: string,
  options?: { temperature?: number; timeout?: number }
): Promise<string> {
  const { baseUrl, apiKey, model, temperature: defaultTemp } = config.llm;
  const url = `${baseUrl}/chat/completions`;

  const controller = new AbortController();
  const timeoutId = setTimeout(
    () => controller.abort(),
    options?.timeout || 10000
  );

  try {
    const res = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${apiKey}`,
      },
      body: JSON.stringify({
        model,
        temperature: options?.temperature ?? defaultTemp,
        messages: [{ role: 'user', content: prompt }],
      }),
      signal: controller.signal,
    });

    if (!res.ok) {
      throw new Error(`LLM 接口异常 ${res.status}`);
    }

    const data = await res.json();
    return data.choices?.[0]?.message?.content || '';
  } catch (err: any) {
    if (err.name === 'AbortError') {
      throw new Error('LLM 调用超时');
    }
    throw err;
  } finally {
    clearTimeout(timeoutId);
  }
}

使用对比

改造前(LangChain,受 tiktoken 阻塞,会超时):

typescript 复制代码
const llm = getLLM();
const response = await llm.invoke(prompt);

改造后(直接 fetch,不受 tiktoken 影响,飞一般的速度)

typescript 复制代码
const content = await invokeLLM(prompt);

效果对比(肉眼可见的提升)

表格

指标 改造前(LangChain) 改造后(直接 fetch)
意图分析 30~60s+ / 超时 2~3s
消息预处理 30~40s 2~3s
对话回复 30~40s 2~5s
整体接口 基本不可用 稳定流畅

一句话总结:不可用 直接拉满到生产可用

其他方案(为什么我不推荐)

1、预下载 tiktoken 文件

  • 优点:保留精确 token 计算
  • 缺点:要维护本地文件、升级易炸、麻烦

2、走代理

  • 优点:不用改代码
  • 缺点:部署复杂、线上环境不一定允许、不稳定

3、加大超时到 60s

  • 优点:最简单
  • 缺点:用户直接跑光

4、最优方案:绕开 tiktoken(本方案)

  • 零依赖
  • 不改环境
  • 不搞代理
  • 上线即生效
  • 速度直接拉满

注意事项(生产必看)

  1. 此方法不支持流式输出,需要流式再单独封装 SSE。(这个也简单)
  2. 不计算 token,如需计费 / 限流,要自己实现轻量分词。(几乎不用)
  3. 需要 tool calling / 复杂 Agent 场景,仍可保留 LangChain。(酌情处理)
  4. 兼容所有 OpenAI 格式接口:自建 / 第三方 / 阿里 / 百度 / 字节 等。(几乎也能满足)

总结

很多时候 LLM 慢不是模型慢,而是你用的库在偷偷做你不需要的事情

tiktoken 这个坑,国内 90% 使用 LangChain.js 的团队都踩过。希望这篇实战优化,能帮你把 LLM 接口从超时重灾区 拉回秒级体验

相关推荐
猩猩程序员5 小时前
前端学习 AI Agent 开发
前端
Younglina6 小时前
打了3年羽毛球球才发现:我对自己的装备和胜率一无所知
前端·后端
风骏时光牛马6 小时前
Bash脚本高阶实战与常见报错完整代码案例详解
前端
kartjim6 小时前
我用 AI 一小时写了一个世界杯数据可视化平台|前端 VibeCoding 初体验
前端·程序员·ai编程
lichenyang4536 小时前
从一个 WebView Demo 开始,理解 ASCF 小程序底座到底在做什么
前端
牧艺6 小时前
用 Next.js 搭建 AI Agent 前端编排:从 Plan 到 SSE Trace 的完整实践
前端·agent
行者全栈架构师6 小时前
UniApp集成vk-uview-ui组件库详解:打造高效UI开发体验
前端·vue.js
林希_Rachel_傻希希6 小时前
js里面的proxy理解。以及vue3响应式数据设计底层
前端·javascript·面试
sunrains6 小时前
uniapp x 动态Tabbar(切换无闪烁)+动角标+主题切换+自定义tabbar页面导航栏样式设置 支持服务端动态配置根据角色动态设置Tabbar
前端
把马铃薯变成土豆6 小时前
前端Stripe跨境支付对接感想
前端·源码