Rezumarea asistată de LLM a rezumatelor și postării bluesky prin R

URMĂREȘTE-NE
16,065FaniÎmi place
1,142CititoriConectați-vă

Cum identificăm articolele relevante în domeniile noastre? Acest proiect folosește exemple de fluxuri de jurnal RSS cu rezumate, folosește LLMS pentru a extrage puncte de interes și împărtășește informații despre curiozitatea Bluesky – stimulând.

Motivații

Există nenumărate articole excelente publicate zilnic, dar cum identificăm care sunt relevante pentru domeniile noastre? Cum putem extrage esența unui articol – în mod specific, abstractul său? Să construim ceva care să folosească Bluesky ca forum pentru a stimula curiozitatea și învățarea.

Planul este de a folosi fluxurile RSS furnizate de jurnale (care sperăm să includă rezumate) și apoi să folosească aceste rezumate cu un LLM pentru a surprinde esența. Acest lucru ar putea declanșa curiozitatea în mai multe moduri: poate ceva pare neobișnuit sau intrigant, ceea ce ne determină să verificăm dacă LLM a interpretat -o corect. Poate că am citit deja rezumatul și articolul, dar LLM a identificat ceva ce ne -a lipsit.

În urma rezumatului generației sumare 🤣, cum putem folosi Bluesky ca platformă pentru a împărtăși aceste informații? Vom explora cum să implementăm toate aceste capacități în R.

Obiective:

Obțineți ID RSS Feed

library(tidyverse)
library(tidyRSS)

url <- "https://academic.oup.com/rss/site_5269/3135.xml"

(df <- tidyfeed(url))

## # A tibble: 46 × 13
##    feed_title       feed_link feed_description feed_language feed_pub_date      
##                                                       
##  1 Clinical Infect… http://a… "n    "         en-us         2025-02-24 00:00:00
##  2 Clinical Infect… http://a… "n    "         en-us         2025-02-24 00:00:00
##  3 Clinical Infect… http://a… "n    "         en-us         2025-02-24 00:00:00
##  4 Clinical Infect… http://a… "n    "         en-us         2025-02-24 00:00:00
##  5 Clinical Infect… http://a… "n    "         en-us         2025-02-24 00:00:00
##  6 Clinical Infect… http://a… "n    "         en-us         2025-02-24 00:00:00
##  7 Clinical Infect… http://a… "n    "         en-us         2025-02-24 00:00:00
##  8 Clinical Infect… http://a… "n    "         en-us         2025-02-24 00:00:00
##  9 Clinical Infect… http://a… "n    "         en-us         2025-02-24 00:00:00
## 10 Clinical Infect… http://a… "n    "         en-us         2025-02-24 00:00:00
## # ℹ 36 more rows
## # ℹ 8 more variables: feed_last_build_date , feed_generator ,
## #   item_title , item_link , item_description ,
## #   item_pub_date , item_guid , item_category 

Este atât de simplu! Două linii de coduri. Apreciez foarte mult că Oxford Publisher adaugă rezumat la feed -ul lor RSS. Acest lucru este foarte util pentru a trimite la LLM pentru rezumare.

Pentru a o seta ca automatizare, ar trebui să tragem date vechi și să vedem dacă se potrivește cu date noi. Să presupunem că avem deja date vechi care au fost salvate rda. Voi simula date vechi, dar eliminând 50% din întâmplare dfca așa. Și creați o coloană sent cu număr 1modul meu de a afirma că acestea au fost postate pe Bluesky.

Obțineți date vechi

df_old <- df |>
  slice_sample(prop = 0.5) |>
  mutate(sent = 1)

Dar, în realitate, l -am încărca din fișierul de date ca așa.

(load("old_data.rda")) #assuming it's saved under df_old 

## (1) "df_old"

Rețineți, de asemenea, că nu există niciun rezumat LLM în DF_OLD Mock.

Potriviți -vă cu date noi

Să presupunem asta dfnoul flux RSS capturat este noile noastre date, pe care le vom dori anti_join Cei vechi să -i returneze decât pe cele care în datele noastre vechi.

df_new <- df |>
  anti_join(df_old, by = "item_link") |>
  mutate(sent = 0)

df_new(10,"item_description") |> pull()

## (1) "AbstractBackgroundBlastomycosis is an environmentally acquired fungal infection that can result in severe pulmonary illness and high hospitalization rates. In 2023, a blastomycosis outbreak was detected among workers at a paper mill in Delta County, Michigan.MethodsWe included patients with clinical and laboratory evidence of blastomycosis who had spent ≥40 hours in Delta County since 1 September 2022 and had illness onset 1 December 2022–1 July 2023. We assessed epidemiological and clinical features of patients and evaluated factors associated with hospitalization. We performed whole-genome sequencing to characterize genetic relatedness of clinical isolates from 8 patients.ResultsIn total, 131 patients were identified; all had worked at or visited the mill. Sixteen patients (12%) were hospitalized; 1 died. Compared with nonhospitalized patients, more hospitalized patients had diabetes (P = .03) and urine antigen titers above the lower limit of quantification (P < .001). Hospitalized patients were also more likely to have had ≥1 healthcare visits before receiving a blastomycosis diagnostic test (P = .02) and to have been treated with antibiotics prior to antifungal prescription (P = .001). All sequenced isolates were identified as Blastomyces gilchristii and clustered into a distinct outbreak cluster.ConclusionsThis was the largest documented blastomycosis outbreak in the United States. Epidemiologic evidence indicated exposures occurred at or near the mill, and genomic findings suggested a common exposure source. Patients with diabetes may have increased risk of hospitalization, and elevated urine antigen titers could indicate greater disease severity. Early suspicion of blastomycosis may prompt earlier diagnosis and treatment, potentially reducing unnecessary antibiotic prescriptions and improving patient outcomes."

După cum puteți vedea, analizând rândul 10 al datelor, CID RSS Feed include abstract! 🙌 Norocos pentru noi!

Rezumați prin LLM

## install openai virtualenv
## Run this if it's your first time by uncommenting it
## reticulate::virtualenv_install(envname = "openai", packages = c("openai","atproto"))
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)

## (1) "26,233 adults studied (median age 71). 60.9% treated on day 0; n30-day mortality: 7.5% (day 0), 8.5% (day 1), 10.2% (days 2-5). Delay increases ndeath risk (OR 1.14 & 1.40). 🚨💊"

Aici puteți personaliza cu adevărat probabil. Ce este important pentru tine pe care ai vrea să -l vezi dintr -un rezumat, astfel încât să faci clic pe linkul jurnalului și să citești mai multe? Pentru mine, sunt statisticile. Prin urmare, am specificat asta în prompt. Deoarece Bluesky permite doar 300 de grafene (personaje vizibile), am experimentat cu 200.250.300 și am găsit 200 pare a fi cel mai sigur, astfel încât să nu trecem și să ajungem să dăm o eroare. Pentru îmbunătățiri viitoare, ar fi bine să creăm o funcție care să verifice acest lucru și să re-ruleze promptul cu reglare sau să-l fileze.

Postează pe Bluesky

Vă rugăm să rețineți că, acest lucru demonstrează doar postarea unui titlu specific df_new((10,)). Putem modifica acest cod mai târziu, astfel încât să nu fie atât de rigid. Acest lucru este doar pentru a arăta cum să trimiteți postări albastre prin intermediul atproto. Acesta este un pachet R numit bskyr Pe care îl puteți folosi și pentru a trimite postări, deși nu sunt sigur dacă puteți încorpora extern.

atproto <- import("atproto")
Client <- atproto$Client
models <- atproto$models
client_utils <- atproto$client_utils

## Login
bsky <- Client()
bsky$login("username", "password") #change to your username and app password

## Build Text
text <- client_utils$TextBuilder()
text$text(summary)

## Embed 
embed <- models$app$bsky$embed$external$Main(
      external = models$AppBskyEmbedExternal$External(
          uri=df_new((10, 'item_link')),  # The main link
          title=df_new((10, 'item_title')), # Title of the preview
          description=df_new((10, 'item_description')),  #  # Description of the link
      ))

bsky$send_post(text = text, embed = embed)

Salvați -l și postați mai târziu

Este posibil să fi observat că de fapt avem o coloană în df_old chemat sent. Aceasta este să activăm postarea de titluri pe care nu le -am postat. Practic, atribuim 1 ori de câte ori am trimis o postare și 0 Ori de câte ori există un nou titlu pe care nu l -am postat. În cele din urmă, va trebui să salvăm întregul DataFrame. Să punem tot codul la un loc doar pentru a avea sens. Vom presupune, de asemenea, că avem deja old_data.rdași, de asemenea, avem deja env. openai şi atproto.

### 1. Get Feed
library(tidyverse)
library(tidyRSS)

url <- "https://academic.oup.com/rss/site_5269/3135.xml"
df <- tidyfeed(url)

### 2. Get Old Data
load("old_data.rda") #df_old is our dataframe

### 3. Match feed and old data, to return only ones that are not in old data
df_new <- df |>
  anti_join(df_old, by = "item_link") |>
  mutate(sent = 0)

### 4. Randomly pull a title that has not been sent previously
topic_to_send <- df_new |> 
  filter(sent == 0) |>
  slice_sample(n = 1)

### 5. OpenAI LLM summarization
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 = topic_to_send |> pull(item_description)
  )
  ),
  temperature = 0
)

summary <- response$choices((1))$message$content

### 6. Post on Bluesky
atproto <- import("atproto")
Client <- atproto$Client
models <- atproto$models
client_utils <- atproto$client_utils

## Login
bsky <- Client()
bsky$login("username", "password") #change to your username and app password

## Build Text
text <- client_utils$TextBuilder()
text$text(summary)

## Embed 
embed <- models$app$bsky$embed$external$Main(
      external = models$AppBskyEmbedExternal$External(
          uri=topic_to_send((1, 'item_link')),  # The main link
          title=topic_to_send((1, 'item_title')), # Title of the preview
          description=topic_to_send((1, 'item_description')),  #  # Description of the link
      ))

## Post
bsky$send_post(text = text, embed = embed)

### 7. Combine old and new 
df_old <- rbind(df_old, df_new) |>
  mutate(sent = case_when(
    item_link == topic_to_send$item_link ~ 1,
    TRUE ~ sent
  ))

save(df_old, file = "old_data.rda")

Nu cel mai elegant cod, dar obțineți conceptul și asta va face deocamdată 🤣. Puteți seta apoi Scheduler Task sau Crontab pentru a -l rula o dată pe săptămână, etc. Partea de postare poate fi probabil separată de întregul script, astfel încât noul dvs. ecran de flux și postare să fie separate. Voi lăsa asta la codificarea voastră creativă 🙌

Limitare/oportunități de îmbunătățire

  • Modelul LLM nu este perfect. Este posibil să nu rezume întotdeauna corect. Mai ales cu anii, o modalitate prin care ne putem îmbunătăți, este să stabilim anul curent
    • Mi -am dat seama că atunci când articolele nu afirmă în mod explicit anul curent, acesta va fi implicit până în 2023. 🤔
  • Limitarea personajelor în interogarea LLM
  • Nu este gratuit, dar GPT-4O-Mini este destul de accesibil. 50% reducere dacă se lovește
  • Abrevierile sunt greu de înțeles uneori
    • Proiectul viitor ar fi să adăugați legende de prescurtare pe un fir
  • Uneori, dacă rezumatul conține mai mult de 300 de grafice (caractere cam), nu vom putea posta
    • trebuie să creăm o funcție care verifică 300 grafen și apoi să facă ceva pentru a o modifica
  • convertit openai şi bluesky să -și folosească API -ul HTTP, astfel încât să nu ne bazăm reticulate și modulele respective
    • httr2 va fi un instrument excelent pentru acest lucru

Gânduri finale

  • Acest lucru nu înlocuiește oamenii, ar fi minunat dacă oamenii pot rezuma pe toate, dar știm că acest lucru nu este posibil.
  • Oferă un fragment, un instantaneu și, de asemenea, puteți personaliza promptul pentru ceea ce este important pentru dvs., aici vreau statistici în mod specific. Dar puteți schimba promptul după bunul plac, experimentați cu acesta!
  • La prima citire sau privire, este posibil să nu vă atragă atenția, dar postările ulterioare ale altora ar putea declanșa o atenție suplimentară. Sau ceea ce mi -a atras atenția a fost dacă nu prea am înțeles prescurtările … doar din curiozitate m -a făcut să verific ce înseamnă acestea

Confirmare

Apreciez foarte mult feedback -ul lui Jonathan Ryder și Joseph Marcus cu privire la acest proiect! Au ajutat la verificarea încrucișată a ceea ce a fost important de rezumat, ce nu a fost. Ce rezumat a avut sens, ceea ce nu. De asemenea, a oferit legături bune pentru monitorizarea jurnalului de identitate. Chiar nu ar fi putut să o facă fără ideea și supravegherea lor. Dă -le o urmărire pe 🦋

Lecții învățate

  • Oxford oferă un cadru bun de schemă de furaje, inclusiv abstract
  • Ingineria promptă este importantă
  • Învăţat () în R înseamnă a -l imprima
  • Deși nu face parte din cele de mai sus, dar am citit httr2 pachet și cred că ar fi o modificare excelentă pentru codul de mai sus, astfel încât să nu depindem de python module
  • A avea prieteni pentru a oferi feedback cu privire la proiect este important! Când scriem aceste cod, vom avea prejudecăți inerente că arată grozav 🤣. Dar a avea alții te va readuce pe pământ. Am învățat atât de multe de la Joseph și Jonathan. Vă mulțumim că ați făcut parte din asta!

Dacă vă place acest articol:

Dominic Botezariu
Dominic Botezariuhttps://www.noobz.ro/
Creator de site și redactor-șef.

Cele mai noi știri

Pe același subiect

LĂSAȚI UN MESAJ

Vă rugăm să introduceți comentariul dvs.!
Introduceți aici numele dvs.