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: https://medium.com/@muhammad.taha/turn-your-raspberry-pi-into-a-local-ai-agent-to-help-you-figure-out-your-appliances-4aea2d39ee91
Bài viết liên quan: