Tugas RAG: Lab 1-3 (chunking, indeks, Hit@k)

This commit is contained in:
Rinaldo
2026-11-02 10:39:22 +07:00
commit 9cd6d9c546
6 changed files with 620 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
chroma_db/
__pycache__/
.venv/
+93
View File
@@ -0,0 +1,93 @@
%PDF-1.4
%“Œ‹ž ReportLab Generated PDF document (opensource)
1 0 obj
<<
/F1 2 0 R /F2 3 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
>>
endobj
4 0 obj
<<
/Contents 9 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 8 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
5 0 obj
<<
/Contents 10 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 8 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
6 0 obj
<<
/PageMode /UseNone /Pages 8 0 R /Type /Catalog
>>
endobj
7 0 obj
<<
/Author (\(anonymous\)) /CreationDate (D:20261102020454+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20261102020454+00'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (\(unspecified\)) /Title (Panduan Akademik Universitas Nusantara Digital) /Trapped /False
>>
endobj
8 0 obj
<<
/Count 2 /Kids [ 4 0 R 5 0 R ] /Type /Pages
>>
endobj
9 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2211
>>
stream
Gat=,fll+.&:O:SbbL?=W*Ha#52!3qe%"pf,3@'&O9nhs(TAYlec#@$!P$7;jOVA."G-VcE9GLnO'MU.k4:;T\m!N8^Nno9[!X&N*/Cp!PAZ[&IpI<Db/o=6;)"TdjtlA>$eN$>P7-aM]&U"La&,$G1HrtUq&nQ6]@1/Bf?9ONpS*JO.-NE$Z-63?577'^QBeI=7<c<U74h+tf.+U1p48MK>(38C#DYRPDtlCn08M[^?G:HTs.O^O5a\h=,B$9Md-V\he^4+u(9r*nr<iBNetQ'm?I4rcFfX[[bpL:):%=?]jhhA,O`-=#S]-=)7R-=D+$NLRo9E&Sm0;k;SU)fb7iRTTP"M/+9U8Z">)=9M@]bOu@<_'aPPN.ljdnq'&S%`abXG0-;^M"*;"R0mC.t6g[N16-6UXA2cLAC>HKjAJefd;U+Y&eSfSGu>,#^osRE,P_!NfVYa>9i(nOdCS[u(Wu1BtpZJZdM\RKOomYpM\MGHfXP<0@5%O"M)OePPM\4X\Y,%uoA0/--1fk\Nn=Yu*$%=sTP4>*IZrJ+.%<UHo&UAW9n/$/dPIFs+AjVes*nS@BAH+\8N"%2&LY3eqp]A6B9?$97N='kOLMl.Fida#Y65Vc.PlkgA14PA1$W.+>>`GWoZb+_;Rj^[Ha[!4jf%=Al2%U_rJ!8ZYh:&=6!@RM\8YF(1Dk'_L`C0Zms]ZPEF<\2l/e*Zql&W9G1U@eiXV3'`mi>#idHac^X50f4;^X/DZAb[s57JK1]nZ0jX=RPN;@0n`O\,&E<mN/fDj2re<m+5+[qq:eTG#rJhsIb$'tlt$$NUA)YL+2jKrBXOd]X:45!UC,hC10-7aA1mCWT$o6\]g)MO>oQ:a-Q&@)%[:/U<?iK7BQ:Y/p],B"gS=u%PlS2\!N_I[:9Q*JL!&'t_iV*gDpk&Qj\I0Y=UA[+0eh7]P;>ZZWo,lAMd3'Caq60CAcfGbh626?)LD?ER.sV?JVG[_U"mQ(Zne<Y$FX1fSh\.#S`]EMjE63-ns4$F(pO_L8nBpO<2.s7&r/Zs5SQ)&!=+Dtf-kpGMHH_jm7M2G_F$`k\-JaF7"e#>O$t5mUBO[7)k@m!+hIi_(>-DX<3#8jKFMS7j2f^l:P.hiK)%+XO(\GKN')F^%3cP>2@"lNThjT`.I$*W!R7"_o:ArYB#$R*k8A?%0API"^\?XnK3c4GmJGTMiOES3pG9_5W%p)F0FAd&\5[LeY>38Z(Aij6a-Qg<h_&r":6S%nB0JP.C:h;HGH)`CVn.LZ__\4!)r,M4R`mIsArkSc>/f`=DJp<.hM2=kO1sQPk$f=@*g[Nn^VBf/=0um[P8iLhOO:qh[8Vi:K-g6QX*mp]RDY\gMo''9K6Al71ijM@F1?sq_naRnXqeh2NKA(^I4.)66i?46PfmBX2!Xe2A`jRcXr1%qfo1j-VN>I+=e?RdLpc62`<>Qd18.gD>1R07"UU27=J(c<KCp.PAd<m%f*6:0s3?=]+Io$V_4/.ppB;nUUNfT^+V]#K9s)rYNao*aPIFs2Ak-:E@p-kRCK2R0TL;FqV>pqlNtpp`P9t%8i%o?*,*a]!dMpc@B"Za/]`njHFh>^74YT4/2Jh!*af;SrV1,.3NoK4#'KWBa=8d3Uc_()IT:`=aB9/ZfmS,W!9SCg3p#dYR\`5st1Iuo%PTPL-*"=e\Z#V7.!j%[;B(jPu<51V_VE-o&D,]E$/Ym<RXs$j$AL'tSD)#`;(_f#(4?kku4PtQ?&K%7A)nNHk>+"F_fq9KqmhSWWe'oK/\ECSV5qN0`Y1^FWhHaKcqc)ag4:9b/KK/ebEaRE`bCS;[>b=m!^u)B.F!kCU<T,fCXO:#6.`J5$C:"\D59_ss6H>7i>_\N5%XCl-Zr_%%#3h]F^kk2/TWbu/)OXhZ(n&S2^-6TB9`[d_=B#oChnCN-8TNChhYa0`]ubV28r2>(UZ?3?HoS0WqfXc<U21nGPc8Lo1u=UmqT.YbqqFWi'0";U?1G-#149EZ<eF!:-hHRZ-L/df/cQC/e\Z9#;%Lu,h0*_Mk5$KcX>XoCf$s@Go]l7'<5KqBoZK80kcQFgRB8&8iQQ,H?&J$C\YcS'"p3d9iYtoC-/frKgktAV0NqRi)'ES]gHsN1X\=LbnDf`<pZ'9N7S_,pbmr))D/Fb+GSpCh?,Dq_%>Vd;*7QgLC<bj^d^1%jlR(XC;s1$iRnbM5l/H'<Efsu~>endstream
endobj
10 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 486
>>
stream
GasJNhbMo['ZTV5']131.'lQN_C]5\VG7f+i;l^96Yd<]T'ZgNm<<C#!k[)q-*bafn+RjC)G6<umiN%Ll1+;ML0gO&KO2)[oO%-fDgjS?:q9omC3A'Mh/t(cP#+"JGE)7[PFOHjO]mQ3Q0O7RfrE"#;mPYUcI"kMs%pZ&A\A2IYP!AG/+l4?Z`4WRQ14;1(68?32O6i'(_/NYa=5:R&iT+CLtUs:KVSL>_Q_oha-XCISFtI\KZab>(^XfGBRdC:<WNj]Z3>5!?7KbMgXc`TEUi#m*[m:?XV:iW`#]GY*_A1/+]4hBn&-9f';@`$"j/N"H33mio"/C-nQ=gSgj@Ft6QP>l.NQ&R9@:P[\f<_lU"9nU*]qH@KI5CPX9s6=Ur>UF[)_o[fd`i@;eEg!9Ar=eT>t1;#G!.8I):gn6W$PpMH3tUP2@(nGO,c%Ar:O,;UpseiG9sU5d9@3b:1I)T6I/:SaO^MKB<^2#2=S~>endstream
endobj
xref
0 11
0000000000 65535 f
0000000061 00000 n
0000000102 00000 n
0000000209 00000 n
0000000321 00000 n
0000000524 00000 n
0000000728 00000 n
0000000796 00000 n
0000001109 00000 n
0000001174 00000 n
0000003476 00000 n
trailer
<<
/ID
[<879ad10a394d9f4a532c177317478ceb><879ad10a394d9f4a532c177317478ceb>]
% ReportLab generated PDF document -- digest (opensource)
/Info 7 0 R
/Root 6 0 R
/Size 11
>>
startxref
4053
%%EOF
+93
View File
@@ -0,0 +1,93 @@
%PDF-1.4
%“Œ‹ž ReportLab Generated PDF document (opensource)
1 0 obj
<<
/F1 2 0 R /F2 3 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
>>
endobj
4 0 obj
<<
/Contents 9 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 8 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
5 0 obj
<<
/Contents 10 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 8 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
6 0 obj
<<
/PageMode /UseNone /Pages 8 0 R /Type /Catalog
>>
endobj
7 0 obj
<<
/Author (\(anonymous\)) /CreationDate (D:20261102020454+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20261102020454+00'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (\(unspecified\)) /Title (Pengantar Kecerdasan Buatan dan RAG) /Trapped /False
>>
endobj
8 0 obj
<<
/Count 2 /Kids [ 4 0 R 5 0 R ] /Type /Pages
>>
endobj
9 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2234
>>
stream
Gat=,968iI%)1n+i0\[#XX2^oEn`G<[cq;!9te#@BT%ui8X*7"]@28E+%X*CbdN_?V2uf"!Zt_YBnjCLI;_`P2_4abYE6VW<_jD^Jd&8?e%=/`5A+Cp)96j,S-RZt2qL$?NLAO7dPIC&#Bi(/JlP4t27&/aAe`RCg!/&)[T<d+J!E&0g5(JIac1*Ld0`TdX>"lEpcR?GImpDN>.pfVXcq,p)ud9gRP+o:G.2),?Vkt*DLTjqSqd@&eq3=Lh,gj_m>s*n142sYr7['^Z"Hh-ZdAM9cJ"`h?^=)ie<AT7;393q(55AWhFMrT^V,,E73W=taClgL6DpW:dSaD:[5G7`T=e.GZBg2h(6&Aq,C8b'Z,(HGcmk%D6KC%[<+AkSqQjtVlMM@WBT:8]59KPLKsn)r(Y$T26;PsBBI=-E84c7\(<m#(&=Fp]Xmp(EK`0-UfN/6D6=gOr!St4SnWf>>`*>_[-jNC;'`Q@F4f_Ep18Bn12RqfFbN9;Sb'm4tUVI658N'JUMV_gQXK9ObLs0T@DjGB^dYj$YOG[[(7mIb1FRW?Kd<i`9SZVm9?A_IS[%p$G7Ot$i/mXB7\TiYbDXRgC>+OeJ4%Jplm^Na]E(:P6#PZb60auZ5Bd67FM'5,>DsK`taoo)HP\/SpoLbp\:Fe;YX7u)5.BCqIC;IJ:r&Ei<"k=8(eQBi%C^O%?(2SQ-r1m,S2cn%IZAbu91X)VE5"Z7f[`n5pmr20kn7<.NM=MD%:5uJ6.M_UBKNj6%I5C;9#HejBMH78ib!kK&B1!4R`)J5ofPtk(?@_lDja78&&8#c2b$D.oT5rL%##AM>_M?MA7`WHs*:sEM=L"baftd%b&sq]O'PZ)GK,]9Nr$$i4G8/uq52U>i1+FN"*9-rHm"hr@,ROP4[#s%s,5$npGZB*PqpZo2gfPf_);Fp[>5KU"pn8='SOVB#0\W\(+J,>H/`tO)&mRq.Me,Gk:LHNLfMt=tjbtem\jk,slRg%/->uA(5a_q;38ZEKc5fM)$;Y?PBV9[0HQFMkidp<rd\b/V[qm-o^n+A8&NLo5%PYp\l3Q9hkWH"J8,+4U#+2:iBj[T@e5]Q6Yq-kY+&fQ9qFj/%>c3-t5kB!'3nb(L(\ICgp6Z=BgUj%h[lrC:OsBn/#NQONF!n\-IdRj=KT\(c&YJ6m?o\U5@\@W%q=#G0oPY8VdX_r_"atT1ZYjKc[5&oqRsX=aMf&tWrRsC%f'q+k!UbCG07GL34@=Q-%&;H.o!;Qu[NCThk\*PZhu%AY2\5+<RRYC@U:@kD.FOiN$,)ZD=>Ho9!f^OYn^XU<0b7Wo5&k\K(k?g;=H1P4asVmAXLZdVr5^md'5@]\a<@gS<?d@(T0g7ZA`_/$)Q&Ft([02<H.kLnhOag6`0[GC(US63$r1]7>)>.u2JA9E!0(o+?#OA?W;Ro2m!@_J8GOEM;^'Dk`nS-cdjL"3'ts^;Nj<g;"Lf7aKPTmHN9@uC\M-@M5`MkW%UtO+k3F2L?hhnXDiX']Vhmeo+P:fOc)^;4X3jVHPuXUEJ2ihUR6o&9,_IM@'7YX&6)sLHYH'H`FpF!t;2`h>_M'RV(T*X6,U0TP>aa0W=)PTalFep`BC/mj<p&%OLg).RHhLuuRYTk,E/<MChsF6)8]q;kr!:k@Zb$\B^;Y6RB^)Xnbfp>T)BEKKL4K8d?/*_+p#4';54,N:`&qPfE5sQaJX"h'QmsqsE<1hDN*s='c@'Yj'A;:PRRZ0B)emYi!]<f!Zu.KM!/S;r@#D7l"XnC_XT_rJguG3@UNh7;h"+kjM5T$>&^Y`S4<nk/43$-#K&9KNr#]eDEd#g7J*1Y&/dP9K;`FH]nZ/o4$t0_]#%QPPC]^fg?ud&V/lta#Y%T`2BSQ9__.`LBEj$#59X1YSUuI$%`=eqVn-I>87!)7qb@qW0#]@IN6L8OR@kZ^K8%=nX--0%CngR'E->#?%4m%M`A)t_=NA&?Q>qm7+']Pbl`;h#+=dB?U[^9Tf,^iI+]qq("9Il4i'HAj4;BIL@%la>mF#9@67qo%bok9si=T-[AQ<`2_2g?:,i/6C*-\35V8+T,.J,-""E,f]WmE5at5F$TaoY2NY<9I4$eFsD3N.;E:#t^JQ04,=0PB!9Yr4k"S/"-m7KCP-K?C@2hkB`@&Wu*`C:\LAim?%k!\0!5mD.3n2&*&M=UeuYK$MNt6X5Q."X8(;!I2UBnF;^CBX:>=SPkd,m;2!6o"/`l=d/~>endstream
endobj
10 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 682
>>
stream
GasId9lJKW&A71-m'/uVYhLG!ie!7W8.YodO1hXn[a+$gcfTY+o'[GXWC\QBMA/MQm*=tA,c;'CI/L-NUA"i)rg6W58&Z/'bJfX%0D(s*rKu\ZjEaO=o$q3UH.ZL+0=(,%Y@j6IVff&!MY=D-)gC*IRr1E(HtTDn=!sJ"otK`R+-_*AOkl/Q;*$*Gg!t3&?X0nS;_f8f9bqZ,'"c5VajQc52AF,OjKP;Z#&\s_hbnHeiminQSq<1>fMa]cA5iWtUCr$-1PiZ=DPblQX\uQ&cB=M`IU7"`Xn`.f7.sV"<]V6<Uu<Y_A$lG=!]k?t<he;6\P]SkWZYN?#>C[tb_Y/_@na>QJ^3cKf!md:"JK`]_W51C>YJ\<k;Y#($f"dUGpeloh230*0lD9Lg$X$E[jPg.-[\`Om;8ZK4h+a&/!Y@$IK%/D=D*@>#0VP*i']uXA-%#I*M;Z#A.MU??G'IFra2Gtc!P8Y9jGYDEG(@Kftb?*q5Jp+<5q0D6p>*''W*6Z_-r)^lbl<>7S#OM@X'rdTVfLmZ%5S]'7uTMHZ%NuQ7/]Pi+iSpc=m9+Y3\,tp>$ac9Q=OGiXC,:ORHSZ[$Y+FB,KOsI]WPHif<8(M]At(jr3d`6RY(80dk]nR@bCQ#5u(XoFgd&PG=("q\6!oWm6WS!Y,0gE4roCOCl6.*r8j=~>endstream
endobj
xref
0 11
0000000000 65535 f
0000000061 00000 n
0000000102 00000 n
0000000209 00000 n
0000000321 00000 n
0000000524 00000 n
0000000728 00000 n
0000000796 00000 n
0000001098 00000 n
0000001163 00000 n
0000003488 00000 n
trailer
<<
/ID
[<5ceaba85e137fd7c645771c15882af0c><5ceaba85e137fd7c645771c15882af0c>]
% ReportLab generated PDF document -- digest (opensource)
/Info 7 0 R
/Root 6 0 R
/Size 11
>>
startxref
4261
%%EOF
+93
View File
@@ -0,0 +1,93 @@
%PDF-1.4
%“Œ‹ž ReportLab Generated PDF document (opensource)
1 0 obj
<<
/F1 2 0 R /F2 3 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
>>
endobj
4 0 obj
<<
/Contents 9 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 8 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
5 0 obj
<<
/Contents 10 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 8 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
6 0 obj
<<
/PageMode /UseNone /Pages 8 0 R /Type /Catalog
>>
endobj
7 0 obj
<<
/Author (\(anonymous\)) /CreationDate (D:20261102020454+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20261102020454+00'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (\(unspecified\)) /Title (Peraturan Kepegawaian PT Lintas Data Mandiri) /Trapped /False
>>
endobj
8 0 obj
<<
/Count 2 /Kids [ 4 0 R 5 0 R ] /Type /Pages
>>
endobj
9 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2202
>>
stream
GatU4a`?/p&A@B[qC&s$L?*<^goNJT/B_&!Al?27!fGJ=_ds[J.$acqY5s6uXEM-R*g0X:+1bf4YX^>F4T5/I'A]eT\%b&rHs4:H(H;Gt+6_&DeULY,]5fg)q/kUI(<Zl)@)CG-e`I3c]hTP*jq-X<[pW(Ic[mLHj"C65VUlDc5K8t%<$_nJ&sr,X]F'Mia#-$$U(K82'TJchU53>+hpHaClh>K_&clCtn2CP1R<IN^VqgN4i"!+#(6&.e:5%[gF>%ZBXgWg*NB.Ze-41.K(nB8iGu<bS2+h;$="Dh@\n5fYes;3Y%H]Z^*"nCT6pdraO9O/Y0863KQ&CAhQ)]euL#]af_OL/;^RssGm-?&P2@qM,#,b%sX+jtp$hYek[k^&fgDp'!TtH4k6VUfs+qLq)MaNoJ/3r5i.0gKAM*k.QM3Jph&Y6@0A<IdKDY/T3K/f"Q#M\61k(7?p!oWSLA.<.DW)8ta*<L"U%?u%H.!]Hr`n7/jeL=+$=NO,3hO?1RAT:%FS@1TafEu4_-l6S,]bs72:r;8>9f]S!'TUSZCgO&u6n+R"!S1XRD&))d;t.S"2RP"mc?W/Y:/Q,qr_."m;q-$T&(O3SMaS<CGrZ<do5_,rb0,eH:#(O;k,]3jQjl=,XM$@BHhV$B[])!n25bRaNA(&:M_l).l\=cs8i=*S[lp.ED:f*^VA)TU:r7AA)gm@T#$#7aUQ_ZlJ-0?-i)"/S9f8e3"s>aR:fS1i5b@2XDkN4QQ7s<^R$u)'Ti]2BZOm>j"OKZ"YV0"2b*g<b6F4mH.V_P)5CHj\g;#762^V<U2GP*%.L14LQ"d3VR!VjR26(=hMD%@D<.M_'->0;9%8Kf<l<hhkrpdp[k\bg>-E;gB,#/$KN?.C+3.akcM6`2p82dL#W`0LMDJ97!CRoSH,i8+U8`m`CS'2Z_Gi%@<cGH1Y;igXE3+0SW>\l[Z]g_Ei0tk4ASY'D-fq0'o#"G$.ElM,t=CD2VYg<chV%59)nO:1D/c((/5V^rNijPu0B@+kFOC!D?RT(Q1TsX,4,MF5kBpXh.I""gM-`nNpp8pTuS;,IppFedGQ[P*l8fQ##K`ld:TH6Y^\!BGHj'Eou/;X]gMjeq29C9/\GhQ^Z-$$pLWb$khTCdk]Q-MnbKU6?4#H`iEq.,b^3Wc'jQVPQbn.8@,%d"@AU-b(ZlEl?=da&>)B;S<=Aj>FNIK,\?SJhYVq&HU4pp[qWVQRCu+X6ZP:S0=0Y?cD83^EKmUQlmCgG*n/10e=$%sKYni3M6%pY.F8fWf/;No@s;9kNc0?L4'2_6Z28^hDkCYM&Zo-pR+_\&6]2\DF214dgZ3cjreiqBWLIVrooMmsaaG+IW-mLO>h#22Ve[2&B_T@+c8MIqhJI^LukB".n!qBXUa';hmQhg=pQ#U.J$P[2;p1%(rWYBX=VG><&daW;02,"1+Z;*r:?!rqksW]4&B.I=c0L4*?0RD2WKh"=8>G*,h:;\4/Ff>$Ht?'H>^aU2@rAUM&@t>&@n[gS$pRIB\Q+V=T`3])Bp[D%hnM!RA$MHKf"K.sFuFGD4,`<a1'!]\;pUE1%pu3lb+dSm-P#SQ@Wc!3kF:a]e877&/c$b/,`Ule(pj"fK267C.W[d4L6,Z`M5QU][e_Q$e'$bCUlX>b*WlR7Zije([?*!FM8rZ0i\%q_%;0>Q[Qm>HpLj&Jss=>gQqKEFYOdQ%>u]XgebXh16NB3j+`U(tdFgdk-tlEMl9mi/hk\WB0LTa_@Z`QeVJfGpLbgXtB'Xgj=O8KrE>Vn:JiMR,_?1eoa*!(+^i?IarPF3[<4t*ogj2RTl,cQ``ja7k(Gb#5M\#AUH]LABJG3/2>e0\6<I$6&qq/h<N?OCjOrJ*&C&om]]-mM>Jgu;]3)TL>>*lUa>^U4V5SLMNq__3hK<WbOG(2eHn#J%W!Te<ZJE-Q9r.i@qDfAiBUU?_,0%9T$$-p@'5R;?7U2Y+Ka@k#>oM7_eUf5[g7nR`CNaKYK1EdY6!I+?E3?7HSUl?P\Dau?$rK-1QuJIlqQW?q!>Q_?f:O?m?m,N;mlO+U&<F2*1FFh31la?klmtemEL%1CKWgI/8L(M,/9qp)mGN\_9n;/X1;K9''1h(kQI'jRZkn1Iue8-j]p#1O5BVT2mdo'DoU#PLO;K[n.j'Qf\GWcE@@h]2"k)_<;Ag+ZBkVkB-;?CDKPP933U3s~>endstream
endobj
10 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 304
>>
stream
Gas2Db>,r?$q9pdMS#YiV4"[tkn],Y=L;Od^e'8;SO^OZrZ?>fZ=&57=ao1:b5rEJaM,.!S&G"K+DStNLjcghr4ooq^'jIuC`9tS%V=B9>#oU;apgQNX;*7gDBS>;<Fo3Z,<ZKHaMLgtFNPQN#m2an0hbb2R]n30@LD188R6?]#?ppRPrL3[DB-c>M7'DkC/I0,%@N=!D'6$Dcb*l@aX[&%/_;@7&Vh&L_(+3arTlC/#-RI2Sa%8?gp4_Bs#FcKOq0o*[h+0$R5Ao,@?/7gWVi=IpRU":]Pt?on6DX]!D-L;m/~>endstream
endobj
xref
0 11
0000000000 65535 f
0000000061 00000 n
0000000102 00000 n
0000000209 00000 n
0000000321 00000 n
0000000524 00000 n
0000000728 00000 n
0000000796 00000 n
0000001107 00000 n
0000001172 00000 n
0000003465 00000 n
trailer
<<
/ID
[<d3b9bab359ca956a55a3370806ad840f><d3b9bab359ca956a55a3370806ad840f>]
% ReportLab generated PDF document -- digest (opensource)
/Info 7 0 R
/Root 6 0 R
/Size 11
>>
startxref
3860
%%EOF
+95
View File
@@ -0,0 +1,95 @@
[
{
"pertanyaan": "Berapa biaya pendaftaran mahasiswa baru jalur reguler?",
"kata_kunci": [
"350.000"
]
},
{
"pertanyaan": "Berapa SKS maksimum yang boleh diambil jika IPS sebelumnya 3,2?",
"kata_kunci": [
"24 SKS"
]
},
{
"pertanyaan": "Berapa IPK minimal untuk predikat cum laude?",
"kata_kunci": [
"3,51"
]
},
{
"pertanyaan": "Berapa biaya cuti akademik dibanding UKT?",
"kata_kunci": [
"25 persen"
]
},
{
"pertanyaan": "Siapa yang berhak mendapat beasiswa kurang mampu?",
"kata_kunci": [
"3.000.000"
]
},
{
"pertanyaan": "Berapa hari cuti tahunan karyawan tetap?",
"kata_kunci": [
"12 hari kerja"
]
},
{
"pertanyaan": "Kapan gaji karyawan dibayarkan?",
"kata_kunci": [
"tanggal 25"
]
},
{
"pertanyaan": "Berapa lama cuti melahirkan?",
"kata_kunci": [
"3 bulan"
]
},
{
"pertanyaan": "Berapa uang harian perjalanan dinas dalam negeri?",
"kata_kunci": [
"400.000"
]
},
{
"pertanyaan": "Berapa lama satu surat peringatan berlaku?",
"kata_kunci": [
"enam bulan"
]
},
{
"pertanyaan": "Apa itu RAG dan bagaimana cara kerjanya?",
"kata_kunci": [
"embedding",
"chunk"
]
},
{
"pertanyaan": "Berapa ukuran chunk yang umum dipakai?",
"kata_kunci": [
"500 sampai 1000"
]
},
{
"pertanyaan": "Apa kelemahan pencarian semantik?",
"kata_kunci": [
"angka",
"kode"
]
},
{
"pertanyaan": "Apa yang diukur oleh Hit@k?",
"kata_kunci": [
"k hasil teratas",
"proporsi"
]
},
{
"pertanyaan": "Siapa yang memperkenalkan istilah kecerdasan buatan?",
"kata_kunci": [
"McCarthy"
]
}
]
+243
View File
@@ -0,0 +1,243 @@
"""
rag_lab.py - Kode untuk Lab 1, 2, dan 3 (chunking, indeks & pencarian, Hit@k)
Instalasi:
pip install pypdf chromadb sentence-transformers
Cara pakai:
python rag_lab.py lab1 dokumen/contoh.pdf
python rag_lab.py lab2 # indeks semua PDF di dokumen/ lalu tanya
python rag_lab.py lab3 pertanyaan_uji.json # hitung Hit@k
Format pertanyaan_uji.json:
[
{"pertanyaan": "Berapa batas usia pendaftar?", "kata_kunci": ["usia", "35 tahun"]},
...
]
"""
import os
os.environ["HF_HUB_DISABLE_SYMLINKS_WARNING"] = "1" # sembunyikan peringatan symlink di Windows
import json
import random
import sys
from pathlib import Path
import chromadb
from chromadb.utils import embedding_functions
from pypdf import PdfReader
FOLDER_DOKUMEN = Path("dokumen")
FOLDER_DB = "chroma_db"
# Model multibahasa, cocok untuk teks Indonesia
MODEL_EMBEDDING = "paraphrase-multilingual-MiniLM-L12-v2"
_embed_fn = None
_client = None
def _embedding():
global _embed_fn
if _embed_fn is None:
_embed_fn = embedding_functions.SentenceTransformerEmbeddingFunction(
model_name=MODEL_EMBEDDING
)
return _embed_fn
def _db():
global _client
if _client is None:
_client = chromadb.PersistentClient(path=FOLDER_DB)
return _client
# ---------------------------------------------------------------- Lab 1
def baca_pdf(path):
"""Baca seluruh teks PDF menjadi satu string."""
reader = PdfReader(str(path))
halaman = []
for p in reader.pages:
teks = p.extract_text() or ""
halaman.append(teks.strip())
return "\n\n".join(halaman)
def potong(teks, ukuran=1000, overlap_persen=15):
"""
Potong teks menjadi chunk berukuran ~`ukuran` karakter dengan overlap.
Berusaha memotong di akhir kalimat/paragraf agar tidak terpotong di tengah.
"""
overlap = int(ukuran * overlap_persen / 100)
chunks = []
mulai = 0
n = len(teks)
while mulai < n:
akhir = min(mulai + ukuran, n)
if akhir < n:
# cari titik potong terbaik di 25% terakhir jendela
batas_cari = mulai + int(ukuran * 0.75)
jendela = teks[batas_cari:akhir]
titik = max(jendela.rfind("\n\n"), jendela.rfind(". "), jendela.rfind(".\n"))
if titik != -1:
akhir = batas_cari + titik + 1
potongan = teks[mulai:akhir].strip()
if potongan:
chunks.append(potongan)
if akhir >= n:
break
mulai_baru = max(akhir - overlap, mulai + 1)
# geser maju sampai awal kata berikutnya agar tidak memotong kata
while mulai_baru < akhir and not teks[mulai_baru - 1].isspace():
mulai_baru += 1
mulai = mulai_baru
return chunks
def lab1(path_pdf):
teks = baca_pdf(path_pdf)
print(f"Total karakter: {len(teks)}")
print("\n=== 500 karakter pertama ===")
print(teks[:500])
random.seed(42)
for ukuran in (500, 1000, 1500):
overlap_persen = 15
chunks = potong(teks, ukuran, overlap_persen)
print(f"\n{'=' * 60}")
print(f"Ukuran {ukuran}, overlap {overlap_persen}% -> {len(chunks)} chunk")
for i in random.sample(range(len(chunks)), min(3, len(chunks))):
print(f"\n--- chunk #{i} ({len(chunks[i])} karakter) ---")
print(chunks[i])
akhir = chunks[i].rstrip()[-1:]
if akhir not in ".!?:)":
print("[!] Chunk tidak berakhir di tanda baca: kemungkinan terpotong")
# ---------------------------------------------------------------- Lab 2
def buat_indeks(nama_koleksi, ukuran, overlap_persen=15, folder=FOLDER_DOKUMEN):
"""Indeks semua PDF di `folder` ke koleksi baru bernama `nama_koleksi`."""
try:
_db().delete_collection(nama_koleksi)
except Exception:
pass
koleksi = _db().create_collection(
nama_koleksi,
embedding_function=_embedding(),
metadata={"hnsw:space": "cosine"},
)
pdfs = sorted(Path(folder).glob("*.pdf"))
if not pdfs:
sys.exit(f"Tidak ada PDF di folder '{folder}/'.")
for pdf in pdfs:
chunks = potong(baca_pdf(pdf), ukuran, overlap_persen)
if not chunks:
print(f"[!] {pdf.name}: tidak ada teks terbaca (PDF hasil scan?)")
continue
koleksi.add(
ids=[f"{pdf.stem}-{i}" for i in range(len(chunks))],
documents=chunks,
metadatas=[{"sumber": pdf.name, "urutan": i} for i in range(len(chunks))],
)
print(f"{pdf.name}: {len(chunks)} chunk -> koleksi '{nama_koleksi}'")
return koleksi
def ambil_koleksi(nama_koleksi):
return _db().get_collection(nama_koleksi, embedding_function=_embedding())
def query(nama_koleksi, pertanyaan, k=3, tampil=True):
"""Cari k chunk paling mirip. Mengembalikan list dict."""
hasil = ambil_koleksi(nama_koleksi).query(query_texts=[pertanyaan], n_results=k)
out = []
for doc, meta, jarak in zip(
hasil["documents"][0], hasil["metadatas"][0], hasil["distances"][0]
):
out.append({"teks": doc, "sumber": meta["sumber"], "skor": 1 - jarak})
if tampil:
print(f"\nPertanyaan: {pertanyaan}")
for r in out:
print(f" [{r['skor']:.3f}] {r['sumber']}: {r['teks'][:700].replace(chr(10), ' ')}...\n")
return out
def lab2():
buat_indeks("chunk_1000", ukuran=1000)
print("\nKetik pertanyaan (kosong untuk selesai). Coba 5 biasa + 2 parafrase.")
while True:
q = input("\n> ").strip()
if not q:
break
query("chunk_1000", q, k=3)
# ---------------------------------------------------------------- Lab 3
def hit_at_k(nama_koleksi, soal, k):
"""
Hit@k = proporsi pertanyaan yang kata kuncinya muncul
(minimal satu kata kunci) di salah satu dari k chunk teratas.
Mengembalikan (skor, daftar_pertanyaan_gagal).
"""
hit = 0
gagal = []
for s in soal:
hasil = query(nama_koleksi, s["pertanyaan"], k=k, tampil=False)
gabungan = " ".join(" ".join(r["teks"].split()) for r in hasil).lower()
if any(" ".join(kw.split()).lower() in gabungan for kw in s["kata_kunci"]):
hit += 1
else:
gagal.append(s["pertanyaan"])
return hit / len(soal), gagal
def lab3(path_soal):
soal = json.loads(Path(path_soal).read_text(encoding="utf-8"))
print(f"{len(soal)} pertanyaan uji dimuat.")
ukuran_uji = {"chunk_500": 500, "chunk_1500": 1500}
for nama, ukuran in ukuran_uji.items():
buat_indeks(nama, ukuran)
print(f"\n{'Koleksi':<12}{'Hit@1':>8}{'Hit@3':>8}{'Hit@5':>8}")
gagal_per_koleksi = {}
for nama in ukuran_uji:
baris = []
for k in (1, 3, 5):
skor, gagal = hit_at_k(nama, soal, k)
baris.append(skor)
if k == 5:
gagal_per_koleksi[nama] = gagal
print(f"{nama:<12}" + "".join(f"{b:>8.2f}" for b in baris))
print("\nPertanyaan yang gagal pada k=5:")
for nama, gagal in gagal_per_koleksi.items():
print(f"\n[{nama}] {len(gagal)} gagal")
for g in gagal:
print(f" - {g}")
print("\nTulis kesimpulan 3-4 kalimat: ukuran terbaik, jenis pertanyaan yang gagal, dugaan penyebab.")
# ---------------------------------------------------------------- CLI
if __name__ == "__main__":
if len(sys.argv) < 2:
sys.exit(__doc__)
perintah = sys.argv[1]
if perintah == "lab1" and len(sys.argv) == 3:
lab1(sys.argv[2])
elif perintah == "lab2":
lab2()
elif perintah == "lab3" and len(sys.argv) == 3:
lab3(sys.argv[2])
else:
sys.exit(__doc__)