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
相关产品推荐
相关产品推荐

