LangChain: Công cụ không thể bỏ qua trong thời đại AI tạo sinh

LangChain là một framework hỗ trợ tích hợp và triển khai các Large Language Model (LLM) trong ứng dụng, mở ra khả năng tương tác mạnh mẽ với ngôn ngữ tự nhiên trong nhiều hệ thống công nghệ. Cùng đi sâu vào bài viết để khám phá cách ảnh hưởng của LangChain trong thế giới kỹ thuật số hiện đại.

This post is also available in English

Upload image

Large Language Model (LLM) là những machine learning framework tiên tiến, phát triển nhờ những đột phá về năng lực tính toán. LLM đã cách mạng hóa lĩnh vực xử lý ngôn ngữ tự nhiên (NLP), mở đường cho nhiều ứng dụng trong thế giới công nghệ, nhanh chóng tạo được chỗ đứng trong thời đại kỹ thuật số của chúng ta. Ngày nay, LLM có mặt ở khắp nơi từ chatbot đến hệ thống đề xuất nội dung, ngay cả trong những tương tác hàng ngày của chúng ta như công cụ tìm kiếm hoặc đề xuất nội dung trên mạng xã hội.

Việc tích hợp và triển khai các mô hình LLM trong thiết kế ứng dụng thường đòi hỏi hiểu biết chuyên môn và một bộ công cụ chuyên dụng. Với xu hướng tích hợp AI vào hệ thống để xử lý ngôn ngữ tự nhiên, LangChain là công cụ được tạo ra nhằm giúp xâu chuỗi, điều phối và tương tác có cấu trúc với các LLM. So với các phương pháp truyền thống, việc sử dụng LangChain giúp tích hợp LLM vào hệ thống dễ dàng, cho phép tương tác động với nhiều nguồn dữ liệu khác nhau để mang lại trải nghiệm phong phú, thay vì chỉ gọi API tiêu chuẩn. Bên cạnh đó, LangChain còn giúp developer xây dựng các agent có khả năng suy luận và chia nhỏ vấn đề, đưa context và bộ nhớ vào quá trình xử lý task,…

Trong bài viết này, hãy cùng tìm hiểu về LangChain - framework mạnh mẽ để tạo ra các ứng dụng hỗ trợ Large Language Model.

Tổng quan

LangChain framework cung cấp các module cho phép bạn quản lí nhiều khía cạnh cụ thể trong tương tác với LLM. Các module này bao gồm:

  • Model I/O: là thành phần nền tảng để giao tiếp với các language model. Module này cung cấp giao diện để thực hiện tương tác liền mạch với bất kì language model nào. Trong đó:

    • Đầu vào được định nghĩa bằng Prompts - tập hợp các hướng dẫn hay input mà user cung cấp giúp language model hiểu được ngữ cảnh và tạo câu trả lời mạch lạc.
    • Phần xử lý là language model được LangChain hỗ trợ tích hợp với 2 loại chính là LLMs (nhận một chuỗi đầu vào → trả chuỗi kết quả) và Chat models (nhận danh sách Chat Message → trả một Chat Message).
    • Ngoài ra, module này còn kết hợp các output parser để chuyển đổi kết quả đầu ra từ văn bản thô thành thông tin có tổ chức và có cấu trúc.

    Chỉ với module Model I/O, người dùng đã có thể thực hiện tương tác và tích hợp LLM vào ứng dụng của mình một cách đơn giản và thuận tiện.

Upload image

Cấu trúc Model I/O module

  • Retrieval: là thành phần giúp tạo điều kiện thuận lợi cho việc tích hợp dữ liệu chuyên biệt của người dùng vào giai đoạn generation của language model bằng cách sử dụng Retrieval Augmented Generation (RAG). Langchain hỗ trợ các chức năng như document loaders, document transformers, text embedding models và nhiều thuật toán truy xuất cũng như cách lưu trữ dữ liệu dưới dạng vector, đảm bảo việc sử dụng dữ liệu hiệu quả và phù hợp ngữ cảnh.

Upload image

Sơ đồ Retrieval module

  • Chains: là thành phần giúp thiết kế những xử lí phức tạp bằng cách kết nối các LLM với nhau hoặc với các thành phần khác, bao gồm các chuỗi khác. Cách tiếp cận này vừa đơn giản vừa hiệu quả, giúp phát triển các ứng dụng phức tạp, nâng cao khả năng bảo trì. Một minh họa chuỗi đơn giản như: chuỗi lấy thông tin đầu vào của người dùng, chuyển thành định dạng của PromptTemplate, chuyển đến xử lý với LLM, và tổng hợp lại kết quả.
  • Agents: là thành phần giúp ứng dụng sử dụng language model làm công cụ suy luận để quyết định thứ tự của hành động một cách linh hoạt. LangChain cung cấp nhiều loại Agents kết hợp với đa dạng Tools giúp agent tương tác với các tiện ích chung, với Chains hay với các agent khác, tạo thành một framework mạnh mẽ để xử lý các tác vụ phức tạp.
  • Memory: là thành phần đóng vai trò then chốt đối với các ứng dụng có giao diện đối thoại, cho phép đưa luồng hội thoại trước đó vào LLM, giúp duy trì ngữ cảnh tương tác của người dùng. LangChain cung cấp rất nhiều tiện ích để tích hợp memory vào hệ thống, hỗ trợ các hoạt động cơ bản như đọc và viết, đảm bảo hệ thống có thể truy cập vào các tin nhắn trong quá khứ hoặc duy trì cập nhật liên tục.
  • Callbacks: là thành phần giúp đăng kí các event để khai thác các giai đoạn khác nhau của LLM, hữu ích trong các tác vụ như logging, monitoring, streaming,… Các event này được kích hoạt bởi các CallbackHandler tương ứng với từng sự kiện đã đăng kí. Ngoài ra, LangChain cũng cung cấp một số built-in handler như StdOutCallbackHandler giúp ghi lại tất cả sự kiện trong quá trình thực thi.

Demo

Tiếp theo, ta hãy cùng tìm hiểu một số chức năng mà LangChain cung cấp qua demo thực tế.

Mục tiêu

Trong demo này, ta sẽ xây dựng một ứng dụng hỏi đáp, sử dụng LLM để tạo câu trả lời. Bạn có thể xây dựng ứng dụng hỏi đáp khái quát hoặc về một đề tài tự chọn. Trong demo này, ta sẽ chọn đề tài về Cloud Design Patterns. Demo sẽ sử dụng VertexAI.

Bạn cũng có thể sử dụng LLM khác như OpenAI hay Azure OpenAI, tìm hiểu thêm về LLM được LangChain hỗ trợ tại đây.

Input là câu hỏi của người dùng về Cloud Design Patterns.

Output là các design pattern phù hợp với câu hỏi Input, được trả lời bởi LLM (trong demo này là VertexAI).

Question: Which design pattern should I use when...
Answer:
- <Pattern 1>: <reason to choose this pattern>
- <Pattern 2>: <reason to choose this pattern>
- <Pattern 3>: <reason to choose this pattern>

Upload image

Ứng dụng Model I/O module của LangChain vào demo

Cài đặt thư viện

Ta cài đặt những thư viện cần thiết như sau:

langchain
google-cloud-aiplatform

Import thư viện

from langchain.llms import VertexAI
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain.output_parsers import PydanticOutputParser
from google.oauth2 import service_account
from langchain.pydantic_v1 import BaseModel, Field
from typing import List

Định nghĩa cấu trúc kết quả trả về

Đầu tiên, ta cần định nghĩa cấu trúc kết quả trả về, từ đó tạo ra format_instructions cho language model ở bước tạo prompt template kế tiếp. LangChain hỗ trợ nhiều dạng OutputParser phù hợp với nhu cầu khác nhau. Ở đây, ta muốn kết quả trả về có định dạng JSON, nên sẽ dùng PydanticOutputParser.

Ta lần lượt định nghĩa các Class mô tả cấu trúc kết quả trả về như sau:

# Define the DesignPattern model
class DesignPattern(BaseModel):
    pattern: str = Field(description="name of design pattern")
    reason: str = Field(description="reason to choose this design pattern")

# Define the Response model
class Response(BaseModel):
    answer: List[DesignPattern]

Sau đó, ta tạo OutputParser object tương ứng:

# Initialize the output parser with the Response model
output_parser = PydanticOutputParser(pydantic_object=Response)

Định nghĩa prompt template

Tiếp theo, ta tạo định nghĩa prompt template như sau:

# Define the prompt template for the retrievalQA
format_instructions = output_parser.get_format_instructions()
prompt_template = """
    You are a helpful assistant that can answer questions about cloud design pattern.
    
    Answer the following question: {question}
    
    List out name of top 3 suitable design patterns and brief explain reason
    {format_instructions}
"""
# Create a prompt instance with the defined template
PROMPT = PromptTemplate(
        template=prompt_template, 
        input_variables=["question"],
        partial_variables={"format_instructions": format_instructions}
    )

Trong đó:

  • Sử dụng OutputParser ở bước trước để tạo format_instructions. Trong demo này, format_instructions được tạo sẽ có dạng như sau và được thêm vào prompt template:

    The output should be formatted as a JSON instance that conforms to the JSON schema below.
    
    As an example, for the schema {"properties": {"foo": {"title": "Foo", "description": "a list of strings", "type": "array", "items": {"type": "string"}}}, "required": ["foo"]}
    the object {"foo": ["bar", "baz"]} is a well-formatted instance of the schema. The object {"properties": {"foo": ["bar", "baz"]}} is not well-formatted.
    
    Here is the output schema:
    ```
    {"properties": {"answer": {"title": "Answer", "type": "array", "items": {"$ref": "#/definitions/DesignPattern"}}}, "required": ["answer"], "definitions": {"DesignPattern": {"title": "DesignPattern", "type": "object", "properties": {"pattern": {"title": "Pattern", "description": "name of design pattern", "type": "string"}, "reason": {"title": "Reason", "description": "reason to choose this design pattern", "type": "string"}}, "required": ["pattern", "reason"]}}}
    ```
    
  • Prompt template được tạo sẽ có 1 variables cần truyền vào khi xử lý là “question” - câu hỏi của người dùng.

Tạo LLM và thực thi

Tiếp theo, ta tạo các object về LLM và thực thi để lấy kết quả trả về như sau:

# Initialize service account credentials
credentials = service_account.Credentials.from_service_account_file("<path_to_service_account>")
# Initialize the VertexAI and LLMChain instances
llm = VertexAI(project="<your_gcp_project>", credentials=credentials, max_output_tokens=1000)
chain = LLMChain(llm=llm, prompt=PROMPT)

# Define the question and print it
question = "Which design pattern should I use when design a webpage with long-time-processing backend"
print(f"Question: {question}")

# Run the chain to get the response
response = chain.run(question=question)
response_data = output_parser.parse(response)

# Print the design patterns and their reasons
print("Answer: ")
for design_pattern in response_data.answer:
    print(f"- {design_pattern.pattern}: {design_pattern.reason}")

Trong đoạn code trên, ta có thể thấy việc kết nối và sử dụng VertexAI được LangChain hỗ trợ vô cùng đơn giản, ngắn gọn nhưng vẫn có thể tùy chỉnh thông số cần thiết.

Kết quả:

Question: Which design pattern should I use when design a webpage with long-time-processing backend
Answer:
- Asynchronous Messaging: Asynchronous messaging allows the backend to process the request without blocking the user interface. This is important for long-time-processing tasks, as it allows the user to continue using the application while the task is being processed.
- Command Query Responsibility Segregation (CQRS): CQRS separates the read and write operations on a database, which can improve performance for long-time-processing tasks. This is because read operations can be performed without blocking write operations, and vice versa.
- Event Sourcing: Event sourcing stores all changes to a database as a sequence of events. This can make it easier to track and replay changes, which can be useful for long-time-processing tasks.

Thêm tài liệu tham khảo cho ứng dụng

Như vậy, ta đã xem qua demo đơn giản sử dụng Model I/O Module của LangChain để kết nối và sử dụng LLM. Tiếp theo, ta sẽ nâng cấp demo như sau: Thêm tài liệu để LLM tham khảo khi trả lời câu hỏi về Cloud Design Patterns. Ở đây, ta sẽ thêm tài liệu của Azure. Để thực hiện, ta sẽ sử dụng Document Loader, Embed, Retriever của Retrieval Module trong LangChain.

Upload image

Ứng dụng Retrieval module của LangChain vào demo

Cài đặt thêm thư viện

Ta cài đặt thêm những thư viện cần thiết như sau:

faiss-cpu
tiktoken
unstructured
playwright

Import thêm thư viện

from langchain.document_loaders import PlaywrightURLLoader
from langchain.embeddings import VertexAIEmbeddings
from langchain.vectorstores import FAISS

Cập nhật prompt template

Ta cập nhật đoạn code tạo prompt template như sau:

prompt_template = """
    You are a helpful assistant that can answer questions about cloud design pattern.
    
    Answer the following question: {question}
    Use this docs for references: {docs}
    
    List out name of top 3 suitable design patterns and brief explain reason
    {format_instructions}
"""
# Create a prompt instance with the defined template
PROMPT = PromptTemplate(
        template=prompt_template, 
        input_variables=["question", "docs"],
        partial_variables={"format_instructions": format_instructions}
    )

Trong đó, ta đã thêm hướng dẫn “Use this docs for references: {docs}” cho LLM khi thực hiện trả lời câu hỏi, và thêm input variable "docs" tương ứng cho prompt template.

Lấy dữ liệu từ tài liệu Azure và tạo reference data

Khi LLM thực thi, ta cần truyền thông tin tham khảo reference_data thông qua input variable “docs”. Ta thêm đoạn code như sau:

# Create reference data from url
urls = ["https://learn.microsoft.com/en-us/azure/architecture/patterns/"]
loader = PlaywrightURLLoader(urls=urls)
data = loader.load()
embeddings = VertexAIEmbeddings(project="<your_gcp_project>", credentials=credentials)
reference_data = FAISS.from_documents(data, embeddings)

Trong đó:

  • Ta sử dụng thư viện Playwright, được LangChain wrap lại thông qua PlaywrightURLLoader, để lấy thông tin từ url.
  • Convert thông tin lấy được dưới dạng text thành dạng vector bằng thư viện FAISS (Facebook AI Similarity Search) để xử lý và lưu trữ hiệu quả.

Thực thi LLM

Ta cập nhật đoạn code LLM như sau:

# Run the chain to get the response
docs = reference_data.similarity_search(question, k=4)
docs_page_content = " ".join([d.page_content for d in docs])
response = chain.run(question=question, docs=docs_page_content)
response_data = output_parser.parse(response)

Trong đó, ta đã truyền thêm thông tin về Azure Cloud Design Patterns từ tài liệu của Azure để làm thông tin tham khảo. Ta có được kết quả như sau:

Question: Which design pattern should I use when design a webpage with long-time-processing backend
Answer:
- Asynchronous Request-Reply: This pattern decouples backend processing from a frontend host, where backend processing needs to be asynchronous, but the frontend still needs a clear response.
- Cache-Aside: This pattern improves performance by loading data on demand into a cache from a data store.
- Circuit Breaker: This pattern handles faults that might take a variable amount of time to fix when connecting to a remote service or resource.

Ta có thể thấy câu trả lời đã có những thông tin tham khảo từ tài liệu của Azure.

Cải tiến ứng dụng bằng RetrievalQA Chain

Qua demo được nâng cấp, ta đã sử dụng Retrieval Module trong LangChain để tích hợp thêm thông tin vào bước xử lý của LLM. Tuy nhiên, với code hiện tại, hệ thống phải chạy lại bước lấy và xử lý dữ liệu từ url khá tốn thời gian. Để tăng tốc, ta có thể lưu trữ lại reference_data bằng các Vector Store được hỗ trợ bởi LangChain. Nhưng trong demo này, ta sẽ sử dụng Chain Module của LangChain để hiện thực hóa RetrievalQA object với khả năng kết hợp mô hình ngôn ngữ với dữ liệu truy xuất, để chỉ input reference_data một lần và sử dụng cho nhiều câu hỏi về sau.

Upload image

Ứng dụng Chain module của LangChain vào demo

Import thêm thư viện

from langchain.chains import RetrievalQA

Cập nhật prompt template

Ta cập nhật đoạn code tạo prompt template như sau:

prompt_template = """
    You are a helpful assistant that can answer questions about cloud design pattern.
    
    Use the following pieces of context to answer the question.
    {context}
    
    Answer the following question: {question}
    
    List out name of top 3 suitable design patterns and brief explain reason
    {format_instructions}
"""
# Create a prompt instance with the defined template
PROMPT = PromptTemplate(
        template=prompt_template, 
        input_variables=["context", "question"],
        partial_variables={"format_instructions": format_instructions}
    )

Trong prompt template, ta đã thay thế hướng dẫn sử dụng reference data (“Use this doc…”) bằng hướng dẫn sử dụng context, và cập nhật lại input variable tương ứng.

Tạo RetrievalQA và thực thi

Ta tạo RetrievalQA object như sau:

# Create RetrievalQA
chain_type_kwargs = {"prompt": PROMPT}
retrievalqa = RetrievalQA.from_chain_type(llm=VertexAI(credentials=credentials, max_output_tokens=1000), chain_type="stuff", retriever=reference_data.as_retriever(), chain_type_kwargs=chain_type_kwargs)

Trong đó, ta thiết lập LLM object, Retriever object ngay trong RetrievalQA. Tiếp theo, ta sẽ thực hiện gọi run từ retrievalqa thay cho LLMChain object (”chain”) trong đoạn code trước. Do đó, ta có thể bỏ các đoạn code hiện tại dưới các comment “Initialize the VertexAI and LLMChain instances” và “Run the chain to get the response”.

Sau đó, ta cập nhật đoạn code thực thi như sau:

# Create a prompt instance with the defined template
...
# Initialize service account credentials
...
# Create reference data from url
...
# Create RetrievalQA
chain_type_kwargs = {"prompt": PROMPT}
retrievalqa = RetrievalQA.from_chain_type(llm=VertexAI(credentials=credentials, max_output_tokens=1000), chain_type="stuff", retriever=reference_data.as_retriever(), chain_type_kwargs=chain_type_kwargs)

# Define the questions and execute
questions = [
        "Which design pattern should I use when design a webpage with long-time-processing backend",
        "Which design pattern should I check when design a microserver system",
        "Which design pattern to prevent single point of failure"
]
for question in questions:
    print(f"Question: {question}")

    # Run the chain to get the response
    response = retrievalqa.run(question)
    response_data = output_parser.parse(response)
    
    # Print the design patterns and their reasons
    print("Answer: ")
    for design_pattern in response_data.answer:
        print(f"- {design_pattern.pattern}: {design_pattern.reason}")
        print()

Kết quả:

Question: Which design pattern should I use when design a webpage with long-time-processing backend
Answer:
- Asynchronous Request-Reply: This pattern allows the backend processing to be asynchronous, while still providing a clear response to the frontend.
- Circuit Breaker: This pattern can handle faults that might take a variable amount of time to fix when connecting to a remote service or resource.
- Queue-Based Load Leveling: This pattern can help to smooth intermittent heavy loads on the backend.

Question: Which design pattern should I check when design a microserver system
Answer:
- Sidecar: Sidecar pattern is suitable for designing a microservice system because it allows components of an application to be deployed into a separate process or container, providing isolation and encapsulation. This can help to improve the scalability, reliability, and maintainability of a microservice system.
- Ambassador: The Ambassador pattern is a good choice for designing a microservice system because it allows you to create helper services that send network requests on behalf of a consumer service or application. This can help to improve the performance and scalability of your microservice system.
- Circuit Breaker: The Circuit Breaker pattern is a good choice for designing a microservice system because it allows you to handle faults that might take a variable amount of time to fix when connecting to a remote service or resource. This can help to improve the reliability and availability of your microservice system.

Question: Which design pattern to prevent single point of failure
Answer:
- Bulkhead: Bulkhead pattern isolates elements of an application into pools so that if one fails, the others will continue to function.
- Circuit Breaker: Circuit Breaker pattern handles faults that might take a variable amount of time to fix when connecting to a remote service or resource.
- Leader Election: Leader Election pattern coordinates the actions performed by a collection of collaborating task instances in a distributed application by electing one instance as the leader that assumes responsibility for managing the other instances.

Từ điểm này, ta có thể nâng cấp thêm cho ứng dụng như tùy chỉnh cách lấy dữ liệu từ tài liệu tham khảo bằng cách định nghĩa thêm các tham số trong phương thức as_retriever, hoặc ta có thể dùng RetrievalQAWithSourcesChain để có thêm dữ liệu về source mà model đã sử dụng nhằm trả lời câu hỏi từ reference data,… Đây là những ứng dụng phức tạp hơn của LangChain mà ta có thể thảo luận trong một bài viết khác.

Với demo trên, ta đã sử dụng các module của LangChain như Model I/O, Retrieval, Chain để cài đặt một ứng dụng trả lời câu hỏi về design pattern sử dụng VertexAI có tham khảo từ tài liệu của Azure. Qua đó thấy được các chức năng mà LangChain hỗ trợ, cũng như cách sử dụng LangChain để dễ dàng tương tác với LLM và tích hợp vào hệ thống.

Ứng dụng

Với khả năng của mình, LangChain có thể hỗ trợ xây dựng một số hệ thống như:

  • Question-Answering sử dụng tài liệu tham khảo: Tính năng của LangChain cho phép người dùng cung cấp ngữ cảnh và đặt các câu hỏi cụ thể. Sau đó, hệ thống sẽ tương tác thông minh với dữ liệu văn bản để đưa ra câu trả lời chính xác.
  • Code Completion Tool: Sử dụng LangChain để tích hợp OpenAI vào công cụ hoàn thiện code, giúp đề xuất code phù hợp với ngữ cảnh cho developer.
  • Summarization: LangChain có thể tóm tắt nhiều loại văn bản khác nhau, chẳng hạn như cuộc gọi, bài viết, sách, tài liệu học thuật, tài liệu pháp lý, lịch sử người dùng hoặc tài liệu tài chính. Công cụ này có thể trích xuất thông tin cần thiết một cách hiệu quả từ nhiều loại nội dung.
  • Chatbot: Trong lĩnh vực dịch vụ khách hàng, LangChain có thể giúp Chatbot hiểu và trả lời các truy vấn của khách hàng bằng nhiều ngôn ngữ, nâng cao chức năng của chúng và mang lại sự tương tác hấp dẫn với người dùng.
  • Interacting with APIs: LangChain có thể tương tác, đọc hiểu ngữ cảnh của người dùng, lựa chọn và tương tác với API phù hợp, giúp tìm kiếm và xử lý dữ liệu từ nhiều nguồn khác nhau dễ dàng hơn.

Kết luận

LangChain không chỉ là một framework mạnh mẽ giúp tương tác và tích hợp LLM vào các ứng dụng, mà còn là một giải pháp linh hoạt cho nhiều tác vụ trong lĩnh vực xử lý ngôn ngữ tự nhiên. Với các module và chức năng mà LangChain cung cấp, việc xây dựng và triển khai các ứng dụng NLP trở nên dễ dàng và hiệu quả hơn bao giờ hết. Hãy tận dụng sức mạnh của LangChain để mở ra những khả năng mới trong thế giới kỹ thuật số hiện đại.

Atekco - Home for Authentic Technical Consultants