AppFunctions là phiên bản tương đương của máy chủ MCP trên Android. Các công cụ này cho phép bạn chia sẻ một số phần trong logic của ứng dụng với các tác nhân và trợ lý AI.
Tác nhân sử dụng nội dung mô tả để chọn AppFunction đáp ứng yêu cầu. Quá trình lựa chọn này hoạt động giống như quy trình khám phá công cụ MCP truyền thống, trong đó các ứng dụng AI sử dụng nội dung mô tả về máy chủ và công cụ MCP để đưa ra lựa chọn. Ứng dụng của bạn đóng vai trò là máy chủ MCP Android và mỗi AppFunction đóng vai trò là một trong các công cụ của máy chủ đó. Cung cấp các nội dung mô tả sau:
- Nội dung mô tả về ứng dụng (máy chủ MCP Android). Nội dung mô tả ứng dụng là điểm truy cập đầu tiên để các tác nhân hiểu được chức năng của ứng dụng. Để giúp các tác nhân khám phá ứng dụng của bạn, hãy bắt đầu bằng miền chính của ứng dụng và liệt kê ngắn gọn các nhiệm vụ mà AppFunctions cho phép ứng dụng hoàn thành.
- Tự viết nội dung mô tả ứng dụng trong tệp XML siêu dữ liệu của ứng dụng.
- Sử dụng nội dung mô tả ứng dụng để mô tả những điều mà nội dung mô tả hàm riêng lẻ không thể mô tả, chẳng hạn như thứ tự gọi hàm.
- Nội dung mô tả AppFunction (các công cụ). Mỗi nội dung mô tả hàm giải thích hành vi của một AppFunction. Nội dung mô tả chức năng thường được cấu trúc xoay quanh một động từ, chẳng hạn như cập nhật, tạo hoặc đặt trước, và một tài nguyên, chẳng hạn như chế độ cài đặt, kênh hoặc phòng.
Nguyên tắc mô tả hàm
Tác nhân dựa vào nội dung mô tả hàm để quyết định hàm nào phù hợp với yêu cầu và cách gọi hàm đó.
Viết nội dung mô tả ngắn gọn, rõ ràng về AppFunction
Nội dung mô tả càng rõ ràng thì tác nhân càng có thể chọn đúng hàm cho một yêu cầu một cách đáng tin cậy. Bảng sau đây liệt kê những việc nên làm và những việc nên tránh trong phần mô tả hàm:
| Nên | Không nên |
|---|---|
| Mô tả mục đích của hàm và kết quả khi chạy hàm bằng ngôn ngữ rõ ràng và trung lập. | Đừng sử dụng nội dung mô tả làm câu lệnh. Các lệnh như "Bạn PHẢI nói" và "Bạn phải yêu cầu người dùng" hướng dẫn tác nhân cách hành xử thay vì mô tả chức năng. |
| Đưa thông tin quan trọng lên đầu phần mô tả và viết ngắn gọn. | Đừng thêm nội dung thừa vào phần mô tả, chẳng hạn như "Hàm này được dùng để". Nội dung thừa khiến nội dung mô tả dài hơn nhưng không rõ ràng hơn. |
| Phản ánh chính xác chức năng của mã, bao gồm cả các tham số và kết quả đầu ra. Ví dụ: một hàm được mô tả là chỉ đọc không được ghi hoặc xoá dữ liệu. | Không sử dụng tên gây hiểu lầm, chẳng hạn như một hàm có tên là fetchData nhưng lại xoá dữ liệu người dùng. |
| Viết nội dung mô tả hàm theo định dạng KDoc, bao gồm cả tham số và kiểu dữ liệu trả về. Làm theo hướng dẫn về kiểu Kotlin để đặt tên và mô tả dễ hiểu mà không cần biết về cách triển khai. | Đừng viết hoa quá nhiều hoặc dùng ký tự đặc biệt để thu hút sự chú ý vào các phần của nội dung mô tả, chẳng hạn như **CRITICAL** hoặc VERY IMPORTANT. |
| Sử dụng tên mà con người có thể đọc được cho AppFunctions. | |
| Nêu rõ ràng và trực tiếp chức năng của từng AppFunction. | Đừng đưa ra hướng dẫn theo quy trình liên quan đến các chức năng khác, chẳng hạn như "Trước tiên, hãy tìm người liên hệ". Tác nhân sử dụng nội dung mô tả ứng dụng của bạn để lên kế hoạch gọi trên các hàm, vì vậy, hãy mô tả thứ tự đó trong nội dung mô tả ứng dụng. |
| Mô tả những gì tác nhân có thể mong đợi từ hàm, bằng ngôn ngữ chẳng hạn như "Nếu bạn có X, thì Y". | |
| Giả sử các tác nhân có hướng dẫn riêng về cách sử dụng AppFunctions nói chung. Nội dung mô tả hàm cho biết chức năng của hàm, còn hướng dẫn của tác nhân cho biết cách sử dụng hàm. | Đừng cố ghi đè hoặc nhúng các chỉ dẫn ở cấp tác nhân vào nội dung mô tả hàm. |
Viết mã có tính đóng gói
Mỗi nội dung mô tả hàm phải độc lập để hướng dẫn tác nhân sử dụng hàm đó chứ không phải hàm nào khác. Bảng sau đây liệt kê các nguyên tắc đóng gói:
| Nên | Không nên |
|---|---|
| Viết nội dung mô tả hàm cụ thể và tập trung, với một mục đích duy nhất và rõ ràng. Ví dụ: "tạo, tìm và xoá việc cần làm cá nhân". | Đừng viết nội dung mô tả quá rộng, vì điều này có thể khiến tác nhân gọi hàm của bạn vào những thời điểm không phù hợp hoặc vì lý do không chính đáng. |
| Đừng tạo AppFunction đa năng hoặc hiển thị nhiều AppFunction cho cùng một trường hợp sử dụng. Tác nhân có nhiều khả năng chọn sai hàm khi một số hàm có ý định tương tự nhau. | |
| Đừng tham chiếu hoặc cố gắng gây ảnh hưởng đến hành vi của các AppFunction khác trong ứng dụng của bạn hoặc trong các ứng dụng khác. Điều này có thể tạo ra các phần phụ thuộc và tác dụng phụ ngoài ý muốn. |
Các hành động được phép và việc chia sẻ dữ liệu
Hãy làm theo các nguyên tắc sau để bảo vệ quyền riêng tư và sự an toàn của người dùng khi tác nhân sử dụng các chức năng của ứng dụng:
| Nên | Không nên |
|---|---|
| Chỉ thực hiện các thao tác an toàn, không gây hại mà người dùng thường làm trong ứng dụng của bạn, trong đó tác nhân có thể cung cấp các tham số rõ ràng từ yêu cầu của người dùng, chẳng hạn như tạo một việc cần làm có tiêu đề hoặc tìm kiếm ghi chú. | |
| Chỉ chia sẻ thông tin không mang tính cá nhân hoặc bí mật cao, hoặc dữ liệu mà người dùng đồng ý chia sẻ một cách rõ ràng cho hành động đó. | Không cấp cho tác nhân quyền truy cập rộng rãi vào dữ liệu người dùng hoặc tài nguyên hệ thống. |
| Đừng thiết kế AppFunctions yêu cầu hoặc trả về mật khẩu, thông tin tài chính, khoá riêng tư hoặc thông tin cá nhân có độ nhạy cảm cao khác. | |
| Thực hiện các hành động mà người dùng yêu cầu, ngay cả khi các hành động đó chạy sau hoặc chạy ở chế độ nền, chẳng hạn như gửi tin nhắn vào thời điểm mà người dùng chọn. | Đừng thực hiện những hành động mà người dùng không yêu cầu, chẳng hạn như các tác vụ tự bắt đầu hoặc vượt quá yêu cầu. |
| Tập trung vào chức năng hưởng lợi từ ngôn ngữ tự nhiên, chẳng hạn như các nhiệm vụ dễ diễn đạt trong cuộc trò chuyện hơn là thông qua thao tác điều hướng trên giao diện người dùng, ví dụ: "[Tác nhân AI], bạn có thể giúp tôi tìm một số thời gian rảnh vào tuần tới trên lịch của tôi, sau đó đề xuất một chuyến bay đến Chicago vào một trong những ngày đó không?" | |
| Trên các hệ số hình dạng ngoài điện thoại, hãy tôn trọng quyền riêng tư của người dùng trên các thiết bị dùng chung, chẳng hạn như TV, Android Auto và Android Automotive OS. | Đừng tiết lộ thông tin hoặc thực hiện các hành động mà giao diện người dùng của ứng dụng không hỗ trợ trên kiểu dáng đó. |
| Xác định rõ ràng và duy nhất mục đích của hàm. | Không được mạo danh một công cụ hoặc dịch vụ khác. Tên và nội dung mô tả của hàm không được giống với các công cụ khác đến mức gây nhầm lẫn, vì điều này có thể khiến tác nhân chọn nhầm hàm của bạn. |
Thiết kế tham số và giá trị trả về
Việc thiết kế tham số và giá trị trả về một cách cẩn thận sẽ giúp tránh được sự mơ hồ và giảm khả năng xảy ra các lệnh gọi không hợp lệ. Hãy tuân theo các quy ước sau:
| Nên | Không nên |
|---|---|
| Chỉ xác định tham số cho dữ liệu mà hàm của bạn cần. | Đừng yêu cầu thông tin mà hàm không sử dụng. Việc này giúp giảm thiểu việc lộ dữ liệu và đơn giản hoá việc sử dụng hàm. |
Sử dụng tên tham số phản ánh dữ liệu đầu vào dự kiến, chẳng hạn như
taskId, searchQuery hoặc
recipientEmail. |
Đừng sử dụng tên như full_context hoặc conversation_history. Những tên như vậy không rõ ràng vì các tác nhân sử dụng những thuật ngữ này cho bối cảnh và nhật ký trò chuyện của riêng họ. Một tác nhân
có thể diễn giải sai và truyền một lượng lớn dữ liệu không cần thiết đến
hàm của bạn, dẫn đến việc rò rỉ dữ liệu. |
Sử dụng các loại mà AppFunctions hỗ trợ thay vì tự xác định, ví dụ: LocalDate, LocalTime, LocalDateTime hoặc Instant cho ngày và giờ, và Uri cho đường liên kết. Thư viện này biểu thị các loại này theo cùng một cách trong mọi ứng dụng, nhờ đó, các tác nhân sẽ nhận được một định dạng nhất quán. Để biết danh sách đầy đủ các loại được hỗ trợ, hãy xem AppFunctionSerializable. |
|
| Nếu một tham số không cần thiết cho mục đích của hàm, hãy đặt tham số đó thành không bắt buộc. Điều này giúp tác nhân linh hoạt hơn khi chỉ có một phần thông tin từ người dùng. Để biết thêm thông tin, hãy xem phần Tham số không bắt buộc. | |
| Nếu AppFunction của bạn cập nhật các tài nguyên hiện có, hãy đảm bảo rằng mỗi tham số có thể phân biệt giữa "đặt thành rỗng" và "không thay đổi". Để biết ví dụ, hãy xem phần Nội dung cập nhật cho tài nguyên hiện có. | |
Để cho phép tác nhân mở một màn hình cụ thể trong ứng dụng, hãy trả về một PendingIntent cho màn hình đó. Tác nhân có thể chạy PendingIntent như hiện tại mà không cần tạo ý định cho ứng dụng của bạn. Hãy tạo ý định đó bằng một ý định tường minh và FLAG_IMMUTABLE để tác nhân không thể thay đổi đích đến của ý định. |
Thông số tùy chọn
Chỉ các loại có thể rỗng, nguyên gốc, danh sách và mảng mới có thể là không bắt buộc. Khi tác nhân bỏ qua một tham số không bắt buộc, thư viện sẽ truyền giá trị mặc định của loại (null, 0, false hoặc trống) thay vì giá trị mặc định mà bạn khai báo.
Giải quyết các giá trị mặc định có ý nghĩa trong phần thân hàm, như trong ví dụ sau:
/**
* Creates a note.
*
* @param title The title of the note.
* @param content The body text of the note.
* @param attachments The files to attach to the note.
* @param owner The ID of the user who owns the note.
* @return The created note.
*/
@AppFunctionDeclaration(isDescribedByKDoc = true)
suspend fun createNote(
title: String, // Required
content: String? = null, // Optional
attachments: List<Attachment> = emptyList(), // Optional
owner: String? = null, // Optional
): Note = withContext(Dispatchers.IO) {
noteRepository.createNote(
title = title,
// The library ignores declared default values and passes null for
// omitted nullable parameters, so resolve meaningful defaults here.
content = content ?: "",
attachments = attachments,
owner = owner ?: currentUserId(),
)
}
Nội dung cập nhật đối với tài nguyên hiện có
Ví dụ sau đây sử dụng một trình bao bọc SetField có thể rỗng cho mỗi trường mà tác nhân có thể cập nhật. SetField rỗng có nghĩa là "không thay đổi" và SetField(null) có nghĩa là "đặt thành rỗng":
/** A new value for a field of an existing item. */
@AppFunctionSerializable(isDescribedByKDoc = true)
data class SetField<T>(
/** The new value of the field. */
val value: T,
)
/**
* Updates an existing note. Omitted fields keep their values.
*
* @param noteId The ID of the note to update.
* @param title The new title of the note.
* @param content The new body text of the note.
* @return The updated note.
*/
@AppFunctionDeclaration(isDescribedByKDoc = true)
suspend fun updateNote(
noteId: String,
title: SetField<String>? = null,
content: SetField<String?>? = null,
): Note = withContext(Dispatchers.IO) {
val note = noteRepository.getNote(noteId)
?: throw AppFunctionElementNotFoundException(
"No note found with ID $noteId"
)
noteRepository.updateNote(
noteId = noteId,
// A null SetField means "don't change", and SetField(null) means
// "set to null", so don't use ?: for nullable fields.
title = title?.value ?: note.title,
content = if (content != null) content.value else note.content,
)
}
Xử lý lỗi
Khi một lệnh gọi không thành công, nhân viên hỗ trợ sẽ nhận được ngoại lệ mà hàm của bạn gửi, bao gồm cả loại và thông báo của ngoại lệ đó. Hãy tuân theo các quy ước sau để tác nhân có thể khôi phục sau lỗi hoặc báo cáo lỗi cho người dùng:
| Nên | Không nên |
|---|---|
Gửi lớp con AppFunctionException phù hợp với nguyên nhân, kèm theo thông báo mô tả vấn đề bằng ngôn ngữ đơn giản, ví dụ: AppFunctionInvalidArgumentException khi đối số không hợp lệ hoặc AppFunctionElementNotFoundException khi mục được yêu cầu không tồn tại. Loại lỗi cho tác nhân biết loại lỗi nào đã xảy ra và thông báo cung cấp cho tác nhân thông tin chi tiết cần thiết để khôi phục, chẳng hạn như sửa đối số hoặc thông báo cho người dùng. Ví dụ về updateNote trong phần Nội dung cập nhật cho tài nguyên hiện có cho thấy mẫu này. |
Đừng để các ngoại lệ khác thoát khỏi hàm của bạn. Tác nhân nhận được các lỗi này dưới dạng AppFunctionAppUnknownException, không xác định được loại lỗi. |