You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js调用Python的后端部署Vercel遇spawn ENOENT错误求助

解决Vercel部署Node.js调用Python脚本的spawn ENOENT错误

问题核心

Vercel的Node运行时环境未预装Python,且Node与Python运行时完全隔离,跨Runtime调用Python脚本不符合Vercel的部署模型,导致spawn python找不到可执行文件。

修复方案:重构为Python Serverless Function

放弃Node调用Python的方式,直接用Vercel的Python Serverless Function处理摘要请求,步骤如下:

1. 创建Python Serverless Function

在项目根目录新建api/summary.py(Vercel默认识别api/目录下的文件为Serverless Functions),整合原Python脚本与Node中的逻辑:

import json
import math
import nltk
from sumy.summarizers.luhn import LuhnSummarizer
from sumy.parsers.plaintext import PlaintextParser
from sumy.nlp.tokenizers import Tokenizer
from nltk.tokenize import sent_tokenize

# 自动下载nltk所需的tokenizer数据
try:
    nltk.data.find('tokenizers/punkt')
except LookupError:
    nltk.download('punkt')

def handler(event, context):
    # 解析请求体
    req_body = json.loads(event['body'])
    text = req_body['text']
    len_param = int(req_body['length'])
    
    word_counter = len(text.split(" "))
    sum_vals = {
        1: [30, 130],
        2: [100, 300],
        3: [150, 400],
        4: [200, 500]
    }
    
    # 处理不同长度参数的逻辑
    if len_param == 0:
        # 生成带项目符号的摘要
        sentences = sent_tokenize(text)
        parser = PlaintextParser.from_string(text, Tokenizer("english"))
        summarizer_luhn = LuhnSummarizer()
        summary = summarizer_luhn(parser.document, int(math.ceil(len(sentences)/3)))
        
        document_parser = [str(i) for i in summary]
        final_sentence = "\n".join([f"\u2022 {doc}" for doc in document_parser])
    else:
        # 处理指定长度的摘要
        target_len = len_param
        if target_len == 1:
            if word_counter < sum_vals[1][0]:
                return {
                    'statusCode': 400,
                    'headers': {'Access-Control-Allow-Origin': '*'},
                    'body': json.dumps({'error': 'This sentence is too short to summarize.'})
                }
            use_vals = sum_vals[1]
        else:
            use_vals = sum_vals[target_len] if word_counter >= sum_vals[target_len][0] else sum_vals[target_len-1]
        
        sentences = sent_tokenize(text)
        parser = PlaintextParser.from_string(text, Tokenizer("english"))
        summarizer_luhn = LuhnSummarizer()
        summary_sentence_count = int(math.ceil(len(sentences)/3))
        summary = summarizer_luhn(parser.document, summary_sentence_count)
        
        document_parser = [str(i) for i in summary]
        final_sentence = "\n".join(document_parser)
    
    # 返回结果
    return {
        'statusCode': 200,
        'headers': {
            'Access-Control-Allow-Origin': '*',
            'Content-Type': 'application/json'
        },
        'body': json.dumps({'summary': final_sentence})
    }

2. 配置Python依赖

在项目根目录新建requirements.txt,列出所有依赖包:

sumy
nltk

3. 修改Vercel配置文件

更新vercel.json,简化路由规则:

{
    "version": 2,
    "routes": [
        {
            "src": "/summary",
            "dest": "/api/summary.py",
            "methods": ["POST"],
            "headers": {
                "Access-Control-Allow-Origin": "*"
            }
        }
    ]
}

4. 调整前端请求

修改前端的请求路径,指向Vercel的Python Serverless Function:

// 移除自定义headers(后端已配置),修改请求路径
let result = await Axios.post(url + "/api/summary", data)
    .then((res) => {
        console.log("Then 1", res.data)
        return res.data.summary
    })
    .then((result) => {
        console.log("Then 2", result)
        document.getElementsByClassName("text-output")[0].value = result
        setloadingSubmit(false) 
        return result
    })
    .catch((err) => {
        console.log("There was an error: ", err)
        setloadingSubmit(false)
        return "Error"
    })

关键说明

  • Vercel的Node与Python运行时完全隔离,无法通过child_process跨Runtime调用脚本,必须将Python逻辑转为独立的Serverless Function。
  • 原代码中的Hex编码可移除,直接返回字符串即可避免编码问题。
  • 新增NLTK数据自动下载逻辑,避免部署后因缺少tokenizer数据报错。

内容的提问来源于stack exchange,提问作者kimberly modeste

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 23:17:16