Section 4/141 menit
4. Desain Schema @Generable yang Optimal
4. Desain Schema @Generable yang Optimal
Prinsip Desain Schema
1. Flat lebih baik dari Nested yang Dalam
swift
// ❌ Terlalu dalam: model lebih sulit mengisi semua nested fields dengan akurat
@Generable
struct Report {
var metadata: Metadata
var content: Content
var analysis: Analysis
}
// di mana masing-masing punya 3-4 field nested lagi
// ✓ Flat atau maksimal 2 level: lebih akurat dan prediktabel
@Generable
struct Report {
@Guide(description: "Judul laporan")
var title: String
@Guide(description: "Nama penulis")
var authorName: String
@Guide(description: "Tanggal dalam format YYYY-MM-DD")
var date: String
@Guide(description: "Poin-poin utama laporan, maksimal 5")
var keyPoints: [String]
@Guide(description: "Kesimpulan satu paragraf")
var conclusion: String
}
2. Array dengan Batas Eksplisit
swift
// ❌ Tanpa batas: model mungkin menghasilkan terlalu banyak atau terlalu sedikit
@Generable
struct TaggedContent {
var tags: [String]
}
// ✓ Batas eksplisit di @Guide
@Generable
struct TaggedContent {
@Guide(description: "Antara 3 sampai 7 tag relevan, lowercase, dipisah tanpa duplikasi")
var tags: [String]
}
3. Optional yang Bermakna
swift
// ❌ Semua optional: model tidak tahu field mana yang penting
@Generable
struct ProductExtract {
var name: String?
var price: Double?
var brand: String?
var description: String?
}
// ✓ Required hanya yang selalu ada, optional untuk yang memang tidak pasti
@Generable
struct ProductExtract {
@Guide(description: "Nama produk — selalu ada dalam listing")
var name: String // Required — selalu ada
@Guide(description: "Harga dalam angka, nil jika tidak disebutkan eksplisit")
var price: Double? // Optional — mungkin tidak ada
@Guide(description: "Merek atau brand, nil jika produk generik")
var brand: String? // Optional — tidak selalu ada
@Guide(description: "Deskripsi singkat 1-2 kalimat")
var summary: String // Required — selalu bisa dibuat dari konteks
}
Komposisi Schema untuk Domain Kompleks
swift
// Domain model yang dipecah jadi schema yang lebih kecil dan dapat dikomposisi
@Generable
enum Priority: String {
case critical, high, medium, low
}
@Generable
enum IssueType: String {
case bug, feature, improvement, documentation, question
}
@Generable
struct IssueLabel {
@Guide(description: "Nama label, lowercase dengan dash, contoh: ui-bug, performance")
var name: String
@Guide(description: "Kategori label: technical, product, atau process")
var category: String
}
@Generable
struct ParsedIssue {
@Guide(description: "Judul issue yang singkat dan deskriptif")
var title: String
@Guide(description: "Jenis issue berdasarkan konten")
var type: IssueType
@Guide(description: "Prioritas berdasarkan dampak dan urgensi yang disebutkan")
var priority: Priority
@Guide(description: "Dua hingga empat label yang relevan")
var labels: [IssueLabel]
@Guide(description: "Langkah reproduksi jika ini bug, nil untuk non-bug")
var reproductionSteps: [String]?
@Guide(description: "Estimasi kompleksitas: simple, moderate, atau complex")
var complexity: String
}
Validasi Post-Generation
@Generable menjamin tipe data, tapi tidak menjamin nilai yang valid secara semantik. Selalu validasi setelah generate:
swift
struct IssueParser {
private let session = LanguageModelSession()
func parse(issueText: String) async throws -> ParsedIssue {
let raw = try await session.respond(
to: "Parse issue GitHub berikut:\n\n\(issueText)",
generating: ParsedIssue.self
).content
return try validate(raw)
}
private func validate(_ issue: ParsedIssue) throws -> ParsedIssue {
guard !issue.title.trimmingCharacters(in: .whitespaces).isEmpty else {
throw IssueParseError.emptyTitle
}
guard issue.labels.count >= 2 else {
throw IssueParseError.insufficientLabels
}
// Validasi semantik lainnya
return issue
}
}