Nắm vững LangChain: Hướng dẫn dành cho nhà phát triển xây dựng ứng dụng LLM mạnh mẽ

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

Bắt đầu nhanh: Khi tiếng chuông báo thức vang lên lúc 2 giờ sáng và bạn cần một giải pháp LLM

Đã 2 giờ sáng. Hệ thống sản xuất gặp sự cố, và bạn đang nhìn chằm chằm vào những dữ liệu phức tạp, phi cấu trúc đòi hỏi một phản hồi tức thì, thông minh.

Suy nghĩ đầu tiên của tôi trong những tình huống như vậy luôn là: LLM có thể giúp tôi gỡ rối nhanh đến mức nào? Đây chính là lúc LangChain trở thành một công cụ vô giá. Nó không chỉ là một thư viện; nó là một framework được thiết kế để đơn giản hóa sự phức tạp khi làm việc với các Mô hình Ngôn ngữ Lớn, cho phép bạn xây dựng các ứng dụng thực hiện các tác vụ phức tạp vượt xa phản hồi prompt cơ bản.

Hãy bắt đầu với những điều cơ bản. Khi mỗi giây đều quý giá, bạn cần một hướng dẫn rõ ràng, súc tích.

Cài đặt: Đưa LangChain vào máy của bạn

Để bắt đầu, bạn sẽ cần các công cụ cần thiết. Nếu bạn chưa cài đặt, hãy cài đặt LangChain. Tôi thường làm điều này trong một môi trường ảo để duy trì một thiết lập sạch sẽ.


pip install langchain_community langchain_openai

Bạn có thể tự hỏi tại sao chúng ta lại cài đặt cả langchain_communitylangchain_openai. LangChain gần đây đã tái cấu trúc codebase của mình thành các gói module. Gói langchain_community cung cấp nhiều tích hợp phổ biến, như các LLM khác nhau, trình tải tài liệu và vector stores. Trong khi đó, langchain_openai xử lý riêng các chức năng của OpenAI. Nếu bạn đang làm việc với một nhà cung cấp LLM khác, bạn sẽ cài đặt gói chuyên dụng của họ; ví dụ, langchain_google_genai cho Google Gemini.

Cuộc gọi LLM đầu tiên của bạn: "Hello World" của AI

Tiếp theo, hãy để một LLM thực hiện một tác vụ. Bạn sẽ cần một khóa API OpenAI. Luôn lưu trữ nó một cách an toàn, lý tưởng nhất là dưới dạng biến môi trường. Mặc dù việc export trực tiếp có thể đủ cho việc kiểm thử nhanh, nhưng luôn sử dụng một hệ thống quản lý bí mật phù hợp cho bất kỳ ứng dụng nghiêm túc nào.


import os
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate

# Đảm bảo khóa API OpenAI của bạn được đặt làm biến môi trường
# os.environ["OPENAI_API_KEY"] = "YOUR_ACTUAL_OPENAI_API_KEY"

# Khởi tạo LLM
llm = ChatOpenAI(model="gpt-3.5-turbo")

# Định nghĩa một prompt template đơn giản
prompt = ChatPromptTemplate.from_messages([
    ("system", "Bạn là một trợ lý AI hữu ích."),
    ("user", "{input}")
])

# Tạo một chuỗi đơn giản: prompt | llm
chain = prompt | llm

# Gọi chuỗi với đầu vào của bạn
response = chain.invoke({"input": "Giải thích khái niệm 'deadlock' trong hệ điều hành bằng một câu."})

print(response.content)

Đoạn mã Python này khởi tạo một mô hình trò chuyện OpenAI, định nghĩa một prompt đơn giản, sau đó tạo và gọi một "chain" (một chuỗi các hoạt động). Bạn sẽ nhận được một giải thích súc tích về deadlock. Chỉ trong khoảng năm phút, bạn đã khai thác một mô hình ngôn ngữ lớn với nỗ lực tối thiểu. Khả năng tạo mẫu nhanh chóng này chính là điều tôi ưu tiên khi làm việc dưới áp lực.

Tìm hiểu sâu: Hiểu cách LangChain hoạt động bên trong

Khi cuộc khủng hoảng tức thì đã được ngăn chặn, đã đến lúc nắm bắt cách LangChain tạo điều kiện cho quá trình này. Việc dựa vào các hệ thống không rõ ràng trong môi trường sản xuất thường dẫn đến nhiều cảnh báo vào đêm khuya hơn.

LangChain không chỉ là một wrapper; nó cung cấp một cách tiếp cận có cấu trúc để kết hợp các thành phần mạnh mẽ thành các ứng dụng phức tạp. Trong kinh nghiệm chuyên môn của tôi, việc thực sự hiểu rõ các khối xây dựng cơ bản này là một kỹ năng thiết yếu. Nó giúp bạn vượt xa việc chạy các ví dụ và thực sự xây dựng các giải pháp mạnh mẽ, được điều khiển bởi AI.

Các thành phần cốt lõi: Bộ công cụ AI thiết yếu của bạn

LangChain được cấu trúc xung quanh một số trừu tượng quan trọng:

Mô hình Ngôn ngữ (LLMs)

Về cốt lõi, LangChain yêu cầu một LLM. Nó cung cấp một giao diện thống nhất cho các mô hình đa dạng, bao gồm GPT của OpenAI, Gemini của Google, Claude của Anthropic, hoặc thậm chí các mô hình mã nguồn mở cục bộ như Llama 2 thông qua các công cụ như Ollama. Trừu tượng hóa này có nghĩa là bạn có thể dễ dàng hoán đổi các mô hình với những điều chỉnh mã tối thiểu. Đây là một lợi thế đáng kể, đặc biệt khi tối ưu hóa chi phí, hiệu suất hoặc các khả năng cụ thể.


from langchain_openai import ChatOpenAI
from langchain_google_genai import ChatGoogleGenerativeAI

# Sử dụng OpenAI
openai_llm = ChatOpenAI(model="gpt-4o")

# Sử dụng Google Gemini (đảm bảo GOOGLE_API_KEY đã được thiết lập)
gemini_llm = ChatGoogleGenerativeAI(model="gemini-pro")

print(openai_llm.invoke("Hello").content)
print(gemini_llm.invoke("Hi").content)

Prompt Templates: Đảm bảo đầu vào LLM nhất quán

Mặc dù các prompt văn bản thô hoạt động tốt cho các tác vụ một lần, nhưng các ứng dụng đòi devotion consistency. Prompt templates cho phép bạn định nghĩa các cấu trúc lặp lại, tự động chèn các biến. Điều này rất quan trọng để ngăn chặn các lỗ hổng prompt injection và đảm bảo LLM nhận được đầu vào được định dạng tốt một cách nhất quán.


from langchain_core.prompts import ChatPromptTemplate

issue_template = ChatPromptTemplate.from_messages([
    ("system", "Bạn là kỹ sư DevOps cấp cao được giao nhiệm vụ phân tích các sự cố sản xuất."),
    ("user", "Phân tích báo cáo sự cố sản xuất sau:\nID sự cố: {incident_id}\nNhật ký lỗi: {error_logs}\nTác động người dùng: {user_impact}\n\nCung cấp tóm tắt về nguyên nhân gốc rễ và các bước giảm thiểu ban đầu.")
])

formatted_prompt = issue_template.format_messages(
    incident_id="P-2026-03-22-001",
    error_logs="{'service_a': 'Connection refused to DB', 'service_b': '500 internal server error'}",
    user_impact="All users unable to access features."
)

# Bây giờ bạn có thể chuyển formatted_prompt cho một LLM
# print(formatted_prompt)

Chains: Điều phối các hoạt động LLM

Sức mạnh thực sự của LangChain trở nên rõ ràng khi bạn liên kết các thành phần với nhau. Một "chain" đơn giản là một chuỗi các hoạt động được định nghĩa, trong đó đầu ra của một bước liền mạch trở thành đầu vào cho bước tiếp theo. Toán tử `|`, một phần của LangChain Expression Language (LCEL), làm cho quá trình này trở nên cực kỳ trực quan và dễ đọc.

Hãy coi nó như một pipeline của Unix được thiết kế đặc biệt cho các hoạt động của LLM. Bạn có thể kết hợp prompt templates với LLM, hoặc thậm chí xâu chuỗi nhiều lời gọi LLM để đạt được lý luận phức tạp hơn.


from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

llm = ChatOpenAI(model="gpt-3.5-turbo")
output_parser = StrOutputParser()

analysis_prompt = ChatPromptTemplate.from_template(
    "Cho nhật ký lỗi sau: {log_message}\nNguyên nhân khả thi nhất là gì? Hãy súc tích."
)

# Một chuỗi lấy thông báo nhật ký, nhắc LLM và phân tích đầu ra thành một chuỗi
analysis_chain = analysis_prompt | llm | output_parser

log_message = "Error 403: User not authorized to access resource /admin/dashboard"
result = analysis_chain.invoke({"log_message": log_message})
print(f"Nguyên nhân khả thi: {result}")

Output Parsers: Cấu trúc phản hồi LLM cho ứng dụng của bạn

LLM tạo ra văn bản, nhưng ứng dụng của bạn thường yêu cầu dữ liệu có cấu trúc, chẳng hạn như JSON hoặc danh sách. Output parsers thu hẹp khoảng cách này bằng cách chuyển đổi văn bản LLM thô thành một định dạng có thể sử dụng được. Khả năng này rất cần thiết để tương tác theo chương trình với đầu ra của LLM.


from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.pydantic_v1 import BaseModel, Field

# Định nghĩa cấu trúc đầu ra mong muốn của bạn bằng Pydantic
class IncidentSummary(BaseModel):
    root_cause: str = Field(description="Nguyên nhân gốc rễ súc tích của sự cố")
    severity: str = Field(description="Mức độ nghiêm trọng (ví dụ: Nghiêm trọng, Cao, Trung bình, Thấp)")
    mitigation_steps: list[str] = Field(description="Danh sách các hành động giảm thiểu tức thì")

llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
parser = JsonOutputParser(pydantic_object=IncidentSummary)

prompt = ChatPromptTemplate.from_messages([
    ("system", "Bạn là một nhà phân tích sự cố tỉ mỉ. Định dạng phản hồi của bạn dưới dạng JSON theo schema này: {format_instructions}"),
    ("user", "Phân tích sự cố này: Pool kết nối cơ sở dữ liệu đã cạn kiệt, gây ra lỗi 503 trên API gateway.")
]).partial(format_instructions=parser.get_format_instructions())

chain = prompt | llm | parser

incident_data = chain.invoke({})
print(incident_data)
print(f"Nguyên nhân gốc rễ: {incident_data['root_cause']}")

Retrieval: Kết nối với các nguồn kiến thức bên ngoài

Mặc dù các LLM rất mạnh mẽ, nhưng kiến thức của chúng bị giới hạn trong dữ liệu huấn luyện. Đối với thông tin thời gian thực, chuyên biệt theo miền hoặc độc quyền, bạn cần một phương pháp để chèn dữ liệu bên ngoài. Đây là lúc retrieval trở nên quan trọng. Nó thường bao gồm một loạt các bước:

  1. Tải các tài liệu khác nhau, chẳng hạn như PDF, trang web hoặc wiki nội bộ.
  2. Chia các tài liệu này thành các đoạn nhỏ hơn, dễ quản lý hơn.
  3. Nhúng các đoạn này thành các biểu diễn vector.
  4. Lưu trữ các biểu diễn này trong một cơ sở dữ liệu vector.
  5. Truy vấn cơ sở dữ liệu vector để truy xuất các đoạn liên quan nhất dựa trên đầu vào của người dùng.
  6. Chuyển các đoạn đã truy xuất này cho LLM dưới dạng thông tin ngữ cảnh.

Mặc dù một bài viết chuyên dụng sẽ bao gồm việc xây dựng các ứng dụng RAG (Retrieval-Augmented Generation) đầy đủ, nhưng việc hiểu retrieval là nền tảng cho bất kỳ ứng dụng LLM nào cần truy cập thông tin ngoài kiến thức chung của nó. Đó là cách bạn chống lại ảo giác và đảm bảo độ chính xác thực tế.

Sử dụng nâng cao: Xây dựng hệ thống LLM sẵn sàng cho sản xuất

Khi bạn đã quen thuộc với các khái niệm cơ bản, LangChain trao quyền cho bạn để xây dựng các hệ thống tinh vi. Đây là lúc bạn chuyển từ các script đơn giản sang các ứng dụng có khả năng đưa ra quyết định, tương tác với API và duy trì trạng thái.

Agents và Tools: Trao quyền cho LLM với khả năng tự chủ

Agents cung cấp một khả năng thực sự mang tính chuyển đổi. Chúng cho phép một LLM đưa ra quyết định chiến lược về việc sử dụng "tools" nào để đạt được một mục tiêu cụ thể.

Tools có thể đa dạng, bao gồm các hành động như tìm kiếm web, gọi API nội bộ, lấy dữ liệu từ cơ sở dữ liệu hoặc thực thi mã. Agent quan sát trạng thái hiện tại, chọn một hành động thích hợp, thực thi nó bằng cách sử dụng một tool, quan sát kết quả và lặp lại chu trình này cho đến khi đạt được mục tiêu. Cơ chế này biến các LLM thành những người giải quyết vấn đề chủ động.

Dưới đây là một ví dụ minh họa về việc sử dụng công cụ tìm kiếm:


from langchain_openai import ChatOpenAI
from langchain import agents
from langchain_community.tools import WikipediaQueryRun
from langchain_community.utilities import WikipediaAPIWrapper

llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)

# Định nghĩa một công cụ
wikipedia = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper())
tools = [wikipedia]

# Khởi tạo một agent có quyền truy cập vào công cụ
agent_executor = agents.create_react_agent(
    llm, tools, verbose=True
)

# Agent giờ đây có thể sử dụng Wikipedia để trả lời các câu hỏi
# response = agent_executor.invoke({"input": "Who is the current CEO of Google?"})
# print(response["output"])

Cài đặt verbose=True rất quan trọng để debug các agent. Nó tiết lộ quá trình suy nghĩ nội bộ của LLM khi nó cân nhắc nên sử dụng công cụ nào và lý do của nó. Khi một agent không hoạt động như mong đợi, việc quan sát độc thoại nội bộ của nó cung cấp những hiểu biết quan trọng, giống như việc hiểu các nhật ký của hệ thống trong một sự cố lúc 2 giờ sáng.

Memory: Ghi nhớ các tương tác trước đó

Đối với các ứng dụng đàm thoại, một LLM phải "ghi nhớ" các tương tác trước đó. Các module memory của LangChain quản lý điều này bằng cách chèn các lượt trước đó vào prompt hiện tại. Có nhiều loại memory khác nhau, từ memory đệm đơn giản đến memory tóm tắt phức tạp hơn, mỗi loại phù hợp với các trường hợp sử dụng và độ phức tạp đàm thoại khác nhau.


from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferMemory

llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)

prompt = ChatPromptTemplate.from_messages([
    ("system", "Bạn là một chatbot thân thiện."),
    MessagesPlaceholder(variable_name="history"),
    ("user", "{input}")
])

# Khởi tạo bộ nhớ
memory = ConversationBufferMemory(return_messages=True)

# Tạo một chuỗi đàm thoại
conversation = ConversationChain(
    llm=llm,
    prompt=prompt,
    memory=memory,
    verbose=True
)

# Mô phỏng một cuộc trò chuyện
conversation.invoke({"input": "Xin chào!"})
conversation.invoke({"input": "Tên tôi là Alex. Bạn có thể làm gì cho tôi?"})
response = conversation.invoke({"input": "Bạn có thể nhắc lại tên tôi là gì không?"})

print(response["response"])

Callbacks: Khả năng quan sát thiết yếu cho các hoạt động LLM

Giống như bất kỳ phần mềm phức tạp nào, các ứng dụng LLM yêu cầu khả năng quan sát mạnh mẽ. Hệ thống callback của LangChain cho phép bạn chèn các hook vào các giai đoạn khác nhau trong quá trình thực thi của một chain hoặc agent. Điều này vô cùng quý giá cho việc ghi nhật ký, debug, giám sát mức sử dụng token và phân tích độ trễ. Khi một ứng dụng LLM chệch khỏi hành vi mong đợi, các callback trở thành một công cụ debug không thể thiếu, giúp bạn tỉ mỉ theo dõi chính xác điều gì đã xảy ra và vì sao.


from langchain.callbacks import StdOutCallbackHandler
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate

handler = StdOutCallbackHandler()
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)

prompt = ChatPromptTemplate.from_template("Thủ đô của {country} là gì?")
chain = prompt | llm

# Chạy chuỗi với trình xử lý callback
response = chain.invoke({"country": "Pháp"}, config={"callbacks": [handler]})
print(response.content)

Quan sát cách trình xử lý callback xuất ra thông tin chi tiết về lời gọi LLM, bao gồm các token đã tiêu thụ và thời gian phản hồi. Mức độ hiểu biết này hoàn toàn quan trọng để tối ưu hóa hiệu suất hoặc khắc phục sự cố trong môi trường sản xuất.

Mẹo thực tế: Điều hướng thế giới phát triển LLM đang phát triển nhanh chóng

Xây dựng ứng dụng với LLM thường có cảm giác như dấn thân vào một lãnh thổ chưa được khám phá, với các mô hình và kỹ thuật mới xuất hiện hàng ngày. Dưới đây là một vài chiến lược tôi đã áp dụng để duy trì sự tỉnh táo và hiệu quả:

Debug: Đồng minh không thể thiếu của bạn

Việc debug các ứng dụng LLM đặt ra những thách thức độc đáo do tính chất không xác định của chúng. Luôn bật verbose=True trên các agent và chain. Tận dụng các callback và in các bước trung gian một cách cẩn thận. Nếu một agent đi chệch hướng, bạn cần kiểm tra quá trình suy nghĩ của nó để xác định chính xác nơi nó đã đi sai. Hãy coi lý luận của LLM như bất kỳ hệ thống phức tạp nào khác; kiểm tra trạng thái của nó ở mọi giai đoạn.

Quản lý chi phí hiệu quả

Việc sử dụng token có thể tích lũy nhanh chóng. Hãy nhận thức rõ về độ dài của prompt, đặc biệt khi kết hợp bộ nhớ. Tóm tắt lịch sử trò chuyện khi nó trở nên quá dài.

Sử dụng các mô hình tiết kiệm hơn, chẳng hạn như GPT-3.5-turbo, cho các tác vụ đơn giản hoặc lọc ban đầu. Chỉ nâng cấp lên các mô hình mạnh mẽ, đắt tiền hơn như GPT-4o khi thực sự cần thiết. Các cơ chế caching của LangChain, ví dụ, sử dụng SQLite cache, cũng có thể giảm đáng kể các lời gọi LLM lặp lại cho các đầu vào giống hệt nhau, tiết kiệm cả thời gian và chi phí.


from langchain_openai import ChatOpenAI
from langchain.globals import set_llm_cache
from langchain.cache import SQLiteCache

set_llm_cache(SQLiteCache(database_path=".langchain.db"))

llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)

# Lần gọi đầu tiên - sẽ truy cập LLM
response1 = llm.invoke("Định nghĩa của polymorphism trong OOP là gì?")
print(f"Lần gọi đầu tiên: {response1.content[:50]}...")

# Lần gọi thứ hai với cùng đầu vào - sẽ truy cập cache
response2 = llm.invoke("Định nghĩa của polymorphism trong OOP là gì?")
print(f"Lần gọi thứ hai (đã cache): {response2.content[:50]}...")

Luôn cập nhật thông tin, nhưng tránh chạy theo mọi điều mới lạ

Framework LangChain, cũng như bối cảnh LLM rộng lớn hơn, phát triển với tốc độ nhanh chóng. Hãy chú ý đến tài liệu và ghi chú phát hành của họ. Tuy nhiên, hãy chống lại sự thôi thúc xây dựng lại hoàn toàn ứng dụng của bạn mỗi khi một tính năng mới được giới thiệu. Tích hợp các khả năng mới một cách chiến lược, chỉ khi chúng thực sự giải quyết một vấn đề cấp bách hoặc mang lại sự cải thiện đáng kể về hiệu suất hoặc hiệu quả chi phí.

Kiểm thử nghiêm ngặt là chìa khóa

Việc kiểm thử các ứng dụng LLM vốn đã khó khăn, vì các unit test truyền thống thường không đủ. Thay vào đó, hãy tập trung vào các integration test mạnh mẽ để xác thực hành vi tổng thể của các chain và agent của bạn. Sử dụng các framework đánh giá, chẳng hạn như LangSmith của LangChain hoặc các lựa chọn thay thế mã nguồn mở khác, để đo lường chính xác hiệu suất so với tập dữ liệu vàng. Bạn cần xác nhận rằng LLM của bạn không chỉ tạo ra phản hồi, mà những phản hồi này còn nhất quán *chính xác* và đáng tin cậy.

LangChain vượt xa việc chỉ là một thư viện; nó đại diện cho một phương pháp luận toàn diện để cấu trúc các tương tác LLM của bạn.

Nó mang lại trật tự cho một không gian phát triển thường có cảm giác hỗn loạn, trao quyền cho bạn xây dựng các ứng dụng mạnh mẽ, thông minh mà không bị vướng vào quá nhiều mã boilerplate. Cho dù bạn đang xây dựng một chatbot đơn giản hay một agent tự trị phức tạp, việc hiểu và tận dụng framework này sẽ đẩy nhanh đáng kể chu trình phát triển của bạn và nâng cao độ tin cậy của các giải pháp AI của bạn.

Share: