0
0
Lập trình
Hưng Nguyễn Xuân 1
Hưng Nguyễn Xuân 1xuanhungptithcm

Vai trò của cấu trúc đồ thị trong ứng dụng LLM

Đăng vào 20 giờ trước

• 6 phút đọc

Giới thiệu

Việc tích hợp các Mô hình Ngôn ngữ Lớn (LLM) vào các ứng dụng web không chỉ đơn thuần là tổ chức một cuộc thảo luận giữa ứng dụng và LLM. Đôi khi, chương trình cần hỏi một thứ gì đó trong một vòng lặp, đôi khi cần đánh giá đầu ra của LLM, và thường xuyên đóng vai trò như một trung gian giữa con người và LLM. Từ khóa cho điều này là orchestration. Để thực hiện orchestration chất lượng cao, chúng ta cần những trừu tượng mạnh mẽ. Trong bài viết này, chúng ta sẽ khám phá tại sao cấu trúc đồ thị là một lựa chọn tốt cho việc này.

Tại sao sử dụng cấu trúc đồ thị trong tích hợp AI?

Như đã đề cập, ngay cả một luồng đơn giản cũng có thể trở nên khó quản lý. Ví dụ, chúng ta có thể cần gọi một LLM một cách đệ quy cho đến khi đạt được kết quả mong muốn, hoặc có thể có hai hoặc nhiều LLM cộng tác trên một nhiệm vụ duy nhất. Để chuẩn hóa các quy trình này và làm cho chúng dễ đọc và dễ bảo trì hơn, chúng ta có thể định nghĩa các hàm như là các nút trong một đồ thị, được kết nối bởi các cạnh chỉ định rõ ràng những gì sẽ xảy ra sau mỗi cuộc gọi hàm. Cách tiếp cận này cũng cho phép chúng ta chia sẻ trạng thái giữa các cuộc gọi hàm, điều này rất quan trọng, vì các luồng liên quan đến LLM thường yêu cầu lưu trữ ngữ cảnh của nhiệm vụ.

Cấu trúc đồ thị là gì?

Cấu trúc đồ thị là một cấu trúc dữ liệu phi tuyến tính bao gồm các nút được kết nối bởi các cạnh, chỉ ra mối quan hệ giữa các nút. Một đồ thị có thể không có hướng hoặc có hướng, và cũng có thể là tuần hoàn hoặc không tuần hoàn. Cấu trúc đồ thị rất linh hoạt, thường được sử dụng để đại diện cho mối quan hệ giữa các thực thể khác nhau. Tính linh hoạt này khiến nó trở thành một lựa chọn hoàn hảo cho trường hợp sử dụng của chúng ta.

Thực hiện qua mã

Trong bài viết trước, chúng ta đã triển khai mẫu Map-Reduce để tóm tắt một blog bằng cách tóm tắt các bài viết đã được thu thập. Mỗi bài viết được gửi đến LLM để tóm tắt song song. Nếu danh sách kết quả quá dài, nó sẽ được chia nhỏ thành các danh sách con và tóm tắt lại với một prompt giảm, đệ quy, để đạt được mức độ nén mong muốn.

Chuẩn bị Utils

Chúng ta cần định nghĩa một số tiện ích để xử lý dữ liệu và chạy mô hình:

javascript Copy
import { ChatOpenAI } from "@langchain/openai";
import { TokenTextSplitter } from "@langchain/textsplitters";

const MAX_TOKEN = 1000;

const textSplitter = new TokenTextSplitter({
  chunkSize: MAX_TOKEN,
  chunkOverlap: 0,
});
const llm = new ChatOpenAI({
  model: "gpt-5-mini",
  apiKey: process.env.OPENAI_API_KEY,
});

Định nghĩa trạng thái

Cải tiến đầu tiên cho mã của chúng ta là có một công cụ để định nghĩa một trạng thái chia sẻ, thậm chí làm cho nó an toàn với kiểu dữ liệu. Định nghĩa trạng thái trông như sau:

javascript Copy
import { Annotation } from "@langchain/langgraph";

const OverallState = Annotation.Root({
  contents: Annotation<string[]>,
  summaries: Annotation<string[]>({
    reducer: (state, update) => state.concat(update),
  }),
  collapsedSummaries: Annotation<Document[]>,
  finalSummary: Annotation<string>,
});

Định nghĩa các hàm nút

Các hàm mà chúng ta sẽ định nghĩa phản ánh bốn bước mà chúng ta đã khám phá trong bài viết trước:

  1. Giai đoạn tiền xử lý

Dữ liệu được trả về từ trình thu thập sẽ được truyền vào việc gọi đồ thị của chúng ta.

javascript Copy
import { runSitemapBasedScraper } from "./scraper/main";
import graph from "./summarizer/with-langgraph/graph";

async function main() {
  const scrappingResults = await runSitemapBasedScraper([
    "https://www.aboutjs.dev",
  ]);

  const filteredScrapedResults = scrappingResults.filter((result) => {
    if (result.error) {
      console.error(`❌ ${result.url}: ${result.error}`);
    }
    return result.success;
  });

  const summarized = await graph.invoke({
    contents: filteredScrapedResults.flatMap((result) =>
      result.posts.map((post) => post.content),
    ),
  });
  console.log("summarized", summarized.finalSummary);
}

void main();

Các giai đoạn khác

Tiếp theo, chúng ta sẽ triển khai các giai đoạn MappingReducing theo cách tương tự, sử dụng cấu trúc đồ thị, từ đó giúp mã trở nên rõ ràng và dễ hiểu hơn. Các hàm sẽ được định nghĩa tương ứng để xử lý và tóm tắt các tài liệu.

Những Thực Hành Tốt Nhất

  1. Sử dụng đồ thị khi tổ chức luồng phức tạp: Đối với các luồng phức tạp, cấu trúc đồ thị giúp quản lý và mở rộng dễ dàng hơn.
  2. Chia sẻ trạng thái giữa các hàm: Sử dụng các cạnh để kết nối các nút và chia sẻ trạng thái giữa chúng.
  3. Kiểm tra và sửa lỗi thường xuyên: Đảm bảo rằng bạn kiểm tra các hàm trước khi triển khai.
  4. Tài liệu rõ ràng: Viết tài liệu rõ ràng cho từng hàm và cách chúng tương tác với nhau.

Những Cạm Bẫy Thường Gặp

  • Quá phức tạp: Đừng làm cho cấu trúc đồ thị trở nên quá phức tạp, điều này có thể gây khó khăn cho việc bảo trì.
  • Thiếu kiểm tra: Đảm bảo kiểm tra đầy đủ cho từng phần để tránh lỗi trong quá trình chạy.
  • Không tận dụng tối đa các tính năng của langchain: Khám phá và sử dụng đầy đủ các tính năng mà thư viện này cung cấp.

Mẹo Tối Ưu Hiệu Suất

  • Sử dụng batch requests: Khi làm việc với LLM, hãy sử dụng các yêu cầu batch để tiết kiệm thời gian và tài nguyên.
  • Giảm thiểu độ trễ: Tối ưu hóa các hàm để giảm thiểu độ trễ trong quá trình xử lý.
  • Theo dõi hiệu suất: Giám sát hiệu suất của ứng dụng thường xuyên để phát hiện và khắc phục kịp thời.

Giải Quyết Vấn Đề

  • Đầu ra không như mong đợi: Kiểm tra kỹ từng bước trong luồng để xác định vấn đề.
  • Lỗi trong quá trình gọi hàm: Đảm bảo rằng tất cả các hàm đều được định nghĩa và gọi đúng cách.

Kết luận

Với langgraph, chúng ta có thể triển khai đồ thị một cách dễ dàng. Langgraph cung cấp cho chúng ta các nguyên tắc tiện lợi để tận dụng cấu trúc đồ thị trong luồng mà không cần viết bất kỳ mã cấp thấp nào cho trừu tượng đồ thị. Bằng cách xây dựng các khung xung quanh các cấu trúc dữ liệu đã được biết đến, chúng ta có thể tự tin rằng mọi thành viên trong nhóm mới đều có thể trở nên hiệu quả nhanh chóng. Trong trường hợp xây dựng các ứng dụng LLM, điều này là rất quan trọng, vì chúng ta thường cần triển khai các luồng phức tạp để xử lý nhiều nhiệm vụ với đầu ra không xác định. Cảm ơn bạn đã đọc bài viết này, hy vọng bạn đã học được điều gì đó từ nội dung của tôi. Hãy theo dõi bài viết tiếp theo!

Câu hỏi thường gặp

  1. Đồ thị là gì?
    Đồ thị là một cấu trúc dữ liệu phi tuyến tính bao gồm các nút và các cạnh nối chúng lại với nhau.

  2. Tại sao nên sử dụng đồ thị trong lập trình?
    Đồ thị giúp tổ chức và dễ dàng quản lý các luồng phức tạp, đặc biệt trong các ứng dụng AI.

  3. langgraph là gì?
    langgraph là một thư viện giúp xây dựng các luồng dựa trên cấu trúc đồ thị một cách dễ dàng và hiệu quả.

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào