🙈 a făcut un pachet R redundant pentru un simplu apeluri OpenAI, dar adevăratul câștig a fost în sfârșit să învețe cum să construiți un pachet R! 🛠️ este eficient? Absolut nu! A meritat timpul și experiența? Da! O voi face din nou? Da! Se va rupe? Da! 🤣
Motivații
Am folosit o mulțime de LLM în ultima vreme în special OpenAI GPT4OMINI prin intermediul reticulate
. Încercând să încărcați reticulate
-> Import OpenAI -> Initializează -l -> Introduceți API_KEY etc din nou și din nou ori de câte ori vrem să -l folosim pentru un proiect nou sau un nou script, care este foarte redundant, obositor și așa mai departe.
Recent, cu motivație de la
Alec Wong
Vă recomandăm cu drag să citesc pachetul R după ce i -am pus o întrebare noob „Cum face un test și să facă modificări ale unui pachet R existent și al unei cereri de tragere?” 🤣 Cartea este minunată! Îmi place foarte mult prima parte, de fapt te învață cum să creezi un pachet R chiar de la început! Înainte de a se scufunda în detalii. Am constatat că este extrem de răcoritor, deoarece puteți crea cu ușurință un MVP chiar în avans pentru uz personal urmând un flux de lucru! 🙌
Având în vedere LLM și dorind să învățăm cum să creăm un pachet R (ahem corect, cele mai bune practici 🫡), de ce nu încercăm să facem propriul nostru pachet numit myopenai
!? Bine, există deja un pachet genial numit ellmer
iar proiectul meu actual este cel mai redundant (de unde și titlul), dar ce modalitate mai bună de a începe să înveți? Și o vom face în mod ineficient, prin intermediul reticulate
(Acest lucru este atunci când vedeți experți R scutură din cap de neîncredere 🤭) în loc de API -ul HTTP! 🤣 Alăturați -mă pentru a învăța cum să creați un pachet R!
Cu toate acestea, în mod serios, acesta nu este un mod eficient de a utiliza API -ul OpenAI … este doar în scop demonstrativ.
Codul original
reticulate::use_virtualenv("openai") library(reticulate) openai <- import("openai") OpenAI <- openai$OpenAI client = OpenAI(api_key = 'YOUR API KEY') ## change this to yours response <- client$chat$completions$create( model = "gpt-4o-mini", messages = list(dict( role = "system", content = "You are a summarizer. Please summarize the following abstract. Include statistics.Use emoji to shorten characters. The summary must not exceed 200 characters—if you reach 200 characters, stop immediately. Do not add any extra commentary or exceed the 200-character limit." ), dict( role = "user", content = df_new(10,"item_description") |> pull() ) ), temperature = 0 ) (summary <- response$choices((1))$message$content)
Cele de mai sus au fost codul nostru anterior. Acum vom folosi codul de mai sus, îl vom transforma într -o funcție și sperăm că la sfârșitul proiectului vom avea o bibliotecă numită myopenai
Și putem numi o funcție chat()
și trebuie doar să introduceți promptul sau să țevi promptul în funcție.
Obiective:
Fluxul de lucru
O notă rapidă pentru mine:
- Încărcați devTool, create_package ()
- use_git (), probabil va avea nevoie de repornire
- Încărcați DevTools din nou, scrieți prima funcție
- util_r („funcție”), apoi introduceți funcția acolo
- Load_all ()
- verifica()
- Editați descrierea
- util_mit_license ()
- Introduceți scheletul roxygen2 pe funcție, editați
- document()
- Din moment ce avem nevoie de alte pachete, use_package ()
- verifica()
usethis::create_package
usethis::create_package("myopenai")
S -ar putea să vedeți așa ceva dacă aveți deja un .git
În directorul de lucru, încercați să creați un pachet într -un nou director care nu are un director părinte cu .git
în ea și se va deschide la o nouă sesiune de proiect cu BASIC, ceva de genul usethiss::create_package("/Users/you/Document/myopenai")
. Veți observa că aveți fișiere de bază, cum ar fi.
Citiți aici pentru ce reprezintă directoarele sau fișierele.
use_git
library(devtools) use_git()
Veți primi 2 întrebări, practic dacă doriți să utilizați GIT pentru controlul versiunilor și, de asemenea, dacă doriți să vă angajați. Spune da amândoi. Atunci sesiunea de proiect va reporni. Rețineți că de fiecare dată când repornește, trebuie să reîncărcați devtools
Scrieți o funcție
Nu ne -am dat seama care este cea mai bună modalitate de a face acest lucru încă, scriem un cod funcțional minim în altă parte și apoi îl copiem în proiect sau doar creăm un script R Untitlted și scriem un cod funcțional minim înainte use_r()
funcţie? Oricum, deocamdată creați un script fără titlu și începeți să scrieți înainte să -l lipiți într -un fișier R mai organizat.
chat <- function(prompt="how are you?", system="", temp=0, max_tokens = 500L) { ## If virtualenv does not exist, create and install openai if (reticulate::virtualenv_exists(envname = "openai")==FALSE) { reticulate::virtualenv_create(envname = "openai", packages = c("openai")) } ## Start Env reticulate::use_virtualenv("openai") ## Initialize OpenAI OpenAI = reticulate::import("openai")$OpenAI client = OpenAI(api_key = "YOUR API KEY") ## Prompt response = client$chat$completions$create( model = "gpt-4o-mini", messages = list(reticulate::dict( role = "system", content = system ), reticulate::dict( "role" = "user", "content" = prompt )), temperature = temp, max_tokens = max_tokens ) ## Response message = response$choices((1))$message$content return(message) }
Bine, codul de mai sus este destul de asemănător cu originalul nostru, dar cel puțin acum este o funcție și avem inserați niște parametri impliciti, astfel încât atunci când apelăm chat()
Fără parametri în el, cel puțin va returna ceva. Nota de mai sus nu este deloc eficientă, cel mai bine să comunici cu OpenAI API este încă prin intermediul httr2
veți avea mult mai puține dependențe. De asemenea, rețineți că mai sus este din nou, nu pentru cea mai mare practică de codificare defensivă, dar putem îmbunătăți de acolo, inclusiv setarea cheii API în .Rprofile
.
usethis::use_r()
usethis::use_r("chat")
Acest lucru va crea un fișier R necompletat numit chat.R
în folderul R. Apoi copiem codul nostru de funcții minime în acest script. Nu rulați încă nimic! Acesta este următorul pas. Nu trebuie neapărat să creăm un script R pentru fiecare funcție. Ar trebui să-l numim cel puțin acolo unde este Machine & Human Readable, Sortat-Friendly, dacă data folosită Citește aici mai multe despre Ghidul de stil Tidy.
Despre organizare? Este greu, ca brevetul inteligent
Este greu să descrieți exact cum ar trebui să vă organizați codul pe mai multe fișiere. Cred că cea mai bună regulă este că, dacă puteți da unui fișier un nume concis care își evocă încă conținutul, ați ajuns la o organizație bună. Dar a ajunge la acest punct este greu. -Ghid de stil Tidyverse
devtools::load_all()
Pe consolă, introduceți următoarele:
devtools::load_all()
Acest lucru va face întregul pachet disponibil / interactiv. Și atunci ne putem testa funcția și depunerea dacă există o eroare. După cum puteți vedea în imaginea de mai sus, când apelăm chat()
a returnat un răspuns. Cartea explică diferența dintre source
şi running as a library
Aici
devtools::check()
devtools::check()
Funcția check()
este esențial, deoarece efectuează teste cuprinzătoare pentru a asigura că toate componentele unui pachet R funcționează corect, prinzând probleme mai devreme atunci când sunt mai ușor de remediat. Validează faptul că pachetul dvs. respectă standardele CRAN, menținând calitatea codului și prevenind problemele din aval pentru utilizatori. Verificarea periodică stabilește obiceiuri bune de dezvoltare, în special înainte de a -ți împărtăși pachetul cu alții sau de a -l trimite către depozite.
Imaginea de mai sus a arătat că avem 2 avertismente, să o remediem!
Editați descrierea
Înainte de a începe să remediem cele 2 lucruri de mai sus, să edităm descrierea noastră.
Simțiți -vă liber să editați titlul, descrierea, informațiile despre autor
usethis::use_mit_license()
# if you already loaded `devtools` you can just use_mit_license()
Introduceți scheletul roxygen2 pe funcție
Faceți clic pe zona funcției de chat, apoi accesați Code > Insert Roxygen2 Skeleton
Atunci va arăta ceva de genul
Atunci îl puteți edita ca atare
#' GPT4omini chat convenience #' #' @param system System Prompt #' @param prompt User Prompt #' @param temp Temperature #' @param max_tokens Max Tokens #' #' @return String Response #' @export #' #' @examples #' chat("what is 2+2?") chat <- function(prompt="how are you?", system="", temp=0, max_tokens = 500L) { ## If virtualenv does not exist, create and install openai if (reticulate::virtualenv_exists(envname = "openai")==FALSE) { reticulate::virtualenv_create(envname = "openai", packages = c("openai")) }
devtools::document()
document()
Funcția convertește comentariile Roxygen2 în fișierele sursă R în fișiere de documentare R adecvate, creând în mod specific .Rd
Fișiere necesare pentru paginile de ajutor și actualizarea fișierului spațiului de nume. Acest proces transformă comentariile speciale care pot fi citite de oameni în structura formală de documentare cerută de R, ceea ce face ca funcțiile pachetului dvs. să fie descoperit și utilizabile prin intermediul sistemului de ajutor standard.
Și când faci asta
?chat
Vei vedea asta
usethis::use_package()
usethis::use_package("reticulate")
use_package()
funcția declară formal dependențe adăugând pachete la Imports
Câmp din fișierul de descriere al pachetului dvs., permițându -vă să utilizați funcții din pachete externe precum reticulate
.
Să alergăm check()
Din nou și install()
devtools::check()
Da. Bun semn !!!
devtools::install()
Și acum puteți merge la RStudio și codul de mai jos ar trebui să funcționeze!
library(myopenai) "what is 10 + 5?" |> chat() ## (1) "10 + 5 equals 15."
🙌🤘🤩 Am făcut -o !!!
Îmbunătățirea suplimentară a pachetului
Împărțiți fișierele la Chat.R
şi utils.R
Chat.r
#' Set you Open API key #' #' @param key your API key #' #' @return set environment #' @export #' #' @examples #' dontrun{ #' set_api_key("your-api-key-here") #' } set_api_key <- function(key=NULL) { if (is.null(key)) { cli::cli_abort("Need to insert API key on key parameter")} if (!is.null(key)) { key_old <- Sys.getenv("OPENAI_KEY") if (key==key_old) { cli::cli_alert_warning("Your previous API key is the same as the one currently provided") } if (key!=key_old) { set_env(key) cli::cli_alert_success("New API Key Inserted") rstudioapi::restartSession() } if (key_old=="") { set_env(key) cli::cli_alert_success("API Key Inserted") rstudioapi::restartSession() } } } #' GPT4omini chat convenience #' #' @param system System Prompt #' @param prompt User Prompt #' @param temp Temperature #' @param max_tokens Max Tokens #' @param api_key use `set_api_key` to set it in R environment #' #' @return String Response #' @export #' #' @examples #' chat("what is 2+2?") chat <- function(prompt="how are you?", system="", temp=0, max_tokens = 500L, api_key = openai_api_key()) { # check to see if virtualenv is installed if (reticulate::virtualenv_exists(envname = "openai")==FALSE) { reticulate::virtualenv_create(envname = "openai", packages = c("openai")) rstudioapi::restartSession() } # check to see if there is api_key set if (api_key=="") { cli::cli_abort("No API key found. Need to insert API key by using {.code set_api_key()}") } ## Start Env reticulate::use_virtualenv("openai") ## Initialize OpenAI OpenAI = reticulate::import("openai")$OpenAI client = OpenAI(api_key = api_key) ## Prompt response = client$chat$completions$create( model = "gpt-4o-mini", messages = list(reticulate::dict( role = "system", content = system ), reticulate::dict( "role" = "user", "content" = prompt )), temperature = temp, max_tokens = max_tokens ) ## Response message = response$choices((1))$message$content return(message) }
utils.r
openai_api_key <- function() { key <- Sys.getenv("OPENAI_KEY") if (key=="") { cli::cli_abort("No API key found. Need to insert API key by using {.code set_api_key()}") } else { return(key) } } set_env <- function(key) { renviron_path <- file.path(Sys.getenv("HOME"), ".Renviron") api_key_entry <- paste0("OPENAI_KEY=",key) if (!file.exists(renviron_path)) { file.create(renviron_path) } # Read existing content existing_content <- readLines(renviron_path, warn = FALSE) if (!any(grepl("^OPENAI_KEY=", existing_content))) { # Append the new entry write(api_key_entry, renviron_path, append = TRUE) } else { # Replace existing entry existing_content(grepl("^OPENAI_KEY=", existing_content)) <- api_key_entry writeLines(existing_content, renviron_path) } }
Citiți mai multe din carte pentru a obține o intuiție de ce avem chat.R
şi utils.R
. Practic, doriți funcții care sunt exportate pentru a fi în chat.R
sau fișier R similar și funcții de susținere care nu sunt exportate pentru a fi în utils.R
în loc de toate funcțiile din 1 fișier, în principal pentru lizibilitate / organizare. Aceasta este o idee foarte bună.
Există multe alte lucruri pe care să le îmbunătățești. Putem petrece multe ore revizuind acest lucru! Să trecem la testing
Adăugați testarea unității
use_test("chat")
Acest lucru va crea un alt folder (tests
) și fișier (test-chat.R
)
Test-chat.R
test_that("chat("2+2?") make sure it returns 4", { expect_equal(chat("what is 2+2? return answer only") |> as.numeric(), 4) })
Apoi introducem ceva de genul de mai sus pentru a ne asigura că returnează răspunsul pe care vrem să -l asigurăm că funcționează. Și va arăta așa ceva după.
test()
🙌🤘🤩 Am făcut -o !!!
Aceasta este o imagine excelentă a fluxului de lucru propriu -zis din carte.
Confirmare
Sunt recunoscător pentru carte și, de asemenea, stilul de a pune exemplul simplu chiar în față pentru a stimula curiozitatea și a avea sfârșitul în minte. Acest lucru face ca învățarea să fie foarte inspirată. De asemenea, vreau să mulțumesc
Alec Wong
Pentru că m -a ghidat către această carte care m -a împins să învăț fluxul de lucru al creării unui pachet R. Acum am o idee despre cum funcționează acest lucru, face ca contribuția la pachetele R existente să fie un pas mai aproape !!! woo hoo !!! Nu că am multe de contribuit 🤣
Lecții învățate
- Flux de lucru al pachetului R învățat/cele mai bune practici (recomandăm cu mare drag o citire)
- Învăţat
Sys.getenv
și cum să -l scrii în mediul R - Am învățat câteva elemente de bază ale
cli
pachet - Învăţat
dontrun{}
pe exemplul Roxygen2, astfel încât să nu execute funcția. Înainte de astaset_api_key()
Exemplu a continuat să seteze OpenAI_KEY layour-api-key-here
laugh out loud - Învăţat
grepl
a fost întotdeauna obișnuit custr_detect
Acum cel puțin putem folosi regex cu bază
Dacă vă place acest articol: