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

FastAPI接口JSON编码触发RecursionError问题求助

FastAPI接口RecursionError排查问题

调用FastAPI接口时出现RecursionError,但直接在代码中运行对应逻辑无异常,怀疑存在循环引用但未找到问题根源,报错信息如下:

File "/Users/x/.virtualenvs/y/lib/python3.11/site-packages/fastapi/encoders.py", line 331, in jsonable_encoder    
    return jsonable_encoder(            
           ^^^^^^^^^^^^^^^^^   
File "/Users/x/.virtualenvs/y/lib/python3.11/site-packages/fastapi/encoders.py", line 287, in jsonable_encoder    
    encoded_value = jsonable_encoder(                    
                    ^^^^^^^^^^^^^^^^^   
File "/Users/x/.virtualenvs/y/lib/python3.11/site-packages/fastapi/encoders.py", line 331, in jsonable_encoder    
    return jsonable_encoder(            
           ^^^^^^^^^^^^^^^^^   
File "/Users/x/.virtualenvs/y/lib/python3.11/site-packages/fastapi/encoders.py", line 331, in jsonable_encoder    
    return jsonable_encoder(            
           ^^^^^^^^^^^^^^^^^   
File "/Users/x/.virtualenvs/y/lib/python3.11/site-packages/fastapi/encoders.py", line 287, in jsonable_encoder    
    encoded_value = jsonable_encoder(                    
                    ^^^^^^^^^^^^^^^^^   
File "/Users/x/.virtualenvs/y/lib/python3.11/site-packages/fastapi/encoders.py", line 331, in jsonable_encoder    
    return jsonable_encoder(            
           ^^^^^^^^^^^^^^^^^   
File "/Users/x/.virtualenvs/y/lib/python3.11/site-packages/fastapi/encoders.py", line 279, in jsonable_encoder    
    encoded_key = jsonable_encoder(                  
                  ^^^^^^^^^^^^^^^^^   
File "/Users/x/.virtualenvs/y/lib/python3.11/site-packages/fastapi/encoders.py", line 216, in jsonable_encoder    
    if isinstance(obj, BaseModel):        
       ^^^^^^^^^^^^^^^^^^^^^^^^^^ 
RecursionError: maximum recursion depth exceeded

相关代码

api.py

from utils.pdfToText import pdfToText
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from repository.document_repository import Document, add_document
from repository.base_model_repository import BaseModel
from repository.embedding_repository import generate_embeddings, get_top_matches, Embedding, get_embeddings
import uvicorn
app = FastAPI()

database = BaseModel.get_database()
@app.get('/search')
def search_chunks(query: str, n: int = 5):
    return get_top_matches(query, n)

repository/embedding_repository.py

from repository.base_model_repository import BaseModel
from repository.document_repository import Document
from pgvector.peewee import VectorField
from peewee import ForeignKeyField, TextField
from utils.createChunks import create_chunks
from utils.client import create_embeddings

class Embedding(BaseModel):
    url = ForeignKeyField(Document, backref='embeddings')
    text = TextField()
    embedding = VectorField(dimensions=1536)

def get_top_matches(query, n):
    query_emb = create_embeddings(query)
    res =  Embedding.select().order_by(Embedding.embedding.cosine_distance(query_emb)).limit(n).dicts()
    return res

client.py

from openai import OpenAI

client = OpenAI()

def create_embeddings(text, model="text-embedding-3-small"):
    text = text.replace("\n", " ")
    return client.embeddings.create(input = [text], model=model).data[0].embedding

排查与解决思路

  • 序列化循环引用触发递归:FastAPI的jsonable_encoder处理Peewee模型反向引用时会陷入循环。Embedding的url关联Document,而Document通过backref='embeddings'又关联回Embedding,序列化时会反复调用编码器导致递归溢出。
  • 返回结果类型未彻底转换:get_top_matches返回的是Peewee的查询对象(即使调用了.dicts()),FastAPI会自动深度遍历序列化这个对象,而本地运行时仅打印字典不会触发完整递归逻辑。
  • 显式转换为纯字典列表:修改get_top_matches,将查询结果转为纯Python字典列表,避免FastAPI处理Peewee模型对象:
    def get_top_matches(query, n):
        query_emb = create_embeddings(query)
        res = list(Embedding.select().order_by(Embedding.embedding.cosine_distance(query_emb)).limit(n).dicts())
        return res
    
  • 移除反向引用或配置序列化忽略:如果不需要Document到Embedding的反向关联,删除backref='embeddings';或自定义编码器忽略反向引用字段。
  • 用Pydantic模型包装返回结果:定义Pydantic模型仅包含需要返回的字段,只序列化必要数据,避免自动遍历关联对象:
    from pydantic import BaseModel
    
    class EmbeddingResponse(BaseModel):
        text: str
        # 按需添加其他字段,不包含关联的Document对象
    
    def get_top_matches(query, n):
        query_emb = create_embeddings(query)
        res = Embedding.select().order_by(Embedding.embedding.cosine_distance(query_emb)).limit(n)
        return [EmbeddingResponse(text=item.text) for item in res]
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 08:13:22