MobileAgentSDK adalah pustaka Android yang membungkus MobileAgent SDK berbasis C++ dan menyediakan antarmuka yang ramah Kotlin/Java. SDK ini terhubung ke server MobileAgent melalui WebSocket untuk mengeksekusi, mengelola, dan memantau tugas agen.
Unduh SDK
Penggunaan
Impor AAR
Salin file AAR yang dihasilkan ke dalam proyek Anda dan tambahkan dependensi berikut ke file build.gradle Anda:
dependencies {
implementation(files('libs/sdk-release.aar'))
}Penggunaan dasar
Opsi 1: Gunakan Flow (disarankan)
Flow menyediakan gaya pemrograman reaktif yang sangat cocok untuk korutin Kotlin:
import com.wuying.mobileagentsdk.MobileAgentSdk
import com.wuying.mobileagentsdk.MobileAgentEvent
import com.wuying.mobileagentsdk.MobileAgentState
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.launch
import kotlinx.coroutines.flow.collectLatest
// Gunakan try-with-resources untuk mengelola siklus hidup secara otomatis.
MobileAgentSdk().use { sdk ->
// Kumpulkan event dalam korutin.
lifecycleScope.launch {
sdk.events.collectLatest { event ->
when (event) {
is MobileAgentEvent.StateChanged -> {
when (event.state) {
MobileAgentState.CONNECTED -> {
Log.d("SDK", "Connected")
// Eksekusi tugas.
sdk.executeTask("Please help me open settings", 10)
}
MobileAgentState.DISCONNECTED -> {
Log.d("SDK", "Disconnected")
}
else -> {}
}
}
is MobileAgentEvent.TaskResult -> {
Log.d("SDK", "Task complete: ${event.resultText}")
}
is MobileAgentEvent.Stream -> {
Log.d("SDK", "Stream data: ${event.content}")
}
else -> {}
}
}
}
// Anda juga dapat mengumpulkan hanya jenis event tertentu.
lifecycleScope.launch {
sdk.events
.filterIsInstance<MobileAgentEvent.StateChanged>()
.collect { event ->
Log.d("SDK", "State: ${event.state}")
}
}
lifecycleScope.launch {
sdk.events
.filterIsInstance<MobileAgentEvent.TaskResult>()
.collect { event ->
Log.d("SDK", "Task result: ${event.resultText}")
}
}
// Terhubung ke server.
sdk.connect("wss://localhost:30005/ws")
// SDK secara otomatis dihapus di akhir blok use.
}Opsi 2: Gunakan callback
Pendekatan ini ditujukan untuk proyek yang tidak menggunakan korutin:
import com.wuying.mobileagentsdk.MobileAgentSdk
import com.wuying.mobileagentsdk.MobileAgentCallback
import com.wuying.mobileagentsdk.MobileAgentState
MobileAgentSdk().use { sdk ->
// Atur callback.
sdk.setCallback(object : MobileAgentCallback {
override fun onStateChanged(state: MobileAgentState) {
when (state) {
MobileAgentState.CONNECTED -> {
Log.d("SDK", "Connected")
sdk.executeTask("Please help me open settings", 10)
}
MobileAgentState.DISCONNECTED -> {
Log.d("SDK", "Disconnected")
}
else -> {}
}
}
override fun onTaskResult(
taskId: String,
result: String,
resultText: String,
success: Boolean,
actualSteps: Int
) {
Log.d("SDK", "Task complete: $resultText")
}
override fun onStream(
taskId: String,
step: Int,
agent: String,
contentType: String,
content: String
) {
Log.d("SDK", "Stream data: $content")
}
// Implementasikan callback lain sesuai kebutuhan.
})
// Terhubung ke server.
sdk.connect("wss://localhost:30005/ws")
}Manajemen siklus hidup
SDK mengimplementasikan antarmuka AutoCloseable, yang mendukung pola try-with-resources:
// Disarankan: Kelola siklus hidup secara otomatis.
MobileAgentSdk().use { sdk ->
sdk.connect("wss://localhost:30005/ws")
// Gunakan SDK...
} // Secara otomatis memanggil close() untuk melepas sumber daya.
// Atau, kelola siklus hidup secara manual.
val sdk = MobileAgentSdk()
try {
sdk.connect("wss://localhost:30005/ws")
// Gunakan SDK...
} finally {
sdk.close() // atau sdk.destroy()
}API
Manajemen koneksi
connect(url: String)
Terhubung ke server menggunakan URL WebSocket:
sdk.connect("wss://localhost:30005/ws")connectWithTicket(ticket: String)
Terhubung menggunakan tiket (memerlukan dukungan dari sisi server):
sdk.connectWithTicket("your-ticket-here")disconnect()
Putuskan koneksi dari server:
sdk.disconnect()Manajemen tugas
executeTask(task: String, maxSteps: Int): String
Eksekusi tugas dan kembalikan ID tugas:
val taskId = sdk.executeTask("Please help me open the settings app", 10)pauseTask(taskId: String)
Jeda tugas:
sdk.pauseTask(taskId)resumeTask(taskId: String)
Lanjutkan tugas:
sdk.resumeTask(taskId)cancelTask(taskId: String)
Batalkan tugas:
sdk.cancelTask(taskId)Kueri status
getState(): MobileAgentState
Ambil status koneksi saat ini:
val state = sdk.getState()
when (state) {
MobileAgentState.CONNECTED -> // Connected
MobileAgentState.CONNECTING -> // Connecting
MobileAgentState.DISCONNECTED -> // Disconnected
}API Flow
SDK menyediakan Flow events yang mengeluarkan semua jenis event. Anda dapat menggunakan filterIsInstance untuk memfilter event tertentu:
// Kumpulkan semua event.
sdk.events.collect { event ->
when (event) {
is MobileAgentEvent.StateChanged -> // Tangani perubahan status.
is MobileAgentEvent.TaskResult -> // Tangani hasil tugas.
is MobileAgentEvent.Stream -> // Tangani data aliran.
// ...
}
}
// Kumpulkan hanya jenis event tertentu.
sdk.events
.filterIsInstance<MobileAgentEvent.StateChanged>()
.collect { event ->
// Tangani hanya perubahan status.
}Jenis event:
Jenis event | Deskripsi |
| Status koneksi berubah. |
| Daftar to-do diperbarui. |
| Tugas dimulai. |
| Langkah dimulai. |
| Data aliran diterima. |
| Langkah selesai. |
| Hasil tugas diterima. |
API Callback
Antarmuka MobileAgentCallback menyediakan metode callback berikut. Semua metode memiliki implementasi kosong bawaan, sehingga Anda hanya perlu meng-override yang diperlukan:
Metode | Deskripsi |
| Status koneksi berubah |
| Daftar to-do diperbarui |
| Tugas dimulai |
| Langkah dimulai |
| Data aliran diterima |
| Langkah selesai |
| Hasil tugas diterima |
| Pesan JSON mentah yang belum diurai. |
Kelas data
MobileAgentState
Enum untuk status koneksi:
enum class MobileAgentState {
DISCONNECTED, // Disconnected
CONNECTING, // Connecting
CONNECTED // Connected
}TodoItem
Kelas data untuk item to-do:
data class TodoItem(
val id: String,
val title: String,
val status: String,
val details: String
)MobileAgentEvent
Kelas sealed yang merepresentasikan semua jenis event yang mungkin:
sealed class MobileAgentEvent {
data class StateChanged(val state: MobileAgentState)
data class TodoWrite(val todos: Array<TodoItem>, val count: Int)
data class TaskStart(val taskId: String, val task: String, val maxSteps: Int)
data class StepStart(val taskId: String, val step: Int)
data class Stream(val taskId: String, val step: Int, val agent: String, val contentType: String, val content: String)
data class StepEnd(val taskId: String, val step: Int, val status: String, val summary: String)
data class TaskResult(val taskId: String, val result: String, val resultText: String, val success: Boolean, val actualSteps: Int)
}Penggunaan lanjutan
Mengurai pikiran agen
MobileAgent menggunakan tag <conclusion> dalam output alirannya untuk membungkus proses berpikir agen. Anda dapat mengekstrak konten ini dari event Stream.
Format <conclusion>
Agen menggunakan tag XML untuk menandai proses berpikirnya dalam output:
<conclusion>This is the agent's thought process...</conclusion>Implementasi Kotlin
Kode berikut menunjukkan cara mengurai konten pikiran saat diterima:
import com.wuying.mobileagentsdk.MobileAgentSdk
import com.wuying.mobileagentsdk.MobileAgentEvent
class ConclusionParser {
private val conclusionBuffer = StringBuilder()
private var inConclusion = false
/**
* Memproses data aliran untuk mengekstrak konten dari tag <conclusion>.
*/
fun processStream(content: String): String? {
val result = StringBuilder()
for (char in content) {
if (!inConclusion) {
// Tidak berada di dalam kesimpulan; periksa awal <conclusion>.
conclusionBuffer.append(char)
// Periksa <conclusion>.
if (conclusionBuffer.length >= 12) {
if (conclusionBuffer.endsWith("<conclusion>")) {
inConclusion = true
conclusionBuffer.clear()
result.append("[Thought] ")
} else if (conclusionBuffer.length > 12) {
// Simpan 11 karakter terakhir untuk pemeriksaan berikutnya.
conclusionBuffer.delete(0, conclusionBuffer.length - 11)
}
}
} else {
// Di dalam kesimpulan; periksa tag penutup.
conclusionBuffer.append(char)
// Periksa </conclusion>.
if (conclusionBuffer.length >= 13) {
if (conclusionBuffer.endsWith("</conclusion>")) {
// Menemukan tag penutup.
inConclusion = false
val conclusionContent = conclusionBuffer.toString()
.dropLast(13) // Hapus </conclusion>.
.trim()
if (conclusionContent.isNotEmpty()) {
result.append(conclusionContent).append("\n")
}
conclusionBuffer.clear()
continue
} else if (conclusionBuffer.length > 13) {
// Simpan 12 karakter terakhir untuk pemeriksaan berikutnya.
conclusionBuffer.delete(0, conclusionBuffer.length - 12)
}
}
// Jika bukan bagian dari tag penutup potensial, tampilkan segera.
if (!couldBeEndTag(conclusionBuffer.toString())) {
result.append(char)
}
}
}
return if (result.isNotEmpty()) result.toString() else null
}
/**
* Memeriksa apakah akhir buffer bisa menjadi awalan dari tag penutup.
*/
private fun couldBeEndTag(buffer: String): Boolean {
val prefixes = listOf(
"<", "</", "</c", "</co", "</con", "</conc",
"</concl", "</conclu", "</conclus", "</conclusi",
"</conclusio", "</conclusion"
)
return prefixes.any { buffer.endsWith(it) }
}
/**
* Mengatur ulang status parser.
*/
fun reset() {
conclusionBuffer.clear()
inConclusion = false
}
}
// Contoh penggunaan
MobileAgentSdk().use { sdk ->
val parser = ConclusionParser()
lifecycleScope.launch {
sdk.events
.filterIsInstance<MobileAgentEvent.Stream>()
.collect { event ->
// Uraikan dan cetak output.
val output = parser.processStream(event.content)
if (output != null) {
print(output)
}
}
}
sdk.connect("wss://localhost:30005/ws")
}Versi yang disederhanakan
Jika output real-time tidak diperlukan, Anda dapat mengurai konten setelah seluruhnya diterima:
/**
* Mengekstrak semua konten dari tag <conclusion> dalam string lengkap.
*/
fun extractConclusions(content: String): List<String> {
val conclusions = mutableListOf<String>()
val regex = "<conclusion>(.*?)</conclusion>".toRegex(RegexOption.DOT_MATCHES_ALL)
for (match in regex.findAll(content)) {
val conclusion = match.groupValues[1].trim()
if (conclusion.isNotEmpty()) {
conclusions.add(conclusion)
}
}
return conclusions
}
// Contoh penggunaan
lifecycleScope.launch {
sdk.events
.filterIsInstance<MobileAgentEvent.TaskResult>()
.collect { event ->
// Ekstrak proses berpikir dari hasil tugas.
val conclusions = extractConclusions(event.resultText)
println("Proses berpikir agen:")
conclusions.forEachIndexed { index, conclusion ->
println("${index + 1}. $conclusion")
}
}
}Output
[Thought] The user wants to open the Settings app. I need to find its package name and Activity.
[Thought] By querying the system app list, I found the Settings app: com.android.settings/.Settings
[Thought] Now I will start the Settings app.
Task complete: Successfully opened the settings app.Menggunakan Flow dan callback bersamaan
Anda dapat menggunakan kedua pendekatan secara bersamaan tanpa gangguan:
MobileAgentSdk().use { sdk ->
// Gunakan Flow untuk menangani logika utama.
lifecycleScope.launch {
sdk.events
.filterIsInstance<MobileAgentEvent.TaskResult>()
.collect { event ->
// Tangani hasil tugas.
}
}
// Gunakan callback untuk logging.
sdk.setCallback(object : MobileAgentCallback {
override fun onRawMessage(rawJson: String) {
// Catat pesan mentah untuk debugging.
Log.d("SDK_RAW", rawJson)
}
})
sdk.connect("wss://localhost:30005/ws")
}Penanganan error
try {
MobileAgentSdk().use { sdk ->
sdk.connect("wss://localhost:30005/ws")
}
} catch (e: IllegalStateException) {
Log.e("SDK", "Gagal membuat SDK", e)
} catch (e: Exception) {
Log.e("SDK", "Error koneksi", e)
}Periksa status SDK
val sdk = MobileAgentSdk()
// Periksa apakah SDK telah dihapus.
if (sdk.isDestroyed()) {
Log.w("SDK", "SDK telah dihapus.")
}
// Periksa apakah SDK valid.
sdk.checkNotDestroyed() // Melempar exception jika telah dihapus.Konfigurasi ProGuard
Jika Anda mengaktifkan obfuscation kode, Anda harus mempertahankan metode native SDK. SDK sudah menyertakan file proguard-rules.pro, sehingga biasanya Anda tidak perlu konfigurasi tambahan.
Jika Anda perlu mengonfigurasinya secara manual:
# MobileAgentSDK
-keep class com.wuying.mobileagentsdk.** { *; }
-keepclassmembers class com.wuying.mobileagentsdk.MobileAgentSdk {
private native <methods>;
}Dependensi
SDK memiliki dependensi runtime minimal:
dependencies {
// Satu-satunya dependensi runtime
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
}Untuk tetap ringan, SDK tidak menyertakan framework UI apa pun, seperti AppCompat atau Material.