Cùng bắt đầu xây dựng một tác nhân AI, cung cấp kiến thức từ hướng dẫn sử dụng của tất cả các thiết bị của mình và để nó trả lời các câu hỏi của mình khi cần. Tác nhân này chạy trên Raspberry Pi của mình và luôn sẵn sàng giúp mình. Có nhiều khuôn khổ ngoài kia giúp bạn xây dựng các tác nhân AI. LlamaIndex là khuôn khổ tôi đã chọn, nhưng bạn có thể xây dựng nó bằng bất kỳ khuôn khổ tác nhân nào. Vì tôi muốn ứng dụng này chạy miễn phí, tôi đã sử dụng LLM nguồn mở cho dự án. Ollama rất tuyệt vời cho mục đích này, cho phép bạn tải xuống LLM nguồn mở.
Trong bài viết này chúng ta đã xem xét:
- Cách xây dựng tác nhân RAG và ReAct bằng LlamaIndex.
- Các yếu tố cần cân nhắc khi chạy chương trình này trên máy có công suất thấp như Raspberry Pi.
- Sử dụng Streamlit để xây dựng giao diện người dùng đơn giản cho ứng dụng.
Chuẩn bị không gian cho hướng dẫn sử dụng
Bắt đầu bằng cách thu thập các hướng dẫn sử dụng mà bạn muốn AI truy cập và lưu chúng vào một thư mục cụ thể. Tôi chỉ cần tải mọi thứ lên trong thư mục này: docs/user_manuals.
Xây dựng một công cụ truy vấn từ các tài liệu
Tiếp theo, chúng ta cần xử lý bit RAG của tác nhân AI này. Điều này bao gồm việc lấy các tệp PDF từ thư mục, đọc và phân đoạn chúng, chuyển đổi các khối thành nhúng và lưu chúng vào cơ sở dữ liệu vector. Đây là tổng quan rất nhanh về khía cạnh chuẩn bị dữ liệu của RAG và người đọc được khuyến khích tìm hiểu sâu hơn về nó. Mặc dù quá trình này nghe có vẻ phức tạp, nhưng may mắn thay, LlamaIndex cho phép chúng ta thực hiện điều này chỉ với 3 dòng mã:
documents = SimpleDirectoryReader(“docs/user_manuals”).load_data(show_progress=True)”docs/user_manuals”).load_data(show_progress=True)
index = VectorStoreIndex.from_documents(documents, show_progress=True)
query_engine = index.as_query_engine()
Tuy nhiên, trước khi xác định chỉ mục và công cụ truy vấn, mô hình LLM nhúng cần phải được thay đổi. LlamaIndex sử dụng các mô hình OpenAI theo mặc định, nhưng bạn có thể sử dụng bất kỳ mô hình nào bạn thích. Trong trường hợp của tôi, việc sử dụng mô hình nguồn mở từ Ollama có thể được thực hiện như sau:
Settings.embed_model = OllamaEmbedding(model_name=”all-minilm:22m”)“all-minilm:22m”)
Người đọc được khuyến khích thử nghiệm với các mô hình nhúng khác nhau. Tôi đã thành công khác nhau với những mô hình tôi đã thử, chẳng hạn nhưnomic-embed-text, nhưngall-minilm:22mcho tôi thành công nhất. Với mô hình nomic, nó thường không phân tích cú pháp và nhúng một số phần nhất định của một số tài liệu. Bạn có thể thử mã khởi động 5 dòng do LlamaIndex cung cấp để kiểm tra hiệu suất của các mô hình nhúng:LlamaIndex — LlamaIndex. Tuy nhiên, tôi quyết định không sử dụng mã đó cho dự án của mình vì tôi muốn tác nhân của mình suy luận thông qua phản hồi mà nó nhận được từ công cụ truy vấn — tôi sẽ nói thêm về điều đó sau.
Xây dựng tác nhân ReAct
Mộttác nhân ReAct, viết tắt của Reason + Act, là một tác nhân lý luận suy nghĩ và hành động từng bước để giải quyết một nhiệm vụ phức tạp. Khá đơn giản để tạo một tác nhân ReAct trong LlamaIndex và cung cấp cho nó các công cụ. Đầu tiên, chúng ta sẽ chuyển đổi công cụ truy vấn được xác định trước đó thành một công cụ mà tác nhân có thể sử dụng:
user_manual_query_tool = QueryEngineTool.from_defaults(
query_engine,
name=”user_manual_query_tool”,”user_manual_query_tool”,
description=(
“A RAG engine with various user manuals of appliances.”,
“Use a detailed plain text question as input to the tool.”),
)
Bước tiếp theo là xác định tác nhân ReAct, cung cấp cho nó quyền truy cập vào công cụ truy vấn hướng dẫn sử dụng:
system_prompt = “”””””
System prompt:
You are an AI assistant specialized in answering questions about appliances using information from user manuals.
1. Always rely on the **user_manual_query_tool** to retrieve information; do not use prior knowledge.
2. If a query cannot be answered using the tool, respond with: “I cannot find this information in the user manuals provided.”
3. Do not attempt to generate answers without using the tool.
4. Give detailed responses to the user’s queries, explaining everything in steps.
5. Return the response in markdown format.
Note: The tool name is **user_manual_query_tool**, and it should be used for all queries.
“””
self.agent = ReActAgent.from_tools(
tools=[user_manual_query_tool],
verbose=True,
context=system_prompt
)
Tôi cũng đã định nghĩa một lời nhắc hệ thống cho tác nhân ở trên, để tôi có được câu trả lời nhất quán cho các truy vấn của mình từ hướng dẫn sử dụng. Nếu không có lời nhắc này, đôi khi tôi thấy tác nhân trả về phản hồi từ kiến thức của riêng mình, thay vì truy vấn chỉ mục kiến thức.
Ngoài ra, LlamaIndex sử dụng OpenAI LLM làm mặc định, nhưng chúng ta có thể dễ dàng thay đổi cài đặt để sử dụng Ollama:
Settings.llm = Ollama(model=”llama3.1″)”llama3.1″)
Tuy nhiên, tại thời điểm này, tôi bắt đầu nghi ngờ liệu điều này có thể chạy trên Raspberry Pi của tôi hay không, vì phải dựa vào llama3.1 (tham số 8b) có thể là một mô hình mạnh mẽ để chạy trên Raspberry Pi. Để kiểm tra mọi thứ, tôi đã tạo một lớp cho tác nhân:
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Settings
from llama_index.llms.ollama import Ollama
from llama_index.embeddings.ollama import OllamaEmbedding
from llama_index.core.agent import ReActAgent
from llama_index.core.tools import QueryEngineTool
import time
import nest_asyncio
nest_asyncio.apply()
class UserManualAgent:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super(UserManualAgent, cls).__new__(cls, *args, **kwargs)
return cls._instance
def __init__(self):
if not hasattr(self, ‘initialized’):
Settings.embed_model = OllamaEmbedding(model_name=”all-minilm:22m”)
Settings.llm = Ollama(model=”llama3.1″)
documents = SimpleDirectoryReader(“docs/user_manuals”).load_data(show_progress=True)
index = VectorStoreIndex.from_documents(documents, show_progress=True)
query_engine = index.as_query_engine()
user_manual_query_tool = QueryEngineTool.from_defaults(
query_engine,
name=”user_manual_query_tool”,
description=(
“A RAG engine with various user manuals of appliances.”,
“Use a detailed plain text question as input to the tool.”),
)
system_prompt = “””
System prompt:
You are an AI assistant specialized in answering questions about appliances using information from user manuals.
1. Always rely on the **user_manual_query_tool** to retrieve information; do not use prior knowledge.
2. If a query cannot be answered using the tool, respond with: “I cannot find this information in the user manuals provided.”
3. Do not attempt to generate answers without using the tool.
4. Give detailed responses to the user’s queries, explaining everything in steps.
5. Return the response in markdown format.
Note: The tool name is **user_manual_query_tool**, and it should be used for all queries.
“””
self.agent = ReActAgent.from_tools(
tools=[user_manual_query_tool],
verbose=True,
context=system_prompt
)
print(“done initializing agent”)
self.initialized = True
def query(self, question):
print(“user query: “, question)
start_time = time.time()
try:
response = self.agent.chat(question)
response_time = time.time() – start_time
print(“response time: “, response_time)
except ValueError as e:
response = “I cannot find this information in the user manuals provided.”
print(e)
return response
if __name__ == “__main__”:
agent = UserManualAgent()
response = agent.query(“How to setup the Roborock vacuum?”)
print(response)
Lưu ý rằng tôi đã tạo một singleton cho lớp này, do đó các lệnh gọi riêng biệt đến hàm tạo không khởi tạo tác nhân nhiều lần.
Bước tiếp theo là cài đặt Ollama và các mô hình phụ thuộc trên Raspberry Pi, kéo mã đã đề cập ở trên, cài đặt tất cả các phụ thuộc và chạy lớp để xem nó có hoạt động không. Và như tôi lo sợ, LLM đã hết thời gian chờ khi phản hồi. Tuy nhiên, tôi nên đề cập rằng tôi đã có thể chạy và trò chuyện với llama3.1 trên Raspberry Pi một mình. Tuy nhiên, nó không hoạt động hoàn toàn với tác nhân ReAct. Tôi bắt đầu thử nghiệm xung quanh với các mô hình Ollama khác nhau và các giá trị thời gian chờ và sự kết hợp cuối cùng đã hoạt động là:
Settings.llm = Ollama(model=”tinyllama”, request_timeout=60*5)”tinyllama”, request_timeout=60*5)
Sự kết hợp này hầu như không gây ra thời gian chờ, nhưng vẫn chậm. Đối với tôi, thời gian phản hồi nằm trong khoảng 45–60 giây. Có thể đạt được hiệu suất tốt hơn với phiên bản RAM 16 GB của Pi hoặc sử dụng SSD để lưu trữ thay vì thẻ SD và tôi có thể cân nhắc những điều này trong tương lai.
Đây là hạn chế khi sử dụng một mô hình nhỏ hơn như TinyLlama làm tác nhân lý luận và tôi nhận ra rằng tôi có thể phải thử nghiệm với các mô hình.
Cuối cùng tôi quyết định thay thế Ollama trong thiết lập của mình bằngOpenRoutervà sử dụng LLM nguồn mở từ đó. Nó có giới hạn về số lượng yêu cầu mà người dùng có thể thực hiện mỗi phút, nhưng là một giải pháp thay thế tuyệt vời khi bạn không có đủ tài nguyên phần cứng để chạy mô hình LLM lớn hơn.
LlamaIndex cũng cung cấp khả năng tích hợp dễ dàng với OpenRouter. Mã cuối cùng cho lớp hướng dẫn sử dụng như sau:
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Settings
from llama_index.llms.openrouter import OpenRouter
from llama_index.embeddings.ollama import OllamaEmbedding
from llama_index.core.agent import ReActAgent
from llama_index.core.tools import QueryEngineTool
import nest_asyncio
nest_asyncio.apply()
import time
from dotenv import load_dotenv
import os
load_dotenv()
class UserManualAgent:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super(UserManualAgent, cls).__new__(cls, *args, **kwargs)
return cls._instance
def __init__(self):
if not hasattr(self, ‘initialized’):
Settings.embed_model = OllamaEmbedding(model_name=”all-minilm:22m”)
llm = OpenRouter(
api_key=os.getenv(“OPEN_ROUTER_KEY”),
model=”meta-llama/llama-3.2-3b-instruct:free”,
request_timeout=60*5
)
Settings.llm = llm
Settings.chunk_size = 700
Settings.chunk_overlap = 20
documents = SimpleDirectoryReader(“docs/user_manuals”).load_data(show_progress=True)
index = VectorStoreIndex.from_documents(documents, show_progress=True)
query_engine = index.as_query_engine()
user_manual_query_tool = QueryEngineTool.from_defaults(
query_engine,
name=”user_manual_query_tool”,
description=(
“A RAG engine with various user manuals of appliances.”,
“Use a detailed plain text question as input to the tool.”),
)
system_prompt = “””
System prompt:
You are an AI assistant specialized in answering questions about appliances using information from user manuals.
1. Always rely on the **user_manual_query_tool** to retrieve information; do not use prior knowledge.
2. If a query cannot be answered using the tool, respond with: “I cannot find this information in the user manuals provided.”
3. Do not attempt to generate answers without using the tool.
4. Give detailed responses to the user’s queries, explaining everything in steps.
5. Return the response in markdown format.
Note: The tool name is **user_manual_query_tool**, and it should be used for all queries.
“””
self.agent = ReActAgent.from_tools(
tools=[user_manual_query_tool],
verbose=True,
context=system_prompt
)
print(“done initializing agent”)
self.initialized = True
def query(self, question):
print(“user query: “, question)
start_time = time.time()
try:
response = self.agent.chat(question)
response_time = time.time() – start_time
print(“response time: “, response_time)
except ValueError as e:
response = “I cannot find this information in the user manuals provided.”
print(e)
return response
if __name__ == “__main__”:
agent = UserManualAgent()
response = agent.query(“How to setup the Roborock vacuum?”)
print(response)
Sau khi thay đổi kích thước khối và chồng lấn khối, tôi cũng đạt được hiệu suất nhanh hơn khi nhúng tài liệu vào Raspberry Pi.
Tạo giao diện người dùng bằng streamlit để tương tác với tác nhân
Bước cuối cùng là tạo một giao diện người dùng nhanh bằng streamlit để tương tác với tác nhân. Đây là mã cho giao diện người dùng:
import streamlit as st
from user_manual_agent import UserManualAgent
@st.cache_resource
def initialize_agent():
user_manual_agent = UserManualAgent()
return user_manual_agent
# Function to cycle through messages
def display_interface(user_manual_agent):
# Streamlit app
st.title(“User Manual Agent”)
st.write(“Ask any questions you have about your appliances :sunglasses:”)
# Input text box for user query
user_query = st.text_input(“Enter your question:”)
# Button to submit the query
if st.button(“Submit”):
if user_query:
with st.spinner(“Fetching response…”):
response = user_manual_agent.query(user_query)
st.markdown(“**Response**: \n\n”)
st.markdown(response.response)
else:
st.write(“Please enter a question.”)
user_manual_agent = initialize_agent()
display_interface(user_manual_agent)
Lưu ý việc sử dụng @st.cache_resourcekhi tạo tác nhân. Điều này ngăn không cho tác nhân được khởi tạo nhiều lần nếu ứng dụng được truy cập nhiều lần.
Bây giờ bạn cũng có thể biến Raspberry Pi thành trợ lý cá nhân!
Nguôn: //medium.com/@muhammad.taha/turn-your-raspberry-pi-into-a-local-ai-agent-to-help-you-figure-out-your-appliances-4aea2d39ee91
PyMuPDF chuyển đổi tài liệu với hệ thống đa tác nhân.
//medium.com/@pankaj_pandey/b47b05aacf73
Chuyển đổi tài liệu hiệu quả là rất quan trọng đối với các tổ chức xử lý khối lượng lớn thông tin phi cấu trúc. Việc chuyển đổi tài liệu — dù là PDF, hình ảnh được quét hay tệp văn bản — thành dữ liệu có cấu trúc, có thể đọc được bằng máy theo truyền thống là một quá trình tốn nhiều công sức. Các giải pháp hiện đại hiện nay tận dụng Hệ thống đa tác nhân (MAS) , phân bổ nhiệm vụ giữa các tác nhân chuyên biệt, để hợp lý hóa và tự động hóa quy trình này.
Sự tiến hóa của việc chuyển đổi tài liệu
Phương pháp truyền thống so với phương pháp hiện đại
Phương pháp truyền thống:
- Dựa vào xử lý hàng loạt, hệ thống dựa trên quy tắc hoặc ứng dụng độc lập.
- Thường cần can thiệp thủ công và khó mở rộng quy mô hoặc thích ứng với các tài liệu khác nhau…
Kỹ thuật hiện đại:
- Sử dụng trí tuệ nhân tạo, máy học và kiến trúc MAS.
- Cung cấp các giải pháp theo mô-đun, có khả năng mở rộng và tự động hóa để xử lý nhiều loại tài liệu khác nhau với sự can thiệp tối thiểu của con người.
Xu hướng và kỹ thuật mới nhất
- Xử lý phân tán:
MAS triển khai nhiều tác nhân làm việc đồng thời trên các phần khác nhau của quy trình chuyển đổi, giúp giảm thời gian xử lý và tăng thông lượng. - Tích hợp với AI tạo sinh:
Tận dụng các mô hình ngôn ngữ lớn (LLM) cho các tác vụ như tóm tắt, nhận dạng thực thể và trích xuất nội dung giúp nâng cao độ chính xác và tính thông minh của quy trình chuyển đổi. - Hiểu tài liệu đa phương thức:
Các hệ thống như MDocAgent kết hợp phân tích văn bản, bố cục và hình ảnh để hiểu rõ hơn các tài liệu phức tạp — một bước tiến đáng kể so với các phương pháp tiếp cận đơn phương thức. - Kiến trúc đám mây gốc và kiến trúc vi dịch vụ:
Triển khai các tác nhân dưới dạng vi dịch vụ trên nền tảng đám mây cho phép mở rộng quy mô động và quản lý tài nguyên tốt hơn, đảm bảo khả năng phục hồi trong thời gian tải cao. - Xác thực dữ liệu tự động:
Kết hợp các tác nhân kiểm tra lỗi, ghi nhật ký và xác thực đảm bảo tính chính xác và độ tin cậy của dữ liệu được chuyển đổi.
Lợi ích của Hệ thống đa tác nhân trong Phát triển ứng dụng
Khả năng mở rộng và hiệu suất
- Xử lý song song:Nhiều tác nhân hoạt động đồng thời trên các tác vụ khác nhau, giúp giảm đáng kể độ trễ.
Tính linh hoạt và tính mô-đun
- Kiến trúc Plug-and-Play:MAS cho phép các nhà phát triển hoán đổi hoặc cập nhật các thành phần riêng lẻ mà không ảnh hưởng đến toàn bộ hệ thống.
Khả năng phục hồi và khả năng chịu lỗi
- Chuyển đổi dự phòng phân tán:Nếu một tác nhân bị lỗi, các tác nhân khác vẫn tiếp tục xử lý, đảm bảo hoạt động liên tục — một khía cạnh quan trọng đối với các ứng dụng quan trọng.
Chất lượng dữ liệu được nâng cao
- Đảm bảo chất lượng tự động:Các tác nhân tiền xử lý và hậu xử lý giúp cải thiện độ chính xác và tính nhất quán của dữ liệu được chuyển đổi.
Tổng quan về kiến trúc của đường ống chuyển đổi tài liệu dựa trên MAS
Một hệ thống chuyển đổi tài liệu dựa trên MAS điển hình bao gồm một số thành phần chính:
- Nhập tài liệu:Hệ thống chấp nhận nhiều định dạng tài liệu khác nhau (ví dụ: PDF, hình ảnh, tệp văn bản).
- Tác nhân tiền xử lý:Làm sạch và chuẩn hóa dữ liệu thô (loại bỏ nhiễu, chuẩn hóa định dạng).
- Công cụ trích xuất:Trích xuất nội dung cần thiết bằng các kỹ thuật như OCR cho hình ảnh hoặc NLP cho dữ liệu văn bản.
- Công cụ chuyển đổi dữ liệu:Chuyển đổi nội dung được trích xuất thành các định dạng có cấu trúc như JSON hoặc XML.
- Tác nhân xác thực và ghi nhật ký:Đảm bảo độ chính xác của đầu ra và ghi lại thông tin chi tiết về quá trình xử lý lỗi và kiểm tra trong tương lai.
- Tích hợp với mô hình AI:Các tác nhân tùy chọn tận dụng LLM để tăng cường các tác vụ như tóm tắt, dịch thuật và nhận dạng thực thể.
Triển khai Python: Một ví dụ thực tế
Ví dụ Python sau đây trình bày một MAS đơn giản để chuyển đổi tài liệu.
Trong ví dụ này, ba tác nhân làm việc cùng nhau để:
- Trích xuất văn bản từ tài liệu PDF.
- Xác định và trích xuất địa chỉ email từ văn bản.
- Chuyển đổi dữ liệu đã trích xuất sang định dạng JSON.
Thiết lập môi trường
Cài đặt thư viện cần thiết: pip install PyMuPDF
Định nghĩa các tác nhân
PDFReaderAgent:Trích xuất văn bản từ PDF bằng PyMuPDF:
import fitz # PyMuPDF
class PDFReaderAgent:
def __init__(self, file_path):
self.file_path = file_path
def extract_text(self):
text = ""
with fitz.open(self.file_path) as pdf:
for page_num in range(len(pdf)):
page = pdf.load_page(page_num)
text += page.get_text()
return text
DataExtractionAgent: Sử dụng biểu thức chính quy để trích xuất địa chỉ email:
import re
class DataExtractionAgent:
def __init__(self, text):
self.text = text
def extract_data(self):
emails = re.findall(
r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b',
self.text
)
return {"emails": emails}
FormatConversionAgent: Chuyển đổi dữ liệu đã trích xuất thành JSON:
import json
class FormatConversionAgent:
def __init__(self, data):
self.data = data
def to_json(self, output_file):
with open(output_file, 'w') as f:
json.dump(self.data, f, indent=4)
print(f"Data saved to {output_file}")Điều phối các tác nhân
Bộ điều khiển điều phối quy trình làm việc:
class DocumentConversionController:
def __init__(self, file_path, output_file):
self.file_path = file_path
self.output_file = output_file
def convert(self):
# Step 1: Extract text from PDF
reader_agent = PDFReaderAgent(self.file_path)
text = reader_agent.extract_text()
print(“Text extraction complete.”)
# Step 2: Process extracted text to extract data (emails in this case)
extraction_agent = DataExtractionAgent(text)
data = extraction_agent.extract_data()
print(“Data extraction complete.”)
# Step 3: Convert the data into JSON format
conversion_agent = FormatConversionAgent(data)
conversion_agent.to_json(self.output_file)
print(“Document conversion complete.”)
# Execute the conversion process
if __name__ == “__main__”:
file_path = “sample_document.pdf” # Ensure this file exists in your working directory
output_file = “output_data.json”
controller = DocumentConversionController(file_path, output_file)
controller.convert()
Đánh giá tính bảo mật của hệ thống bằng cách sử dụng dữ liệu được thu thập thụ động, mà không chủ động tìm cách xâm phạm dữ liệu đó.
DeepSeek-OCR: OCR PDF sang Markdown cho tài liệu phức tạp
Khi một công việc OCR đơn giản trở thành một tuần đầy lỗi, bảng bị đọc sai, tiêu đề bị xáo trộn và mọi thứ lệch vài pixel, DeepSeek-OCR xuất hiện như một giải pháp mới mẻ.
Nó không chỉ đọc các pixel. Nó nén và hiểu tài liệu như một tác vụ thị giác-ngôn ngữ. Sau đó, nó xuất ra văn bản/Markdown sạch mà bạn có thể sử dụng.
DeepSeek-OCR là một mô hình OCR thị giác-ngôn ngữ 3 tỷ tham số từ DeepSeek. Nó chuyển đổi hình ảnh/trang PDF thành Markdown có cấu trúc. Mô hình này được thiết kế cho các tài liệu dài, lộn xộn và thực tế (bảng, biểu mẫu, ảnh chụp màn hình).
Điểm mới/khác biệt:
- Nó sử dụng “nén quang học ngữ cảnh”.
- Nó biểu diễn văn bản dưới dạng hình ảnh độ phân giải cao.
- Mô hình có thể xử lý các tài liệu dài hơn nhiều với ít token hơn.
- Giảm token được báo cáo là 7-20 lần.
Tại sao bạn nên quan tâm:
- Quy trình làm việc với tài liệu dài nhanh hơn/rẻ hơn.
- Bảng tốt hơn.
- Quy trình hàng loạt dễ dàng hơn.
- Hỗ trợ vLLM đã được tích hợp sẵn cho việc phục vụ kiểu sản xuất.
Ý tưởng lớn:
- OCR truyền thống: phát hiện ký tự, tập hợp từ, đoán cấu trúc.
- DeepSeek-OCR: xử lý toàn bộ trang như một ngữ cảnh trực quan. Sau đó, nó tạo văn bản/Markdown với nhận thức về bố cục.
- Nghiên cứu gọi đây là nén ngữ cảnh văn bản dài thông qua ánh xạ 2D quang học. Nó báo cáo tiết kiệm token 7-20 lần với độ trung thực cao.
- Điều này cho phép nó mở rộng quy mô sang các tệp PDF lớn mà không làm tăng ngân sách token của bạn.
- Đây không phải là sản phẩm “hơi nước”. Có một thẻ mô hình thực, một kho lưu trữ GitHub và các báo cáo thực địa về tốc độ dưới một giây/trang trên GPU cao cấp.
Các phiên bản & cài đặt sẵn:
Thẻ mô hình của DeepSeek cung cấp các cài đặt sẵn suy luận đơn giản thông qua base_size, image_size và crop_mode. Hãy coi chúng như các “chế độ”.
- Tiny:
base_size=512,image_size=512,crop_mode=False.- Khi nào: Ưu tiên tốc độ hơn độ chính xác, quét nhanh, các phiên bản VRAM thấp.
- Small:
base_size=640,image_size=640,crop_mode=False.- Khi nào: Các trang chung không có bảng dày đặc.
- Base:
base_size=1024,image_size=1024,crop_mode=False.- Khi nào: Hầu hết các tệp PDF/ảnh chụp màn hình; cân bằng tốt giữa chất lượng và tốc độ.
- Large:
base_size=1280,image_size=1280,crop_mode=False.- Khi nào: Các tài liệu thiết kế/sơ đồ/phông chữ nhỏ nơi chi tiết quan trọng.
- “Gundam” (tên của họ):
base_size=1024,image_size=640,crop_mode=True.- Khi nào: Bảng dày đặc và bố cục chật chội—chế độ cắt giúp mô hình phóng to vào các vùng.
Mẹo chuyên nghiệp: Bắt đầu với Base. Nếu bảng trông không ổn, hãy thử Gundam. Nếu VRAM của bạn hạn chế, hãy chuyển sang Small/Tiny.
CPU so với GPU: Điều gì thực sự hiệu quả:
- Kích thước mô hình: Khoảng 6-7 GB trọng số (bf16). Chạy hoàn toàn trên CPU có thể thực hiện được nhưng thường quá chậm cho sản xuất. Một GPU NVIDIA hiện đại là lựa chọn tối ưu.
- VRAM thoải mái: 8-12 GB là đủ cho các cài đặt sẵn tiêu chuẩn; nhiều hơn sẽ giúp thông lượng hàng loạt. Thẻ mô hình hiển thị 3B tham số và mặc định là bf16; các ví dụ nhắm mục tiêu CUDA 11.8 + PyTorch 2.6.
- Thông lượng thực tế: Báo cáo cộng đồng cho thấy <1 giây mỗi trang trên Ada A6000; và các tin tức báo chí tuyên bố “hàng trăm nghìn trang/ngày” trên A100 ở quy mô lớn (đó là một tuyên bố về quy trình, nhưng nó cho thấy tiềm năng thực sự). Luôn kiểm tra trên nội dung của bạn.
- Mẹo sản xuất: Nó hiện được vLLM hỗ trợ (tại thời điểm viết bài) để phục vụ/xử lý hàng loạt hiệu quả. Sử dụng điều này khi bạn chuyển từ sổ ghi chép sang microservices.
DeepSeek-OCR tỏa sáng ở đâu:
- Bảng phức tạp: Tạo bảng Markdown với ít chỉnh sửa sau hơn.
- Ảnh chụp màn hình/biểu mẫu nơi bố cục quan trọng đối với ý nghĩa.
- Các tệp PDF dài (tài liệu chính sách, RFQ, danh mục) nơi ngân sách token thường bị vượt quá.
- Được hỗ trợ bởi báo cáo nén token 7-20 lần của bài báo và giải mã ưu tiên hình ảnh.
Colab: Chạy PDF của bạn từ đầu đến cuối (tải lên → Markdown → tải xuống ZIP):
Dưới đây là một chuỗi ô Colab sạch mà bạn có thể chạy. Nó khớp với các phiên bản phụ thuộc từ thẻ mô hình và bao gồm một đường dẫn nhanh + thử lại cho các trang dày đặc. Flash-Attention là tùy chọn; nó sẽ tự động quay lại nếu không khả dụng.
Nó làm gì:
- Cài đặt các phụ thuộc (PyTorch tương thích CUDA, Transformers, pdf2image, poppler).
- Cho phép bạn tải lên PDF của mình.
- Chuyển đổi các trang thành PNG.
- Tải
deepseek-ai/DeepSeek-OCR. - Suy luận từng trang thành Markdown (với chế độ thử lại cho các trang dày đặc).
- Nén tất cả các đầu ra (MD từng trang + MD kết hợp + timings.csv) để tải xuống.
1) GPU + phụ thuộc: Tại sao lại là các phiên bản chính xác này?
!nvidia-smi
!pip -q install --upgrade pip
!pip -q install torch==2.6.0 torchvision==0.21.0 torchaudio==2.6.0 --index-url //download.pytorch.org/whl/cu118
!pip -q install transformers==4.46.3 tokenizers==0.20.3 einops addict easydict
!pip -q install flash-attn==2.7.3 --no-build-isolation || echo "flash-attn not available; falling back to eager attention"
!apt-get -y update >/dev/null 2>&1 && apt-get -y install poppler-utils >/dev/null 2>&1
!pip -q install pdf2image pillow bs4 pandas tabulateĐiều gì đang xảy ra:
!nvidia-smixác nhận một GPU được gắn vào thời gian chạy Colab của bạn và hiển thị phiên bản trình điều khiển/CUDA. Nếu bạn không thấy card NVIDIA, hãy chuyển thời gian chạy → GPU.- Các phiên bản PyTorch (
torch==2.6.0 + cu118 wheel) khớp với CUDA 11.8. Điều này tránh lỗi “CUDA mismatch” kinh điển. transformers + tokenizerstải mô hình DeepSeek-OCR.einops,addict,easydictlà các tiện ích nhẹ mà một số mã từ xa sử dụng.flash-attntăng tốc chú ý trên các GPU được hỗ trợ. Phần|| echo ...đảm bảo sổ ghi chép của bạn không dừng nếu FlashAttention không thể xây dựng; nó sẽ quay lại sau.poppler-utils + pdf2imagechuyển đổi các trang PDF thành PNG. Không có Poppler, pdf2image không thể làm gì.
Nếu mọi thứ thất bại:
- FlashAttention thường thất bại trên các T4 cũ hơn hoặc khi công cụ của Colab thay đổi. Điều đó không sao; mã của bạn đã quay lại.
- Nếu PyTorch import báo “CUDA not available”, thời gian chạy của bạn chỉ là CPU. Chọn lại thời gian chạy GPU.
Các tùy chỉnh bạn có thể thực hiện:
- Các GPU mới hơn đôi khi thích các gói mới nhất. Nếu Colab cập nhật CUDA, hãy chuyển sang gói
--index-urlphù hợp. - Nếu bạn muốn chỉ CPU (gỡ lỗi), hãy cài đặt các gói CPU từ PyPI mặc định và bỏ qua các gói CUDA (nó sẽ chậm).
2) Tải lên PDF: Các biện pháp bảo vệ:
from google.colab import files
up = files.upload()
assert len(up) == 1, "Upload exactly one PDF"
pdf_path = list(up.keys())[0]
print("PDF:", pdf_path)Mục đích: Biểu mẫu Colab để chọn một PDF cục bộ. assert là một bất biến đơn giản: chính xác một tệp. Nếu bạn muốn xử lý hàng loạt, hãy xóa assert và lặp qua up.items().
Lỗi thường gặp: Sai loại tệp. Thêm kiểm tra nhanh:
assert pdf_path.lower().endswith(".pdf"), "Please upload a .pdf file"3) PDF → hình ảnh: DPI là nút điều khiển chất lượng so với tốc độ của bạn:
from pdf2image import convert_from_path
from pathlib import Path
import os
out_dir = Path("dpsk_ocr_outputs")
img_dir = out_dir / "pages"
md_dir = out_dir / "markdown_pages"
os.makedirs(img_dir, exist_ok=True)
os.makedirs(md_dir, exist_ok=True)
IM_DPI = 180 # 160–200 là một phạm vi tốt
images = convert_from_path(pdf_path, dpi=IM_DPI, fmt="png",
output_folder=str(img_dir),
output_file="page",
paths_only=True)
images = sorted(images)
print(f"Extracted {len(images)} page image(s) → {img_dir}")Tại sao: DeepSeek-OCR tiêu thụ hình ảnh, không phải PDF. Chúng tôi tiền xử lý từng trang.
Đánh đổi DPI:
- DPI thấp hơn (ví dụ: 150-180): nhanh hơn, VRAM nhỏ hơn, có thể bỏ lỡ các phông chữ nhỏ.
- DPI cao hơn (ví dụ: 220-300): bảng/văn bản nhỏ sắc nét hơn, nhiều bộ nhớ GPU và thời gian hơn.
Mẹo: Nếu bạn đang OCR các bản quét có văn bản nhỏ, hãy thử IM_DPI = 220. Nếu bạn gặp lỗi OOM, hãy giảm lại.
4) Tải DeepSeek-OCR: trust_remote_code & dự phòng chú ý:
import torch, os
from transformers import AutoModel, AutoTokenizer, GenerationConfig
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
torch.backends.cuda.matmul.allow_tf32 = True
try:
torch.set_float32_matmul_precision("high")
except Exception:
pass
model_id = "deepseek-ai/DeepSeek-OCR"
attn_impl = "flash_attention_2"
try:
import flash_attn # noqa
except Exception:
attn_impl = "eager"
tok = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True)
if tok.pad_token is None and tok.eos_token is not None:
tok.pad_token = tok.eos_token
model = AutoModel.from_pretrained(
model_id,
trust_remote_code=True,
_attn_implementation=attn_impl,
torch_dtype=torch.bfloat16,
use_safetensors=True,
).to("cuda").eval()Các khái niệm cần hiểu:
trust_remote_code=Truecho phép mô hình tải mã Python tùy chỉnh từ kho lưu trữ (cần ở đây vìmodel.infer(...)là tùy chỉnh). Về mặt bảo mật, chỉ bật tính năng này cho các kho lưu trữ bạn tin cậy.- Triển khai chú ý: chúng tôi ưu tiên FlashAttention để tăng tốc, nhưng chúng tôi rõ ràng quay lại “eager” nếu import thất bại. Đó là lý do tại sao cài đặt trước đó của bạn có thể “thất bại” và mọi thứ vẫn tiếp tục.
- bfloat16 (bf16): giảm một nửa bộ nhớ so với fp32 với tổn thất chất lượng tối thiểu, được hỗ trợ rộng rãi trên NVIDIA hiện đại. Nếu GPU của bạn phàn nàn, hãy thử
torch_dtype=torch.float16hoặc cuối cùng làfloat32(chậm hơn, nhiều VRAM hơn). - TF32/matmul tốt hơn:
allow_tf32vàset_float32_matmul_precision("high")cho phép NVIDIA sử dụng tensor core cho một số hoạt động; một lợi thế tốc độ an toàn trên Ampere+.
Công tắc an toàn nhỏ:
device = "cuda" if torch.cuda.is_available() else "cpu"
model = AutoModel.from_pretrained(...).to(device).eval()Chạy trên CPU sẽ chậm, nhưng điều này giữ cho sổ ghi chép có thể sử dụng được cho những người đọc không có GPU.
Cấu hình tạo: Tại sao lại sử dụng các giá trị này?
# If model has no default config, set ours
if not hasattr(model, "generation_config") or model.generation_config is None:
model.generation_config = GenerationConfig()
gc = model.generation_config
gc.pad_token_id = tok.pad_token_id
gc.eos_token_id = tok.eos_token_id
gc.do_sample = False # deterministic
gc.num_beams = 1 # no beam search (faster)
gc.top_p = 1.0; gc.temperature = None
gc.max_new_tokens = 512 # cap runaway generations
gc.no_repeat_ngram_size = 6
gc.repetition_penalty = 1.15
gc.length_penalty = 0.9
model.generation_config = gc- Giải mã xác định (
do_sample=False) giữ cho đầu ra ổn định giữa các lần chạy—điều này hoàn hảo cho OCR. max_new_tokens=512ngăn một trang “chạy quá đà”. Nếu bạn thấy bị cắt cụt, hãy điều chỉnh lên 768/1024—nhưng hãy chú ý đến độ trễ.- Các biện pháp bảo vệ lặp lại (
no_repeat_ngram_size,repetition_penalty) giảm bớt những dòng trùng lặp kỳ lạ mà LLM đôi khi phát ra.
5) Bước làm sạch: Regex để loại bỏ các thẻ phát hiện:
import re
REF_DET_RE = re.compile(r"<\|(?:ref|det)\|>.*?<\|/(?:ref|det)\|>", re.DotAll)
def clean_ocr_markdown(s: str) -> str:
s = REF_DET_RE.sub("", s or "")
lines, out, last, run = s.splitlines(), [], None, 0
for line in lines:
if line == last:
run += 1
if run < 4:
out.append(line)
else:
last, run = line, 1
out.append(line)
return "\n".join(out).strip()Nó làm gì:
- Loại bỏ các thẻ nội bộ như
<|ref|> ... <|/ref|>mà mô hình có thể thêm vào để phát hiện siêu dữ liệu. - Gộp các dòng trùng lặp ngẫu nhiên (giữ tối đa 3 bản sao liên tiếp).
Sửa lỗi quan trọng: Trong Python, cờ là re.DOTALL (tất cả chữ hoa). Nếu bạn sử dụng re.DotAll, bạn sẽ gặp lỗi thuộc tính trên một số phiên bản Python. Sử dụng:
re.compile(pattern, re.DOTALL)Tại sao gộp dòng ở 3? Đó là một sự thỏa hiệp: loại bỏ sự lặp lại rõ ràng mà không làm mất các danh sách hợp lệ.
6) Chiến lược suy luận: Đường dẫn nhanh, sau đó là thử lại thông minh hơn:
prompt = (
"\n"
"<|grounding|>Convert the document to clean, concise Markdown.\n"
"- Use Markdown tables (not HTML) for tabular data.\n"
"- Do not include detection tags like <|ref|> or <|det|>.\n"
)
BASE_SIZE = 1024Thiết kế lời nhắc: Hãy nghĩ như một yêu cầu sản phẩm:
- Rõ ràng về bảng Markdown (không phải HTML).
- Yêu cầu tránh các thẻ phát hiện để chúng ta không cần làm sạch nhiều sau này.
Chiến lược hai giai đoạn:
def infer_once(img_path, image_size, crop_mode, save_results):
txt = model.infer(
tok,
prompt=prompt,
image_file=str(img_path),
output_path=str(md_dir),
base_size=BASE_SIZE, # global canvas scaling
image_size=image_size, # actual input resize
crop_mode=crop_mode, # enable region zoom for dense layouts
save_results=save_results,
test_compress=False,
eval_mode=True
)
return (txt or "").strip()
def infer_with_retry(img_path):
# 1) fast path: quick & cheap
t0 = time.time()
txt = infer_once(img_path, image_size=512, crop_mode=False, save_results=False)
if len(txt) >= 40: # crude signal that we got real text
return txt, time.time()-t0, "512/no-crop"
# 2) smarter path: zoom in & crop (great for tables/tiny fonts)
txt2 = infer_once(img_path, image_size=640, crop_mode=True, save_results=True)
if len(txt2) >= 40:
return txt2, time.time()-t0, "640/crop"
# 3) last resort: if the helper wrote result.mmd, use itTại sao điều này hoạt động:
- Giai đoạn 1 nhanh cho các trang thông thường.
- Giai đoạn 2 sử dụng
crop_mode(hành vi giống “Gundam” của DeepSeek) để phóng to vào các vùng dày đặc, rất hữu ích cho bảng. - Chúng tôi tính thời gian mỗi trang và ghi lại chế độ nào thành công. Điều đó trở thành một điểm chuẩn nhỏ cho tập dữ liệu của bạn.
Mẹo: Nếu các tệp PDF của bạn có nhiều bảng, hãy đảo ngược thứ tự (thử cắt trước).
Ghi chú thông lượng:
- Bạn đang xử lý từng trang một. Để tăng tốc, bạn có thể xử lý hàng loạt trên nhiều GPU hoặc quy trình, nhưng hãy giữ nó đơn giản trong Colab.
7) Duy trì kết quả: Từng trang, kết hợp và CSV thời gian:
combined_md_path = out_dir / (Path(pdf_path).stem + "_combined.md")
timing_csv_path = out_dir / "timings.csv"
with open(combined_md_path, "w", encoding="utf-8") as mdout, \
open(timing_csv_path, "w", newline="", encoding="utf-8") as csvf:
writer = csv.writer(csvf)
writer.writerow(["page_index","image_name","seconds","mode","chars"])
for i, img_path in enumerate(images, 1):
txt, secs, mode = infer_with_retry(img_path)
cleaned = clean_ocr_markdown(txt)
# per-page MD
per_page_md = md_dir / f"page_{i:04d}.md"
with open(per_page_md, "w", encoding="utf-8") as f:
f.write((cleaned or "[EMPTY OCR OUTPUT]") + "\n")
# append to combined
mdout.write(f"\n\n\n\n{cleaned or '[EMPTY OCR OUTPUT]'}\n")
# benchmarking breadcrumbs
writer.writerow([i, Path(img_path).name, f"{secs:.2f}", mode, len(cleaned)])
print(f"Page {i}/{len(images)} | {secs:.2f}s | mode={mode} | chars={len(cleaned)}")Đầu ra:
- Tổng thời gian: 395.05 giây cho 11 trang.
- MD từng trang:
dpsk_ocr_outputs/markdown_pages. - MD kết hợp:
dpsk_ocr_outputs/NIPS-2017-attention-is-all-you-need-Paper_combined.md. - CSV thời gian:
dpsk_ocr_outputs/timings.csv.
Tại sao cấu trúc này:
- MD từng trang cho phép bạn xử lý lại chỉ những trang bạn quan tâm sau này.
- Một
combined.mdhoàn hảo để xem xét sau hoặc đưa vào phân đoạn RAG. timings.csvlà tài liệu quý giá để điều chỉnh hiệu suất. Sắp xếp theo các trang chậm và kiểm tra xem điều gì đặc biệt, thường là phông chữ nhỏ hoặc bảng dày đặc.
8) Đóng gói đầu ra: Tải xuống một lần nhấp:
import shutil
zip_path = shutil.make_archive("deepseek_ocr_outputs", "zip", root_dir=str(out_dir))
print("ZIP:", zip_path)
from google.colab import files
files.download(zip_path)Tại sao: Các hộp cát Colab biến mất. Nén mọi thứ có nghĩa là người đọc của bạn có thể lấy các thư mục pages/, markdown_pages/, combined.md và timings.csv cùng một lúc.
Liên kết mã GitHub:
//github.com/pillaiharish/LLM-AI-Agents-Learning-Journey/tree/main/notebooks/deepseek-ocr-run
Các khái niệm chính:
- DPI kiểm soát độ trung thực đầu vào. Nó ảnh hưởng đến độ chính xác và VRAM/thời gian.
base_sizeso vớiimage_size: Hãy nghĩ “canvas toàn cầu” so với “thay đổi kích thước thực tế”.crop_mode=True: Hướng dẫn mô hình kiểm tra các vùng con (rất tốt cho bảng).- bf16: Giảm một nửa bộ nhớ, chất lượng gần fp32 trên NVIDIA hiện đại. Nếu không được hỗ trợ, hãy thử fp16.
- Giải mã xác định: OCR là trích xuất, không phải viết sáng tạo; giữ
do_sample=False. - Chiến lược thử lại: Các biện pháp bảo vệ chống lại các trang khó mà không cần giám sát thủ công.
trust_remote_code: Bắt buộc đối vớimodel.infer(...), nhưng chỉ bật cho các kho lưu trữ đáng tin cậy.
Cần điều chỉnh gì khi bạn gặp khó khăn?
- Bảng bị hỏng? Chuyển sang Gundam (
crop_mode=True,image_size=640) hoặc tăngimage_sizelên 1024 nếu VRAM cho phép. - Hết bộ nhớ? Giảm
image_size(512/640), sử dụng các cài đặt sẵn Tiny/Small hoặc giảm kích thước lô (xử lý từng trang một). - Chậm? Đảm bảo thời gian chạy GPU; thử cài đặt
flash-attn(đã có trong ô). Nếu bạn đang đưa vào sản xuất, hãy chuyển sang vLLM để xử lý hàng loạt các yêu cầu. - Xây dựng
flash_attnthất bại: Dự kiến trên một số GPU Colab. Bạn đã quay lại; bỏ qua cảnh báo. - OOM (hết bộ nhớ): Giảm
IM_DPI, sử dụngimage_size=512hoặc tắtcrop_mode. Trường hợp xấu nhất, chuyển sang CPU (chậm) để ít nhất có được thứ gì đó. - Lỗi Regex: Sử dụng
re.DOTALL(không phảire.DotAll). - Các dòng lặp lại kỳ lạ trong Markdown: Giữ bộ gộp trùng lặp hoặc thắt chặt nó (cho phép tối đa 2 thay vì 3).
- Trang trống: Kiểm tra xem trang PDF có phải là trang vector với văn bản nhỏ không; thử
IM_DPI=220 + crop mode.
So sánh với ngăn xếp thông thường:
- OCR cổ điển (Tesseract/PaddleOCR): Tuyệt vời cho “văn bản thuần túy” và các công việc nhỏ. Nhưng đối với các tài liệu nặng bố cục có bảng, cách tiếp cận VLM của DeepSeek-OCR + đầu ra Markdown thường có nghĩa là ít dọn dẹp sau này hơn.
- Các dịch vụ được quản lý (Textract, Google Document AI): Thuận tiện. Nếu bạn cần cơ sở hạ tầng riêng, chi phí dự đoán được hoặc kiểm soát Markdown/bảng cụ thể, ngăn xếp mở này có thể linh hoạt hơn, đặc biệt với vLLM và xử lý hàng loạt.
Ghi chú phần cứng:
- Phát triển khả thi tối thiểu: GPU NVIDIA VRAM 8-12 GB cho suy luận một trang; dự kiến vài giây/trang tùy thuộc vào cài đặt sẵn/DPI. Cộng đồng cho thấy tốc độ nhanh hơn nhiều trên Ada/A100 cao cấp.
- Xử lý hàng loạt nghiêm túc: Sử dụng vLLM và một card A100/L40S/A6000; mở rộng quy mô bằng cách phân chia các tệp PDF trên các worker. Thẻ HF xác nhận hỗ trợ vLLM chính thức.
Nguồn & đọc thêm:
- Thẻ mô hình (HF): cách sử dụng, cài đặt sẵn (Tiny/Small/Base/Large/Gundam) và hỗ trợ vLLM.
- Bài báo (arXiv): “DeepSeek-OCR: Contexts Optical Compression”: giảm token 7-20 lần.
- Phát hành/GitHub: cài đặt + ví dụ vLLM & Transformers.
- Báo cáo thực địa/tin tức: tuyên bố thông lượng và giải thích nén ngữ cảnh.
Tham khảo: medium.com

Bài viết liên quan: