From 9e2c249e76ae16e3f76e9e0eb26b938ab40f3c4c Mon Sep 17 00:00:00 2001 From: Deborah Ferreira Date: Sat, 13 Apr 2024 16:38:43 +0100 Subject: [PATCH] Improving example text: function calling --- examples/ExampleFunctionCalling.m | 125 ++++++++++++++++++++++++++++ examples/ExampleFunctionCalling.mlx | Bin 5103 -> 9826 bytes 2 files changed, 125 insertions(+) create mode 100644 examples/ExampleFunctionCalling.m diff --git a/examples/ExampleFunctionCalling.m b/examples/ExampleFunctionCalling.m new file mode 100644 index 0000000..93d96aa --- /dev/null +++ b/examples/ExampleFunctionCalling.m @@ -0,0 +1,125 @@ +%% Analyze Scientific Papers Using ChatGPT Function Calls +% This example shows how to extract recent scientific papers from ArXiv, summarize +% them using ChatGPT, and write the results to a CSV file using the |openAIFunction| +% function. +%% +% * The example contains three steps: +% * Define a custom function for ChatGPT to use to process its input and output. +% * Extract papers from ArXiv. +% * Use ChatGPT to assess whether a paper is relevant to your query, and to +% add an entry to the results table if so. +%% Initialize OpenAI API Function and Chat +% Use |openAIFunction| to define functions that the model will be able to requests +% calls. +% +% Set up the function to store paper details and initiate a chat with the OpenAI +% API with a defined role as a scientific paper expert. +% +% Define the function that you want the model to have access to. In this example +% the used function is |writePaperDetails|. + +f = openAIFunction("writePaperDetails", "Function to write paper details to a table."); +f = addParameter(f, "name", type="string", description="Name of the paper."); +f = addParameter(f, "url", type="string", description="URL containing the paper."); +f = addParameter(f, "explanation", type="string", description="Explanation on why the paper is related to the given topic."); + +chat = openAIChat("You are an expert in filtering scientific papers. " + ... + "Given a certain topic, you are able to decide if the paper" + ... + " fits the given topic or not.", Tools=f); + +function writePaperDetails(name, url, desc) +filename = "papers_to_read.csv"; +T = table(name, url, desc, VariableNames=["Name", "URL", "Description"]); +writetable(T, filename, WriteMode="append"); +end +%% Extract Papers From ArXiv +% Specify the category of interest, the date range for the query, and the maximum +% number of results to retrieve from the ArXiv API. + +category = "cs.CL"; +endDate = datetime("today", "Format","uuuuMMdd"); +startDate = datetime("today", "Format","uuuuMMdd") - 5; +maxResults = 40; +urlQuery = "https://export.arxiv.org/api/query?search_query=" + ... + "cat:" + category + ... + "&submittedDate=["+string(startDate)+"+TO+"+string(endDate)+"]"+... + "&max_results=" + maxResults + ... + "&sortBy=submittedDate&sortOrder=descending"; + +options = weboptions('Timeout',160); +code = webread(urlQuery,options); +%% +% Extract individual paper entries from the API response and use ChatGPT to +% determine whether each paper is related to the specified topic. +% +% ChatGPT will parse the XML file, so we only need to extract the relevant entries. + +entries = extractBetween(code, '', ''); +%% Write Relevant Information to Table +% Determine the topic of interest. + +topic = "Large Language Models"; +%% +% Loop over the entries and see if they are relevant to the topic of interest. + +for i = 1:length(entries) + prompt = "Given the following paper:" + newline +... + string(entries{i})+ newline +... + "Is it related to the topic: "+ topic +"?" + ... + " Answer 'yes' or 'no'."; + [text, response] = generate(chat, prompt); + +%% +% If the model classifies this entry as relevant, then it tries to request a +% function call. + + if contains("yes", text, IgnoreCase=true) + prompt = "Given the following paper:" + newline + string(entries{i})+ newline +... + "Given the topic: "+ topic + newline + "Write the details to a table."; + [text, response] = generate(chat, prompt); + +%% +% If |function_call| if part of the response, it means the model is requesting +% a function call. The function call request should contain the needed arguments +% to call the function specified at the end of this example and defined with |openAIFunctions|. + + if isfield(response, "tool_calls") + funCall = response.tool_calls; + functionCallAttempt(funCall); + end + end +end +%% +% Read the generated file. + +data = readtable("papers_to_read.csv", Delimiter=",") +%% Helper Function +% This function handles function call attempts from the model, checking the +% function name and arguments before calling the appropriate function to store +% the paper details. + +function functionCallAttempt(funCall) +%% +% The model can sometimes hallucinate function names, so you need to ensure +% that it's suggesting the correct name. + +if funCall.function.name == "writePaperDetails" + try +%% +% The model can sometimes return improperly formed JSON, which needs to be handled. + + funArgs = jsondecode(funCall.function.arguments); + catch ME + error("Model returned improperly formed JSON."); + end +%% +% The model can hallucinate arguments. The code needs to ensure the arguments +% have been defined before calling the function. + + if isfield(funArgs, "name") && isfield(funArgs, "url") && isfield(funArgs,"explanation") + writePaperDetails(string(funArgs.name), string(funArgs.url), string(funArgs.explanation)); + end +end +end +%% +% _Copyright 2023-2024 The MathWorks, Inc._ \ No newline at end of file diff --git a/examples/ExampleFunctionCalling.mlx b/examples/ExampleFunctionCalling.mlx index a4ae7ab6f2619c58a19070fd35d705102984c70e..47f695cca02ea1c2760eece6f7b3a4b507cc8c89 100644 GIT binary patch delta 8367 zcmZ9SWl$Wjo5mL??yxMjxVw9i;$EC0#ogUu(c-$eyK8ZGmtut$ch{mtuXi&ybN|b{ z`I7gSOx`Dxe0V08bUTXm!>}j>oYZe*$N<1774!!z5k%M4b+rxe^};adj|6ip-nd+= z5Q&h>i0!DJ=UHqPeb%`dCt-Rh^;yyfd_YZk&f7z-FS3XkQ*(ENnlGnM0=ZM(VXkiq zKAWgzMVs1^H8j4+a&TR?{3CDVjOFgp`rh8LVNn_)8^|{@a#C^Mz{vmN9<*z9;orbV z)b3Tw2|4~&sv>Zq6gyo!sx3s!5~GA-YqAe+nVg@ySzo`D$yoH3)HBF5le>iP)&COU zdzw~(6Wz^}5l2<$Us;H~{>W3yR%&x{yi)ERVffN-%(}4Oo9snwi&R-A2*Up!XtP6- z!xnci|L(BWYdFL!X!%VTs$?b@+qux6xUB zm`vHKqsGf?@!HEJb9JGS63@{+L`d2e8BIi$-oJE{zuF^+A4Gona{iZ<*UkKdB!^;n zZZ&UMWB2R?yQ8g#X_kV=sg5+`Z+DB@5YI$LM?C4G7Y+|&5Ec<(r0YTnf>2G7r6gY({ftX2 zaWz{T)~5(#&K{UYrEb-%=tvqfs@Bb%heT~En5Su~&40)T#r8iZPO zpie=!X9ZK#jCiOyr8_tbvJL}2Ra<)DH>Qll5)E<^U77~T@Td#aB9Z`{FL=Rh5LH0^ zVuK-f=M0yA0uJi=;fvE~qMs5%4X!b|B-HCg`W!}j=We6ine7#}qvk{=i7I?3_@?3- z?lyOl35l3%z?%zL*3_Y{Yt0eJZp}A1U57!N)M!_oPtyq5!6BBR^~hVv6W&55&R@k})>U}zJFMMLpu zM~WRj(vV~V+psx=UGBvCq*8uA=?$SDQ@X`5UWMzj2}Q(@s33rve3LgjWZL9 zfq(TWVd^Eygz8ZkiUX?@`>g~{rJ8rPv1{gFgFv0zb2>xHBD8prk?P3hj1#F1YM1e` z5qmT42>F+sJUTFFM&b1GF2;-T?Rzi0P#1Dk{IF-|vmVkeB%wkUY-39D^u?_8(@*F< zR~x;1-S=54-Xn#w;@HEy$4M_5wt{8SuU5CGYrZ=ZlZw7u=#epKzbnM5mdochBRy#A zJTr;=DcfE+R;(W&L63RCyN|bVf=r{IUdU2+Q`Grg%l z-s+QtFIEG;?K1{_E>Aa;6tKalGyC{%V(gS1n8~K%{Hr(#(wwZwhid&*$}&Je)HGT0 zA$}$9Pga>iVwL=ekB>?-ZUG#!wD;p)O^tBvkJnFs!_>UvNl7Bk@z*xMb_=7bkAAJc z9uWXDcT!yyv2K~#aFt@2v>FM#3T~%;REA;S*D3!F3RMZhwt3L9Fb)!)NHAOn$urCD zma5-RrHiLCK{S6B3A)fziQ<#QovbZoMq{2M8+k03IF#gCLg}9nEXk* zk@rGbF*6gMLwE!Q;aQ(W?T;1Q-0iX$|K+3OYu+0^NV<%>q&<$m`ZYJgBbdaaFl}92 zJ+W_ajm7Bqd5O!a}rm(ob-~WCf#&`;dv91k$;cPqUcyJ=<9PIUbp< z{3$co$sMx|*VK|NA-D&e0y@PgDJ^pdwhnE~&1;xsm}Y#TX2PgvQtZs`1a{*KI&l`y zvVuJKEgG*Qm=E#LPDGh9%P+)uIl@H`(^H;bJ6ku;{+3hjR%QJWq!v+?^)qgR%PUNH z>M*b2A%mzSo68^6;^ynW2yBdbFH0yZcHp9PEJ~JTWpOH3UH|77ESN z$#8Ajqm0P{D%q4(;i8LA`DhG6vk*z6{Tp?pNy4roJ!S=!d}>7d)|Do5-Bcko$VMzr z?g$OKb)+q}4|QzjDORr(5p3e^_@tMuOvZaPPY{3C^ZoAmZS-=*p9|q`is{$uzTdq% zac7z3cuN#3L4IFbMX^R1e;$#{GUrF0h~mIL@TK6$84CUIHwwS{;jWZBlNeIrvOI67 zg}rnvOdx|mnOpVP>Qnghw`K|V9)*%wi?*qZRvZ~Adu&$ zGMFc`ScD9;N%J$cFh5ydZAzmi385m98*PZ?AIUjrH!;W_hau3HS@?r6B2Ly!EXL1=B~`VxiN@O$pYyU_P)hdil%T=GNs@)~OHa zi`IMUEdJ-*>UNo4wCe6=&P_+1zUgasqq%JX<(fA!{mv*;2ZOWhSx1r{cTCo+a1sdF zV_MJ1NStCrlMnC1&fkk0QPJT^&$AcTl6TZ;0AvZE&#CS0RUkC8?iTbvokhfb98rea(83(vbQ_`dg`(&{rxSkEPX#- zQ%x$WV|9sh#l_D1TsWyFWe*BjQ7UQq)|ZfuheXZ=C%;znanw8i#-xlbgcu(AV?**7 zM`E&6aG#=QQE!hDrohw9G&9K6yjmIR=)0PwCtQ$3j_%I++VXJyXL`%~@`nr+4{;T{ z#&-Ccd3&QwQHIN(W;Yy>%t`(<42NE)`HG?pMHj;OZKM zkL3BQN7_~y>2W6xBHo!;JCN;R@-?3s?X+456l`yQadBpUe(8|%9{a61US_aFFB}i` z5Es$B9E2|(2s1qa7T$c@eE*FH ziCNDpj)=+hTg{TgpS*?`7w?=gPyabPMGvR4_gi@0U3`r+*xi?ibd-@0ouNG6}*!x@|=FSe%W&_(<8%lPHHRzW3O$58{S|KBWqK+5+x7N$ z70l9cje6vhuDUe(^r#j`Gvp@)+7=})>UOB`1uEO3B)=jTC*P>{_11)iC1)+|UMlnn2ScB*jVIT;8R#KwVpWNgK{jLbOf z3b>2m-vnNQ*Q`R;bkirKLdYjnGdszr6qlc&;!47^9A8FD3-QoGm`5O z7xMz0&h^9DNkey|Wk zt#0OQl(w}=vwOmE#4e}3M2UYI+7?%tLpHzaJT+2?CzpoeM6_MmOik!oh@eXfVy*JQ zgrIPkSuA!56q%Y4*=V>3c`V&rnV)A85L?LxAne_MR4uQ(7Q zilK;Q!;XePV^861nr~{5aiC>XXqnRJZ(MXsWPVP095F>M-+Z{%$OKz&rS%zCc z^2Lxj4Q}OrE}~1xl<4^cu%6IckMvUSCl*Qk>VrK4_k3jOqgm{jO%@}ISw^wlzJ0Sw zBfDH$kg*^XU>f=*k0$aFCc2yx{v9ho3_4D4rCx^N#wJFl>@&?cF|{_nvi#W9QxQ97 z2wSO2PCC+xBPT)}vItOYmg^_54Acb6jzRd92rl3syuW?_)sD@p&IsPq$t&+fSPjv# zLQ_TQT3{VO4F;RywK3i5&Xrk5>scEWu`_NPKREQ&_v;&%XLwKs%igCx%CjJ7@C5t9 zaVrNN_@$;C$)IM<3UNoa-vnvKX*Pb-=u7%W{jKRcEa_jX4rk_EKA@FZ5`c^YQ5n)a zX_t@Fl%HA($3?ai2%7W@af8*a4B{>+jEGF+a?ie{)TI9BGVFVe^(50^Jjc6ybNI7A$>!NZ;<$a%CpL^@(8}re@O!o|E(K?WZZ} z2*L=nR^vzln5hz&)8Pw!U{zyP=ncq2t<1CF+diYbIf*;Xx(UCm2Lj0>enO-fkjyZK z43GebMx>0Mb`%vb?ZMYf7bdr&t3*^I?EJ>jY4N5cMot`QnPelM1rRK(IRm+mbTh=G z2->fhaoMY!mV*6C)qx^%AH0bI3#gOiVe0Jz3FoM7^>7)c#(&dUgN`v7LGW5cRQ%aA zTLgtM(2Kcr!q70&Ff%KSJBVzdwYFOui5&ebfy(&pjZB3TE=FtL=9$BJMz)uOjz!EaUmXVk*FZ^Vzk+ zr=z(2zZXV>Kvr%HF8afxk*9n@P5bi$9pthPQ{2Z-s=jy} zDJ_7!|3ZMTSs;)fWkR7KENoWgwM#GG@>v}NWfz3xFKWP}NK&XYxIn53uxYA_f=f7( ziIg6uwSkTy7PP`1N3}5oS#_1~l{t`y+pJIl@wQ-ph?KVKka+>egc0>libM}dbQDiL z!i%W^x*Ae066lCc5Q;5jmCwg>{(d1N8v}qKPK?EMmJ7eofAx4);kCy~;{WMMJ;VK| z`pujarI}Wcnt9SWQ;4hu1Lsvfc_2hZRO# z66qWom5waP9MC5R#QjnWagX8&45*U{TH`zEFc7+zqBnFVeF@`2oE=kO9&}Q@A7JTQ z&0``XZCVBI{Hh?6&_%XwCRH6l2O zP}+byeXGAMnAS9F!ddn2f_l<2LERxul(-G2}u! ztZdi?tM%^UF`mKEMQ~PgJ~~LolZVQxrS5mnnRpEuuc5H z%qfzi-O!X;NhQU3h26CPaDfhmbl;8F1h3WL+mG2>WDuPWHo_W%4W49KuaXPuirKYh ztl*KBT_z|;(Sl_%tb`;cV5&nXV|p}41dBL9(wp-#vvAC_?v0@b!Sx9YJqV}mH|AqIr*YcoKq97EDX`+yD3KKTAZ zrMpwwiOC^ch(11NjmayC-)6s8fUUsBtr%i9y{+uXXCx}?1 ztRt0eA9_FouDC5r@sQSpN5!~*`|bxe>l*k@AOFlM>`{{U2`-uHcdn?!*F+p5u1^^j zA1ArLUxFIIv?HTLzI?n;2VD@1`qJaO@gCDIvV#c;Qdf(-Z~c*!$zfJ-X%fj>-6*kGU8#|J0m(nlhF_Of z`<^7oXR69!B*jUr!=`Uy*<7v5L6gyI)UAw)&oO8!L2IoEt1mo|*d()0IRtH2&`FhR zQ$Xaq=82bGa+f&r4-ATH=RgEC!Jefjk4$asdkpG)EVj)^Q~}kip%K(;P!*lXTrh>4 z7!$eY!*2(C7w>h1GzCt>P8h~F%_IdhF^}PWD#j9{6p$a|p|}4GgyC|h7VP%PIny8r zc96T=AbT!8rBe$6fN?8{R$)CkRdLc`xkQ{{aqf}Rkx6v8A9kM;36rulShA05FC>8cGrA*Xf3Zm=<)Y+%! zl0Nb1xiqGe1POd|I)POXu+pg7!3(@b7h>9kL1q>6%OipyZM-qb)tTZcYo>8l@3=nb zF=;ffxPT59f|M(J@PhRE#rx43TJV3GMDd`S)J6WP3jV^pqQZnuGkTUn?lThhvO06z zJ1L)Hh=wkZVdnf>O@BVxwvikWXC4>n6|PjpN$R$`!7ELuc)321u->p$NjNT z7-{df*Ql2?M_r~^C;b+nu&D3X=r1rtCpDYA<=(bt3jDDT9Z?}i>}6T=blc-QumK9FlEd zD;d0ts2{%&kZj}~)>rh=sNMzb1Ys;&97~?*L$km5W>8E~1~ckR)ncOF;X%cv)P*v{ zG7JUQf?VF&kg=X4zeF`ZV2%9dZRbizo979%PVy69_fZ$`k)A{y3Xg%Lel6Xti!W-@ zP|mX2;xUl3{vDCj@&ZofKz{x^$S7T?`o!NLM$j%6Hsye@D(*op_X7?01f6F1qS_{i zg^KEY(pK(~B9^t$Mw(pvo9hgG!Y>|3Xzt?mjrL;>>7yp;i#g4kt;nO0e06qJLQmmd zIGGP-M_B9fWlcRBe%%u%-~N5TS34G3z6xA?PE%=r9Rp78B?L>BoqlRq=s-0eDvT*vq1=RoW)|4a0wG+FuUijT8G z+LGSqf1$PEvlA^-bvApnG#uhfnSN&xwdueW#@73SGX3&Za3JT|^LVVo{=K5-=!qUQn&0*S$JUb-{vJnumLu;%E`hqN@kgt=fh{rO#0*Hh?x?d^Qa ze?8^-w$nw&JRmxEPC0gE_1qxgt7mjT+n=#7JvafIa%Q6QImT#cIhue z$+646apjPc(JM0E6@Kf(nuhz!Td_0enqJ@+GKg;(o%V(gl^v^OfEwrQKJ#CP2H-HQ z=2UlCnePuTn3|VCQ_gF&46bg~O)XlpGR{3LTJlJ_eAlPWAT3;t@3-l{FU>40ds8xU z=*Ff#@o^!Z|9g&vEppwtZmM%KEI-F*9Ij6_3H?hSnH#uqEnHZ5c>Y^pb^QX;x(<6L z@(Hzmodo8rB`a{9&I-1rGbC_bi2EE_ntOF!wa=h)cg;7d?HB{@e#P0f{=wFDP;S6~ z$}JoQ@qC8qz=+3E-MQXf2<8tTw_1@dZ|GrKK=u7Q;6IJNQ1)s?s0BJBM1GJR`0erm zNbz8w)Ivx`I3BEsS=d0|G79;34>Xq~J=@YFoj4rfyPZ8xA!`k}}39_xO6QwX6 zc@9{rXRQqTu)Iw7??YpV#=3KN2#kJ!__@m5lzErmLS9bY@c2M4pwft|HB%RAzl(U+ zS`bJiHZ*d()KfR~(M`_;66S}iM^0FRT1XM0_2E<8z>u=EpIVEH+KT6LY_Jy2kyr?F zZhdm1qgItuy_5S6*kBeW`(AsSBV(*v8^+R*Cgp z#;&<+0;c%$jpSN#Z*Dq2L}30?vv%`g4QMw8KLlMUdArRmB)5fA{vMFYLZq(8+hU#7 zS~tq72Am)qZ$1Xd8N%*lq)KIuQ4%Zg-*jqjl zoPaVc4!r0L&4@pJWokQFSUR7NC|XG;+)$5`^hmlQ^4XlE=RC6vn4k4|V&q85$y1!; z%Eic~EqRiCGTM^73s3g8TB8t3ylSf>B)-dzOv_xA_uTIuzUBB=D@2!wZW)ReNQ{*$ z`!Dwf11Fm*`cGggy#dfB%olVk?P}Mye-g8+^>Ey5uk>V!@LWy8e+|Mw}+{6m!fktF{E|L-I+3Mdw?94tL0bQY5yx`%@W{tpfRg9TCj QgNXl0N`?TW{_o6x0iP4l$p8QV delta 3587 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