Bỏ qua đến nội dung chính

DSPy: The End of Prompt Engineering - Kevin Madura, AlixPartners

TL;DR

  • DSPy là một framework khai báo mạnh mẽ, được thiết kế để xây dựng phần mềm mô-đun bằng cách coi các Mô hình Ngôn ngữ Lớn (LLM) là "công dân hạng nhất", giúp đơn giản hóa việc phát triển ứng dụng LLM.
  • Nó trừu tượng hóa các chi tiết phức tạp của prompt engineering và parsing, cho phép nhà phát triển tập trung vào việc khai báo ý định, logic chương trình thay vì điều chỉnh chuỗi.
  • Cách tiếp cận này giúp lặp lại nhanh chóng, xây dựng các chương trình bền vững trước các thay đổi mô hình LLM và tối ưu hóa hiệu suất, lý tưởng cho việc giải quyết đa dạng các vấn đề kỹ thuật.

metric feedback

tune adapters + modules

Signatures — declare input/output types

Modules — program logic, PyTorch-like

Adapters — translate Signature to prompt

LLM — Claude, GPT, Gemini

Optimizers + Metrics — auto-improve

Điểm chính

  • DSPy là một framework khai báo để xây dựng phần mềm mô-đun, nơi các Mô hình Ngôn ngữ Lớn (LLM) được coi là các hàm gọi được với đảm bảo về kiểu dữ liệu đầu vào và đầu ra.
  • Nó hoạt động ở mức độ trừu tượng cao hơn so với các thư viện khác, giúp giảm thiểu việc viết code lặp lại cho prompt, phân tích cú pháp JSON, cho phép tập trung vào logic nghiệp vụ cốt lõi.
  • DSPy cho phép xây dựng các chương trình Python thực thụ sử dụng LLM, thay vì chỉ điều chỉnh chuỗi prompt, tạo điều kiện cho việc lặp lại và thử nghiệm nhanh chóng.
  • Thiết kế hệ thống của DSPy giúp các ứng dụng trở nên mạnh mẽ và linh hoạt trước sự thay đổi liên tục của các mô hình LLM bên dưới.
  • Các khái niệm cốt lõi bao gồm: Signatures (khai báo ý định, đầu vào/đầu ra), Modules (cấu trúc logic chương trình, dựa trên PyTorch), Tools (các hàm Python được LLM sử dụng), Adapters (dịch Signatures thành prompt), Optimizers (cải thiện chương trình) và Metrics (đo lường thành công).
  • Signatures có thể là chuỗi đơn giản hoặc đối tượng dựa trên lớp (tương tự Pydantic), trong đó tên trường hoạt động như các mini-prompt để hướng dẫn LLM.
  • DSPy cung cấp nhiều Modules tích hợp sẵn như DSPy.Predict (gọi LLM cơ bản), Chain of Thought, React (cho phép LLM sử dụng tool), và Program with Thought (buộc LLM suy nghĩ bằng mã).

Từ vựng

  • framework — khung làm việc
  • GitHub repo — kho lưu trữ GitHub
  • first-class citizen — công dân hạng nhất
  • Large Language Model (LLM) — Mô hình Ngôn ngữ Lớn
  • prompt — lời nhắc/câu lệnh
  • declarative — khai báo
  • signature — chữ ký
  • module — mô-đun
  • tool — công cụ
  • adapter — bộ điều hợp
  • optimizer — bộ tối ưu hóa
  • metric — số liệu/thước đo
  • sentiment classifier — bộ phân loại cảm xúc
  • multimodal — đa phương thức
  • web research agent — tác nhân nghiên cứu web
  • paradigm — mô hình/nguyên tắc

Nội dung chi tiết

Giới thiệu DSPy

Cảm ơn mọi người đã tham gia. Hôm nay tôi ở đây để nói chuyện với các bạn về DSPy. Mọi người cứ thoải mái đặt câu hỏi hoặc trao đổi trong suốt buổi nói chuyện. Tôi không định dành trọn một tiếng rưỡi. Tôi biết đây là phiên cuối cùng trong ngày, nên hãy giữ không khí thoải mái.

Tôi sẽ bắt đầu với một chút thông tin cơ bản. Không muốn đi sâu vào quá nhiều slide. Về mặt kỹ thuật, tôi là một nhà tư vấn, vì vậy tôi phải trình bày một vài slide. Nhưng chúng ta sẽ đi sâu vào mã nguồn ở nửa sau buổi nói chuyện. Có một kho lưu trữ GitHub repo mà bạn có thể tải xuống để làm theo và tự mình thử nghiệm.

Có bao nhiêu người ở đây đã từng nghe nói về DSPy? Vâng, gần như tất cả mọi người, thật tuyệt vời. Có bao nhiêu người đã thực sự sử dụng nó hàng ngày trong môi trường sản xuất hoặc tương tự? Ba người. OK, tốt. Hy vọng hôm nay chúng ta có thể "chuyển đổi" thêm một số bạn.

Ở cấp độ cao, DSPy – thông tin này lấy trực tiếp từ trang web – là một framework khai báo cho cách bạn có thể xây dựng phần mềm mô-đun. Và quan trọng nhất đối với một người như tôi, tôi không nhất thiết là một kỹ sư viết mã cả ngày mỗi ngày. Như tôi đã đề cập trước đây, tôi thiên về một nhà tư vấn kỹ thuật. Vì vậy, tôi gặp phải nhiều vấn đề khác nhau. Đó có thể là một cuộc điều tra cho một công ty luật. Đó có thể là việc giúp một công ty hiểu cách cải thiện quy trình của họ, cách triển khai AI nội bộ. Có thể chúng ta cần xem xét 10.000 hợp đồng để xác định một điều khoản hoặc đoạn văn cụ thể. Và vì vậy, DSPy đã là một cách thực sự tuyệt vời đối với cá nhân tôi và nhóm của tôi để lặp lại rất, rất nhanh chóng. Tôi đang xây dựng các ứng dụng này. Quan trọng nhất là xây dựng các chương trình. Nó không phải là kiểu lặp lại với các prompt và điều chỉnh qua lại. Nó là xây dựng một chương trình Python thực thụ. Và DSPy là một cách thực sự tốt để bạn làm điều đó.

Tôi đã đề cập trước đây, có một repo trực tuyến. Nếu bạn muốn tải xuống ngay bây giờ, chỉ cần thiết lập mọi thứ. Tôi sẽ chiếu cái này lên màn hình sau. Nhưng nếu bạn muốn vào đây, chỉ cần tải xuống một số mã nguồn. Nó đã được xây dựng trong vài ngày qua. Nó sẽ không phải là mã nguồn hoàn hảo ở cấp độ sản xuất. Nó thiên về các tiện ích và những thứ nhỏ nhặt ở đây và đó chỉ để minh họa tính hữu ích, minh họa điểm mà chúng ta đang nói đến ngày hôm nay. Trong đó, tôi sẽ đi qua tất cả các trường hợp sử dụng khác nhau này. Đó là một sentiment classifier đi qua một PDF, một số công việc multimodal, một web research agent rất, rất đơn giản, phát hiện ranh giới của một PDF document. Bạn sẽ thấy cách tóm tắt văn bản có độ dài tùy ý và sau đó đi vào một optimizer với JEPO.

DSPy: Mô hình ngôn ngữ lớn làm công dân hạng nhất

Nhưng trước khi làm điều đó, chỉ để thiết lập mức độ hiểu biết chung. Điều lớn nhất đối với cá nhân tôi là DSPy là một cách thực sự tuyệt vời để phân tách logic của bạn thành một chương trình coi các Mô hình ngôn ngữ lớn (LLMs) như một first-class citizen. Vì vậy, cuối cùng, về cơ bản bạn chỉ đang gọi một hàm mà bên dưới nó chỉ là một Mô hình ngôn ngữ lớn (LLM). Và DSPy cung cấp cho bạn một cách thực sự trực quan, dễ dàng để làm điều đó với một số đảm bảo về các loại đầu vào và đầu ra. Tất nhiên, có structured outputs. Tất nhiên, có những cách khác để làm điều này, Pydantic và những cách khác. Nhưng DSPy có một bộ các thành phần nguyên thủy mà khi bạn kết hợp tất cả lại với nhau, cho phép bạn xây dựng một phần mềm mạch lạc, mô-đun mà sau đó bạn có thể tối ưu hóa. Và chúng ta sẽ đi sâu vào điều đó sau một phút.

Tại sao nên dùng DSPy?

Vì vậy, chỉ một vài lý do tại sao tôi là một người ủng hộ nhiệt tình như vậy. Nó nằm ở một mức độ trừu tượng rất tốt. Vì vậy, tôi muốn nói rằng nó không gây cản trở cho bạn nhiều như LangChain. Và đó không phải là một lời chỉ trích LangChain. Đó chỉ là một mô hình khác biệt về cách DSPy được cấu trúc. Và nó cho phép bạn tập trung vào những điều thực sự quan trọng. Vì vậy, bạn không viết choices, zero messages, content. Bạn không thực hiện phân tích cú pháp chuỗi. Bạn không làm một loạt các thứ dưới vỏ bọc. Bạn chỉ đang khai báo ý định của mình về cách bạn muốn chương trình hoạt động, những gì bạn muốn đầu ra, đầu vào và đầu ra của mình là gì.

Vì điều này, nó cho phép bạn tạo ra các chương trình máy tính, như tôi đã đề cập trước đây, không chỉ là điều chỉnh chuỗi và gửi chúng qua lại. Bạn đang xây dựng một chương trình trước tiên. Nó chỉ tình cờ cũng sử dụng các Mô hình ngôn ngữ lớn (LLMs). Và thực sự, phần quan trọng nhất của điều này là Omar Khateb, người sáng lập của nó, hoặc nhà phát triển ban đầu của nó, đã có một podcast thực sự hay với A16Z, khoảng hai hoặc ba ngày trước. Nhưng anh ấy đã nói một cách rất hay. Anh ấy nói nó được xây dựng theo tư duy hệ thống. Và nó thực sự là về cách bạn đang mã hóa hoặc diễn đạt ý định của mình về những gì bạn muốn làm. Quan trọng nhất, theo cách có thể chuyển giao. Vì vậy, thiết kế hệ thống của bạn, tôi tưởng tượng, chương trình của bạn sẽ không di chuyển nhanh chóng như khả năng của mô hình bên dưới. Khi chúng ta thấy các bản phát hành gần như mỗi ngày, các khả năng khác nhau, các mô hình tốt hơn. Và vì vậy, DSPy cho phép bạn cấu trúc nó theo cách giữ lại luồng kiểm soát, nó có thể giữ lại ý định của hệ thống, chương trình của bạn, đồng thời cho phép bạn chuyển đổi từ mô hình này sang mô hình khác đến mức bạn muốn hoặc cần.

Các cộng đồng đến miễn phí. Không có phân tích cú pháp JSON, những thứ như vậy. Một lần nữa, nó nằm ở một mức độ trừu tượng tốt, nơi bạn vẫn có thể hiểu những gì đang diễn ra dưới vỏ bọc. Nếu bạn muốn, bạn có thể vào và điều chỉnh mọi thứ. Nhưng nó cho phép bạn tập trung vào những gì bạn muốn làm, đồng thời giữ lại mức độ chính xác mà tôi nghĩ hầu hết chúng ta đều muốn có khi xây dựng chương trình của mình. Như đã đề cập, nó mạnh mẽ đối với các thay đổi về mô hìnhparadigm. Vì vậy, bạn có thể, một lần nữa, giữ logic của chương trình của mình, nhưng vẫn tích hợp các Mô hình ngôn ngữ lớn (LLMs) về cơ bản là cùng dòng.

Các thư viện khác và Lưu ý

Tuy nhiên, có những thư viện tuyệt vời khác ngoài kia, như Pydantic AI, LangChain, và còn rất nhiều thư viện khác cho phép bạn làm những điều tương tự, Agno là một ví dụ khác. Đây chỉ là một quan điểm. Và nó có thể không hoàn hảo cho trường hợp sử dụng của bạn. Đối với tôi, tôi mất một chút thời gian để thực sự hiểu cách DSPy hoạt động. Và bạn sẽ thấy lý do tại sao trong giây lát. Vì vậy, tôi chỉ khuyên bạn nên có một tâm trí cởi mở, thử nghiệm với nó, chạy mã, điều chỉnh mã, làm bất cứ điều gì bạn cần làm, và chỉ xem nó có thể hoạt động như thế nào đối với bạn.

Và thực sự, buổi nói chuyện này thiên về những cách mà tôi thấy nó hữu ích. Nó không phải là một luận văn về những điều phức tạp của mọi ngóc ngách của DSPy. Nó thiên về, tôi đã gặp phải những vấn đề này và bây giờ tôi tự nhiên tìm đến DSPy để giải quyết chúng, và đây là lý do tại sao. Và hy vọng là bạn có thể khái quát hóa một số điều này cho các trường hợp sử dụng của riêng mình.

Các khái niệm cốt lõi của DSPy

Chúng ta sẽ đi qua mọi thứ khá nhanh ở đây. Nhưng các khái niệm cốt lõi của DSPy thực sự gói gọn trong có thể là năm hoặc sáu khái niệm mà bạn thấy trên màn hình ở đây. Vì vậy, chúng ta sẽ đi sâu vào từng khái niệm này chi tiết hơn, nhưng ở cấp độ cao:

  1. Signatures: Chỉ định những gì bạn muốn hàm của mình làm. Đây là lúc bạn chỉ định đầu vào, đầu ra của mình; cả đầu vào và đầu ra đều có thể được định kiểu. Và bạn giao phó phần còn lại của việc triển khai cho Mô hình ngôn ngữ lớn (LLM). Chúng ta sẽ thấy tất cả điều đó kết hợp với nhau như thế nào trong giây lát.
  2. Modules: Là các cách để cấu trúc logic chương trình của bạn một cách hợp lý. Chúng dựa trên các signatures. Vì vậy, một module có thể có một hoặc nhiều signatures được nhúng bên trong nó, ngoài logic bổ sung, và nó dựa trên PyTorch và một số khía cạnh về phương pháp luận về cách nó được cấu trúc. Và bạn sẽ thấy điều đó sẽ diễn ra như thế nào trong giây lát.
  3. Tools: Chúng ta đã quen thuộc với các tools, MCP và những công cụ khác, và thực sự các tools, về cơ bản theo cách DSPy nhìn nhận chúng, chỉ là các hàm Python. Vì vậy, đó chỉ là một cách để bạn rất dễ dàng đưa các hàm Python ra cho Mô hình ngôn ngữ lớn (LLM) trong hệ sinh thái DSPy, nếu bạn muốn.
  4. Adapters: Nằm giữa signature của bạn và lời gọi Mô hình ngôn ngữ lớn (LLM) thực tế. Như chúng ta đều biết, các prompt cuối cùng chỉ là các chuỗi văn bản được gửi đến Mô hình ngôn ngữ lớn (LLM). Các signatures là một cách để bạn diễn đạt ý định của mình ở cấp độ cao hơn. Và vì vậy, các adapters là những thứ nằm giữa hai thứ đó. Vì vậy, đó là cách bạn dịch các đầu vào và đầu ra của mình sang một định dạng về cơ bản mở rộng từ signature ban đầu của bạn thành một định dạng cuối cùng là prompt được gửi đến Mô hình ngôn ngữ lớn (LLM). Và vì vậy, có một số tranh luận hoặc một số nghiên cứu về việc liệu một số mô hình nhất định có hoạt động tốt hơn với XML chẳng hạn, hoặc BAML, hoặc JSON, hoặc những định dạng khác hay không. Và vì vậy, các adapters cung cấp cho bạn một trừu tượng hóa tốt, dễ dàng để về cơ bản kết hợp và phối hợp chúng tùy ý khi bạn muốn.
  5. Optimizers: Là phần thú vị nhất. Và vì một lý do nào đó, là phần gây tranh cãi nhất của DSPy, đây là điều đầu tiên mà mọi người nghĩ đến, hoặc ít nhất khi họ nghe về DSPy, họ nghĩ đến optimizers. Chúng ta sẽ thấy một câu nói sau một phút. Nó không phải là optimizers trước tiên. Nó chỉ là một lợi ích bổ sung tốt đẹp và một khả năng tốt đẹp mà DSPy cung cấp ngoài khả năng cấu trúc chương trình của bạn với các signatures, modules và mọi thứ khác.
  6. Metrics: Được sử dụng song song với optimizers để về cơ bản xác định cách bạn đo lường thành công trong chương trình DSPy của mình. Vì vậy, các optimizers sử dụng các metrics để xác định xem nó có đang đi đúng hướng hay không, nếu bạn muốn.

Signatures: Diễn đạt ý định khai báo

Signatures tôi đã đề cập trước đây, đó là cách bạn diễn đạt ý định khai báo của mình. Nó có thể là những chuỗi siêu đơn giản. Và đây là phần kỳ lạ nhất đối với tôi ban đầu, nhưng bây giờ lại là một trong những phần mạnh mẽ nhất của nó. Hoặc nó có thể là các đối tượng dựa trên lớp phức tạp hơn. Nếu bạn đã sử dụng Pydantic, về cơ bản đó là những gì nó chạy dưới vỏ bọc.

Đây là một ví dụ về một trong các signatures dựa trên lớp. Một lần nữa, về cơ bản nó chỉ là một đối tượng Pydantic. Điều siêu thú vị về điều này là tên của các trường tự chúng hoạt động gần như là các mini-prompt như một phần của prompt đó. Và bạn sẽ thấy điều này trở nên sống động như thế nào trong giây lát. Nhưng điều cuối cùng được truyền đến mô hình từ một thứ như thế này là nó sẽ nói, OK, đầu vào của bạn sẽ là một tham số được gọi là text. Và nó dựa trên tên của tham số cụ thể đó trong lớp này. Và vì vậy những thứ này thực sự được truyền qua. Và vì vậy, rất quan trọng để có thể đặt tên các tham số của bạn theo cách trực quan để mô hình có thể nhận diện được nó. Và bạn có thể thêm một số ngữ cảnh bổ sung hoặc bất cứ điều gì bạn có vào trường mô tả ở đây.

Vì vậy, hầu hết, nếu không phải tất cả, điều này, vâng, đó là mã Python được định kiểu đúng. Nhưng nó cũng đóng vai trò gần như là một prompt cuối cùng, đưa vào mô hình. Và điều đó về cơ bản được dịch thông qua việc sử dụng các adapters. Và vì vậy, chỉ để làm nổi bật ở đây, giống như những thứ này, những thứ tối hơn một chút và in đậm. Đó là những thứ thực sự là một phần của prompt được gửi đi. Và bạn sẽ thấy DSPy hoạt động với tất cả những điều này và định dạng theo cách mà, một lần nữa, cho phép bạn chỉ lo lắng về những gì bạn muốn, lo lắng về việc xây dựng signature của mình thay vì tìm cách diễn đạt tốt nhất một điều gì đó trong prompt.

Khán giả: [Câu hỏi không rõ]

Đúng vậy, chính xác.

Khán giả: [Câu hỏi không rõ]

Vì vậy, câu hỏi dành cho những người trực tuyến là, điều gì sẽ xảy ra nếu tôi đã có một prompt tuyệt vời và đã làm tất cả công việc này? Tôi là một prompt engineer tuyệt vời. Tôi không muốn công việc của mình biến mất hay bất cứ điều gì. Vâng. Vì vậy, bạn hoàn toàn có thể bắt đầu với một prompt tùy chỉnh hoặc một cái gì đó mà bạn đã chứng minh là hoạt động thực sự tốt. Và bạn hoàn toàn đúng. Điều đó có thể được thực hiện trong chính docstring. Có một số phương pháp khác để bạn có thể chèn về cơ bản là các chỉ dẫn hệ thống hoặc thêm những thứ bổ sung vào một số phần nhất định của prompt cuối cùng. Và hoặc, tất nhiên, bạn có thể chỉ cần chèn nó vào chuỗi cuối cùng. Nó chỉ là một chuỗi được DSPy xây dựng. Vì vậy, chắc chắn, điều này không nhất thiết ngăn cản bạn. Nó không ngăn cản bạn thêm vào một super prompt mà bạn đã có, hoàn toàn có thể. Và theo ý của bạn, nó có thể đóng vai trò là một điểm khởi đầu tốt để xây dựng phần còn lại của hệ thống.

Đây là một phiên bản rút gọn của cùng một thứ, mà đối với tôi, lần đầu tiên tôi nhìn thấy điều này, điều này làm tôi bối rối. Nhưng đó chính xác là cách nó hoạt động. Bởi vì bạn về cơ bản, một lần nữa, đang giao phó việc triển khai logic hoặc bất cứ điều gì cho DSPymô hình để về cơ bản tìm ra những gì bạn muốn làm. Vì vậy, trong trường hợp này, nếu tôi muốn một sentiment classifier siêu, siêu đơn giản, về cơ bản đó là tất cả những gì bạn cần.

Thúc đẩy Thử nghiệm và Lặp lại nhanh chóng

Bạn chỉ cần nói: "OK, tôi sẽ cung cấp cho bạn văn bản làm đầu vào. Tôi muốn sắc thái cảm xúc (sentiment) dưới dạng số nguyên (integer) làm đầu ra." Bây giờ, bạn có thể muốn chỉ định thêm một số hướng dẫn, ví dụ: "Sắc thái cảm xúc của bạn, số thấp hơn có nghĩa là tiêu cực, số cao hơn có nghĩa là tích cực hơn", v.v. Nhưng nó cung cấp cho bạn một cách dễ dàng và tiện lợi để xây dựng khung (scaffold) cho những thứ này mà không phải lo lắng về việc tạo toàn bộ prompt bằng tay. Nó giống như: "OK, tôi chỉ muốn xem cái này hoạt động như thế nào." Và nếu nó hoạt động, thì tôi có thể thêm các hướng dẫn bổ sung và tạo một module từ nó, hoặc bất cứ điều gì khác. Chính những ký hiệu viết tắt (shorthand) này giúp việc thử nghiệm và lặp lại trở nên cực kỳ nhanh chóng.

Module: Lớp Trừu tượng Cơ bản trong DSPy

Module là lớp trừu tượng cơ bản cho các chương trình DSPy. Có rất nhiều module được tích hợp sẵn. Đây là tập hợp các kỹ thuật prompting, nếu bạn muốn gọi như vậy. Và bạn luôn có thể tạo module của riêng mình. Trở lại câu hỏi trước, nếu bạn có thứ gì đó mà bạn biết rằng hoạt động rất tốt, chắc chắn rồi, hãy đưa nó vào một module. Đó sẽ là giả định cơ bản, module cơ bản mà những người khác có thể xây dựng dựa trên nó. Toàn bộ DSPy được thiết kế để có tính kết hợp (composable) và có thể tối ưu hóa (optimizable). Khi bạn phân tách logic kinh doanh của mình hoặc bất cứ điều gì bạn đang cố gắng đạt được, bằng cách sử dụng các yếu tố cơ bản (primitives) khác nhau này, nó được dự định để khớp và chảy liền mạch với nhau. Chúng ta sẽ nói về optimizer trong ít phút, nhưng ít nhất đối với tôi và kinh nghiệm của nhóm tôi, việc có thể phân tách logic các thành phần khác nhau của một chương trình, nhưng về cơ bản là nhúng trực tiếp các lời gọi LLM (inlining LLM calls), đã mang lại hiệu quả cực kỳ mạnh mẽ cho chúng tôi. Và đó chỉ là một lợi ích bổ sung: cuối cùng, bởi vì chúng tôi đang nằm trong mô hình (paradigm) của DSPy, chúng tôi cũng có thể tối ưu hóa nó.

Các Module Tích hợp sẵn và Kỹ thuật Prompting

Nó đi kèm với một loạt các module tiêu chuẩn được tích hợp sẵn. Tôi không sử dụng nhiều một số module ở dưới, mặc dù chúng rất thú vị. Module cơ bản ở trên cùng là DSPy.Predict. Đó thực sự chỉ là một lời gọi LLM. Đó là một lời gọi thông thường (vanilla call). Chain of thought có lẽ không còn quá phù hợp nữa trong những ngày này vì các mô hình đã phần nào khắc phục (iron those out) những vấn đề đó. Nhưng đó là một ví dụ điển hình về các loại kỹ thuật prompting có thể được tích hợp vào một số module này. Về cơ bản, tất cả những gì nó làm là thêm một số chuỗi từ tài liệu để nói, "OK, hãy suy nghĩ từng bước" hoặc bất cứ điều gì tương tự. Điều tương tự cũng xảy ra với ReactCode Act. React về cơ bản là cách bạn hiển thị các tool cho mô hình. Nó thực hiện việc đóng gói và một số điều bên trong (under the hood) bằng cách lấy các chữ ký (signature) của bạn và đưa các hàm Python mà bạn đã cung cấp làm tool, và React là cách bạn thực hiện một lời gọi tool trong DSPy. Program with Thought khá thú vị. Nó buộc mô hình phải suy nghĩ bằng mã và sau đó trả về kết quả. Nó đi kèm với trình thông dịch Python tích hợp sẵn, nhưng bạn có thể cung cấp một trình thông dịch tùy chỉnh hoặc một loại khung kiểm thử (custom harness) tùy chỉnh nếu bạn muốn. Tôi chưa thử nghiệm nhiều với cái này, nhưng nó rất thú vị. Nếu bạn có một vấn đề hoặc quy trình làm việc có tính kỹ thuật cao, nơi bạn muốn mô hình đưa ra lý luận trong mã ở một số phần nhất định trong quy trình của bạn, đó là một cách thực sự dễ dàng để thực hiện. Và sau đó, một số module khác về cơ bản chỉ là các phương pháp khác nhau để so sánh đầu ra hoặc chạy các tác vụ song song.

Ví dụ Module: Xác thực Nhập liệu Thời gian

Đây là một ví dụ trông như thế nào. Một lần nữa, nó khá đơn giản. Cuối cùng, nó là một lớp Python (Python class). Và vì vậy bạn thực hiện một số khởi tạo ở phía trên. Trong trường hợp này, bạn đang thấy chữ ký viết tắt (shorthand signature) ở đó. Vì vậy, module này, để cung cấp cho bạn một số ngữ cảnh, là một đoạn trích từ một trong các tệp Python nằm trong kho lưu trữ, về cơ bản là nhận một loạt các mục nhập thời gian (time entries) và đảm bảo rằng chúng tuân thủ các tiêu chuẩn nhất định, đảm bảo rằng mọi thứ được viết hoa đúng cách hoặc có dấu chấm ở cuối câu hoặc bất cứ điều gì khác. Đó là từ một trường hợp sử dụng thực tế của khách hàng, nơi họ có hàng trăm nghìn mục nhập thời gian và họ cần đảm bảo rằng tất cả chúng đều tuân thủ cùng một định dạng. Đây là một cách để làm điều đó rất thanh lịch, ít nhất theo ý kiến của tôi, là lấy từ trên xuống, bạn có thể định nghĩa chữ ký. Nó đang thêm một số hướng dẫn bổ sung đã được định nghĩa ở nơi khác. Và sau đó nói rằng đối với module này, lời gọi change tense sẽ chỉ là một lời gọi predict thông thường. Và sau đó khi bạn thực sự gọi module, bạn sẽ vào hàm forward, nơi bạn về cơ bản có thể xen kẽ lời gọi LLM, đó sẽ là lời gọi đầu tiên, và sau đó thực hiện một số loại logic nghiệp vụ được mã hóa cứng bên dưới nó.

Công cụ trong DSPy

Tool, như tôi đã đề cập trước đây, chỉ là các hàm Python thông thường. Đó là giao diện tool của DSPy. Vì vậy, bên trong (under the hood), DSPy sử dụng light LLM. Và vì vậy cần có một loại kết nối giữa hai bên, nhưng về cơ bản, bất kỳ loại tool nào bạn sử dụng ở nơi khác, bạn cũng có thể sử dụng trong DSPy. Và điều này có lẽ hiển nhiên đối với hầu hết các bạn, nhưng đây chỉ là một ví dụ. Bạn có hai hàm: get weathersearch web, bạn bao gồm chúng với một chữ ký. Vì vậy, trong trường hợp này, tôi nói rằng chữ ký là: "Tôi sẽ đưa cho bạn một câu hỏi. Hãy cho tôi một câu trả lời." Tôi thậm chí không chỉ định các kiểu dữ liệu. Mã sẽ tự suy luận (code in fur) điều đó có nghĩa là gì. Tôi đang cung cấp cho nó các tool get weathersearch web. Và tôi nói, "OK, hãy làm việc của bạn, nhưng chỉ thực hiện năm vòng." Để nó không bị lạc vào một cái gì đó điên rồ. Và sau đó một lời gọi ở đây chỉ đơn giản là gọi agent React mà tôi đã tạo ở trên với câu hỏi: "Thời tiết ở Tokyo như thế nào?" Chúng ta sẽ thấy một ví dụ về điều này trong phần mã, nhưng về cơ bản, điều này sẽ cung cấp cho mô hình prompt và các tool và để nó tự làm việc của mình.

Bộ điều hợp (Adapters) để Định dạng Prompt

Vì vậy, adapter mà tôi đã đề cập trước đó một chút, về cơ bản chúng là các bộ định dạng prompt (prompt formatters), nếu bạn muốn gọi như vậy. Mô tả từ tài liệu có lẽ diễn tả tốt nhất: Nó nhận chữ ký của bạn, các đầu vào, các thuộc tính khác và chuyển đổi chúng thành một loại định dạng tin nhắn mà bạn đã chỉ định hoặc mà adapter đã chỉ định. Và vì vậy, làm ví dụ, JSON adapter, lấy một đối tượng Pydantic mà chúng ta đã định nghĩa trước đó, đây là prompt thực tế được gửi đến LLM. Và vì vậy bạn có thể thấy các trường đầu vào. Vì vậy, điều này sẽ được định nghĩa là: "OK, clinical note type là chuỗi (string), patient info là một đối tượng chi tiết bệnh nhân (patient details object), sẽ được định nghĩa ở nơi khác." Và sau đó đây là định nghĩa của patient info. Về cơ bản, JSON dump của đối tượng Pydantic đó. Tốt. Vậy có một adapter cơ bản hoặc mặc định không? Đúng vậy. Điều đó tốt cho hầu hết các trường hợp. Đúng vậy. Câu hỏi là liệu có một adapter cơ bản không, và liệu đây có phải là một ví dụ về nơi bạn muốn làm điều gì đó cụ thể không? Câu trả lời là có. Có một chàng trai, Prashant, mà tôi có tài khoản Twitter của anh ấy ở cuối bài thuyết trình này, nhưng anh ấy rất tuyệt. Anh ấy đã thực hiện một số thử nghiệm so sánh JSON adapter với BAML adapter. Và bạn có thể thấy một cách trực quan, ngay cả đối với chúng ta là con người, cách định dạng này trực quan hơn một chút. Nó có lẽ cũng hiệu quả về mã thông báo (token) hơn. Chỉ cần xem xét, nếu bạn nhìn vào JSON lộn xộn ở đây so với BAML được định dạng tốt hơn một chút ở đây, nó thực sự có thể cải thiện hiệu suất từ 5 đến 10% tùy thuộc vào trường hợp sử dụng của bạn. Vì vậy, đó là một ví dụ điển hình về cách bạn có thể định dạng mọi thứ khác nhau. Phần còn lại của chương trình sẽ không thay đổi gì cả. Bạn chỉ cần chỉ định BAML adapter, và nó hoàn toàn thay đổi cách thông tin được trình bày bên trong (under the hood) cho LLM.

Hỗ trợ Đa phương tiện (Multimodality)

Đa phương tiện (Multimodality), ý tôi là, điều này rõ ràng là ở cấp độ mô hình nhiều hơn, nhưng DSPy hỗ trợ nhiều phương thức (modalities) theo mặc định. Vì vậy, hình ảnh, âm thanh, một số thứ khác. Và cùng một kiểu, bạn chỉ cần đưa nó vào như một phần của chữ ký của bạn, và sau đó bạn có thể nhận được một đầu ra rất đẹp và rõ ràng. Điều này cho phép bạn làm việc với chúng rất, rất dễ dàng, rất nhanh chóng. Và đối với những người tham gia tinh mắt (eagle-eyed participants), bạn có thể thấy dòng đầu tiên ở trên là attachments. Đó có lẽ là một thư viện ít được biết đến hơn. Một chàng trai khác trên Twitter cũng rất tuyệt. Maxine, tôi nghĩ vậy. Anh ấy đã tạo ra thư viện này về cơ bản là một công cụ bao quát mọi thứ (catch-all) để làm việc với các loại tệp khác nhau và chuyển đổi chúng thành một định dạng siêu dễ sử dụng với các LLM. Anh ấy cũng là một fan hâm mộ lớn của DSPy, vì vậy anh ấy đã tạo một adapter cụ thể cho thư viện này. Nhưng đó là tất cả những gì cần thiết để đưa hình ảnh, PDF, hoặc bất cứ điều gì vào. Bạn sẽ thấy một số ví dụ về điều đó. Nó chỉ làm cho cuộc sống của tôi, ít nhất là của tôi, trở nên siêu, siêu dễ dàng. Đây là một ví dụ khác về cùng một loại điều. Đây là một tệp PDF của Form 4, một biểu mẫu công khai của SEC từ Embidia. Ở trên cùng, tôi chỉ cung cấp cho nó liên kết. Tôi nói: "OK, attachments, hãy làm việc của bạn, tải nó xuống, tạo hình ảnh, bất cứ điều gì bạn sẽ làm. Tôi không cần phải lo lắng về nó. Tôi sẽ quan tâm đến nó." Đây là RAG đơn giản (poor man's rag). Nhưng về cơ bản, "OK, tôi muốn thực hiện RAG trên tài liệu này. Tôi sẽ cung cấp cho bạn một câu hỏi. Tôi sẽ cung cấp cho bạn tài liệu. Và tôi muốn câu trả lời." Và bạn có thể thấy nó đơn giản như thế nào, chỉ cần đưa tài liệu vào. "Có bao nhiêu cổ phiếu đã được bán?" Điều thú vị là ở đây, tôi không chắc có dễ nhìn thấy không, nhưng bạn thực sự có hai giao dịch ở đây. Vì vậy, nó sẽ phải thực hiện một số phép toán có khả năng là bên trong (under the hood). Và bạn có thể thấy ở đây, suy nghĩ và cơn lốc cũ. Cứ tiếp tục đi. Hết rồi. Trong bước RAG, nó có tạo ra một vector store nào đó hay tạo embedding để tìm kiếm không? Ý bạn là có nhiều điều đang diễn ra ở phía sau không? Đây là RAG đơn giản (poor man's rag). Tôi nên làm rõ. Đây chỉ đơn giản là kéo các hình ảnh tài liệu. Và tôi nghĩ attachments sẽ thực hiện một số OCR cơ bản bên trong (under the hood). Nhưng nó không làm gì khác ngoài điều đó. Hết rồi. Tất cả những gì chúng tôi đang đưa vào đây. Đối tượng tài liệu thực tế đang được đưa vào chỉ đơn giản là văn bản đã được OCR. Hình ảnh là mô hình sẽ làm phần còn lại.

Bộ tối ưu hóa (Optimizers) trong DSPy

Vì vậy, optimizer. Hãy xem chúng ta đang làm như thế nào. OK. Optimizer là một khái niệm cực kỳ mạnh mẽ, cực kỳ thú vị. Có một số nghiên cứu cho rằng nó hiệu quả ngang bằng, nếu không muốn nói là hiệu quả hơn trong một số tình huống nhất định, so với tinh chỉnh (fine tuning) đối với một số mô hình và tình huống nhất định. Có tất cả các nghiên cứu về học trong ngữ cảnh (in context learning) và những thứ tương tự. Và vì vậy, dù bạn muốn tinh chỉnh và làm tất cả những điều đó, không có gì ngăn cản bạn. Nhưng tôi khuyên bạn nên thử cái này trước để xem bạn có thể đạt được bao xa mà không cần phải thiết lập một đống cơ sở hạ tầng và trải qua tất cả những điều đó. Hãy xem optimizer hoạt động như thế nào. Nhưng về cơ bản, điều nó cho phép bạn làm là DSPy cung cấp cho bạn các yếu tố cơ bản (primitives) mà bạn cần và tổ chức mà bạn cần để có thể đo lường và sau đó cải thiện hiệu suất đó một cách định lượng. Và tôi đã đề cập đến khả năng chuyển giao (transferability) trước đây. Khả năng chuyển giao này được kích hoạt một cách hợp lý thông qua việc sử dụng optimizer. Bởi vì nếu bạn có thể đạt được, OK, tôi muốn có một tác vụ phân loại hoạt động rất tốt với 401. Nhưng có thể nó hơi tốn kém vì nó chạy một triệu lần một ngày. Tôi có thể thử nó với 401 nano không? OK, có thể nó ở mức 70%, bất cứ điều gì. Nhưng tôi chạy optimizer trên 401 nano. Và tôi có thể đưa hiệu suất trở lại có thể là 87%. Có thể điều đó là OK cho trường hợp sử dụng của tôi. Nhưng bây giờ tôi đã giảm hồ sơ chi phí (cost profile) của mình xuống nhiều bậc độ lớn (orders of magnitude). Và chính optimizer cho phép bạn thực hiện loại khả năng chuyển giao mô hình và trường hợp sử dụng đó, nếu bạn muốn gọi như vậy. Nhưng thực sự, tất cả những gì nó làm cuối cùng, bên trong (under the hood), là lặp đi lặp lại điều chỉnh (tweak) prompt, chuỗi đó bên trong (under the hood). Và bởi vì bạn đã xây dựng chương trình của mình bằng cách sử dụng các module khác nhau, DSPy sẽ xử lý tất cả những điều đó cho bạn bên trong (under the hood). Vì vậy, nếu bạn tạo một chương trình với nhiều module và bạn đang tối ưu hóa tất cả những điều đó, bản thân DSPy sẽ tối ưu hóa các thành phần khác nhau để cải thiện hiệu suất đầu vào và đầu ra. Và chúng ta sẽ lấy lời từ chính người đàn ông đó, Omar: "DSPy không phải là một optimizer. Tôi đã nói điều này nhiều lần. Nó chỉ là một tập hợp các trừu tượng lập trình (programming abstractions) hoặc một cách để lập trình. Bạn chỉ tình cờ có thể tối ưu hóa nó."

Tầm quan trọng của Trừu tượng hóa Lập trình và Mô hình tối ưu hóa

Giá trị mà tôi và nhóm của tôi nhận được chủ yếu là nhờ các programming abstraction (trừu tượng hóa lập trình). Đó là một lợi ích đáng kinh ngạc mà bạn cũng có thể chọn tối ưu hóa sau này. Tôi đã nghe Dwarqeshen và Carpathi nói chuyện gần đây, và điều này hoàn toàn phù hợp với những gì tôi đã chuẩn bị cho buổi nói chuyện này. Tôi đã nghĩ về các optimizer (bộ tối ưu hóa). Ai đó thông minh hơn tôi có thể nói chính xác hơn, nhưng tôi nghĩ điều này có lý bởi vì về cơ bản, ông ấy nói rằng việc sử dụng Mô hình ngôn ngữ lớn làm judge (trọng tài) có thể là một điều không tốt. Bởi vì mô hình đang bị đánh giá có thể tìm thấy các adversarial example (ví dụ đối kháng) và làm suy giảm hiệu suất hoặc về cơ bản tạo ra một tình huống mà judge không chấm điểm chính xác. Ông ấy nói rằng mô hình sẽ tìm thấy những kẽ hở nhỏ, những điểm không đáng kể trong các ngóc ngách của mô hình khổng lồ và tìm cách gian lận. Về cơ bản, điều này có nghĩa là Mô hình ngôn ngữ lớn làm judge chỉ có thể đi xa đến một mức nào đó cho đến khi mô hình khác tìm thấy các adversarial example đó.

Nếu bạn đảo ngược và lật ngược điều đó, thì chính thuộc tính này mà các optimizer cho DSPy đang tận dụng để tìm ra các ngóc ngách trong mô hình, dù đó là mô hình lớn hơn hay nhỏ hơn, để cải thiện hiệu suất so với tập dữ liệu của bạn. Đó là những gì optimizer đang làm: nó tìm ra những ngóc ngách trong mô hình để tối ưu hóa và cải thiện hiệu suất đó.

Một quy trình điển hình – tôi sẽ không dành quá nhiều thời gian cho phần này – nhưng khá logic: xây dựng một chương trình bằng cách phân tách logic của bạn thành các module (mô-đun). Bạn sử dụng các metric (chỉ số) để xác định các đường nét cơ bản về cách chương trình hoạt động. Và bạn tối ưu hóa tất cả những điều đó để có được kết quả cuối cùng của mình.

Tối ưu hóa Lời nhắc và các Chỉ số

Trong một buổi nói chuyện khác, Chris Potts, đã đưa ra quan điểm mà tôi đã đề cập trước đây: các optimizer sánh ngang hoặc vượt trội so với hiệu suất của các phương pháp fine-tuning (tinh chỉnh) như GRPO. Vì vậy, khá ấn tượng. Tôi nghĩ đây là một lĩnh vực nghiên cứu năng động. Những người thông minh hơn tôi, như Omar và Chris, đang dẫn đầu trong lĩnh vực này. Nhưng điểm mấu chốt là tôi nghĩ prompt optimization (tối ưu hóa lời nhắc) là một lĩnh vực khá thú vị và đáng để khám phá.

Cuối cùng là các metric. Một lần nữa, đây là những khối xây dựng cho phép bạn xác định thành công trông như thế nào đối với optimizer. Đây là những gì nó đang sử dụng. Và bạn có thể có nhiều metric. Chúng ta sẽ thấy các ví dụ về điều này, một lần nữa, ở cấp độ cao, chương trình của bạn hoạt động trên input (đầu vào) hoặc output (đầu ra). Optimizer sẽ sử dụng các metric để hiểu, OK, chỉnh sửa cuối cùng của tôi trong các prompt (lời nhắc) có cải thiện hiệu suất không? Hay nó làm giảm hiệu suất? Và cách bạn định nghĩa các metric của mình cung cấp phản hồi trực tiếp đó cho optimizer để hoạt động.

Đây là một ví dụ khác, một ví dụ siêu đơn giản từ ví dụ về time entry mà tôi đã đề cập trước đây. Vì vậy, các metric có thể khá nghiêm ngặt về việc liệu điều này có bằng một hay một loại kiểm tra chất lượng nào đó, hoặc chủ quan hơn một chút. Chúng tôi đang sử dụng Mô hình ngôn ngữ lớn làm judge để nói rằng chuỗi được tạo ra này có tuân thủ các tiêu chí khác nhau hay không, bất kể đó là gì, nhưng bản thân điều đó có thể là một metric.

Các khối cơ bản của DSPy và Cộng đồng

Tất cả những điều này là một cách dài dòng để nói rằng, theo ý kiến của tôi, đây có lẽ là điều khác biệt nhất, tất cả những gì bạn cần để xây dựng các workflow (quy trình làm việc) phức tạp tùy ý, data processing pipeline (đường ống xử lý dữ liệu), business logic (logic nghiệp vụ), bất kể đó là gì, các cách khác nhau để làm việc với Mô hình ngôn ngữ lớn. Nếu không có gì khác, DSPy cung cấp cho bạn các primitive (khối cơ bản) mà bạn cần để xây dựng các hệ thống modular (mô-đun hóa) và composable (có thể kết hợp) này.

Nếu bạn quan tâm đến một số người trên mạng, có rất nhiều người khác. Cũng có một cộng đồng Discord. Nhưng thường thì những người này luôn cập nhật những điều mới nhất và tốt nhất. Vì vậy, tôi khuyên bạn nên theo dõi họ. Bạn không cần phải theo dõi tôi, tôi không làm nhiều. Nhưng những người khác ở đó thực sự khá tốt.

Demo Code: Thiết lập Môi trường và Mô hình

OK, phần thú vị, chúng ta sẽ đi vào một số đoạn mã. Nếu bạn chưa có cơ hội, bây giờ là cơ hội cuối cùng để lấy repo. Nhưng tôi sẽ chỉ đi qua một vài ví dụ khác nhau về những gì chúng ta đã nói. OK, tôi sẽ thiết lập Phoenix, đến từ Arise, về cơ bản là một observability platform (nền tảng quan sát). Tôi mới làm điều này hôm nay, vì vậy tôi không biết nó có hoạt động hay không. Nhưng chúng ta sẽ xem. Chúng ta sẽ thử. Nhưng về cơ bản, điều này cho phép bạn có một loạt các tính năng observabilitytracing (theo dõi) cho tất cả các cuộc gọi đang diễn ra ngầm. Chúng ta sẽ xem liệu điều này có hoạt động không. Chúng ta sẽ cho nó thêm năm giây. Nhưng tôi nghĩ nó sẽ tự động làm tất cả những thứ này cho tôi. Vâng, hãy xem. Vâng, được rồi, có gì đó đang diễn ra. OK, tuyệt vời.

Vì vậy, tôi sẽ chạy qua notebook, đây là một tập hợp các use case (trường hợp sử dụng) khác nhau, về cơ bản là áp dụng rất nhiều điều chúng ta vừa thấy vào thực tế. Hãy thoải mái đặt câu hỏi, bất cứ điều gì. Chúng ta sẽ bắt đầu với notebook này. Có một vài chương trình Python đúng nghĩa hơn mà chúng ta sẽ xem xét sau đó. Nhưng thực sự, mục đích là một đánh giá nhanh chóng về các cách khác nhau mà DSPy đã hữu ích cho tôi và những người khác.

Tôi sẽ tải tệp .env. Thông thường, tôi sẽ có một loại config object (đối tượng cấu hình) như thế này, nơi tôi có thể rất dễ dàng sử dụng chúng sau này. Vì vậy, nếu tôi có một vấn đề rất khó hoặc một workload (khối lượng công việc) mà tôi biết sẽ cần sức mạnh của một mô hình mạnh như GPT-5 hoặc thứ gì đó tương tự, tôi sẽ định nghĩa nhiều Mô hình ngôn ngữ lớn. Vì vậy, một sẽ là 4.1, một sẽ là 5. Có thể tôi sẽ làm một 4.1 nano, Gemini 2.5 flash, những thứ như vậy. Và sau đó tôi có thể kết hợp hoặc xen kẽ chúng, tùy thuộc vào những gì tôi nghĩ hoặc những gì tôi khá chắc chắn workload sẽ là. Và bạn sẽ thấy điều đó diễn ra như thế nào trong các classification (phân loại) và những thứ khác. Tôi sẽ kéo thêm một vài thứ khác vào đây. Tôi đang sử dụng OpenRouter cho việc này. Vì vậy, nếu bạn có API key của OpenRouter, tôi khuyên bạn nên cắm nó vào.

Bây giờ tôi có ba Mô hình ngôn ngữ lớn khác nhau mà tôi có thể làm việc cùng: Claude, Gemini, và 4.1 Mini. Và sau đó tôi sẽ hỏi, về cơ bản, đối với mỗi Mô hình ngôn ngữ lớn, ai là người giỏi nhất giữa Google, AnthropicOpenAI? Tất cả chúng đều hơi dè dặt. Chúng nói: "chủ quan, chủ quan, không xác định". Được rồi, tuyệt vời. Điều đó không hữu ích lắm. Nhưng vì DSPy hoạt động trên Pydantic, tôi có thể định nghĩa câu trả lời là một literal (giá trị cố định). Vì vậy, về cơ bản, tôi buộc nó chỉ cung cấp cho tôi ba tùy chọn đó. Và sau đó tôi có thể đi qua từng tùy chọn đó. Và bạn có thể thấy mỗi Mô hình ngôn ngữ lớn tất nhiên đều chọn tổ chức của riêng mình. Lý do những câu trả lời đó quay lại rất nhanh là vì DSPy có caching (bộ nhớ đệm) tự động. Vì vậy, miễn là không có gì thay đổi về định nghĩa signature (chữ ký) của bạn, hoặc về cơ bản, nếu không có gì thay đổi, điều này cực kỳ hữu ích cho việc kiểm thử. Nó sẽ chỉ tải từ cache. Tôi đã chạy điều này trước đó, đó là lý do tại sao chúng quay lại rất nhanh. Nhưng đó là một phần cực kỳ hữu ích khác ở đây.

Hãy xem. OK. Đảm bảo chúng ta đang hoạt động. Nếu tôi thay đổi điều này thành "hello" với một khoảng trắng, bạn có thể thấy chúng ta đang thực hiện một cuộc gọi trực tiếp. OK, tuyệt vời, chúng ta vẫn đang hoạt động.

Demo: Bộ phân loại Cảm xúc và Theo dõi Sử dụng

Một sentiment classifier (bộ phân loại cảm xúc) siêu đơn giản. Rõ ràng, điều này có thể được xây dựng thành một thứ phức tạp tùy ý. Tôi sẽ làm cho nó lớn hơn một chút. Nhưng tôi về cơ bản đang cung cấp cho nó văn bản, cảm xúc mà bạn đã thấy trước đó. Và tôi đang thêm specification (đặc tả) bổ sung đó để nói, OK, thấp hơn là tiêu cực hơn. Cao hơn là tích cực hơn. Tôi sẽ định nghĩa đó là signature của mình. Tôi sẽ truyền điều này vào một predict object (đối tượng dự đoán) siêu đơn giản. Và sau đó tôi sẽ nói, OK, "khách sạn này tệ quá". OK, nó có lẽ khá tiêu cực.

Bây giờ, nếu tôi thay đổi thành "Tôi đang cảm thấy khá vui", thì kết quả là 8. Và điều này có vẻ không ấn tượng lắm, và thực sự không phải vậy. Nhưng phần quan trọng ở đây là nó chỉ thể hiện việc sử dụng shorthand signature (chữ ký viết tắt). Vì vậy, tôi có chuỗi, tôi là số nguyên. Tôi truyền vào các custom instruction (hướng dẫn tùy chỉnh), sẽ nằm trong docstring nếu tôi sử dụng phương pháp dựa trên class (lớp).

Phần thú vị hoặc hữu ích khác về DSPy đi kèm với một loạt thông tin usage (sử dụng) được tích hợp sẵn. Vì nó được cache, nó sẽ là một đối tượng rỗng. Nhưng khi tôi thay đổi nó, bạn có thể thấy rằng tôi đang sử dụng Azure ngay bây giờ. Nhưng đối với mỗi cuộc gọi, bạn sẽ nhận được một bảng phân tích đẹp mắt này. Tôi nghĩ nó từ LiteLLM. Nhưng nó cho phép bạn rất dễ dàng theo dõi usage của mình, token usage (mức sử dụng mã thông báo), v.v., cho observability, optimization và mọi thứ như vậy. Chỉ là những mẹo nhỏ hay ho là một phần của nó ở đây và đó.

Demo: Phân tích Tài liệu với DSPy (Ví dụ Form 4)

Tôi đã thấy ví dụ trước đó trong các slide, nhưng tôi sẽ kéo form 4 đó từ online. Tôi sẽ tạo các doc object này bằng cách sử dụng attachments. Bạn có thể thấy một số thứ này nó đã làm ngầm. Vì vậy, nó đã kéo PDF plumber và tạo markdown từ đó, kéo các image (hình ảnh), v.v. Một lần nữa, tôi không phải lo lắng về tất cả những điều đó. Attachments làm cho điều đó siêu dễ dàng. Tôi sẽ cho bạn thấy những gì chúng ta đang làm việc ở đây. Trong trường hợp này, chúng ta có form 4. Và sau đó tôi sẽ làm cái poor man's react mà tôi đã đề cập trước đó. OK, tuyệt vời. Tổng cộng bao nhiêu cổ phiếu đã được bán? Nó sẽ đi qua toàn bộ chain of thought (chuỗi suy nghĩ) đó và trả về phản hồi. Tất cả đều tốt.

Nhưng sức mạnh theo tôi của DSPy là bạn có thể có các data structure (cấu trúc dữ liệu) phức tạp tùy ý này. Điều đó khá rõ ràng vì nó sử dụng Pydantic và mọi thứ khác. Nhưng bạn có thể sáng tạo một chút với nó. Vì vậy, trong trường hợp này, tôi sẽ nói, OK, một loại document analyzer signature (chữ ký phân tích tài liệu) khác. Tôi sẽ chỉ cung cấp cho nó một tài liệu. Và sau đó tôi sẽ giao cho mô hình định nghĩa cấu trúc của những gì nó nghĩ là quan trọng nhất từ tài liệu. Vì vậy, trong trường hợp này, tôi đang định nghĩa một dictionary object (đối tượng từ điển). Và vì vậy nó sẽ, hy vọng, trả về cho tôi một loạt các cặp key-value (khóa-giá trị) mô tả thông tin quan trọng trong tài liệu một cách có cấu trúc.

Và bạn có thể thấy ở đây, một lần nữa, điều này có lẽ được cache. Nhưng tôi đã truyền vào, tôi đã làm tất cả trên một dòng trong trường hợp này. Nhưng tôi đang nói, tôi muốn thực hiện chain of thought bằng cách sử dụng document analyzer signature. Và tôi sẽ truyền vào input field (trường đầu vào), đó chỉ là tài liệu ở đây. Tôi sẽ truyền vào tài liệu mà tôi đã có trước đó. Và bạn có thể thấy ở đây, nó đã kéo ra một loạt thông tin tuyệt vời một cách siêu có cấu trúc. Và tôi không thực sự phải nghĩ về nó. Tôi chỉ giao phó tất cả điều này cho mô hình, cho DSPy về cách làm điều này.

Bây giờ, tất nhiên, bạn có thể làm ngược lại và nói, OK, tôi có một business use case (trường hợp sử dụng nghiệp vụ) rất cụ thể. Tôi có một cái gì đó cụ thể về định dạng hoặc nội dung mà tôi muốn lấy ra từ tài liệu. Tôi định nghĩa điều đó chỉ là các Pydantic class điển hình của bạn. Vì vậy, trong trường hợp này, tôi muốn kéo ra nếu có nhiều transaction (giao dịch), bản thân schema (lược đồ), thông tin quan trọng như filing date (ngày nộp). Tôi sẽ định nghĩa document analyzer schema signature. Một lần nữa, input field siêu đơn giản, đó chỉ là tài liệu, được parse (phân tích) bởi attachments, cung cấp cho tôi văn bản và image. Và sau đó tôi đang truyền vào document schema parameter, có document schema type, được định nghĩa ở trên.

Và đây thực sự là những gì bạn sẽ truyền vào structured output (đầu ra có cấu trúc), nhưng chỉ làm theo cách của DSPy, nơi nó sẽ cung cấp cho bạn về cơ bản output ở định dạng cụ thể đó. Vì vậy, bạn có thể thấy, kéo ra mọi thứ rất gọn gàng: filing date, form type, bản thân các transaction, và sau đó là câu trả lời cuối cùng. Và điều tuyệt vời là nó hiển thị nó theo cách mà bạn có thể sử dụng notation đó để bạn có thể truy cập rất nhanh các đối tượng kết quả.

Kiểm tra Lịch sử DSPy

Khi xem xét các adapter, tôi sẽ sử dụng một mẹo nhỏ khác từ DSPy, đó là inspect history. Vì vậy, đối với những người muốn biết những gì đang diễn ra ngầm, inspect history sẽ cung cấp cho bạn một raw dump (kết xuất thô) về những gì thực sự đang diễn ra. Vì vậy, bạn có thể thấy ở đây, system message (thông báo hệ thống) được xây dựng ngầm là tất cả những điều này. Bạn có thể thấy input field là tài liệu, output fieldreasoning (lý do) trong schema. Nó sẽ truyền những thứ này vào.

Trích xuất Nội dung và Định dạng Phản hồi

Và sau đó bạn có thể thấy ở đây nội dung tài liệu thực tế đã được trích xuất và đưa vào prompt. Với một số metadata, tất cả điều này được tạo ra bởi attachments. Sau đó, bạn nhận được phản hồi tuân theo một định dạng cụ thể. Vì vậy, bạn có thể thấy các trường khác nhau ở đây. Và đây là phản hồi tương đối tùy ý, về cơ bản là định dạng cho các tên, sau đó được DSPy phân tích cú pháp và trả lại cho bạn với tư cách là người dùng. Vì vậy, tôi có thể gọi response.documentSkema và nhận được kết quả thực tế. Để cho bạn thấy BAML adapter trông như thế nào, về cơ bản chúng ta có thể thực hiện hai lời gọi khác nhau. Đây là một ví dụ từ người bạn Push-Off của tôi trực tuyến một lần nữa. Vì vậy, điều chúng tôi làm ở đây là định nghĩa một Pydantic model, một model siêu đơn giản, patient address và sau đó là patient details. Patient details có đối tượng patient address bên trong nó. Và sau đó chúng ta sẽ nói rằng chúng ta sẽ tạo một DSPy signature siêu đơn giản để nói, lấy một clinical note, là một chuỗi. Patient info là kiểu đầu ra. Vì vậy, tôi sẽ chạy điều này theo hai cách khác nhau, lần đầu tiên với LLM thông minh mà tôi đã đề cập trước đây và chỉ sử dụng adapter tích hợp. Vì vậy, tôi không chỉ định bất cứ điều gì ở đó. Và sau đó lần thứ hai, chúng ta sẽ sử dụng BAML adapter, được định nghĩa ở đó.

So sánh LLMAdapter

Vì vậy, tôi đoán có một vài điều đang diễn ra ở đây. Một là khả năng sử dụng context của Python, đó là dòng bắt đầu bằng with, cho phép bạn về cơ bản thoát khỏi định nghĩa LLM toàn cục và sử dụng một LLM cụ thể chỉ cho lời gọi đó. Vì vậy, bạn có thể thấy, trong trường hợp này, tôi đang sử dụng cùng một LLM, nhưng nếu tôi muốn thay đổi điều này thành LLM của Anthropic hoặc gì đó, tôi nghĩ điều đó sẽ hoạt động. Nhưng về cơ bản, điều đó đang làm chỉ là chuyển lời gọi đó sang LLM khác mà bạn đang định nghĩa cho lời gọi cụ thể đó và có gì đó đã xảy ra. Và tôi đang dùng VPN. Vì vậy, hãy tắt nó đi. Xin lỗi, Alex Partners. OK, tuyệt vời.

Vì vậy, chúng tôi đã có hai lời gọi riêng biệt. Một là đến LLM thông minh, mà tôi nghĩ là 401. Lời gọi kia là đến Anthropic. Mọi thứ khác đều giống hệt nhau, notes giống hệt nhau, v.v. Chúng tôi nhận được cùng một đầu ra chính xác. Điều đó thật tuyệt. Nhưng điều tôi muốn chỉ ra ở đây là bản thân các adapter. Vì vậy, trong trường hợp này, tôi đang thực hiện inspect history = 2. Vì vậy, tôi sẽ nhận được cả hai lời gọi cuối cùng. Và chúng ta sẽ thấy các prompt sẽ khác nhau như thế nào. Và bạn có thể thấy ở đây, cái đầu tiên, đây là JSON schema tích hợp, chuỗi JSON dài điên rồ này. Vâng, các Mô hình ngôn ngữ lớn đủ tốt để xử lý điều đó. Nhưng có lẽ không phải cho những cái siêu phức tạp. Và sau đó bạn thấy ở đây, đối với cái thứ hai, bạn sử dụng ký hiệu BAML, mà như chúng ta đã thấy trong các slide, dễ hiểu hơn một chút. Và trên các trường hợp sử dụng siêu phức tạp, bạn thực sự có thể có một sự cải thiện đáng kể có thể đo lường được.

Ví dụ Đa phương thức và Module của DSPy

Ví dụ đa phương thức, tương tự như trước đây. Tôi sẽ kéo hình ảnh vào. Hãy xem chúng ta đang làm việc với cái gì. OK, tuyệt vời. Chúng ta đang nhìn vào các biển báo đường phố khác nhau này. Và tôi sẽ hỏi một câu hỏi siêu đơn giản. "Bây giờ là mấy giờ? Tôi có thể đỗ xe ở đây không? Và khi nào tôi nên rời đi?" Và bạn có thể thấy tôi chỉ đang truyền vào, một lần nữa, cú pháp viết tắt siêu đơn giản để định nghĩa một signature, sau đó tôi nhận được một giá trị Boolean trong trường hợp này trong một chuỗi về thời gian tôi có thể rời đi.

Vì vậy, bản thân các module, một lần nữa, khá đơn giản. Bạn chỉ cần gói tất cả điều này trong một lớp. Câu hỏi? Ồ, câu hỏi hay. Vâng, khi bạn làm vậy, vâng. Vì vậy, đối với những người trực tuyến, câu hỏi là, liệu nó luôn trả về reasoning theo mặc định không? Khi bạn gọi dspy.chain_of_thought như một phần của module nơi nó được tích hợp, nó sẽ tự động thêm reasoning vào phản hồi của bạn. Bạn không định nghĩa điều đó. Đó là một câu hỏi tuyệt vời. Nó không được định nghĩa trong signature như bạn có thể thấy ở đây. Nhưng nó sẽ thêm vào và hiển thị cho bạn đến mức bạn muốn giữ lại vì bất kỳ lý do gì. Vì vậy, nếu tôi thay đổi điều này thành predict, bạn sẽ không nhận được phản hồi tương tự. Bạn chỉ đơn giản là nhận được phần đó.

Vì vậy, đó thực sự là một sự chuyển tiếp tốt đến các module. Vì vậy, các module về cơ bản chỉ là gói tất cả những điều đó vào một loại logic có thể tái tạo. Và vì vậy, chúng tôi chỉ đơn giản là cung cấp signature ở đây. Chúng tôi nói self-duperdict. Trong trường hợp này, đó chỉ là một minh họa về cách nó được sử dụng như một lớp. Vì vậy, tôi sẽ chỉ thêm định danh module này và một loại bộ đếm. Nhưng đây có thể là bất kỳ loại logic nghiệp vụ tùy ý nào, hoặc luồng điều khiển, hoặc bất kỳ hành động cơ sở dữ liệu nào. Vì vậy, bất kể nó là gì, khi lớp image analyzer này được gọi, hàm này sẽ chạy. Và sau đó khi bạn thực sự gọi nó, đây là lúc nó thực sự sẽ chạy logic cốt lõi. Và bạn có thể thấy tôi chỉ đang truyền vào. Vì vậy, tôi đang khởi tạo nó. Analyzer của AI123. Và sau đó tôi sẽ gọi nó, tuyệt vời. Tôi gọi điều đó. Và bạn có thể thấy bộ đếm tăng lên mỗi khi tôi thực hiện lời gọi. Vì vậy, một ví dụ siêu đơn giản. Chúng ta không có nhiều thời gian. Nhưng tôi sẽ cho bạn thấy một số module khác và cách nó hoạt động.

Tool Calling với React

Về tool calling, khá đơn giản, tôi sẽ định nghĩa hai hàm khác nhau, perplexity searchget URL content, tạo một bio agent module. Vì vậy, điều này sẽ định nghĩa Gemini25Mô hình ngôn ngữ lớn của module cụ thể này. Nó sẽ tạo một đối tượng answer generator, đó là một lời gọi react. Vì vậy, về cơ bản tôi sẽ thực hiện tool calling bất cứ khi nào điều này được gọi. Và sau đó hàm forward chỉ đơn giản là gọi answer generator đó với các tham số được cung cấp cho nó. Và sau đó tôi cũng đang tạo một phiên bản async của hàm đó. Vì vậy, tôi có thể làm điều đó ở đây. Tôi sẽ nói, "OK, xác định các trường hợp một người cụ thể đã ở công ty của họ hơn 10 năm." Nó cần thực hiện tool calling để làm điều này, để có được thông tin cập nhật nhất.

Vì vậy, điều này đang làm, a Bisco lặp qua. Và nó sẽ gọi bio agent đó, đang sử dụng các tool call ở chế độ nền. Và nó sẽ đưa ra quyết định về việc liệu nền tảng của họ có phù hợp theo tiêu chí của tôi hay không. Trong trường hợp này, Satya là đúng. Brian nên là sai. Nhưng điều thú vị ở đây trong khi điều đó đang diễn ra? Tương tự như đối tượng reasoning mà bạn nhận được cho chain of thought, bạn có thể nhận được trajectory cho những thứ như react. Vì vậy, bạn có thể thấy các tool nó đang gọi, các đối số được truyền vào và các quan sát cho mỗi lời gọi đó, điều này hữu ích cho việc gỡ lỗi và các mục đích sử dụng khác. Tôi muốn chuyển sang nội dung khác. Vì vậy, tôi sẽ tua nhanh phần còn lại của điều này. Đây về cơ bản là một phiên bản async của điều tương tự. Vì vậy, bạn sẽ chạy cả hai song song, cùng một ý tưởng.

Đánh giá Mô hình và Tối ưu hóa Prompt (JEPA)

Tôi sẽ bỏ qua ví dụ JEPA ở đây chỉ trong giây lát. Tôi có thể cho bạn thấy đầu ra trông như thế nào. Nhưng về cơ bản, điều này đang làm là tạo một tập dữ liệu. Nó đang cho bạn thấy những gì có trong tập dữ liệu. Nó đang tạo ra nhiều signature khác nhau. Trong trường hợp này, nó sẽ tạo một hệ thống phân loại các loại tin nhắn trợ giúp khác nhau là một phần của tập dữ liệu. Ví dụ, "My sync is broken" hoặc "My light is out" hoặc bất cứ điều gì đó. Tôi muốn phân loại xem nó là tích cực, trung tính, tiêu cực và mức độ khẩn cấp của tin nhắn thực tế. Nó sẽ phân loại nó. Và sau đó nó sẽ đóng gói tất cả các module khác nhau đó vào một support analyzer module duy nhất. Và sau đó từ đó, điều nó sẽ làm là định nghĩa một loạt các metric, dựa trên chính tập dữ liệu. Vì vậy, nó sẽ nói, "OK, làm thế nào chúng ta đánh giá mức độ khẩn cấp?" Đây là một cái rất đơn giản, nơi nó là, OK, nó khớp hoặc không khớp. Và có những cái khác có thể chủ quan hơn một chút. Và sau đó bạn có thể chạy nó. Điều này sẽ mất quá nhiều thời gian. Nó có lẽ mất khoảng 20 phút hoặc lâu hơn. Nhưng điều nó sẽ làm về cơ bản là đánh giá hiệu suất của mô hình cơ sở và sau đó áp dụng các metric đó và lặp đi lặp lại để đưa ra các prompt mới để tạo ra điều đó.

Bây giờ, tôi muốn tạm dừng ở đây chỉ một giây vì có nhiều loại metric khác nhau. Và đặc biệt, đối với JEPA, nó sử dụng phản hồi từ mô hình giáo viên (teacher model) trong trường hợp này. Vì vậy, nó có thể hoạt động với cùng cấp độ mô hình, nhưng đặc biệt, khi bạn đang cố gắng sử dụng, giả sử, một mô hình nhỏ hơn, nó thực sự có thể cung cấp phản hồi bằng văn bản. Vì vậy, nó nói, "Bạn không chỉ phân loại sai, mà nó còn cung cấp cho bạn một số thông tin hoặc phản hồi bổ sung," như bạn có thể thấy ở đây, "vì sao nó sai và câu trả lời đáng lẽ phải là gì," điều này cho phép nó, bạn có thể đọc bài báo, nhưng về cơ bản nó cho phép nó lặp đi lặp lại tìm ra loại "Pareto frontier" về cách nó nên điều chỉnh prompt để tối ưu hóa dựa trên phản hồi đó. Nó về cơ bản chỉ thắt chặt vòng lặp lặp đó. Bạn có thể thấy nó có rất nhiều ở đây và sau đó bạn có thể chạy nó và xem nó hoạt động như thế nào.

Xử lý Tệp Đa dạng và Định tuyến LLM

Nhưng chỉ để cung cấp cho bạn một ví dụ cụ thể về cách mọi thứ kết hợp với nhau. Vì vậy, chúng tôi đã lấy một loạt các ví dụ từ trước. Về cơ bản, chúng tôi sẽ thực hiện một chút phân loại. Vì vậy, tôi có những thứ như hợp đồng. Tôi có hình ảnh. Tôi có những thứ khác nhau mà một chương trình DSPy có thể hiểu và thực hiện một số loại xử lý. Vì vậy, đây là điều chúng tôi thấy khá thường xuyên, chúng ta có thể gặp phải tình huống khách hàng có một đống tệp lớn. Họ không thực sự biết có gì trong đó. Họ muốn tìm thứ gì đó như hồ sơ SEC và xử lý chúng theo một cách nhất định. Họ muốn tìm hợp đồng và xử lý chúng theo một cách nhất định. Có thể có một số hình ảnh trong đó và họ muốn xử lý chúng theo một cách nhất định.

Vì vậy, đây là một ví dụ về cách bạn sẽ làm điều đó, nếu tôi bắt đầu từ dưới cùng ở đây, đây là một tệp Python thông thường. Và nó sử dụng DSPy để làm tất cả những điều vừa được đề cập. Vì vậy, chúng tôi đang kéo các cấu hình. Chúng tôi đang thiết lập LLM thông thường, cái nhỏ, và một cái chúng tôi sử dụng cho hình ảnh. Ví dụ, các mô hình Gemini có thể tốt hơn trong nhận dạng hình ảnh so với các mô hình khác. Vì vậy, tôi có thể muốn ủy quyền hoặc sử dụng một mô hình cụ thể cho một khối lượng công việc cụ thể. Vì vậy, nếu tôi phát hiện một hình ảnh, tôi sẽ định tuyến yêu cầu đến Gemini. Nếu tôi phát hiện thứ gì khác, tôi sẽ định tuyến nó đến 4.1 hoặc bất cứ điều gì đó.

Vì vậy, tôi sẽ xử lý một tệp duy nhất. Và điều nó làm là sử dụng thư viện attachments tiện dụng của chúng tôi để đưa nó vào một định dạng mà chúng ta có thể sử dụng. Và sau đó tôi sẽ phân loại nó. Và điều đó không siêu rõ ràng ở đây, nhưng tôi đang nhận được file type từ lời gọi hàm classify file này. Và sau đó tôi đang thực hiện một loại logic khác nhau, tùy thuộc vào loại tệp đó là gì. Vì vậy, nếu đó là hồ sơ SEC, tôi làm những việc nhất định. Nếu đó là một loại hồ sơ SEC nhất định, tôi làm một cái gì đó khác. Nếu đó là một hợp đồng, có thể tôi sẽ tóm tắt nó. Nếu đó là thứ trông giống như cơ sở hạ tầng thành phố, trong trường hợp này là hình ảnh mà chúng ta đã thấy trước đây, tôi có thể thực hiện một số diễn giải trực quan hơn về nó.

Vì vậy, nếu tôi đi sâu vào classify file siêu nhanh, nó đang chạy document classifier. Và tất cả điều đó là, nó về cơ bản đang thực hiện một predict trên hình ảnh từ tệp. Và đảm bảo nó trả về một loại, đây là gì? Trả về một loại, đó sẽ là document type. Và bạn có thể thấy ở đây, cuối cùng, đó là một signature khá đơn giản. Và vì vậy, những gì chúng tôi đã làm về cơ bản là lấy tệp PDF, trong trường hợp này, lấy tất cả các hình ảnh từ nó, và lấy hình ảnh đầu tiên hoặc một vài hình ảnh đầu tiên, trong trường hợp này là một danh sách hình ảnh làm trường đầu vào. Và tôi đang nói, "OK, chỉ cần cho tôi loại, đây là gì?" Và tôi đang cung cấp cho nó một lựa chọn các loại tài liệu này. Vì vậy, rõ ràng, đây là một trường hợp sử dụng khá đơn giản. Nhưng về cơ bản nó đang nói, "Với ba hình ảnh này, ba trang đầu tiên của tài liệu, liệu đó có phải là hồ sơ cần thiết không, đó có phải là hồ sơ bằng sáng chế không, đó có phải là hợp đồng, cơ sở hạ tầng thành phố, những thứ khá khác nhau không." Vì vậy, mô hình thực sự không nên gặp vấn đề với bất kỳ điều nào trong số đó. Và sau đó chúng tôi có một trường hợp bao quát cho "khác".

Và như tôi đã đề cập trước đây, tùy thuộc vào file type bạn nhận được, bạn có thể xử lý chúng khác nhau. Vì vậy, tôi đang sử dụng mô hình nhỏ để thực hiện cùng loại trích xuất biểu mẫu mà chúng ta đã thấy trước đây. Và sau đó khẳng định, về cơ bản, trong trường hợp này, rằng nó là những gì chúng ta nghĩ. Một hợp đồng, trong trường hợp này, chúng tôi đang nói, hãy xem, tôi còn khoảng 10 phút nữa. Vì vậy, chúng ta sẽ dừng lại sau tệp này. Nhưng đối với hợp đồng cụ thể, chúng tôi sẽ tạo đối tượng Summarizer này.

Tóm tắt và Phát hiện Ranh giới Tài liệu

Vì vậy, chúng ta sẽ đi qua càng nhiều trang càng tốt. Chúng ta sẽ thực hiện tóm tắt đệ quy về tài liệu đó bằng cách sử dụng một hàm DSPY riêng biệt. Và sau đó, chúng ta cũng sẽ phát hiện một số loại ranh giới của tài liệu đó. Vì vậy, chúng ta sẽ nói, tôi muốn các bản tóm tắt và tôi muốn các ranh giới của tài liệu. Và sau đó chúng ta sẽ in những thứ đó ra. Vậy hãy xem liệu tôi có thể chạy cái này không, nó sẽ phân loại. Nó nên được coi là một hợp đồng. Vì vậy, bạn có phụ thuộc vào mô hình để phân loại cơ sở hạ tầng thành phố không? Vâng, câu hỏi là, tôi chỉ dựa vào mô hình để xác định xem đó có phải là cơ sở hạ tầng thành phố hay không. Đúng vậy, ý tôi là, đây chỉ là một ví dụ nhanh và đơn giản trong workshop. Chỉ vì có một hình ảnh biển báo đường phố. Và nếu chúng ta nhìn vào thư mục dữ liệu, tôi có một hợp đồng, một số hình ảnh không liên quan, mẫu đơn đăng ký SEC, và sau đó là bãi đỗ xe nữa. Chúng khá khác nhau. Mô hình sẽ không gặp vấn đề gì khi phân loại đúng từ các danh mục mà tôi đã cung cấp. Trong một trường hợp sử dụng sản xuất nào đó, bạn sẽ muốn nghiêm ngặt hơn nhiều, hoặc thậm chí nhiều lần phân loại, có thể sử dụng các mô hình khác nhau để làm điều đó. Nhưng vâng, với những tùy chọn đó, ít nhất là nhiều lần tôi chạy nó, đã không có vấn đề gì.

Trong trường hợp này, tôi đã cung cấp cho nó một trong các tài liệu hợp đồng này và nó đã chạy một số logic tóm tắt bổ sung ngầm. Vì vậy, nếu tôi xem nhanh cái đó, bạn có thể tìm thấy tất cả những điều này trong mã nguồn. Nhưng về cơ bản, nó sử dụng ba signature riêng biệt để phân tích nội dung của hợp đồng và sau đó tóm tắt chúng. Vì vậy, về cơ bản, nó chỉ làm việc lặp đi lặp lại qua từng chunk của tài liệu để tạo một bản tóm tắt mà bạn thấy ở đây ở phía dưới, và sau đó chỉ để đảm bảo, chúng ta cũng đang phát hiện về cơ bản các ranh giới của tài liệu để nói, được rồi, đây là từ 13 trang, bạn có tài liệu chính, và sau đó là một số phụ lục hoặc các biểu mẫu là một phần của nó. Vậy hãy để tôi mang nó lên thật nhanh, chỉ để cho bạn thấy chúng ta đang làm việc với cái gì. Đây chỉ là một thứ ngẫu nhiên tôi tìm thấy trực tuyến. Và bạn có thể thấy, nó nói tài liệu chính là từ trang 0 đến 6, và trọng lượng, và như vậy là 0, 1, 2, 3, 4, 5, 6, có vẻ hợp lý. Bây giờ chúng ta có phần bắt đầu trong biểu mẫu 1. Biểu mẫu 1, nó nói là hai trang tiếp theo. Điều đó có vẻ khá tốt. Biểu mẫu 2 chỉ có một trang, 9 đến 9. Điều đó trông tốt, và sau đó biểu mẫu 3 đến cuối tài liệu. Điều đó cũng trông khá tốt.

Và cách chúng ta làm điều đó ngầm là về cơ bản lấy PDF, chuyển đổi nó thành một danh sách hình ảnh, và sau đó đối với mỗi hình ảnh, chuyển chúng cho bộ phân loại, và sau đó sử dụng điều đó để, à, chúng ta hãy xem mã nguồn, nhưng về cơ bản, lấy danh sách các phân loại đó, đưa nó cho một signature DSPY khác để nói, với các phân loại tài liệu này, hãy cho tôi cấu trúc, và về cơ bản, hãy cho tôi một cặp khóa gồm tên của phần, và hai số nguyên, một tuple gồm hai số nguyên để phát hiện, hoặc để xác định các ranh giới, về cơ bản. Đó là những gì phần đó làm.

Ví dụ về Cơ sở hạ tầng Thành phố và Tool Calls

Nếu chúng ta quay lại, về cơ sở hạ tầng thành phố, tôi sẽ làm cái này thật nhanh, chỉ vì nó khá thú vị về cách nó sử dụng tool calls. Và trong khi cái này đang chạy, tôi nên sử dụng cái đúng. Vâng. Vâng. Vì vậy, hãy xem cái đó thật nhanh. Vì vậy, đó phải là boundary detector. Có một bài đăng blog về điều này mà tôi đã xuất bản có lẽ vào tháng Tám hoặc khoảng đó, đi sâu vào chi tiết hơn một chút. Mã nguồn thực sự khá tệ trong bài đó. Nó sẽ tốt hơn ở đây. Nhưng về cơ bản, những gì nó làm là, đây có lẽ là logic chính. Vì vậy, đối với mỗi hình ảnh trong PDF, chúng ta sẽ gọi classify page. Chúng ta sẽ thu thập kết quả. Vì vậy, nó đang làm tất cả những việc ghi nhãn không đồng bộ đó. Nói, được rồi, tất cả các phân loại trang khác nhau có thể có. Và sau đó tôi truyền đầu ra của điều đó vào một signature mới nói rằng, với một tuple gồm trang, tôi thậm chí không định nghĩa nó ở đây, với một tuple gồm trang và phân loại, hãy cho tôi cái này, tôi không biết, một đầu ra tương đối phức tạp của một từ điển string, tuple integer integer. Và tôi đưa cho nó tập hợp các hướng dẫn này để nói chỉ cần phát hiện các ranh giới. Điều này rõ ràng là mã không dành cho sản xuất. Nhưng điểm mấu chốt là bạn có thể thực hiện các loại việc này siêu, siêu nhanh chóng. Tôi không chỉ định nhiều, không cung cấp nhiều ngữ cảnh. Và nó hoạt động khá tốt. Nó đã hoạt động khá tốt trong hầu hết các thử nghiệm của tôi. Bây giờ rõ ràng có rất nhiều "low-hanging fruit" về cách cải thiện, tối ưu hóa nó, vân vân.

Nhưng tất cả những gì điều này đang làm là lấy signature đó, các hướng dẫn này, và sau đó tôi gọi React. Và tất cả những gì tôi cung cấp cho nó là khả năng về cơ bản tự phản ánh và gọi get page images. Vì vậy, nó nói, được rồi, tôi sẽ xem xét ranh giới này. Chà, hãy để tôi lấy các hình ảnh trang cho ba trang này để đảm bảo về cơ bản rằng ranh giới là chính xác. Và sau đó nó sử dụng điều đó để xây dựng câu trả lời cuối cùng. Và vì vậy, đây thực sự là một ví dụ hoàn hảo về vòng lặp lặp lại chặt chẽ mà bạn có thể có cả trong việc xây dựng nó. Nhưng sau đó bạn có thể tận dụng khả năng nội quan của mô hình, nếu bạn muốn, để sử dụng function calls chống lại chính dữ liệu, dữ liệu tự tạo, vân vân, để giữ cho vòng lặp đó tiếp tục.

Đầu ra có cấu trúc và DSPY Primitives

Câu hỏi. Vậy dưới tên cruise, đó là tôi. Ý tôi là, vâng, tôi nghĩ đó có lẽ là sự giảm thiểu tiềm năng đầy đủ của nó, nhưng nói chung thì đúng. Ý tôi là, vâng, bạn có thể sử dụng các đầu ra có cấu trúc, nhưng bạn phải làm một đống việc về cơ bản để điều phối việc cung cấp tất cả các thông tin, cho phần còn lại của chương trình. Có thể bạn muốn gọi mô hình khác nhau hoặc sử dụng XML ở đây hoặc sử dụng một loại mô hình khác, hoặc bất cứ điều gì có thể để làm điều đó. Vì vậy, tuyệt đối, tôi không nói đây là cách duy nhất rõ ràng để tạo ra các ứng dụng này mà bạn không nên sử dụng Pydantic hoặc không nên sử dụng các đầu ra có cấu trúc, bạn hoàn toàn nên làm vậy. Đó chỉ là một cách mà một khi bạn đã hiểu rõ các primitivesDSPY cung cấp cho bạn, bạn có thể bắt đầu xây dựng rất nhanh các loại này, có thể nói là, hiện tại chúng là các prototype, nhưng nếu bạn muốn đưa điều này lên cấp độ tiếp theo để sản xuất ở quy mô lớn, bạn có tất cả các yếu tố cần thiết để xây dựng điều đó.

Trình tối ưu hóa và Phân tích Hiệu suất

Có câu hỏi nào khác không? Tôi có lẽ còn khoảng năm phút. Tốt. Vâng, vì vậy, Jepp, và thực ra tôi sẽ kéo lên, tôi đã chạy một cái ngay trước cái này. Cái này sử dụng một thuật toán khác gọi là MIPRO, nhưng về cơ bản các trình tối ưu hóa, miễn là bạn có dữ liệu có cấu trúc tốt, vì vậy đối với những người làm máy học trong phòng, có lẽ là tất cả mọi người, rõ ràng chất lượng dữ liệu của bạn rất quan trọng. Bạn không cần hàng ngàn và hàng ngàn ví dụ một cách cần thiết, nhưng miễn là bạn có đủ, có thể là 10 đến 100 đầu vào và đầu ra, và nếu bạn đang xây dựng các metrics của mình theo cách tương đối trực quan và mô tả chính xác những gì bạn đang cố gắng đạt được, sự cải thiện có thể khá đáng kể.

Và vì vậy, cái công cụ sửa lỗi nhập thời gian mà tôi đã đề cập trước đây, bạn có thể thấy đầu ra của nó ở đây, nó đang lặp lại, nó đang đo các metrics đầu ra cho mỗi cái này, và sau đó bạn có thể thấy tất cả ở phía dưới một khi nó đi qua tất cả các thứ tối ưu hóa của nó. Bạn có thể thấy hiệu suất thực tế trên mô hình cơ bản so với mô hình được tối ưu hóa, trong trường hợp này đã tăng từ 86 lên 89. Và sau đó thú vị là, cái này vẫn đang được phát triển, cái cụ thể này, nhưng bạn có thể chia nhỏ nó theo metrics, vì vậy bạn có thể thấy nơi mô hình đang tối ưu hóa tốt hơn, hoạt động tốt hơn trên các metrics nhất định. Và điều này có thể thực sự cho thấy liệu bạn có cần điều chỉnh metric của mình hay không, có thể bạn cần phân tách metric của mình, có thể có các lĩnh vực khác trong tập dữ liệu của bạn hoặc về cơ bản là cấu trúc chương trình của bạn mà bạn có thể cải thiện, nhưng đó là một cách thực sự tốt để hiểu những gì đang diễn ra ngầm. Và nếu bạn không quan tâm đến một số metrics này và trình tối ưu hóa không hoạt động tốt trên chúng, có thể bạn cũng có thể loại bỏ chúng. Vì vậy, đó là một cách rất linh hoạt để làm tất cả những điều đó.

Đầu ra của Trình tối ưu hóa và Prompt Động

Vâng, vâng, vì vậy đầu ra của trình tối ưu hóa về cơ bản làm một cái khác, nó gần giống như một đối tượng được biên dịch, nếu bạn muốn. Vì vậy, DSPY cũng cho phép bạn lưu và tải các chương trình, vì vậy đầu ra của trình tối ưu hóa về cơ bản là một module mà bạn sau đó có thể serialize và lưu trữ ở đâu đó, hoặc bạn có thể gọi nó sau như bạn làm trong module khác. Và nó chỉ thao tác cách diễn đạt của các prompt, hay nó thực sự như thế nào? Vâng, vâng, ngầm, nó thực sự chỉ lặp lại trên chính prompt. Có thể nó đang thêm các hướng dẫn bổ sung và nói rằng, tôi cứ thất bại ở điều cụ thể này, như không viết hoa tên đúng cách. Tôi cần thêm vào các tiêu chí ban đầu của mình trong prompt một hướng dẫn cho mô hình để nói, bạn phải viết hoa tên đúng cách.

Và Chris, người mà tôi đã đề cập trước đây, có một cách nói rất hay về điều này ở cuối mã nguồn, và tôi sẽ làm hỏng nó bây giờ, nhưng trình tối ưu hóa về cơ bản đang tìm ra các yêu cầu tiềm ẩn mà bạn có thể không chỉ định ban đầu. Nhưng dựa trên dữ liệu, nó giống như một kiểu học sâu dành cho người nghèo, tôi đoán vậy. Nhưng nó học từ dữ liệu, nó học cái gì đang làm tốt, cái gì không làm tốt. Và nó đang xây dựng động một prompt giúp cải thiện hiệu suất dựa trên các metrics của bạn. Tất cả đều, vâng. Vâng, câu hỏi là, liệu nó có hoàn toàn được hướng dẫn bởi LLM không? Vâng, đặc biệt đối với JEPA, LLM để cải thiện hiệu suất của LLM. Vì vậy, nó đang sử dụng LLM để xây dựng động các prompt mới, sau đó được đưa vào hệ thống, được đo lường và sau đó nó lặp lại. Vì vậy, nó đang sử dụng AI để xây dựng AI, nếu bạn muốn. Vâng.

DSPY Hub và Tái sử dụng Chương trình Tối ưu hóa

Tại sao đối tượng giải pháp này không phải là gì? Ồ, chắc chắn là có. Bạn có thể có được nó ngầm. Ý tôi là, câu hỏi là, tại sao bạn không chỉ lấy prompt được tối ưu hóa? Bạn hoàn toàn có thể. Còn gì nữa không? Thế thì còn gì khác ngoài prompt? Chính đối tượng DSPY. Vì vậy, module, cách mọi thứ, chúng ta có thể xem một cái nếu chúng ta có thời gian. Nếu tôi có thể xem liệu các dump của cái gì mới. Vâng, vâng, chắc chắn. Hãy để tôi xem liệu tôi có thể tìm thấy một cái nhanh không. Nhưng về cơ bản, cuối cùng, vâng, bạn nhận được một prompt được tối ưu hóa, một string mà bạn có thể dump ở đâu đó nếu bạn muốn. Thực ra, có rất nhiều phần cho signature, đúng không? Vì vậy, nó giống như cách bạn mô tả trường của mình. Vâng.

Đây là một sự chuyển tiếp hoàn hảo và tôi sẽ kết luận ngay sau đây. Tôi đang thử nghiệm một thứ tôi đã, trong khi tôi đang thử nghiệm thứ này gọi là DSPY Hub mà tôi đã tạo ra để tạo một kho lưu trữ các chương trình đã được tối ưu hóa. Vì vậy, về cơ bản, giống như nếu bạn là một chuyên gia trong bất cứ lĩnh vực nào, bạn tối ưu hóa một LLM chống lại tập dữ liệu này hoặc có một bộ phân loại tuyệt vời cho hình ảnh cơ sở hạ tầng thành phố hoặc bất cứ điều gì, giống như Hugging Face, bạn có thể tải xuống một thứ đã được tối ưu hóa trước. Và sau đó những gì tôi có ở đây, đây là chương trình đã được tải thực tế. Đây sẽ là đầu ra của quá trình tối ưu hóa, hoặc nó là, và sau đó tôi có thể gọi nó như bất cứ thứ gì khác. Và vì vậy bạn có thể thấy ở đây, đây là đầu ra và tôi đã sử dụng chương trình đã được tối ưu hóa mà tôi đã tải xuống từ hub này.

Và nếu chúng ta kiểm tra loaded program, bạn có thể thấy ngầm, đó là một đối tượng predict với một string signature của thời gian và lý luận. Đây cuối cùng là prompt được tối ưu hóa. Đây là đầu ra của quá trình tối ưu hóa, string dài này ở đây. Và sau đó là các đặc tả và định nghĩa khác nhau của đầu vào và đầu ra. Vì vậy, tùy thuộc vào trường hợp sử dụng của bạn. Vì vậy, nếu tôi có một bộ phân loại tài liệu, đó có thể là một ví dụ tốt. Nếu trong doanh nghiệp của tôi tôi gặp phải bất cứ thứ gì, các tài liệu thuộc một loại nhất định, tôi có thể tối ưu hóa một bộ phân loại chống lại những tài liệu đó. Và sau đó tôi có thể sử dụng điều đó ở một nơi khác trong một dự án khác hoặc đại loại thế. Vì vậy, trong số 100.000 tài liệu, tôi muốn tìm chỉ những trang có hóa đơn trên đó làm ví dụ. Chắc chắn, 100%, bạn có thể sử dụng một bộ phân loại ML điển hình để làm điều đó. Điều đó thật tuyệt.

Tối Ưu Hóa Mô Hình và Ứng Dụng Trong Quy Trình Dữ Liệu

Đây chỉ là một ví dụ, nhưng về lý thuyết, bạn cũng có thể huấn luyện hoặc tối ưu hóa một mô hình để thực hiện kiểu phân loại đó hoặc một kiểu tạo văn bản nào đó, hoặc bất cứ điều gì bạn muốn. Khi đó, bạn sẽ có trạng thái tối ưu hóa của mô hình, và nó sẽ tồn tại trong pipeline xử lý dữ liệu của bạn. Bạn có thể sử dụng nó cho các mục đích khác hoặc cung cấp cho các nhóm khác, hoặc bất cứ điều gì phù hợp. Vì vậy, điều này tùy thuộc vào use case cụ thể của bạn. Đối với một thứ như hub, có thể nó không hữu ích vì use case của mỗi cá nhân quá đặc thù, tôi không thực sự biết, nhưng bạn có thể làm bất cứ điều gì bạn muốn với nó.

Học Liên Tục và Tối Ưu Hóa Dữ Liệu

Đây có lẽ là câu hỏi cuối cùng. Về cơ bản, nó sẽ được thêm vào data set, sau đó bạn sẽ sử dụng phiên bản tối ưu hóa mới nhất và cứ tiếp tục tối ưu hóa dựa trên đó. Đúng vậy, bạn sẽ thu thập các dữ liệu khác. Đúng vậy, nếu bạn là một kỹ sư giỏi, bạn có thể đã làm điều đó. Tuy nhiên, tôi không khuyến nghị thay thế các ML model bằng các chương trình DSPire đã tối ưu hóa cho các use case cụ thể – phân loại có thể là một ví dụ tồi, tôi nhận ra điều đó. Nhưng đối với các lĩnh vực khác, về lý thuyết, có, bạn có thể làm điều gì đó tương tự.

DSPire cho Tác Vụ LLM: Tối Ưu Hiệu Năng và Giảm Chi Phí

Đối với các tác vụ LLM cụ thể, tôi chắc chắn rằng tất cả chúng ta đều có những trường hợp thú vị. Nếu bạn có một thứ tương đối được định nghĩa rõ ràng, nơi bạn biết các đầu vào và đầu ra, nó có thể là một ứng cử viên xứng đáng để tối ưu hóa. Ít nhất là để chuyển nó sang một mô hình nhỏ hơn, nhằm duy trì mức hiệu suất với chi phí thấp hơn – đó thực sự là lợi ích lớn nhất mà tôi thấy.

Chi Phí và Thách Thức Ngữ Cảnh Lớn với DSPire

Được rồi, câu hỏi cuối cùng: DSPire cũng có chi phí lớn. Vậy câu hỏi là DSPire có thể đắt không? Và đối với context lớn, bạn đã thấy điều đó như thế nào và bạn đã quản lý nó ra sao? Phần chi phí hoàn toàn tùy thuộc vào bạn. Nếu bạn gọi một hàm hàng triệu lần một cách bất đồng bộ, bạn sẽ phát sinh rất nhiều chi phí. Tôi không nghĩ DSPire làm cho việc gọi mọi thứ dễ dàng hơn, nhưng bản thân nó không đắt. Nó có thể, theo ý bạn, thêm nhiều context vào prompt. Chắc chắn, signature là một chuỗi, nhưng văn bản thực tế được gửi đến mô hình dài hơn nhiều. Điều đó hoàn toàn đúng. Tôi sẽ không nói rằng nó là một yếu tố thúc đẩy chi phí lớn. Tôi nghĩ, suy cho cùng, nó là một paradigm lập trình. Vì vậy, bạn có thể viết adapter nén của riêng mình nếu bạn muốn giảm lượng dữ liệu gửi đến mô hình.

Quản Lý Ngữ Cảnh: Giải Pháp Hiện Tại và Xu Hướng Tương Lai

Về context lớn, tôi nghĩ câu trả lời cũng tương tự: nếu bạn lo lắng về điều đó, có thể bạn có một số logic bổ sung, hoặc trong chính chương trình, hoặc trong một adapter, hoặc một phần của module để theo dõi điều đó; có thể bạn thực hiện một số context compression hoặc tương tự. Rõ ràng, đã có một số buổi nói chuyện rất hay về chủ đề đó trong ba ngày qua. Tôi có cảm giác rằng điều đó sẽ dần biến mất vào một thời điểm nào đó, khi context window trở nên lớn hơn hoặc việc quản lý context được trừu tượng hóa theo một cách nào đó. Tôi thực sự không có câu trả lời, đó chỉ là một linh cảm. Nhưng DSPy, một lần nữa, cung cấp cho bạn các công cụ, các primitive để bạn làm điều đó, nếu bạn chọn và muốn theo dõi trạng thái, kiểm soát việc quản lý đó theo thời gian.

Vậy là xong. Chúng ta sắp phải rời đi rồi. Cảm ơn rất nhiều vì thời gian của quý vị. Tôi thực sự đánh giá cao điều đó.

Góp ý / Báo lỗiPhát hiện sai sót hoặc có ý tưởng cải thiện?