Model Context Protocol: Sampling, Roots?

0 0 0

Người đăng: Trung Đức

Theo Viblo Asia

Mở đầu

Nối tiếp bài viết trước, chúng ta sẽ cùng trao đổi 2 keywords tiếp theo liên quan đến MCP: Sampling và Roots. Let's do it

Sampling - Khi Server "nhờ" LLM sáng tạo thông qua Client

Trong Model Context Protocol (MCP), Sampling là một tính năng mạnh mẽ cho phép các server yêu cầu các LLM tạo ra "completions" (ví dụ: hoàn thiện một đoạn văn bản, trả lời câu hỏi, tóm tắt nội dung) thông qua sự trung gian của client. Điều này có vẻ hơi ngược so với cách chúng ta thường nghĩ (client yêu cầu LLM), nhưng chính thiết kế này lại mang đến những lợi ích độc đáo, đặc biệt là trong việc xây dựng các hành vi "agentic" (hành vi tự chủ, có khả năng ra quyết định và hành động của một tác nhân AI) phức tạp, đồng thời vẫn đảm bảo an ninh và quyền riêng tư cho người dùng.

Sampling hoạt động như thế nào?

Quy trình "sampling" trong MCP được thiết kế với vai trò trung tâm của client và sự kiểm soát của người dùng (human-in-the-loop):

  1. Server Gửi Yêu Cầu: Server gửi một yêu cầu sampling/createMessage đến client. Yêu cầu này chứa thông tin cần thiết để LLM tạo ra một "completion", ví dụ như lịch sử hội thoại, các tùy chọn về model, v.v.
  2. Client Xem Xét và Sửa Đổi (Optional): Client nhận yêu cầu từ server. Tại bước này, client (và người dùng thông qua client) có thể xem xét nội dung yêu cầu, thậm chí sửa đổi nó trước khi gửi đến LLM. Đây là một điểm kiểm soát quan trọng.
  3. Client Thực Hiện "Sampling" từ LLM: Client gửi yêu cầu (đã được xem xét) đến một LLM mà nó có quyền truy cập. LLM xử lý yêu cầu và tạo ra một "completion".
  4. Client Xem Xét Kết Quả Trả Về: Sau khi LLM trả về kết quả, client một lần nữa có cơ hội xem xét "completion" này. Người dùng có thể chấp nhận, sửa đổi hoặc từ chối kết quả.
  5. Client Trả Kết Quả về Server: Cuối cùng, client gửi kết quả (đã qua bước xem xét của người dùng) trở lại cho server đã yêu cầu ban đầu.

Thiết kế human-in-the-loop này đảm bảo rằng người dùng luôn duy trì quyền kiểm soát đối với những thông tin mà LLM tiếp nhận và những nội dung mà LLM tạo ra, điều này rất quan trọng cho bảo mật và quyền riêng tư.

Định dạng mesage request Sampling (sampling/createMessage)

Yêu cầu sampling sử dụng một định dạng message chuẩn hóa, bao gồm các trường chính sau:

{ "messages": [ // Lịch sử hội thoại để LLM có ngữ cảnh { "role": "user" | "assistant", // Vai trò của người nói "content": { "type": "text" | "image", // Loại nội dung // Đối với văn bản: "text": "string", // Đối với hình ảnh: "data": "string", // Dữ liệu ảnh mã hóa base64 "mimeType": "string" // Loại MIME của ảnh (ví dụ: "image/jpeg") } } ], "modelPreferences": { // (Tùy chọn) Ưu tiên của server về việc chọn model "hints": [{ "name": "string" // Gợi ý tên model hoặc dòng model (ví dụ: "claude-3", "sonnet") }], "costPriority": "number", // Ưu tiên giảm chi phí (0-1) "speedPriority": "number", // Ưu tiên tốc độ phản hồi thấp (0-1) "intelligencePriority": "number" // Ưu tiên khả năng của model (0-1) }, "systemPrompt": "string", // (Tùy chọn) System prompt server muốn sử dụng (client có thể bỏ qua/sửa) "includeContext": "none" | "thisServer" | "allServers", // Ngữ cảnh MCP nào cần đưa vào "temperature": "number", // (Tùy chọn) Độ ngẫu nhiên (0.0 - 1.0) "maxTokens": "number", // Số lượng token tối đa LLM được phép tạo ra "stopSequences": ["string"], // (Tùy chọn) Các chuỗi ký tự khiến LLM dừng tạo "metadata": {} // (Tùy chọn) Siêu dữ liệu bổ sung cho nhà cung cấp model cụ thể
}
  • messages: Một mảng các đối tượng tin nhắn, cung cấp lịch sử hội thoại cho LLM. Mỗi tin nhắn có role ("user" hoặc "assistant") và content (có thể là văn bản hoặc hình ảnh).
  • modelPreferences: Cho phép server gợi ý về model LLM mà nó muốn client sử dụng. Client sẽ đưa ra quyết định cuối cùng dựa trên các gợi ý này và các model mà nó có sẵn.
    • hints: Danh sách các gợi ý tên model. Client có thể ánh xạ các gợi ý này sang các model tương đương từ các nhà cung cấp khác nhau.
    • costPriority, speedPriority, intelligencePriority: Các giá trị từ 0 đến 1 thể hiện mức độ ưu tiên tương ứng.
  • systemPrompt: Một chỉ dẫn ở cấp độ hệ thống cho LLM, định hướng cách LLM nên hành xử. Client có quyền sửa đổi hoặc bỏ qua system prompt này.
  • includeContext: Chỉ định ngữ cảnh từ MCP nào nên được đưa vào yêu cầu gửi đến LLM.
    • "none": Không thêm ngữ cảnh nào.
    • "thisServer": Chỉ bao gồm ngữ cảnh từ server đang gửi yêu cầu sampling.
    • "allServers": Bao gồm ngữ cảnh từ tất cả các server MCP đang kết nối. Client là người quyết định cuối cùng ngữ cảnh nào thực sự được gửi đi.
  • Tham số Sampling (temperature, maxTokens, stopSequences): Các tham số này giúp tinh chỉnh cách LLM tạo ra "completion".
  • metadata: Cho phép truyền các tham số đặc thù của từng nhà cung cấp LLM.

Định dạng response Sampling từ Client

Sau khi client nhận được "completion" từ LLM (và có thể đã qua bước chỉnh sửa của người dùng), nó sẽ trả về cho server một kết quả có cấu trúc:

{ "model": "string", // Tên của model LLM đã được sử dụng "stopReason": "endTurn" | "stopSequence" | "maxTokens" | "string", // Lý do LLM dừng tạo nội dung "role": "user" | "assistant", // Thường là "assistant" cho nội dung LLM tạo ra "content": { "type": "text" | "image", "text": "string", "data": "string", // base64 encoded "mimeType": "string" }
}

*Ví dụ request Sampling

{ "method": "sampling/createMessage", "params": { "messages": [ { "role": "user", "content": { "type": "text", "text": "What files are in the current directory?" } } ], "systemPrompt": "You are a helpful file system assistant.", "includeContext": "thisServer", "maxTokens": 100 }
}

Trong ví dụ này, server yêu cầu client tạo một "completion" dựa trên câu hỏi của người dùng về các tệp trong thư mục hiện tại. Server cũng gợi ý một systemPrompt và yêu cầu bao gồm ngữ cảnh từ chính server đó, với giới hạn maxTokens là 100.

Kiểm soát của Human-in-the-Loop

Đây là một khía cạnh thiết kế cốt lõi của Sampling trong MCP:

  • Đối với Prompts (Đầu vào cho LLM):
    • Client nên hiển thị cho người dùng prompt (bao gồm messages, systemPrompt, và ngữ cảnh) mà server đề xuất gửi cho LLM.
    • Người dùng phải có khả năng sửa đổi hoặc từ chối hoàn toàn prompt đó.
    • Client có thể lọc hoặc sửa đổi systemPrompt từ server.
    • Việc quyết định cuối cùng về ngữ cảnh (includeContext) được đưa vào là do client (và người dùng) kiểm soát.
  • Đối với Completions (Kết quả từ LLM):
    • Client nên hiển thị "completion" (nội dung LLM tạo ra) cho người dùng.
    • Người dùng phải có khả năng sửa đổi hoặc từ chối "completion" đó.
    • Client có thể tự động lọc hoặc sửa đổi "completion" dựa trên các quy tắc đã định.
    • Người dùng (thông qua client) kiểm soát model LLM nào được sử dụng.

Những lưu ý quan trọng về bảo mật

Vì sampling liên quan đến việc server yêu cầu client gửi dữ liệu đến LLM và nhận lại kết quả, các yếu tố bảo mật cần được đặt lên hàng đầu:

  • Xác thực nội dung messages: Đảm bảo không có nội dung độc hại được gửi đi.
  • Làm sạch thông tin nhạy cảm: Trước khi gửi đến LLM, các thông tin nhạy cảm (nếu có trong ngữ cảnh hoặc yêu cầu) cần được client xử lý (ví dụ: ẩn đi, loại bỏ) dưới sự kiểm soát của người dùng.
  • Triển khai giới hạn tần suất (Rate Limits): Ngăn chặn việc lạm dụng tài nguyên sampling.
  • Mã hóa dữ liệu khi truyền tải (Encrypt data in transit).
  • Xử lý quyền riêng tư dữ liệu người dùng: Tuân thủ các quy định về bảo vệ dữ liệu.
  • Kiểm soát chi phí (Cost Exposure): Theo dõi và đặt ngưỡng để tránh chi phí phát sinh không kiểm soát.
  • Triển khai timeouts cho các yêu cầu đến LLM.

Roots: xác định "sân chơi" cho Server

Roots là gì?

Về bản chất, một Root là một URI (Uniform Resource Identifier - Định danh Tài nguyên Đồng nhất) mà client gợi ý cho server làm "điểm tập trung" chính. Khi một client kết nối với một server, nó sẽ khai báo những "Roots" mà server nên làm việc cùng. Chúng đóng vai trò như những "ranh giới" để server biết mình nên hoạt động trong phạm vi nào.

Mặc dù "Roots" thường được sử dụng nhiều nhất để chỉ các đường dẫn trong hệ thống tệp tin (ví dụ: một thư mục dự án), chúng có thể là bất kỳ URI hợp lệ nào, bao gồm cả các URL HTTP.

Ví dụ về Roots:

  • file:///home/user/projects/myapp (Một thư mục dự án trên máy cục bộ)
  • https://api.example.com/v1 (Một điểm cuối API trên web)
  • git://github.com/user/repository.git (Một kho mã nguồn Git)

Tại sao cần sử dụng Roots?

Roots mang lại nhiều lợi ích quan trọng trong việc tương tác giữa client và server trong MCP:

  • Hướng dẫn (Guidance): Chúng cung cấp thông tin cho server về các tài nguyên và vị trí có liên quan đến công việc hiện tại. Server sẽ biết "nhìn vào đâu" để tìm kiếm hoặc xử lý dữ liệu.
  • Rõ ràng (Clarity): Roots giúp làm rõ những tài nguyên nào thuộc về "không gian làm việc" (workspace) hiện tại của người dùng. Điều này tránh việc server thao tác nhầm hoặc truy cập vào những vùng không liên quan.
  • Tổ chức (Organization): Việc hỗ trợ nhiều Roots cho phép người dùng (và server) làm việc đồng thời với nhiều bộ tài nguyên khác nhau nhưng vẫn giữ được sự tách biệt logic. Ví dụ, bạn có thể làm việc với một thư mục dự án frontend và một API backend như hai "Roots" riêng biệt.

Roots hoạt động như thế nào?

Cơ chế hoạt động của Roots liên quan đến cả client và server:

  • Về phía Client (khi client hỗ trợ Roots):

    1. Khai báo khả năng (Capability): Client thông báo cho server rằng nó hỗ trợ tính năng roots trong quá trình thiết lập kết nối.
    2. Cung cấp danh sách Roots: Client gửi một danh sách các URI được coi là "Roots gợi ý" (suggested roots) cho server.
    3. Thông báo thay đổi (Tùy chọn): Nếu danh sách Roots thay đổi (ví dụ: người dùng mở một thư mục dự án mới hoặc đóng một thư mục cũ), client (nếu hỗ trợ) sẽ thông báo cho server về sự thay đổi này.
  • Về phía Server (cách server nên tương tác với Roots): Mặc dù Roots được coi là thông tin gợi ý và không phải là một cơ chế thực thi nghiêm ngặt (not strictly enforcing) để giới hạn hoàn toàn server, các server nên:

    1. Tôn trọng (Respect) các Roots mà client cung cấp.
    2. Sử dụng URI của Roots để định vị, truy cập và thực hiện các thao tác liên quan đến tài nguyên (ví dụ: đọc file trong thư mục root, gọi đến API endpoint được định nghĩa là root).
    3. Ưu tiên (Prioritize) các hoạt động diễn ra bên trong ranh giới do Roots xác định. Tránh hoặc hạn chế tối đa việc "đi lạc" ra ngoài các vùng này trừ khi có yêu cầu cụ thể.

Các trường hợp sử dụng phổ biến của Roots

Roots rất hữu ích trong việc định nghĩa:

  • Thư mục dự án (Project directories): Đây là trường hợp sử dụng phổ biến nhất, ví dụ: file:///path/to/your/project.
  • Vị trí kho mã nguồn (Repository locations): Ví dụ, thư mục gốc của một kho Git.
  • Điểm cuối API (API endpoints): Ví dụ: https://your-api.com/api/v2.
  • Vị trí chứa file cấu hình (Configuration locations): Giúp server biết nơi tìm các tệp cài đặt.
  • Ranh giới tài nguyên (Resource boundaries): Nói chung, bất kỳ tập hợp tài nguyên nào mà bạn muốn server tập trung vào.

Best practices khi làm việc với Roots

  • Chỉ gợi ý tài nguyên cần thiết: Tránh khai báo quá nhiều Roots không liên quan, điều này có thể làm server bị "loãng" sự tập trung.
  • Sử dụng tên rõ ràng, mô tả (nếu có): Như trong ví dụ bên dưới, việc đặt name cho mỗi Root giúp dễ hiểu hơn.
  • Theo dõi khả năng truy cập của Root: Client nên đảm bảo rằng các URI được khai báo là Roots thực sự có thể truy cập được.
  • Xử lý thay đổi Root một cách mượt mà: Cả client và server nên có cơ chế để cập nhật và phản ứng với những thay đổi trong danh sách Roots (ví dụ: người dùng thêm/xóa một thư mục dự án).

Ví dụ về cấu hình Roots

Đây là cách một client MCP có thể cung cấp thông tin về Roots cho server:

{ "roots": [ { "uri": "file:///home/user/projects/frontend", "name": "Frontend Repository" }, { "uri": "https://api.example.com/v1", "name": "API Endpoint" } ]
}

Trong ví dụ này:

  • Client gợi ý hai "Roots" cho server.
  • Root thứ nhất: Là một thư mục cục bộ (file:///home/user/projects/frontend), được đặt tên là "Frontend Repository". Server có thể hiểu rằng các thao tác liên quan đến code frontend nên tập trung vào đây.
  • Root thứ hai: Là một điểm cuối API (https://api.example.com/v1), được đặt tên là "API Endpoint". Server có thể sử dụng URI này để thực hiện các lệnh gọi API.

Cấu hình này cho phép server tập trung vào cả kho mã nguồn cục bộ và một API endpoint từ xa, đồng thời giữ chúng tách biệt về mặt logic, giúp việc quản lý và phát triển trở nên có tổ chức hơn.

References

Bình luận

Bài viết tương tự

- vừa được xem lúc

Golang Data Structures and Algorithms - Stack

Giới thiệu. Series về cấu trúc dữ liệu và thuật toán sử dụng Golang.

0 0 41

- vừa được xem lúc

AWS Certified Solutions Architect Professional - Security - Secrets Manager

Introduction. A quick note about AWS Secrets Manager.

0 0 50

- vừa được xem lúc

Golang Data Structures and Algorithms - Queue

Giới thiệu. Series về cấu trúc dữ liệu và thuật toán sử dụng Golang.

0 0 52

- vừa được xem lúc

Terraform Series - Bài 17 - Security - Manage Secrets with Vault

Giới thiệu. Chào các bạn tới với series về Terraform, ở bài trước chúng ta đã tìm hiểu về vấn đề security trong Terraform.

0 0 42

- vừa được xem lúc

Golang Data Structures and Algorithms - Linked Lists

Giới thiệu. Series về cấu trúc dữ liệu và thuật toán sử dụng Golang.

0 0 40

- vừa được xem lúc

AWS Certified Solutions Architect Professional - Security - AWS Certificate Manager

Introduction. A quick note about AWS Certificate Manager.

0 0 34