Dalam dunia yang berbasis data, data tidak terstruktur seperti teks, gambar, audio, video, dan log—bersama dengan data terstruktur dan semi-terstruktur seperti JSON—membentuk aset data inti suatu perusahaan. Data tidak terstruktur dalam bentuk mentah dan beragam ini menyimpan wawasan bisnis yang sangat luas, termasuk umpan balik pengguna, ketentuan kontrak, dan gambar cacat produk. Topik ini menjelaskan cara mengambil dan menganalisis file PDF, seperti prospektus dan kontrak, dalam skenario keuangan untuk membantu Anda membuat keputusan operasional dengan detail halus.
Kemampuan inti
Praktik terbaik ini berfokus pada pemrosesan dan pengambilan data PDF tidak terstruktur menggunakan kemampuan Hologres berikut:
Object Table: Membaca data tidak terstruktur, seperti file PDF, gambar, dan PPT, dari Object Storage Service (OSS) dalam format tabel.
Fungsi AI: Memanggil fungsi AI yang didukung oleh model bahasa besar (LLM) bawaan menggunakan sintaks SQL standar untuk membangun layanan AI.
Transformasi data: Gunakan operator Embed dan Chunk untuk mengubah data tidak terstruktur menjadi data terstruktur guna penyimpanan. Anda dapat menyematkan data secara otomatis tanpa menggunakan algoritma eksternal.
Pengambilan dan analitik data: Gunakan operator seperti
ai_gendanai_summarizeuntuk melakukan operasi seperti inferensi, ringkasan, dan terjemahan menggunakan SQL.
Dynamic Table: Gunakan mode refresh inkremental untuk memproses data tidak terstruktur secara otomatis. Mode ini hanya menghitung data inkremental, sehingga mengurangi perhitungan berulang dan penggunaan sumber daya.
Vector search: Gunakan SQL standar untuk pencarian vektor guna melakukan pencarian kemiripan dan pengenalan skenario pada data tidak terstruktur. Anda dapat menggabungkan pencarian vektor dan skalar dalam kueri yang sama.
Full-text search: Gunakan mekanisme seperti indeks terbalik dan tokenisasi untuk mengambil data tidak terstruktur secara efisien. Fitur ini mendukung berbagai metode pengambilan, seperti pencocokan kata kunci dan pencarian frasa, untuk pengambilan yang lebih fleksibel.
Manfaat solusi
Menggunakan kemampuan inti tersebut, pengambilan dan analisis AI multimodal di Hologres memberikan manfaat berikut:
Alur pemrosesan data AI yang lengkap: Meliputi seluruh proses mulai dari penyematan data, pemisahan chunk, pemrosesan inkremental, hingga pengambilan dan analisis. Pengembang dapat dengan mudah membangun aplikasi AI dengan cara yang sama seperti membangun sistem data besar.
Proses dan analisis data tidak terstruktur dengan SQL standar: Gunakan SQL murni untuk mengekstraksi dan memproses data tidak terstruktur tanpa memerlukan bahasa pengembangan khusus atau sistem eksternal. Hal ini membuat pemrosesan data lebih sederhana dan efisien, serta mengurangi kurva pembelajaran bagi pengembang.
Pengambilan yang lebih akurat, fleksibel, dan cerdas: Dengan mudah membangun pipeline pengambilan hibrida yang menggabungkan pencarian kata kunci, semantik, dan multimodal untuk memenuhi semua kebutuhan skenario—mulai dari pencarian tepat hingga pengenalan niat. Anda juga dapat menggunakan fungsi AI untuk memahami maksud pengguna secara mendalam, asosiasi semantik, dan inferensi kontekstual guna menghadirkan pengambilan yang lebih cerdas.
Lebih aman karena data tidak diekspor: Anda tidak perlu mengekspor data ke sistem eksternal. Solusi ini terintegrasi secara mulus dengan berbagai fitur keamanan Hologres untuk memastikan keamanan data.
Topik ini menjelaskan cara menggunakan kemampuan inti tersebut untuk memproses dan mengambil data tidak terstruktur di Hologres. Hal ini membantu Anda membangun platform data AI multimodal tingkat perusahaan, menghilangkan silodata, dan membuka nilai seluruh data Anda.
Alur kerja
Alur kerja untuk solusi ini adalah sebagai berikut:
Persiapkan set data.
Unggah file PDF dari set data keuangan ke OSS.
Proses data PDF.
Gunakan Object Table untuk membaca metadata file PDF. Kemudian, buat tabel dinamis yang mendukung refresh inkremental untuk menyematkan dan membagi data menjadi chunk. Selain itu, bangun indeks vektor dan teks penuh pada tabel dinamis untuk mempercepat pengambilan selanjutnya.
Gunakan operator
ai_embeduntuk menyematkan pertanyaan dalam bahasa alami. Lalu, gunakan pengambilan ganda teks penuh dan vektor untuk mengambil hasil dan mengurutkannya. Terakhir, gunakan kemampuan inferensi LLM untuk menghasilkan jawaban dengan kemiripan tertinggi.
Persiapan
Persiapan data
Topik ini menggunakan 80 prospektus perusahaan dalam folder PDF dari set data keuangan publik di ModelScope.
Persiapan lingkungan
Beli instans Hologres versi V4.0 atau yang lebih baru dan buat database.
Topik ini menggunakan satu node large-96core-512GB-384GB sebagai contoh.
Deploy model. Model dan alokasi sumber daya untuk solusi ini adalah sebagai berikut:
Nama model
Kategori model
Deskripsi model
vCPU per replika
Memori per replika
GPU per replika
Jumlah replika
to_doc
ds4sd/docling-models
Mengonversi file PDF menjadi dokumen.
20
100 GB
1 card (48 GB)
1
chunk
recursive-character-text-splitter
Membagi dokumen menjadi chunk. Pemisahan chunk direkomendasikan karena setiap file PDF berukuran besar.
15
30 GB
0 cards (0 GB)
1
pdf_embed
BAAI/bge-base-zh-v1.5
Menyematkan dokumen.
7
30 GB
1 card (96 GB)
1
llm
Qwen/Qwen3-32B
Menggunakan LLM untuk melakukan inferensi pada konten dokumen yang diambil berdasarkan prompt.
7
30 GB
1 card (96 GB)
1
CatatanModel menggunakan alokasi sumber daya default.
Prosedur
Unduh file PDF dan unggah ke OSS.
Unduh 80 prospektus (PDF) dari Bosera-JM 14B Challenge Dataset for Finance.
Login ke Konsol OSS, buat bucket, dan unggah file PDF yang telah diunduh ke path bucket tersebut. Untuk informasi lebih lanjut, lihat Simple upload.
Berikan izin.
Login ke Konsol RAM, buat peran RAM, dan berikan izin OSS yang diperlukan.
Kami merekomendasikan memberikan izin
AliyunOSSReadOnlyAccess.Tambahkan izin login dan akses Hologres ke peran RAM.
Akun Alibaba Cloud
Ubah langsung kebijakan kepercayaan peran RAM. Salin dan tempel kode JSON berikut serta perbarui dua parameter berikut:
Action: Perbarui menjadists:AssumeRole.Service: Perbarui menjadihologres.aliyuncs.com.
{ "Statement": [ { "Action": "sts:AssumeRole", "Effect": "Allow", "Principal": { "RAM": [ "acs:ram::1866xxxx:root" ], "Service": [ "hologres.aliyuncs.com" ] } } ], "Version": "1" }Pengguna RAM
Berikan izin kepada pengguna RAM.
Di Konsol RAM, buka , klik Create Policy. Di halaman Create Policy, pilih tab JSON. Salin dan tempel kode JSON berikut untuk membuat kebijakan kustom yang memungkinkan pengguna RAM membuat pemetaan pengguna untuk RoleARN.
Untuk petunjuk lengkap, lihat Buat kebijakan kustom.
{ "Version": "1", "Statement": [ { "Effect": "Allow", "Action": "hologram:GrantAssumeRole", "Resource": "<RoleARN>" } ] }Buka , klik Add Permissions di kolom Actions pengguna RAM target, lalu berikan kebijakan kustom tersebut. Lihat Berikan izin kepada pengguna RAM.
Berikan izin kepada peran RAM.
Ubah kebijakan kepercayaan peran RAM. Parameter utama yang perlu diperbarui adalah sebagai berikut:
Action: Perbarui menjadi
sts:AssumeRole.Service: Perbarui menjadi
hologres.aliyuncs.com.
{ "Statement": [ { "Action": "sts:AssumeRole", "Effect": "Allow", "Principal": { "RAM": [ "acs:ram::1866xxxx:root" ], "Service": [ "hologres.aliyuncs.com" ] } } ], "Version": "1" }
Menyematkan dan membagi file PDF menjadi chunk.
Buat Object Table dan Dynamic Table untuk membaca dan memproses metadata PDF. Untuk menyederhanakan proses multi-langkah ini, Hologres menyediakan prosedur tersimpan. Prosedur tersimpan ini menyediakan kemampuan berikut:
Membuat Object Table untuk membaca metadata PDF.
Membuat tabel sink untuk tabel dinamis dalam mode refresh inkremental guna menyimpan data yang diproses. Indeks vektor dan teks penuh dikonfigurasi pada tabel ini. Tabel dinamis harus direfresh secara manual.
Saat refresh tabel dinamis, data disematkan dan dibagi menjadi chunk menggunakan
ai_embeddanai_chunk.
Kode untuk prosedur tersimpan adalah sebagai berikut:
CALL create_rag_corpus_from_oss( oss_path => 'oss://xxxx/bs_challenge_financial_14b_dataset/pdf', oss_endpoint => 'oss-cn-hangzhou-internal.aliyuncs.com', oss_role_arn => 'acs:ram::186xxxx:role/xxxx', corpus_table => 'public.dt_bs_challenge_financial' );Refresh tabel sink.
Untuk menyelesaikan transformasi data, Anda harus merefresh Object Table dan Dynamic Table secara manual. Hologres menyediakan prosedur tersimpan untuk pemrosesan PDF, yang menyediakan kemampuan berikut:
Refresh Object Table untuk mendapatkan metadata PDF.
Refresh tabel dinamis untuk melakukan penyematan dan pemisahan chunk PDF.
Kode berikut memanggil prosedur tersimpan ini:
CALL refresh_rag_corpus_table( corpus_table => 'public.dt_bs_challenge_financial' );Cari file PDF.
Setelah data diproses, Anda dapat melakukan pencarian vektor atau teks penuh berdasarkan skenario bisnis Anda. Misalnya, Anda dapat mengkueri tren kinerja suatu perusahaan dari prospektusnya untuk menentukan prospek masa depan perusahaan tersebut. Informasi ini dapat menjadi dasar keputusan investasi di masa depan.
Pencarian vektor
Untuk pencarian vektor, Hologres menyediakan fungsi pencarian vektor yang merangkum proses seperti penyematan pertanyaan, konstruksi prompt, dan generasi jawaban oleh LLM. Anda dapat langsung memanggil fungsi ini untuk melakukan pengambilan vektor.
-- Pengambilan hanya vektor + penyusunan ulang AI SELECT qa_vector_search_retrieval( question => 'Seberapa persen peningkatan pendapatan operasional dan laba bersih Goke Microelectronics secara year-over-year pada tahun 2014, 2015, dan 2016 selama periode pelaporan?', corpus_table => 'dt_bs_challenge_financial', prompt => 'Harap analisis apakah tren kinerja berikut bersifat pesimistis atau optimistis dan berikan alasannya: ${question}\n\n Informasi referensi:\n\n ${context}' )Jawaban yang diambil adalah sebagai berikut:
qa_retrieval --------- "Berdasarkan informasi yang diberikan, analisis tren kinerja Goke Microelectronics menghasilkan kesimpulan berikut: ### I. Analisis Tren Kinerja: Pesimistis #### 1. **Pertumbuhan pendapatan yang lambat** - Pendapatan pada tahun 2014 meningkat sebesar **15,13%** year-over-year, tetapi pada tahun 2015 **menurun sebesar 5,21%**. Data untuk tahun 2016 tidak tersedia, tetapi jelas bahwa tren pertumbuhan pendapatan mengalami penurunan signifikan pada tahun 2015. - Tingkat pertumbuhan tahunan gabungan (CAGR) pendapatan dari tahun 2012 hingga 2014 hanya **4,47%**, menunjukkan ekspansi bisnis yang lambat. #### 2. **Penurunan berkelanjutan dalam pertumbuhan laba bersih** - Laba bersih tumbuh sebesar **5,43%** pada tahun 2014 tetapi **menurun sebesar 3,29%** pada tahun 2015. - Setelah mengurangi keuntungan dan kerugian non-recurring, laba bersih yang dapat diatribusikan kepada pemegang saham induk menurun sebesar **3,14%** pada tahun 2014 dan lebih lanjut berkurang sebesar **5,60%** pada tahun 2015. Ini menunjukkan bahwa profitabilitas bisnis utama perusahaan terus memburuk. - CAGR laba bersih setelah mengurangi keuntungan dan kerugian non-recurring dari tahun 2012 hingga 2014 adalah **-4,38%**. Angka ini jauh lebih rendah daripada pertumbuhan pendapatan, menunjukkan bahwa bisnis utama tidak menguntungkan dan pertumbuhan bergantung pada keuntungan dan kerugian non-recurring. #### 3. **Proporsi tinggi keuntungan dan kerugian non-recurring** - Selama periode pelaporan, keuntungan dan kerugian non-recurring menyumbang proporsi tinggi terhadap laba bersih: **17,54%**, **10,25%**, dan **8,06%** pada tahun 2014, 2013, dan 2012, masing-masing. Ini menunjukkan bahwa sebagian laba perusahaan berasal dari faktor non-recurring seperti dukungan kebijakan dan subsidi pemerintah, bukan dari pertumbuhan berkelanjutan bisnis intinya. - Mengandalkan keuntungan dan kerugian non-recurring untuk mempertahankan pertumbuhan laba tidak kondusif bagi pengembangan stabil jangka panjang perusahaan. #### 4. **Penurunan return on equity (ROE)** - ROE rata-rata tertimbang meningkat dari **18,10%** pada tahun 2014 menjadi **24,82%** pada tahun 2015, lalu menjadi **28,23%** pada tahun 2016. Meskipun data tampak menunjukkan pertumbuhan, perlu dicatat bahwa indikator ini dihitung berdasarkan laba bersih setelah mengurangi keuntungan dan kerugian non-recurring, sedangkan laba bersih itu sendiri menurun. Oleh karena itu, pertumbuhan ini mungkin terkait dengan perubahan struktur modal, bukan peningkatan substansial dalam profitabilitas. ### II. Ringkasan 1. **Pertumbuhan bisnis utama yang lambat**: Baik pertumbuhan pendapatan maupun laba bersih menunjukkan tren menurun, terutama penurunan laba bersih, yang mengindikasikan melemahnya profitabilitas. 2. **Ketergantungan tinggi pada keuntungan dan kerugian non-recurring**: Keuntungan dan kerugian non-recurring menyumbang proporsi tinggi terhadap laba perusahaan, menunjukkan bahwa bisnis utama tidak cukup menguntungkan dan keberlanjutan kinerja perusahaan dipertanyakan. 3. **Persaingan pasar yang ketat**: Pasar komputer industri, display, dan catu daya yang dibeli perusahaan sangat kompetitif, dengan harga stabil dan margin laba yang terkompresi. 4. **Lingkungan pasar**: Fluktuasi harga pasar stainless steel dan harga bahan baku mungkin berdampak tertentu pada pendapatan operasional perusahaan. Meskipun perusahaan telah mengambil langkah-langkah untuk mengurangi dampak tersebut, hal ini tetap perlu diperhatikan dalam jangka panjang. ### III. Kesimpulan Secara keseluruhan, tren kinerja Goke Microelectronics bersifat **pesimistis**. Pertumbuhan bisnis utama perusahaan lambat, laba bersih terus menurun, dan sangat bergantung pada keuntungan dan kerugian non-recurring. Keberlanjutan profitabilitas di masa depan dipertanyakan. Perusahaan perlu memperkuat daya saing bisnis intinya, mengoptimalkan struktur biaya, dan meningkatkan profitabilitas bisnis utamanya untuk mencapai pengembangan stabil jangka panjang."Pencarian teks penuh
Untuk pencarian teks penuh, Hologres menyediakan fungsi pencarian teks penuh yang merangkum proses seperti penyematan pertanyaan, konstruksi prompt, dan generasi jawaban oleh LLM. Anda dapat langsung memanggil fungsi ini untuk melakukan pengambilan teks penuh.
--Pengambilan pencarian teks penuh SELECT qa_text_search_retrieval( question => 'Seberapa persen peningkatan pendapatan operasional dan laba bersih Goke Microelectronics secara year-over-year pada tahun 2014, 2015, dan 2016 selama periode pelaporan?', corpus_table => 'dt_bs_challenge_financial', prompt => 'Harap analisis apakah tren kinerja berikut bersifat pesimistis atau optimistis dan berikan alasannya: ${question}\n\n Informasi referensi:\n\n ${context}' );Jawaban yang diambil adalah sebagai berikut:
qa_text_search_retrieval ---------------- "Berdasarkan informasi yang diberikan, tren kinerja keseluruhan Goke Microelectronics pada tahun 2014, 2015, dan 2016 bersifat **pesimistis**. Alasan spesifiknya adalah sebagai berikut: ### 1. **Pertumbuhan pendapatan yang lambat** - Tingkat pertumbuhan pendapatan pada tahun 2014 adalah **15,13%**, tetapi pada tahun 2015 berubah menjadi **-5,21%**, menunjukkan pertumbuhan negatif. - CAGR pendapatan dari tahun 2012 hingga 2014 hanya **4,47%**, menunjukkan bahwa pertumbuhan pendapatan perusahaan lambat dan pengembangan bisnisnya tidak kuat. - Proyeksi pendapatan untuk paruh pertama tahun 2015 hampir sama dengan periode yang sama pada tahun 2014, tetapi laba bersih untuk paruh pertama tahun 2015 **sedikit menurun** dibandingkan periode yang sama tahun sebelumnya, menunjukkan penurunan profitabilitas. ### 2. **Pertumbuhan laba bersih dan laba bersih tanpa pos luar biasa yang buruk** - Tingkat pertumbuhan laba bersih pada tahun 2014 adalah **5,43%**, tetapi turun menjadi **-3,29%** pada tahun 2015, artinya laba bersih menurun. - Tingkat pertumbuhan laba bersih setelah mengurangi keuntungan dan kerugian non-recurring adalah **-3,14%** pada tahun 2014 dan lebih lanjut menurun menjadi **-5,60%** pada tahun 2015, menunjukkan bahwa profitabilitas bisnis utama perusahaan terus menurun. - CAGR laba bersih tanpa pos luar biasa dari tahun 2012 hingga 2014 adalah **-4,38%**, yang secara signifikan lebih rendah daripada CAGR pendapatan. Ini menunjukkan bahwa profitabilitas bisnis utama perusahaan lemah. ### 3. **Fluktuasi arus kas dari aktivitas operasional** - Pada tahun 2014, proporsi kas dari penjualan barang dan penyediaan jasa terhadap pendapatan operasional menurun dibandingkan dua tahun sebelumnya. Hal ini terutama disebabkan oleh **penundaan pembayaran** beberapa pos pendapatan, menunjukkan masalah dalam manajemen arus kas perusahaan. - Pada tahun 2013, proporsi kas untuk pembelian barang dan jasa terhadap biaya operasional relatif tinggi. Hal ini terutama karena **bahan baku dibeli dan produksi diselesaikan** pada tahun tersebut, tetapi beberapa biaya tidak dialihkan hingga tahun 2014, sehingga proporsinya lebih rendah pada tahun 2014. Ini mencerminkan bahwa laju pengadaan dan produksi perusahaan tidak stabil. ### 4. **Indikator investasi dan profitabilitas** - ROE rata-rata tertimbang adalah **18,10%** pada tahun 2014, naik menjadi **24,82%** pada tahun 2015, dan lebih lanjut meningkat menjadi **28,23%** pada tahun 2016. Meskipun ada peningkatan, peningkatan ROE mungkin terutama disebabkan oleh **leverage keuangan** daripada peningkatan profitabilitas bisnis inti. - Mengingat penurunan berkelanjutan dalam laba bersih dan laba bersih tanpa pos luar biasa, peningkatan ROE tidak sepenuhnya mencerminkan peningkatan kinerja operasional perusahaan. ### 5. **Proyeksi kinerja untuk paruh pertama tahun 2015** - Pendapatan operasional yang diperkirakan untuk H1 2015 adalah **CNY 85,05 juta hingga 103,95 juta**, yang mirip dengan **CNY 101,2735 juta** pada H1 2024. Namun, laba bersih yang diperkirakan adalah **CNY 23,40 juta hingga 28,60 juta**, yang lebih rendah daripada **CNY 29,1266 juta** pada tahun 2014, menunjukkan penurunan profitabilitas perusahaan. ### Ringkasan Secara keseluruhan, tren kinerja Goke Microelectronics dari tahun 2014 hingga 2016 bersifat **pesimistis**. Meskipun ROE membaik, pertumbuhan pendapatan yang lambat, penurunan berkelanjutan dalam laba bersih dan laba bersih tanpa pos luar biasa, serta fluktuasi arus kas dari aktivitas operasional menunjukkan bahwa profitabilitas bisnis utama lemah dan kualitas operasional perlu ditingkatkan."Pencarian hibrida
Dalam skenario pencarian hibrida yang menggabungkan pencarian vektor, pencarian teks penuh, dan peringkat, Hologres menyediakan fungsi pencarian hibrida dengan peringkat. Fungsi ini memiliki kemampuan berikut:
Mengambil 20 jawaban teratas menggunakan perhitungan vektor berdasarkan pertanyaan.
Mengambil 20 jawaban teratas menggunakan pencarian teks penuh berdasarkan pertanyaan.
Menggunakan
ai_rankuntuk mengurutkan jawaban dari pencarian vektor dan teks penuh, lalu menghasilkan jawaban teratas.Menggunakan
ai_gendan LLM untuk menghasilkan jawaban akhir berdasarkan prompt dan jawaban yang diambil.
-- Pengambilan ganda teks penuh dan vektor + penyusunan ulang AI SELECT qa_hybrid_retrieval( question => 'Seberapa persen peningkatan pendapatan operasional dan laba bersih Hunan Goke Microelectronics Co., Ltd. secara year-over-year pada tahun 2014, 2015, dan 2016 selama periode pelaporan?', corpus_table => 'dt_bs_challenge_financial', prompt => 'Harap analisis apakah tren kinerja berikut bersifat pesimistis atau optimistis dan berikan alasannya: ${question}\n\n Informasi referensi:\n\n ${context}' );Jawaban yang diambil adalah sebagai berikut:
qa_hybrid_retrieval --- "Berdasarkan informasi yang diberikan, kami dapat menganalisis tren kinerja Goke Microelectronics dan menentukan apakah tren tersebut pesimistis atau optimistis sebagai berikut: --- ### I. **Analisis Tren Pendapatan Operasional** 1. **CAGR 2012-2014**: - CAGR pendapatan operasional adalah **4,47%**, menunjukkan bahwa pertumbuhan pendapatan perusahaan relatif stabil. - Pada tahun 2014, pendapatan operasional adalah **CNY 181,5406 juta**, meningkat **15,13%** dari tahun 2013. - Pada tahun 2015, pendapatan operasional **menurun 5,21%** year-over-year, menunjukkan pertumbuhan negatif. 2. **Kesimpulan**: - Pertumbuhan pendapatan pulih pada tahun 2014 tetapi mengalami penurunan signifikan pada tahun 2015, menunjukkan bahwa ekspansi bisnis perusahaan menghadapi hambatan tertentu. --- ### II. **Analisis Tren Laba Bersih** 1. **CAGR 2012-2014**: - CAGR laba bersih setelah mengurangi keuntungan dan kerugian non-recurring adalah **-4,38%**, lebih rendah daripada CAGR pendapatan operasional. Ini menunjukkan penurunan profitabilitas perusahaan. - Pada tahun 2014, laba bersih tanpa pos luar biasa adalah **CNY 42,731 juta**, menurun **3,14%** dari tahun 2013. - Pada tahun 2015, laba bersih tanpa pos luar biasa lebih lanjut **menurun 5,60%** year-over-year. 2. **Dampak Keuntungan dan Kerugian Non-recurring**: - Proporsi keuntungan dan kerugian non-recurring terhadap laba bersih adalah **17,54%**, **10,25%**, dan **8,06%** pada tahun 2014, 2013, dan 2012, masing-masing, menunjukkan tren peningkatan. - Peningkatan keuntungan dan kerugian non-recurring terutama berasal dari subsidi pemerintah dan pendapatan investasi, bukan dari pertumbuhan berkelanjutan bisnis utama. 3. **Kesimpulan**: - Penurunan dua tahun berturut-turut dalam laba bersih tanpa pos luar biasa menunjukkan bahwa profitabilitas bisnis utama melemah, dan pertumbuhan kinerja bergantung pada keuntungan dan kerugian non-recurring, yang merupakan sinyal mengkhawatirkan. --- ### III. **Arus Kas dan Stabilitas Operasional** 1. **Arus Kas dari Aktivitas Operasional**: - Pada tahun 2014, pendapatan operasional adalah **CNY 181,5406 juta**, tetapi kas dari penjualan barang dan penyediaan jasa tidak dinyatakan dengan jelas, sehingga tidak mungkin menentukan apakah arus kas sehat. - Selama periode pelaporan, simpanan bank perusahaan adalah **CNY 130,6338 juta**, **CNY 41,5254 juta**, dan **CNY 98,6461 juta**, masing-masing. Likuiditas berfluktuasi secara signifikan, tetapi pelanggan utama, pemasok, dan model bisnis tetap stabil. 2. **Kesimpulan**: - Meskipun arus kas perusahaan berfluktuasi, basis pelanggan dan pemasok yang stabil serta model bisnis memberikan jaminan tertentu untuk pengembangan di masa depan. --- ### IV. **Proyeksi Kinerja untuk H1 2015** - Dari Januari hingga Juni 2015, pendapatan operasional yang diperkirakan adalah **CNY 85,05 juta hingga 103,95 juta**, peningkatan signifikan dari **CNY 46,4119 juta** pada H1 2014. - Namun, dari Januari hingga Maret 2015, laba bersih menurun **48,26%** year-over-year, terutama karena proyek dengan pendapatan yang diakui memiliki margin laba kotor yang lebih rendah. --- ### V. **Analisis dan Penilaian Komprehensif** 1. **Faktor Optimistis**: - Pendapatan operasional tumbuh pesat pada tahun 2014, mencapai **15,13%**. - Pendapatan operasional diperkirakan tumbuh signifikan pada H1 2015, menunjukkan bahwa perusahaan mungkin secara bertahap pulih. - Stabilitas pelanggan utama, pemasok, dan model bisnis memberikan fondasi operasional yang baik bagi perusahaan. 2. **Faktor Pesimistis**: - Pada tahun 2015, pendapatan operasional **menurun 5,21%** year-over-year, dan laba bersih juga menurun. - Penurunan dua tahun berturut-turut dalam laba bersih tanpa pos luar biasa menunjukkan profitabilitas bisnis utama yang tidak mencukupi. - Proporsi keuntungan dan kerugian non-recurring meningkat, dan pertumbuhan kinerja bergantung pada subsidi pemerintah dan pendapatan investasi, sehingga kurang momentum pertumbuhan. - Laba bersih dari Januari hingga Maret 2015 turun tajam **48,26%**, menunjukkan fluktuasi kinerja jangka pendek yang signifikan. --- ### **Kesimpulan Akhir: Tren Secara Keseluruhan Pesimistis** - Meskipun pendapatan operasional perusahaan pulih pada tahun 2014 dan diperkirakan tumbuh pada paruh pertama tahun 2015, **penurunan berkelanjutan dalam laba bersih tanpa pos luar biasa**, **ketergantungan pada keuntungan dan kerugian non-recurring untuk pertumbuhan laba bersih**, dan **fluktuasi kinerja jangka pendek yang besar** menunjukkan bahwa pertumbuhan kinerja saat ini kurang berkelanjutan dan stabil. - Oleh karena itu, dari perspektif jangka panjang, tren kinerja perusahaan bersifat **pesimistis**. Perhatian harus diberikan pada profitabilitas bisnis utamanya dan ketergantungannya pada keuntungan dan kerugian non-recurring. --- ### **Rekomendasi** 1. Perhatikan apakah profitabilitas bisnis utama perusahaan akan ditingkatkan. 2. Kurangi ketergantungan pada keuntungan dan kerugian non-recurring dan tingkatkan momentum pertumbuhan. 3. Stabilkan hubungan pelanggan dan pemasok, optimalkan struktur bisnis, dan tingkatkan margin laba kotor."Pencarian hibrida dengan RRF
Setelah menggunakan pencarian vektor dan teks penuh, Anda dapat menggunakan Reciprocal Rank Fusion (RRF) untuk mengurutkan hasil yang diambil. Untuk kenyamanan, Hologres telah merangkum proses ini ke dalam fungsi pencarian hibrida dengan RRF. Untuk informasi lebih lanjut, lihat apendiks. Fungsi ini memiliki kemampuan berikut:
Mengambil 20 jawaban teratas menggunakan perhitungan vektor berdasarkan pertanyaan.
Mengambil 20 jawaban teratas menggunakan pencarian teks penuh berdasarkan pertanyaan.
Menghitung skor RRF untuk jawaban dari pencarian vektor dan teks penuh, lalu menghasilkan N jawaban teratas.
Menggunakan
ai_gendan LLM untuk menghasilkan jawaban akhir berdasarkan prompt dan jawaban yang diambil.
-- Pengambilan ganda teks penuh dan vektor + penyusunan ulang RRF SELECT qa_hybrid_retrieval_rrf( question => 'Seberapa persen peningkatan pendapatan operasional dan laba bersih Goke Microelectronics secara year-over-year pada tahun 2014, 2015, dan 2016 selama periode pelaporan?', corpus_table => 'dt_bs_challenge_financial', prompt => 'Harap analisis apakah tren kinerja berikut bersifat pesimistis atau optimistis dan berikan alasannya: ${question}\n\n Informasi referensi:\n\n ${context}' );Jawaban yang diambil adalah sebagai berikut:
qa_hybrid_retrieval_rrf ------------------ "Berdasarkan informasi yang diberikan, analisis tren kinerja Goke Microelectronics menghasilkan kesimpulan berikut: ### **Penilaian Tren Kinerja: Pesimistis** #### **Alasannya adalah sebagai berikut:** 1. **Pertumbuhan laba bersih lebih rendah daripada pertumbuhan pendapatan:** - Informasi yang diberikan menunjukkan bahwa **CAGR pendapatan operasional perusahaan dari tahun 2012 hingga 2014 adalah 4,47%**, menunjukkan bahwa pertumbuhan bisnis secara keseluruhan relatif stabil. - Namun, **CAGR laba bersih yang dapat diatribusikan kepada pemegang saham induk setelah mengurangi keuntungan dan kerugian non-recurring adalah -4,38%**, yang secara signifikan lebih rendah daripada tingkat pertumbuhan pendapatan. Ini menunjukkan bahwa meskipun pendapatan tumbuh, profitabilitas perusahaan tidak meningkat secara sinkron dan bahkan menurun. Hal ini mungkin disebabkan oleh faktor-faktor seperti meningkatnya biaya, penurunan margin laba kotor, atau pengurangan keuntungan dan kerugian non-recurring. 2. **Fluktuasi signifikan dalam laba bersih:** - Laba bersih dari Januari hingga Maret 2015 menurun 48,26% year-over-year. Alasan utamanya adalah margin laba kotor yang lebih rendah dari proyek dengan pendapatan yang diakui (seperti proyek Wuxi Metro Line 1, yang terutama berbasis outsourcing modul). Ini menunjukkan bahwa kinerja jangka pendek perusahaan rentan terhadap perubahan struktur bisnisnya, menunjukkan tingkat ketidakstabilan tertentu. 3. **Penurunan margin laba kotor dan profitabilitas:** - Disebutkan bahwa "kontribusi laba bisnis utama terhadap laba bersih perusahaan" sedikit menurun pada tahun 2014 dibandingkan tahun 2013, dan tahun 2013 lebih rendah daripada tahun 2012. Ini menunjukkan bahwa profitabilitas bisnis inti perusahaan mungkin melemah, mungkin karena meningkatnya persaingan pasar, meningkatnya biaya, atau perubahan struktur produk. 4. **Proyeksi penurunan laba pada paruh pertama tahun 2015:** - Pendapatan operasional yang diperkirakan untuk H1 2015 adalah CNY 85,05 juta hingga 103,95 juta, yang mirip dengan H1 2014. Namun, laba bersih yang diperkirakan adalah CNY 23,40 juta hingga 28,60 juta, yang lebih rendah daripada CNY 29,1266 juta pada H1 2014. Ini menunjukkan bahwa profitabilitas perusahaan semakin menurun dan mungkin menghadapi tekanan operasional tertentu. 5. **Pertumbuhan bisnis yang lambat:** - Meskipun pertumbuhan pendapatan stabil, penurunan laba bersih menunjukkan bahwa kualitas pertumbuhan bisnis perusahaan tidak tinggi dan belum secara efektif dikonversi menjadi laba. Hal ini dapat memengaruhi kepercayaan investor terhadap pengembangan masa depan perusahaan. ### **Ringkasan:** Tren kinerja keseluruhan Goke Microelectronics bersifat **pesimistis**. Meskipun pendapatan operasional mempertahankan pertumbuhan yang stabil, pertumbuhan laba bersih secara signifikan tertinggal atau bahkan menunjukkan pertumbuhan negatif. Ini menunjukkan bahwa profitabilitas perusahaan menurun, kualitas pengembangan bisnisnya tidak tinggi, dan ada risiko fluktuasi kinerja jangka pendek. Jika perusahaan tidak dapat secara efektif meningkatkan margin laba kotornya, mengendalikan biaya, atau mengoptimalkan struktur produknya, kinerja masa depannya mungkin terus berada di bawah tekanan."
Apendiks: Definisi fungsi dan prosedur
Definisi prosedur tersimpan dan fungsi yang digunakan dalam topik ini disediakan sebagai referensi.
Anda tidak dapat membuat fungsi di Hologres. Prosedur dan fungsi berikut hanya untuk referensi. Anda tidak dapat memodifikasi dan menjalankannya.
Prosedur tersimpan untuk pemrosesan PDF
Buat Object Table dan Dynamic Table
CREATE OR REPLACE PROCEDURE create_rag_corpus_from_oss( oss_path TEXT, oss_endpoint TEXT, oss_role_arn TEXT, corpus_table TEXT, embedding_model TEXT DEFAULT NULL, parse_document_model TEXT DEFAULT NULL, chunk_model TEXT DEFAULT NULL, chunk_size INT DEFAULT 300, chunk_overlap INT DEFAULT 50, overwrite BOOLEAN DEFAULT FALSE ) AS $$ DECLARE corpus_schema TEXT; corpus_name TEXT; obj_table_name TEXT; full_corpus_ident TEXT; full_obj_ident TEXT; embed_expr TEXT; chunk_expr TEXT; parse_expr TEXT; embedding_dims INT; BEGIN -- 1. Pisahkan nama skema dan nama tabel. IF position('.' in corpus_table) > 0 THEN corpus_schema := split_part(corpus_table, '.', 1); corpus_name := split_part(corpus_table, '.', 2); ELSE corpus_schema := 'public'; corpus_name := corpus_table; END IF; obj_table_name := corpus_name || '_obj_table'; full_corpus_ident := format('%I.%I', corpus_schema, corpus_name); full_obj_ident := format('%I.%I', corpus_schema, obj_table_name); -- 2. Jika perlu menimpa, hapus tabel dan indeks terlebih dahulu. IF overwrite THEN DECLARE dyn_table_exists BOOLEAN; rec RECORD; BEGIN -- Periksa apakah tabel dinamis ada. SELECT EXISTS ( SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relname = corpus_name AND n.nspname = corpus_schema ) INTO dyn_table_exists; IF dyn_table_exists THEN -- 2.1 Nonaktifkan auto-refresh untuk tabel dinamis. -- RAISE NOTICE 'Nonaktifkan auto refresh untuk %', full_corpus_ident; -- EXECUTE format('ALTER TABLE IF EXISTS %s SET (auto_refresh_enable=false)', full_corpus_ident); -- 2.2 Temukan dan batalkan tugas refresh yang BERJALAN. FOR rec IN EXECUTE format( $f$ SELECT query_job_id FROM hologres.hg_dynamic_table_refresh_log(%L) WHERE status = 'RUNNING'; $f$, corpus_table ) LOOP RAISE NOTICE 'Temukan tugas refresh yang sedang berjalan: %', rec.query_job_id; IF hologres.hg_internal_cancel_query_job(rec.query_job_id::bigint) THEN RAISE NOTICE 'Pembatalan tugas % berhasil.', rec.query_job_id; ELSE RAISE WARNING 'Pembatalan tugas % gagal.', rec.query_job_id; END IF; END LOOP; -- 2.3 Hapus Tabel Dinamis. EXECUTE format('DROP TABLE IF EXISTS %s;', full_corpus_ident); ELSE RAISE NOTICE 'Tabel dinamis % tidak ada, lewati pembatalan tugas dan penghapusan.', full_corpus_ident; END IF; -- 2.4 Dalam hal apa pun, Object Table harus dihapus. EXECUTE format('DROP OBJECT TABLE IF EXISTS %s;', full_obj_ident); END; END IF; -- 3. Buat Object Table. RAISE NOTICE 'Buat object table: %', obj_table_name; EXECUTE format( $f$ CREATE OBJECT TABLE %s WITH ( path = %L, oss_endpoint = %L, role_arn = %L ); $f$, full_obj_ident, oss_path, oss_endpoint, oss_role_arn ); COMMIT; -- 4. Refresh Object Table. RAISE NOTICE 'Refresh object table: %', obj_table_name; EXECUTE format('REFRESH OBJECT TABLE %s;', full_obj_ident); COMMIT; -- 5. Pilih model parsing dokumen. IF parse_document_model IS NULL OR length(trim(parse_document_model)) = 0 THEN parse_expr := 'ai_parse_document(file, ''auto'', ''markdown'')'; ELSE parse_expr := format( 'ai_parse_document(%L, file, ''auto'', ''markdown'')', parse_document_model ); END IF; -- 6. Pilih model chunking. IF chunk_model IS NULL OR length(trim(chunk_model)) = 0 THEN chunk_expr := format('ai_chunk(doc, %s, %s)', chunk_size, chunk_overlap); ELSE chunk_expr := format( 'ai_chunk(%L, doc, %s, %s)', chunk_model, chunk_size, chunk_overlap ); END IF; -- 7. Pilih model embedding. IF embedding_model IS NULL OR length(trim(embedding_model)) = 0 THEN embed_expr := 'ai_embed(chunk)'; EXECUTE 'SELECT array_length(ai_embed(''dummy''), 1)' INTO embedding_dims; ELSE embed_expr := format('ai_embed(%L, chunk)', embedding_model); EXECUTE format( 'SELECT array_length(ai_embed(%L, ''dummy''), 1)', embedding_model ) INTO embedding_dims; END IF; RAISE NOTICE 'dimensi embedding adalah: %', embedding_dims; -- 8. Buat tabel dinamis untuk menyimpan output RAG. RAISE NOTICE 'buat dynamic table: %', corpus_name; EXECUTE format( $f$ CREATE DYNAMIC TABLE %s( CHECK(array_ndims(embedding_vector) = 1 AND array_length(embedding_vector, 1) = %s) ) WITH ( vectors = '{ "embedding_vector": { "algorithm": "HGraph", "distance_method": "Cosine", "builder_params": { "base_quantization_type": "sq8_uniform", "max_degree": 64, "ef_construction": 400, "precise_quantization_type": "fp32", "use_reorder": true } } }', auto_refresh_mode = 'incremental', freshness = '5 minutes', auto_refresh_enable = 'false' ) AS WITH parsed_doc AS ( SELECT object_uri, etag, %s AS doc FROM %s ), chunked_doc AS ( SELECT object_uri, etag, unnest(%s) AS chunk FROM parsed_doc ) SELECT object_uri, etag, chunk, %s AS embedding_vector FROM chunked_doc; $f$, full_corpus_ident, embedding_dims, parse_expr, full_obj_ident, chunk_expr, embed_expr ); COMMIT; -- 9. Buat indeks teks penuh (nama indeks = nama tabel || '_fulltext_idx'). EXECUTE format( 'CREATE INDEX %I ON %s USING FULLTEXT (chunk);', corpus_name || '_fulltext_idx', full_corpus_ident ); RAISE NOTICE ''; RAISE NOTICE 'Buat corpus RAG berhasil ke tabel: %', corpus_table; RAISE NOTICE ' Indeks vektor adalah: %.embedding_vector', corpus_table; RAISE NOTICE ' Indeks Pencarian teks penuh adalah: %.chunk', corpus_table; END; $$ LANGUAGE plpgsql;Prosedur tersimpan untuk merefresh Object Table dan Dynamic Table
CREATE OR REPLACE PROCEDURE refresh_rag_corpus_table( corpus_table TEXT ) AS $$ DECLARE corpus_schema TEXT; corpus_name TEXT; obj_table_name TEXT; full_corpus_ident TEXT; full_obj_ident TEXT; BEGIN -- 1. Uraikan skema dan nama tabel. IF position('.' in corpus_table) > 0 THEN corpus_schema := split_part(corpus_table, '.', 1); corpus_name := split_part(corpus_table, '.', 2); ELSE corpus_schema := 'public'; corpus_name := corpus_table; END IF; obj_table_name := corpus_name || '_obj_table'; full_corpus_ident := format('%I.%I', corpus_schema, corpus_name); full_obj_ident := format('%I.%I', corpus_schema, obj_table_name); -- 2. Refresh Object Table. RAISE NOTICE 'Refreshing Object Table: %', obj_table_name; EXECUTE format('REFRESH OBJECT TABLE %s;', full_obj_ident); -- 3. Refresh Dynamic Table. RAISE NOTICE 'Refreshing Dynamic Table: %', corpus_name; EXECUTE format('REFRESH TABLE %s;', full_corpus_ident); RAISE NOTICE 'Refresh complete for corpus table %', corpus_table; END; $$ LANGUAGE plpgsql;Prosedur tersimpan untuk menghapus Object Table dan Dynamic Table
CREATE OR REPLACE PROCEDURE drop_rag_corpus_table( corpus_table TEXT ) AS $$ DECLARE corpus_schema TEXT; corpus_name TEXT; obj_table_name TEXT; full_corpus_ident TEXT; full_obj_ident TEXT; rec RECORD; BEGIN -- 1. Uraikan skema dan nama tabel. IF position('.' in corpus_table) > 0 THEN corpus_schema := split_part(corpus_table, '.', 1); corpus_name := split_part(corpus_table, '.', 2); ELSE corpus_schema := 'public'; corpus_name := corpus_table; END IF; obj_table_name := corpus_name || '_obj_table'; full_corpus_ident := format('%I.%I', corpus_schema, corpus_name); full_obj_ident := format('%I.%I', corpus_schema, obj_table_name); -- 2. Hapus tabel. -- 2.1 Nonaktifkan auto-refresh untuk tabel dinamis. -- RAISE NOTICE 'Nonaktifkan auto refresh untuk %', full_corpus_ident; -- EXECUTE format('ALTER TABLE IF EXISTS %s SET (auto_refresh_enable=false)', full_corpus_ident); -- 2.2 Temukan dan batalkan tugas refresh yang BERJALAN. FOR rec IN EXECUTE format( $f$ SELECT query_job_id FROM hologres.hg_dynamic_table_refresh_log(%L) WHERE status = 'RUNNING'; $f$, corpus_table ) LOOP RAISE NOTICE 'Temukan tugas refresh yang sedang berjalan: %', rec.query_job_id; IF hologres.hg_internal_cancel_query_job(rec.query_job_id::bigint) THEN RAISE NOTICE 'Pembatalan tugas % berhasil.', rec.query_job_id; ELSE RAISE WARNING 'Pembatalan tugas % gagal.', rec.query_job_id; END IF; END LOOP; -- 2.3 Hapus Tabel Dinamis. RAISE NOTICE 'Menghapus Dynamic Table: %', corpus_name; EXECUTE format('DROP TABLE IF EXISTS %s;', full_corpus_ident); -- 2.4 Hapus Object Table. RAISE NOTICE 'Menghapus Object Table: %', obj_table_name; EXECUTE format('DROP OBJECT TABLE IF EXISTS %s;', full_obj_ident); RAISE NOTICE 'Penghapusan selesai untuk corpus: %', corpus_table; END; $$ LANGUAGE plpgsql;
Fungsi pencarian vektor
-- Pencarian vektor untuk T&J
CREATE OR REPLACE FUNCTION qa_vector_search_retrieval(
question TEXT,
corpus_table TEXT,
embedding_model TEXT DEFAULT NULL,
llm_model TEXT DEFAULT NULL,
ranking_model TEXT DEFAULT NULL,
prompt TEXT DEFAULT 'Harap jawab pertanyaan berikut dalam ${language} berdasarkan informasi referensi.\n\n Pertanyaan: ${question}\n\n Informasi referensi:\n\n ${context}',
language TEXT DEFAULT 'Chinese',
vector_recall_count INT DEFAULT 20,
rerank_recall_count INT DEFAULT 5,
vector_col TEXT DEFAULT 'embedding_vector'
)
RETURNS TEXT AS
$$
DECLARE
final_answer TEXT;
sql TEXT;
embedding_expr TEXT;
ai_rank_expr TEXT;
ai_gen_expr TEXT;
embedding_model_valid BOOLEAN;
llm_model_valid BOOLEAN;
ranking_model_valid BOOLEAN;
BEGIN
embedding_model_valid := (embedding_model IS NOT NULL AND trim(embedding_model) != '');
llm_model_valid := (llm_model IS NOT NULL AND trim(llm_model) != '');
ranking_model_valid := (ranking_model IS NOT NULL AND trim(ranking_model) != '');
IF embedding_model_valid THEN
embedding_expr := 'ai_embed(' || quote_literal(embedding_model) || ', ' || quote_literal(question) || ')';
ELSE
embedding_expr := 'ai_embed(' || quote_literal(question) || ')';
END IF;
IF ranking_model_valid THEN
ai_rank_expr := 'ai_rank(' || quote_literal(ranking_model) || ', ' || quote_literal(question) || ', chunk)';
ELSE
ai_rank_expr := 'ai_rank(' || quote_literal(question) || ', chunk)';
END IF;
IF llm_model_valid THEN
ai_gen_expr := 'ai_gen(' || quote_literal(llm_model) ||
', replace(replace(replace(' || quote_literal(prompt) ||
', ''${question}'', ' || quote_literal(question) || '), ''${context}'', merged_chunks), ''${language}'', ' || quote_literal(language) || ') )';
ELSE
ai_gen_expr := 'ai_gen(replace(replace(replace(' || quote_literal(prompt) ||
', ''${question}'', ' || quote_literal(question) || '), ''${context}'', merged_chunks), ''${language}'', ' || quote_literal(language) || '))';
END IF;
sql := '
WITH
embedding_recall AS (
SELECT
chunk,
approx_cosine_distance(' || vector_col || ', ' || embedding_expr || ') AS distance
FROM
' || corpus_table || '
ORDER BY
distance DESC
LIMIT ' || vector_recall_count || '
),
rerank AS (
SELECT
chunk,
' || ai_rank_expr || ' AS score
FROM
embedding_recall
ORDER BY
score DESC
LIMIT ' || rerank_recall_count || '
),
concat_top_chunks AS (
SELECT string_agg(chunk, E''\n\n----\n\n'') AS merged_chunks FROM rerank
)
SELECT ' || ai_gen_expr || '
FROM concat_top_chunks;
';
EXECUTE sql INTO final_answer;
RETURN final_answer;
END;
$$ LANGUAGE plpgsql;Fungsi pencarian teks penuh
CREATE OR REPLACE FUNCTION qa_text_search_retrieval(
question TEXT,
corpus_table TEXT,
llm_model TEXT DEFAULT NULL,
ranking_model TEXT DEFAULT NULL,
prompt TEXT DEFAULT 'Harap jawab pertanyaan berikut dalam ${language} berdasarkan informasi referensi.\n\n Pertanyaan: ${question}\n\n Informasi referensi:\n\n ${context}',
language TEXT DEFAULT 'Chinese',
text_search_recall_count INT DEFAULT 20,
rerank_recall_count INT DEFAULT 5,
text_search_col TEXT DEFAULT 'chunk'
)
RETURNS TEXT AS
$$
DECLARE
final_answer TEXT;
sql TEXT;
ai_rank_expr TEXT;
ai_gen_expr TEXT;
llm_model_valid BOOLEAN;
ranking_model_valid BOOLEAN;
BEGIN
llm_model_valid := (llm_model IS NOT NULL AND trim(llm_model) != '');
ranking_model_valid := (ranking_model IS NOT NULL AND trim(ranking_model) != '');
IF ranking_model_valid THEN
ai_rank_expr := 'ai_rank(' || quote_literal(ranking_model) || ', ' || quote_literal(question) || ', chunk)';
ELSE
ai_rank_expr := 'ai_rank(' || quote_literal(question) || ', chunk)';
END IF;
IF llm_model_valid THEN
ai_gen_expr := 'ai_gen(' || quote_literal(llm_model) ||
', replace(replace(replace(' || quote_literal(prompt) ||
', ''${question}'', ' || quote_literal(question) ||
'), ''${context}'', merged_chunks), ''${language}'', ' || quote_literal(language) || ') )';
ELSE
ai_gen_expr := 'ai_gen(replace(replace(replace(' || quote_literal(prompt) ||
', ''${question}'', ' || quote_literal(question) ||
'), ''${context}'', merged_chunks), ''${language}'', ' || quote_literal(language) || '))';
END IF;
sql := '
WITH
text_search_recall AS (
SELECT
chunk
FROM
' || corpus_table || '
ORDER BY
text_search(' || text_search_col || ', ' || quote_literal(question) || ') DESC
LIMIT ' || text_search_recall_count || '
),
rerank AS (
SELECT
chunk,
' || ai_rank_expr || ' AS score
FROM
text_search_recall
ORDER BY
score DESC
LIMIT ' || rerank_recall_count || '
),
concat_top_chunks AS (
SELECT string_agg(chunk, E''\n\n----\n\n'') AS merged_chunks FROM rerank
)
SELECT ' || ai_gen_expr || '
FROM concat_top_chunks;
';
EXECUTE sql INTO final_answer;
RETURN final_answer;
END;
$$ LANGUAGE plpgsql;Fungsi pencarian hibrida dengan peringkat
CREATE OR REPLACE FUNCTION qa_hybrid_retrieval(
question TEXT,
corpus_table TEXT,
embedding_model TEXT DEFAULT NULL,
llm_model TEXT DEFAULT NULL,
ranking_model TEXT DEFAULT NULL,
prompt TEXT DEFAULT 'Harap jawab pertanyaan berikut dalam ${language} berdasarkan informasi referensi.\n\n Pertanyaan: ${question}\n\n Informasi referensi:\n\n ${context}',
language TEXT DEFAULT 'Chinese',
text_search_recall_count INT DEFAULT 20,
vector_recall_count INT DEFAULT 20,
rerank_recall_count INT DEFAULT 5,
vector_col TEXT DEFAULT 'embedding_vector',
text_search_col TEXT DEFAULT 'chunk'
)
RETURNS TEXT AS
$$
DECLARE
final_answer TEXT;
sql TEXT;
embedding_expr TEXT;
ai_rank_expr TEXT;
ai_gen_expr TEXT;
embedding_model_valid BOOLEAN;
llm_model_valid BOOLEAN;
ranking_model_valid BOOLEAN;
BEGIN
embedding_model_valid := (embedding_model IS NOT NULL AND trim(embedding_model) != '');
llm_model_valid := (llm_model IS NOT NULL AND trim(llm_model) != '');
ranking_model_valid := (ranking_model IS NOT NULL AND trim(ranking_model) != '');
IF embedding_model_valid THEN
embedding_expr := 'ai_embed(' || quote_literal(embedding_model) || ', ' || quote_literal(question) || ')';
ELSE
embedding_expr := 'ai_embed(' || quote_literal(question) || ')';
END IF;
IF ranking_model_valid THEN
ai_rank_expr := 'ai_rank(' || quote_literal(ranking_model) || ', ' || quote_literal(question) || ', chunk)';
ELSE
ai_rank_expr := 'ai_rank(' || quote_literal(question) || ', chunk)';
END IF;
IF llm_model_valid THEN
ai_gen_expr := 'ai_gen(' || quote_literal(llm_model) ||
', replace(replace(replace(' || quote_literal(prompt) ||
', ''${question}'', ' || quote_literal(question) || '), ''${context}'', merged_chunks), ''${language}'', ' || quote_literal(language) || ') )';
ELSE
ai_gen_expr := 'ai_gen(replace(replace(replace(' || quote_literal(prompt) ||
', ''${question}'', ' || quote_literal(question) || '), ''${context}'', merged_chunks), ''${language}'', ' || quote_literal(language) || '))';
END IF;
sql := '
WITH
embedding_recall AS (
SELECT
chunk
FROM
' || corpus_table || '
ORDER BY
approx_cosine_distance(' || vector_col || ', ' || embedding_expr || ') DESC
LIMIT ' || vector_recall_count || '
),
text_search_recall AS (
SELECT
chunk
FROM
' || corpus_table || '
ORDER BY
text_search(' || text_search_col || ', ' || quote_literal(question) || ') DESC
LIMIT ' || text_search_recall_count || '
),
union_recall AS (
SELECT chunk FROM embedding_recall
UNION
SELECT chunk FROM text_search_recall
),
rerank AS (
SELECT
chunk,
' || ai_rank_expr || ' AS score
FROM
union_recall
ORDER BY
score DESC
LIMIT ' || rerank_recall_count || '
),
concat_top_chunks AS (
SELECT string_agg(chunk, E''\n\n----\n\n'') AS merged_chunks FROM rerank
)
SELECT ' || ai_gen_expr || '
FROM concat_top_chunks;
';
EXECUTE sql INTO final_answer;
RETURN final_answer;
END;
$$ LANGUAGE plpgsql;Fungsi pencarian hibrida dengan RRF
CREATE OR REPLACE FUNCTION qa_hybrid_retrieval_rrf(
question TEXT,
corpus_table TEXT,
embedding_model TEXT DEFAULT NULL,
llm_model TEXT DEFAULT NULL,
ranking_model TEXT DEFAULT NULL,
prompt TEXT DEFAULT 'Harap jawab pertanyaan berikut dalam ${language} berdasarkan informasi referensi.\n\n Pertanyaan: ${question}\n\n Informasi referensi:\n\n ${context}',
language TEXT DEFAULT 'Chinese',
text_search_recall_count INT DEFAULT 20,
vector_recall_count INT DEFAULT 20,
rerank_recall_count INT DEFAULT 5,
rrf_k INT DEFAULT 60,
vector_col TEXT DEFAULT 'embedding_vector',
text_search_col TEXT DEFAULT 'chunk'
)
RETURNS TEXT AS
$$
DECLARE
final_answer TEXT;
sql TEXT;
embedding_expr TEXT;
ai_rank_expr TEXT;
ai_gen_expr TEXT;
embedding_model_valid BOOLEAN;
llm_model_valid BOOLEAN;
ranking_model_valid BOOLEAN;
BEGIN
embedding_model_valid := (embedding_model IS NOT NULL AND trim(embedding_model) <> '');
llm_model_valid := (llm_model IS NOT NULL AND trim(llm_model) <> '');
ranking_model_valid := (ranking_model IS NOT NULL AND trim(ranking_model) <> '');
IF embedding_model_valid THEN
embedding_expr := 'ai_embed(' || quote_literal(embedding_model) || ', ' || quote_literal(question) || ')';
ELSE
embedding_expr := 'ai_embed(' || quote_literal(question) || ')';
END IF;
IF ranking_model_valid THEN
ai_rank_expr := 'ai_rank(' || quote_literal(ranking_model) || ', ' || quote_literal(question) || ', chunk)';
ELSE
ai_rank_expr := 'ai_rank(' || quote_literal(question) || ', chunk)';
END IF;
IF llm_model_valid THEN
ai_gen_expr := 'ai_gen(' || quote_literal(llm_model) ||
', replace(replace(replace(' || quote_literal(prompt) ||
', ''${question}'', ' || quote_literal(question) || '), ''${context}'', merged_chunks), ''${language}'', ' || quote_literal(language) || ') )';
ELSE
ai_gen_expr := 'ai_gen(replace(replace(replace(' || quote_literal(prompt) ||
', ''${question}'', ' || quote_literal(question) || '), ''${context}'', merged_chunks), ''${language}'', ' || quote_literal(language) || '))';
END IF;
sql := '
WITH embedding_recall AS (
SELECT
chunk,
vec_score,
ROW_NUMBER() OVER (ORDER BY vec_score DESC) AS rank_vec
FROM (
SELECT
chunk,
approx_cosine_distance(' || vector_col || ', ' || embedding_expr || ') AS vec_score
FROM
' || corpus_table || '
) t
ORDER BY vec_score DESC
LIMIT ' || vector_recall_count || '
),
text_search_recall AS (
SELECT
chunk,
text_score,
ROW_NUMBER() OVER (ORDER BY text_score DESC) AS rank_text
FROM (
SELECT
chunk,
text_search(' || text_search_col || ', ' || quote_literal(question) || ') AS text_score
FROM
' || corpus_table || '
) ts
WHERE text_score > 0
ORDER BY text_score DESC
LIMIT ' || text_search_recall_count || '
),
rrf_scores AS (
SELECT
chunk,
SUM(1.0 / (' || rrf_k || ' + rank_val)) AS rrf_score
FROM (
SELECT chunk, rank_vec AS rank_val FROM embedding_recall
UNION ALL
SELECT chunk, rank_text AS rank_val FROM text_search_recall
) sub
GROUP BY chunk
),
top_chunks AS (
SELECT chunk
FROM rrf_scores
ORDER BY rrf_score DESC
LIMIT ' || rerank_recall_count || '
),
concat_top_chunks AS (
SELECT string_agg(chunk, E''\n\n----\n\n'') AS merged_chunks FROM top_chunks
)
SELECT ' || ai_gen_expr || '
FROM concat_top_chunks;
';
EXECUTE sql INTO final_answer;
RETURN final_answer;
END;
$$ LANGUAGE plpgsql;