Tổng quan Python SDK
Tổng quan về Python SDK
Phần tiêu đề “Tổng quan về Python SDK”Bộ công cụ phát triển phần mềm (SDK) Python của NextPDF dành cho các ứng dụng Python cần trích xuất Portable Document Format (PDF) có nguồn gốc rõ ràng. SDK trả về các khối có cấu trúc kèm neo trích dẫn, bao gồm chỉ mục trang, độ tin cậy, hộp giới hạn tùy chọn và mã định danh nút ngữ nghĩa khi PDF nguồn cung cấp cấu trúc đó.
Dùng SDK khi quy trình của bạn cần trả lời các câu hỏi như “Văn bản này lấy từ trang nào?”, “Bảng nào chứng minh cho giá trị này?”, hoặc “Có gì thay đổi giữa hai tệp PDF này?” mà không xem kết quả trích xuất PDF như văn bản thuần không rõ nguồn.
Những gì nó cung cấp
Phần tiêu đề “Những gì nó cung cấp”- Một client
NextPDFđồng bộ cho script, tác vụ theo lô và notebook. - Một client
AsyncNextPDFbất đồng bộ choasyncio, FastAPI và các runtime bất đồng bộ khác. - Một giao diện dòng lệnh (CLI)
nextpdfđể trích xuất một lần từ đường dẫn tệp hoặc đầu vào chuẩn, rồi ghi ra đầu ra chuẩn hoặc một tệp. - Một máy chủ Model Context Protocol (MCP) tùy chọn để các tác nhân trí tuệ nhân tạo (AI) có thể gọi trực tiếp các công cụ trích xuất PDF.
- Một backend từ xa để dùng cho môi trường sản xuất với NextPDF Connect.
- Một backend cục bộ để trích xuất ngoại tuyến, chỉ dùng thư viện, thông qua
pypdf.
Lựa chọn backend
Phần tiêu đề “Lựa chọn backend”Backend từ xa gửi byte PDF tới máy chủ NextPDF Connect. Đây là hướng được khuyến nghị cho môi trường sản xuất vì nó tập trung hóa hành vi trích xuất, xác thực, hạn mức và các kiểm soát vận hành.
Backend cục bộ chạy bên trong tiến trình Python và đọc PDF thông qua pypdf. Backend này hữu ích cho phát triển ngoại tuyến và các PDF có gắn thẻ, nhưng không thể cung cấp hộp giới hạn chính xác và dùng cách trích xuất theo đoạn văn dựa trên heuristic cho các PDF không gắn thẻ. Backend cục bộ chỉ dùng được qua thư viện: hãy đưa một LocalBackend vào AsyncNextPDF để sử dụng. CLI nextpdf và máy chủ MCP không thể dùng backend này. Xem Ma trận lựa chọn backend để có bảng so sánh đầy đủ.
Hạn chế
Phần tiêu đề “Hạn chế”SDK không thực hiện nhận dạng ký tự quang học (OCR). Các PDF được quét hoặc chỉ chứa hình ảnh cần qua một bước OCR trước khi NextPDF có thể trích xuất văn bản nhúng. Bố cục phức tạp, văn bản chồng lấn và các trình tạo PDF bất thường cũng có thể làm giảm chất lượng trích xuất.
CLI nextpdf chỉ hoạt động với backend từ xa và không phải là giao diện dạng luồng. Mỗi lệnh đọc toàn bộ PDF vào bộ nhớ (từ đường dẫn tệp hoặc đầu vào chuẩn), gửi PDF đó tới máy chủ NextPDF Connect, dựng kết quả hoàn chỉnh trong bộ nhớ rồi tuần tự hóa trong một lần ghi duy nhất. Bạn có thể chuyển hướng đầu ra đó tới một tệp bằng --output (hoặc -o) hoặc tới đầu ra chuẩn, nhưng kết quả được đệm toàn bộ, không được tạo theo từng phần. CLI không thể dùng backend pypdf cục bộ.
Chọn client: đồng bộ hay bất đồng bộ
Phần tiêu đề “Chọn client: đồng bộ hay bất đồng bộ”Cả hai client dùng chung không gian tên phương thức ast cho các thao tác trên cây cú pháp trừu tượng (AST) và trả về cùng các mô hình Pydantic. Điểm khác biệt chỉ nằm ở mô hình đồng thời.
| Bối cảnh của bạn | Dùng | Lý do |
|---|---|---|
| Script và tác vụ theo lô | NextPDF (đồng bộ) | Luồng điều khiển tuyến tính; không có vòng lặp sự kiện cần quản lý. |
| Notebook Jupyter | NextPDF (đồng bộ) | run_sync phát hiện vòng lặp sự kiện đang chạy và điều phối tới một luồng worker, nên các lệnh gọi chặn vẫn hoạt động bên trong các ô lệnh. |
Công cụ nextpdf CLI | NextPDF (đồng bộ, nội bộ) | CLI dựng sẵn một client đồng bộ cho bạn. |
asyncio dịch vụ | AsyncNextPDF | Hỗ trợ await nguyên bản; không có bàn giao luồng. |
| FastAPI, Starlette, Asynchronous Server Gateway Interface (ASGI) | AsyncNextPDF | Dùng chung vòng lặp sự kiện của yêu cầu và cùng một nhóm kết nối. |
| Phân tán đồng thời cao | AsyncNextPDF | Chạy nhiều lần trích xuất đồng thời bằng asyncio.gather trên một client dùng chung nhóm kết nối. |
NextPDF bao bọc một AsyncNextPDF nội bộ và chạy mỗi lệnh gọi qua run_sync. Bên trong một vòng lặp sự kiện đang chạy, chẳng hạn như trong notebook, run_sync điều phối coroutine tới một luồng worker riêng với vòng lặp của chính nó, nên bạn không gặp lỗi asyncio.run lồng nhau. Trong dịch vụ asyncio hoặc ASGI, hãy gọi AsyncNextPDF trực tiếp thay vì chịu chi phí bàn giao luồng đó trên mỗi lệnh gọi.
Client bất đồng bộ sở hữu một httpx.AsyncClient để dùng chung nhóm kết nối, vì vậy hãy tái sử dụng một thực thể AsyncNextPDF và đóng nó một lần. Client NextPDF đồng bộ không cung cấp phương thức close(). Với các tải công việc bất đồng bộ chạy lâu, hãy ưu tiên AsyncNextPDF và quản lý vòng đời của nó một cách rõ ràng (xem Mô hình vận hành sản xuất).
Ma trận lựa chọn backend
Phần tiêu đề “Ma trận lựa chọn backend”Một backend triển khai giao thức PdfBackend. Backend từ xa (RemoteBackend) được chọn tự động khi bạn truyền base_url và api_key. Bạn phải đưa backend cục bộ (LocalBackend) vào một cách rõ ràng thông qua tham số backend= của AsyncNextPDF; backend này không được xuất ra từ gói nextpdf cấp cao nhất và không thể truy cập từ CLI hoặc máy chủ MCP.
| Khả năng | Từ xa (RemoteBackend) | Cục bộ (LocalBackend) |
|---|---|---|
| Được chọn bởi | base_url + api_key | AsyncNextPDF(backend=LocalBackend(...)) |
| Mạng | NextPDF Connect qua Hypertext Transfer Protocol Secure (HTTPS) | Không có; chạy trong tiến trình |
| Xác thực, hạn mức, đo lường | Tập trung ở máy chủ | Không có |
| Khả năng quan sát và các kiểm soát vận hành | Phía máy chủ | Không có |
| Trích xuất PDF có gắn thẻ (StructTree) | Có | Có |
| Trích xuất PDF không gắn thẻ | Engine của máy chủ | Tách đoạn văn theo heuristic, độ tin cậy 0.5 |
| Hộp giới hạn | Có (khi máy chủ cung cấp chúng) | Không (bbox là None) |
| Trích xuất bảng trên các PDF không gắn thẻ | Engine của máy chủ | Không trả về bảng nào |
| Truy cập được từ CLI / máy chủ MCP | Có | Không (chỉ dùng thư viện) |
| Khuyến nghị dùng cho | Sản xuất | Phát triển ngoại tuyến, kiểm thử PDF có gắn thẻ |
Dùng backend từ xa cho môi trường sản xuất vì đó là hướng duy nhất có xác thực tập trung, thực thi hạn mức, đo lường và khả năng quan sát. Dùng backend cục bộ cho phát triển ngoại tuyến và kiểm thử trên các PDF có gắn thẻ, đồng thời chấp nhận kết quả theo heuristic, không có hộp giới hạn và không có bảng trên đầu vào không gắn thẻ.
"""Inject the local backend for offline, library-only extraction."""
from nextpdf import AsyncNextPDFfrom nextpdf.backends.local import LocalBackend
async def extract_offline(pdf_bytes: bytes) -> None: """Extract cited text without a NextPDF Connect server.""" async with AsyncNextPDF(backend=LocalBackend()) as client: blocks = await client.ast.extract_cited_text(pdf_bytes) for block in blocks: # Heuristic blocks on untagged PDFs report confidence 0.5. print(block.citation.confidence, block.text)Mô hình vận hành sản xuất
Phần tiêu đề “Mô hình vận hành sản xuất”Trong môi trường sản xuất, hãy chạy backend từ xa với NextPDF Connect. Các ví dụ dưới đây bao gồm tái sử dụng client, xử lý lỗi, thử lại, xử lý hạn mức và thời gian chờ. Mọi ký hiệu được dùng ở đây đều tồn tại trong SDK. SDK không tự thử lại thay cho bạn, nên vòng lặp thử lại là trách nhiệm của bạn.
Tái sử dụng client và dùng chung nhóm kết nối
Phần tiêu đề “Tái sử dụng client và dùng chung nhóm kết nối”RemoteBackend giữ một httpx.AsyncClient thường trực để dùng chung nhóm kết nối. Hãy khởi tạo AsyncNextPDF một lần, dùng chung cho các yêu cầu và đóng khi tắt. Đừng tạo một client cho mỗi yêu cầu.
"""Reuse one pooled async client for the lifetime of the process."""
import asyncioimport osfrom pathlib import Path
from nextpdf import AsyncNextPDF
async def main() -> None: """Run several extractions over a single pooled client.""" base_url = os.environ["NEXTPDF_BASE_URL"] # Treat the API key as a secret; read it from the environment, never hard-code it. api_key = os.environ["NEXTPDF_API_KEY"]
async with AsyncNextPDF(base_url=base_url, api_key=api_key) as client: pdf_paths = (Path("a.pdf"), Path("b.pdf"), Path("c.pdf")) tasks = [ client.ast.get_document_ast(path.read_bytes()) for path in pdf_paths ] documents = await asyncio.gather(*tasks) for document in documents: print(document.page_count, document.estimated_tokens)
if __name__ == "__main__": asyncio.run(main())Trình quản lý ngữ cảnh bất đồng bộ gọi close() khi thoát, qua đó đóng tầng truyền tải bên dưới. Nếu không dùng trình quản lý ngữ cảnh, hãy tự gọi await client.close().
Xử lý lỗi bằng cây phân cấp ngoại lệ
Phần tiêu đề “Xử lý lỗi bằng cây phân cấp ngoại lệ”SDK phát sinh một cây phân cấp ngoại lệ có kiểu. Mọi lỗi đều bắt nguồn từ NextPDFError; các lỗi ở tầng Hypertext Transfer Protocol (HTTP) bắt nguồn từ NextPDFAPIError và mang theo một status_code. Hãy bắt các kiểu cụ thể mà bạn có thể xử lý, rồi quay về kiểu cơ sở.
| Ngoại lệ | Phát sinh khi | Thuộc tính chính |
|---|---|---|
NextPDFError | Kiểu cơ sở cho mọi lỗi của SDK | status_code |
NextPDFAPIError | Mọi lỗi HTTP từ máy chủ | status_code, error_code |
NextPDFLicenseError | HTTP 402; tính năng cần một bậc máy chủ cao hơn | status_code (402) |
QuotaExceededError | HTTP 429; vượt quá giới hạn tốc độ hoặc hạn mức | retry_after |
AstNoStructTreeError | HTTP 422; PDF không gắn thẻ với chế độ heuristic đang tắt | status_code (422) |
AstBuildTimeoutError | HTTP 504; quá trình dựng AST hết thời gian chờ | status_code (504) |
"""Map SDK exceptions to caller-facing outcomes."""
from nextpdf import ( AstBuildTimeoutError, AstNoStructTreeError, AsyncNextPDF, NextPDFAPIError, NextPDFError, NextPDFLicenseError, QuotaExceededError,)
async def safe_extract(client: AsyncNextPDF, pdf_bytes: bytes) -> str: """Extract text, translating known failures into a stable status string.""" try: blocks = await client.ast.extract_cited_text(pdf_bytes) except QuotaExceededError as exc: # exc.retry_after holds the server Retry-After value in seconds, or None. return f"rate-limited; retry after {exc.retry_after}s" except NextPDFLicenseError: return "feature requires a higher server tier" except AstNoStructTreeError: return "untagged PDF; enable heuristic mode or use a tagged PDF" except AstBuildTimeoutError: return "build timed out; reduce the page range" except NextPDFAPIError as exc: return f"server error (status {exc.status_code})" except NextPDFError: return "extraction failed" return "\n".join(block.text for block in blocks)Thử lại các lỗi tạm thời với backoff
Phần tiêu đề “Thử lại các lỗi tạm thời với backoff”SDK không tự động thử lại. Hãy bọc các lệnh gọi trong vòng lặp của riêng bạn để thử lại khi gặp lỗi HTTP tạm thời và tôn trọng giá trị Retry-After của máy chủ, giá trị mà QuotaExceededError cung cấp dưới dạng retry_after (một số nguyên giây, hoặc None). Dùng backoff theo cấp số nhân cho các trạng thái tạm thời khác, và đừng thử lại NextPDFLicenseError.
"""Retry transient failures with exponential backoff and Retry-After support."""
import asynciofrom collections.abc import Awaitable, Callablefrom typing import TypeVar
from nextpdf import NextPDFAPIError, QuotaExceededError
_RETRYABLE_STATUS = frozenset({500, 502, 503, 504})
_T = TypeVar("_T")
async def with_retry( coro_factory: Callable[[], Awaitable[_T]], *, max_attempts: int = 4,) -> _T: """Call coro_factory() with bounded retries on transient server errors.
Args: coro_factory: A zero-argument callable returning a fresh awaitable. max_attempts: Maximum number of attempts before giving up.
Returns: The awaited result of the first successful attempt.
Raises: NextPDFAPIError: When all attempts fail or the error is not retryable. """ delay = 1.0 for attempt in range(1, max_attempts + 1): try: return await coro_factory() except QuotaExceededError as exc: if attempt == max_attempts: raise await asyncio.sleep(exc.retry_after if exc.retry_after is not None else delay) delay *= 2.0 except NextPDFAPIError as exc: if attempt == max_attempts or exc.status_code not in _RETRYABLE_STATUS: raise await asyncio.sleep(delay) delay *= 2.0 raise RuntimeError("unreachable")Quản lý hạn mức, giới hạn tốc độ và thời gian chờ
Phần tiêu đề “Quản lý hạn mức, giới hạn tốc độ và thời gian chờ”Việc thực thi hạn mức và giới hạn tốc độ nằm trên máy chủ. Khi gặp HTTP 429, SDK phát sinh QuotaExceededError và phân tích tiêu đề Retry-After thành retry_after. Backend từ xa cũng hiển thị các tiêu đề X-RateLimit-* trên các phản hồi kết xuất, nên bạn có thể chủ động điều tiết trước khi chạm giới hạn cứng.
Thời gian chờ yêu cầu dùng mặc định cố định là 60 giây tổng cộng, với thời gian chờ kết nối 10 giây (httpx.Timeout(60.0, connect=10.0)). Để giới hạn các lần dựng AST kéo dài, hãy thu hẹp công việc bằng page_range_start, page_range_end, hoặc token_budget thay vì chỉ dựa vào thời gian chờ; một lần dựng quá lâu sẽ trả về AstBuildTimeoutError (HTTP 504).
Các kiến trúc ví dụ
Phần tiêu đề “Các kiến trúc ví dụ”Tác vụ theo lô
Phần tiêu đề “Tác vụ theo lô”Một worker theo lô đọc các tệp PDF, trích xuất văn bản có trích dẫn và ghi ra đầu ra có cấu trúc. Hãy tái sử dụng một client dùng chung nhóm kết nối, giới hạn mức độ đồng thời bằng một semaphore, và áp dụng helper thử lại ở trên.
"""Batch-extract a directory of PDFs over one pooled async client."""
import asyncioimport osfrom pathlib import Path
from nextpdf import AsyncNextPDF
async def run_batch(input_dir: Path, concurrency: int = 8) -> None: """Extract cited text for every PDF in input_dir, bounded by concurrency.""" semaphore = asyncio.Semaphore(concurrency)
async def worker(client: AsyncNextPDF, path: Path) -> None: async with semaphore: blocks = await client.ast.extract_cited_text(path.read_bytes()) out = path.with_suffix(".txt") out.write_text("\n".join(b.text for b in blocks), encoding="utf-8")
async with AsyncNextPDF( base_url=os.environ["NEXTPDF_BASE_URL"], api_key=os.environ["NEXTPDF_API_KEY"], ) as client: await asyncio.gather(*(worker(client, p) for p in input_dir.glob("*.pdf")))Dịch vụ web
Phần tiêu đề “Dịch vụ web”Dịch vụ FastAPI dùng chung một AsyncNextPDF cho các yêu cầu trong suốt vòng đời của ứng dụng, nên mỗi yêu cầu đều tái sử dụng nhóm kết nối. Hãy đọc thông tin xác thực từ môi trường và coi API key là bí mật.
"""FastAPI service that shares one pooled NextPDF client across requests."""
import osfrom contextlib import asynccontextmanager
from fastapi import FastAPI, UploadFile
from nextpdf import AsyncNextPDF
@asynccontextmanagerasync def lifespan(app: FastAPI): """Create the pooled client on startup and close it on shutdown.""" app.state.client = AsyncNextPDF( base_url=os.environ["NEXTPDF_BASE_URL"], api_key=os.environ["NEXTPDF_API_KEY"], ) try: yield finally: await app.state.client.close()
app = FastAPI(lifespan=lifespan)
@app.post("/extract")async def extract(file: UploadFile) -> dict[str, list[str]]: """Return cited text blocks for an uploaded PDF.""" pdf_bytes = await file.read() blocks = await app.state.client.ast.extract_cited_text(pdf_bytes) return {"text": [block.text for block in blocks]}Công cụ tác nhân
Phần tiêu đề “Công cụ tác nhân”Đối với các tác nhân AI, hãy chạy máy chủ MCP. Máy chủ hiển thị các công cụ PDF (ví dụ nextpdf_extract_text, nextpdf_extract_tables, nextpdf_get_ast, nextpdf_info, nextpdf_search, nextpdf_get_outline, nextpdf_diff, và nextpdf_health) qua đầu vào và đầu ra chuẩn. Máy chủ đọc NEXTPDF_BASE_URL và NEXTPDF_API_KEY từ môi trường, nên được hỗ trợ bởi backend từ xa; giống như CLI, máy chủ không thể dùng backend cục bộ. Hãy cài đặt phần mở rộng tùy chọn và chạy module.
pip install "nextpdf[mcp]"python -m nextpdf.mcpXem Máy chủ Python MCP để biết hướng dẫn tích hợp tác nhân, Python CLI để biết cách dùng trong terminal, và Tài liệu tham khảo API Python để xem toàn bộ bề mặt client, mô hình và ngoại lệ.