Đừng xây dựng AI mong manh nữa: Xây dựng Agentic RAG tự sửa lỗi với LangGraph

AI tutorial - IT technology blog
AI tutorial - IT technology blog

Rào cản của ‘Naive RAG’

“Hello World” của AI hiện đại là một pipeline Retrieval-Augmented Generation (RAG) đơn giản. Bạn chia nhỏ các tệp PDF, đưa chúng vào một vector database như Pinecone hoặc Chroma, và lấy ra 3 kết quả hàng đầu để đưa vào prompt cho LLM. Đó là một dự án cuối tuần thú vị. Tuy nhiên, cách tiếp cận tuyến tính này thường gặp rào cản lớn trong môi trường production.

Tôi đã thấy các thiết lập RAG tiêu chuẩn thất bại thường xuyên nhất khi các truy vấn hơi mơ hồ. Ví dụ, nếu người dùng hỏi về một tính năng phần mềm mới ra mắt hôm qua, nhưng vector store của bạn được cập nhật lần cuối từ một tháng trước, LLM có khả năng cao sẽ “ảo giác” (hallucinate) ra một câu trả lời tự tin nhưng sai bét. Nó thiếu khả năng “tự nhận thức” để nhận ra rằng tài liệu nội bộ đã lỗi thời. Trong các thử nghiệm của tôi, các hệ thống RAG ngây thơ (naive RAG) có thể cung cấp ngữ cảnh không liên quan tới 30% thời gian trong các lĩnh vực biến động, dẫn đến sự sụp đổ hoàn toàn niềm tin của người dùng.

Agentic RAG thay đổi cuộc chơi. Thay vì một con đường một chiều (Truy xuất -> Tạo văn bản), chúng ta xây dựng một cổng logic vòng lặp. Nếu dữ liệu truy xuất được là rác, hệ thống sẽ không chỉ đoán mò. Nó sẽ tạm dừng, viết lại câu truy vấn và tìm nạp dữ liệu mới từ web thực tế bằng Tavily. Tôi đã triển khai hệ thống này cho các bot hỗ trợ khách hàng, và tỷ lệ ảo giác đã giảm ngay lập tức và có thể đo lường được.

Kiến trúc của một hệ thống tự sửa lỗi

Để vượt xa khả năng truy xuất cơ bản, chúng ta cần một bộ điều khiển luồng (flow controller) để xử lý các vòng lặp logic và một công cụ tìm kiếm được thiết kế cho máy móc, không phải cho con người.

LangGraph: Logic với các vòng lặp

LangChain tiêu chuẩn hoạt động tốt cho các chuỗi tuyến tính, nhưng nó gặp khó khăn khi bạn cần quay ngược lại. Agentic RAG về bản chất là không tuyến tính. Nếu một tài liệu không vượt qua bước kiểm tra mức độ liên quan, bạn cần quay lại từ đầu.

LangGraph coi suy luận của AI như một máy trạng thái (state machine). Bạn định nghĩa các node (hành động) và edge (đường nối giữa chúng), cho phép agent lặp qua một vấn đề cho đến khi đạt được giải pháp có độ tin cậy cao. Nó giúp quá trình ra quyết định của AI trở nên dễ dự đoán và quan trọng hơn là dễ debug.

Tavily: Công cụ tìm kiếm dành cho LLM

Google và Bing được thiết kế để con người nhấp vào quảng cáo và các bài viết đậm chất SEO. Khi một AI agent tìm kiếm trên web, nó không cần một bài blog 2.000 từ; nó cần các đoạn thông tin thực tế, thô. Tavily lọc bỏ nhiễu—không quảng cáo, không JavaScript rườm rà và không có nội dung rác SEO. Nó đóng vai trò như một mạng lưới an toàn có độ trung thực cao khi cơ sở kiến thức nội bộ của bạn không đủ đáp ứng.

Xây dựng hệ thống

Hãy cùng xây dựng một luồng công việc tự kiểm tra kết quả của chính nó. Hệ thống này truy xuất một tài liệu, đánh giá nó và chỉ kích hoạt tìm kiếm web nếu dữ liệu cục bộ không đủ tốt.

Bước 1: Nền tảng

Bắt đầu bằng cách cài đặt các thư viện cốt lõi. Bạn sẽ cần một OpenAI key và một Tavily API key để thực hiện theo.

pip install langchain-openai langgraph tavily-python chromadb

Bước 2: Định nghĩa trạng thái (State)

Trong LangGraph, đối tượng State là nguồn sự thật duy nhất (single source of truth). Nó theo dõi câu hỏi, các tài liệu tìm thấy và liệu có cần tìm kiếm web hay không khi di chuyển qua graph.

from typing import List, TypedDict

class GraphState(TypedDict):
    question: str
    generation: str
    web_search: str # "Có" hoặc "Không"
    documents: List[str]

Bước 3: Node đánh giá (Grader Node)

Đây là nơi hành vi “agentic” thực sự diễn ra. Chúng ta sử dụng một mô hình tốc độ cao như GPT-4o-mini để đóng vai trò giám khảo. Nếu ngữ cảnh truy xuất được không trả lời trực tiếp prompt của người dùng, grader sẽ chuyển công tắc web_search sang “Có”.

from langchain_openai import ChatOpenAI

def grade_documents(state):
    print("---ĐANG ĐÁNH GIÁ MỨC ĐỘ LIÊN QUAN CỦA TÀI LIỆU---")
    llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
    
    question = state["question"]
    documents = state["documents"]
    
    # Trong ứng dụng thực tế, hãy sử dụng một structured output parser tại đây
    if not documents or "không liên quan" in documents[0]: 
        return {"web_search": "Có", "question": question}
    return {"web_search": "Không", "question": question, "documents": documents}

Bước 4: Tích hợp Tavily

Nếu tìm kiếm nội bộ thất bại, chúng ta sẽ truy cập web. Điều này đảm bảo agent có thể trả lời các câu hỏi về các sự kiện thời gian thực hoặc các chủ đề ngách không có trong cơ sở dữ liệu cục bộ của bạn.

from langchain_community.tools.tavily_search import TavilySearchResults

def web_search(state):
    print("---ĐANG CHUYỂN SANG TÌM KIẾM WEB---")
    question = state["question"]
    tavily = TavilySearchResults(k=3)
    
    results = tavily.invoke({"query": question})
    content = "\n".join([r["content"] for r in results])
    
    return {"documents": [content], "question": question}

Bước 5: Kết nối Graph

Bây giờ chúng ta lập bản đồ logic. Chúng ta định nghĩa một edge có điều kiện dựa trên cờ web_search để quyết định bước tiếp theo.

from langgraph.graph import END, StateGraph

workflow = StateGraph(GraphState)

workflow.add_node("retrieve", retrieve_docs)
workflow.add_node("grade_documents", grade_documents)
workflow.add_node("web_search_node", web_search)
workflow.add_node("generate", generate_answer)

workflow.set_entry_point("retrieve")
workflow.add_edge("retrieve", "grade_documents")

workflow.add_conditional_edges(
    "grade_documents",
    lambda x: x["web_search"],
    {
        "Có": "web_search_node",
        "Không": "generate",
    },
)
workflow.add_edge("web_search_node", "generate")
workflow.add_edge("generate", END)

app = workflow.compile()

Những bài học đắt giá từ thực tế

Sau khi vận hành các hệ thống này trong thực tế, tôi nhận ra rằng logic chỉ là một nửa trận chiến. Dưới đây là ba kinh nghiệm thực tế để mở rộng Agentic RAG:

  • Tinh chỉnh Grader của bạn: Nếu grader quá khắt khe, chi phí API của bạn sẽ tăng vọt vì agent mặc định tìm kiếm web cho mọi truy vấn. Nếu nó quá lười biếng, bạn sẽ quay lại với tình trạng ảo giác. Hãy sử dụng few-shot prompt cho grader để hiệu chuẩn “độ nghiêm ngặt” của nó.
  • Theo dõi Context Window: Kết quả web thô từ Tavily có thể dài hơn 5.000 token. Đừng đổ trực tiếp chúng vào prompt của bạn. Hãy sử dụng phương pháp “Map-Reduce” hoặc một bước tóm tắt nhanh để trích xuất chỉ 500 token liên quan nhất. Việc này giúp tiết kiệm chi phí và cải thiện sự tập trung của LLM.
  • Ngưỡng “Tôi không biết”: Đôi khi, ngay cả web cũng không có câu trả lời. Tôi luôn bao gồm một bước kiểm tra cuối cùng: nếu kết quả tìm kiếm web cũng có độ liên quan thấp, agent nên chuyển tiếp yêu cầu tới con người một cách khéo léo hoặc cung cấp một liên kết trợ giúp đã được kiểm duyệt thay vì bịa ra một sự thật nào đó.

LangGraph mang lại một lợi thế khổng lồ: khả năng quan sát (observability). Khi khách hàng nhận được một phản hồi kỳ lạ, bạn có thể kiểm tra trạng thái của graph và xem chính xác logic đã đi chệch hướng ở đâu. Bạn có thể thấy tài liệu cụ thể nào đã được đánh giá là “liên quan” trong khi lẽ ra không phải, cho phép bạn sửa prompt chỉ trong vài phút.

Lời kết

Nâng cấp từ RAG lên Agentic RAG là sự khác biệt giữa một công cụ tìm kiếm tài liệu thụ động và một trợ lý nghiên cứu chủ động. Bằng cách cung cấp cho hệ thống các công cụ để tự xác thực dữ liệu và tìm kiếm ra bên ngoài khi gặp khó khăn, bạn sẽ xây dựng được thứ gì đó kiên cố hơn nhiều. Hãy bắt đầu với một graph ba node đơn giản. Khi bạn tìm thấy nhiều trường hợp biên (edge case) hơn trong dữ liệu của mình, hãy thêm nhiều vòng lặp hơn. Sự phức tạp này sẽ mang lại giá trị xứng đáng dưới hình thức một AI mà người dùng thực sự tin tưởng.

Share: