From 9981647c5e9adc9c0666a53928ff729bad1b9750 Mon Sep 17 00:00:00 2001 From: TheGeneralist <180094941+thegeneralist01@users.noreply.github.com> Date: Fri, 3 Apr 2026 17:04:04 +0200 Subject: [PATCH] yep --- Cargo.lock | 376 +++++++++++++++++++++++++++++++++++++++ Cargo.toml | 3 + docs/Facharbeit.pdf | Bin 0 -> 134009 bytes docs/Facharbeit.typ | 245 +++++++++++++++++++++++++ docs/refs.bib | 86 +++++++++ docs/template.typ | 57 ++++++ src/classifiers.rs | 55 +++++- src/db.rs | 340 +++++++++++++++++++++++++++++++++++ src/main.rs | 192 ++++++++++++++------ src/scrapers/twitter.rs | 80 ++++++++- tag-tree | 2 + test-classification-list | 7 + 12 files changed, 1384 insertions(+), 59 deletions(-) create mode 100644 docs/Facharbeit.pdf create mode 100644 docs/Facharbeit.typ create mode 100644 docs/refs.bib create mode 100644 docs/template.typ create mode 100644 src/db.rs diff --git a/Cargo.lock b/Cargo.lock index 8ec723c..d87d441 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,33 +2,265 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", +] + [[package]] name = "anyhow" version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "cc" +version = "1.2.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clap" +version = "4.5.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" + [[package]] name = "classifier" version = "0.1.0" dependencies = [ "anyhow", + "clap", + "rusqlite", "serde", "serde_json", + "toml", ] +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "find-msvc-tools" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + [[package]] name = "itoa" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "memchr" version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + [[package]] name = "proc-macro2" version = "1.0.104" @@ -47,6 +279,20 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rusqlite" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" +dependencies = [ + "bitflags", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + [[package]] name = "serde" version = "1.0.228" @@ -90,6 +336,33 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" version = "2.0.113" @@ -101,12 +374,115 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "unicode-ident" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +dependencies = [ + "memchr", +] + +[[package]] +name = "zerocopy" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zmij" version = "1.0.10" diff --git a/Cargo.toml b/Cargo.toml index f7008e2..101540b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,5 +5,8 @@ edition = "2024" [dependencies] anyhow = "1.0.100" +clap = { version = "4.5", features = ["derive"] } +rusqlite = { version = "0.32", features = ["bundled"] } serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.148" +toml = "0.8" diff --git a/docs/Facharbeit.pdf b/docs/Facharbeit.pdf new file mode 100644 index 0000000000000000000000000000000000000000..6ce0682b3f2ad43d12437c8c1c5fc4c3909a78b1 GIT binary patch literal 134009 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 literal 0 HcmV?d00001 diff --git a/docs/Facharbeit.typ b/docs/Facharbeit.typ new file mode 100644 index 0000000..0b20d35 --- /dev/null +++ b/docs/Facharbeit.typ @@ -0,0 +1,245 @@ +#import "template.typ": conf + +#show: conf.with( + title: [Kategorisierung und Tagging von \ Ressourcen mithilfe von LLMs], + authors: ("Nihad Badalov",), + teacher: "Michael Mayer", + due_date: "30.04.2026", + abstract: [ + Im Zeitalter der Informationsüberflutung stellt die langfristige Organisation großer Mengen an Informationen wie Dokumenten, Lernmaterialien und Notizen eine zunehmende Herausforderung dar. Mit wachsendem Umfang werden solche Informationssammlungen häufig unübersichtlich, was den gezielten Zugriff erschwert. Eine verbreitete Methode zur Strukturierung ist das Tagging, bei dem Informationen mit Schlagwörtern zur verbesserten Kategorisierung und Auffindbarkeit versehen werden. Klassische, auf Machine Learning basierende Tagging-Ansätze erweisen sich dabei oft als komplex und sind auf umfangreiche annotierte Trainingsdaten angewiesen, um qualitativ hochwertige Ergebnisse zu erzielen. Vor diesem Hintergrund untersucht diese Arbeit den Einsatz großer Sprachmodelle (Large Language Models, LLMs) und strukturierter Modellausgaben als alternativen Ansatz zur automatisierten Informationsorganisation. + ], +) + +#let pipeline-box(body) = block( + width: 7.6cm, + inset: 8pt, + stroke: 0.8pt + black, + fill: luma(240), +)[ + #align(center)[#body] +] + += Einleitung + +== Motivation + +Digitale Wissensarbeit ist heute durch eine hohe Verfügbarkeit und gleichzeitige Flüchtigkeit von Informationen geprägt. Fachartikel, Blogbeiträge, Dokumentationen, Social-Media-Beiträge und persönliche Notizen werden fortlaufend gesammelt, weiterverarbeitet und in unterschiedliche Werkzeuge übernommen. Mit wachsender Menge steigt jedoch nicht automatisch die Nutzbarkeit dieser Informationen. Ohne eine konsistente Struktur entstehen Sammlungen, in denen relevante Inhalte zwar vorhanden sind, später aber nur schwer wiedergefunden werden. Genau an dieser Stelle gewinnt die Verschlagwortung von Ressourcen an Bedeutung, weil sie Informationen nicht nur speichert, sondern auch inhaltlich erschließbar macht. + +Im persönlichen wie im akademischen Kontext wird Tagging häufig dennoch manuell durchgeführt. Diese Vorgehensweise ist aufwendig, fehleranfällig und stark von der jeweiligen Person abhängig. Unterschiedliche Begriffe, wechselnde Granularität und uneinheitliche Benennungen führen dazu, dass ähnliche Ressourcen nicht unter denselben Schlagwörtern abgelegt werden. Besonders problematisch wird dies bei kurzen, kontextreichen Quellen wie Beiträgen auf X beziehungsweise Twitter, deren Aussage oft implizit bleibt und ohne inhaltliche Einordnung schwer interpretierbar ist. Die Motivation dieser Arbeit besteht deshalb darin, zu untersuchen, ob sich große Sprachmodelle für eine automatisierte, zugleich aber semantisch brauchbare Verschlagwortung solcher Ressourcen eignen. + +== Problemstellung + +Aus informationswissenschaftlicher Sicht handelt es sich bei der automatisierten Verschlagwortung um ein Klassifikationsproblem, das mehrere Schwierigkeiten gleichzeitig vereint. Erstens liegt häufig keine große, sauber annotierte Trainingsmenge vor. Zweitens sind die zu klassifizierenden Texte oft kurz, unvollständig oder sprachlich informell. Drittens sollen die vergebenen Tags nicht flach, sondern in einer hierarchischen Struktur organisiert werden, damit sowohl grobe als auch spezifische Themenbereiche abbildbar sind. Eine Ressource kann zudem mehreren Kategorien gleichzeitig zugeordnet werden, wodurch ein Multi-Label-Szenario entsteht @liu2023. + +Klassische Verfahren des Machine Learning erzielen in klar umrissenen Anwendungsfällen zwar gute Ergebnisse, erfordern aber typischerweise eine vorgängige Merkmalsrepräsentation, Trainingsdaten und eine stabile Problemdefinition @manning2008. Im vorliegenden Projekt liegt die Herausforderung anders: Es existiert bereits ein hierarchischer Tag-Baum, doch es fehlt an umfangreich annotierten Beispielen für das Training eines speziellen Modells. Gleichzeitig soll das System in der Lage sein, auch für neue oder nur teilweise passende Inhalte sinnvolle Vorschläge zu machen. Daraus ergibt sich die zentrale Problemstellung, wie eine praktische, robuste und nachvollziehbare automatische Verschlagwortung unter realen Projektbedingungen umgesetzt werden kann. + +== Zielsetzung und Forschungsfrage + +Ziel dieser Facharbeit ist die theoretische Einordnung und praktische Umsetzung eines Prototyps zur automatisierten Kategorisierung digitaler Ressourcen mithilfe großer Sprachmodelle. Im Mittelpunkt steht nicht der Vergleich zahlreicher Modellvarianten, sondern die Entwicklung einer funktionsfähigen Verarbeitungspipeline, die Ressourcen aus einer Eingabeliste entgegennimmt, deren Inhalte extrahiert, einen strukturierten Klassifikationsaufruf ausführt und die Ergebnisse in einer Datenbank speichert. Der konkrete Prototyp dieses Projekts verarbeitet derzeit Beiträge von X beziehungsweise Twitter, ist in Rust implementiert und nutzt ein extern aufgerufenes LLM zur Zuordnung in eine vorgegebene Tag-Hierarchie. + +Die leitende Forschungsfrage lautet daher: Inwieweit eignen sich Large Language Models dazu, kurze digitale Ressourcen ohne spezielles domänenspezifisches Training automatisch in ein hierarchisches Multi-Label-Tagging-System einzuordnen? Daraus ergeben sich zwei untergeordnete Fragen. Erstens ist zu klären, welche fachlichen Grenzen klassische Verfahren im gegebenen Szenario aufweisen. Zweitens soll untersucht werden, wie eine konkrete Implementierung beschaffen sein muss, damit die LLM-basierte Klassifikation technisch zuverlässig und praktisch nutzbar wird. + += Grundlagen + +== Informationsretrieval und Metadaten + +Informationsretrieval bezeichnet den Bereich der Informatik und Informationswissenschaft, der sich mit der Speicherung, Strukturierung, Suche und Wiederauffindung von Informationen beschäftigt. Anders als bei klassischen Datenbanksystemen stehen dabei häufig un- oder halbstrukturierte Inhalte im Mittelpunkt, etwa Texte, Webseiten oder Dokumente. Ziel ist es, aus einer größeren Informationsmenge diejenigen Elemente zu identifizieren, die für eine Anfrage oder einen Nutzungskontext relevant sind @manning2008. Für die Qualität eines Retrieval-Systems ist daher nicht allein entscheidend, ob Inhalte vorhanden sind, sondern ob sie in einer Form beschrieben werden, die ihre spätere Auffindbarkeit unterstützt. + +Metadaten übernehmen in diesem Zusammenhang eine zentrale Rolle. Sie beschreiben Ressourcen durch zusätzliche Merkmale wie Titel, Autor, Thema, Entstehungszeitpunkt oder Schlagwörter. Solche beschreibenden Informationen schaffen eine zweite Ebene über dem eigentlichen Inhalt und ermöglichen es, Dokumente nicht nur wortwörtlich, sondern auch semantisch oder organisatorisch zu erschließen. In digitalen Wissenssammlungen sind Tags eine besonders flexible Form solcher Metadaten: Sie können thematische Zuordnungen herstellen, Querverbindungen sichtbar machen und den Übergang zwischen freier Sammlung und systematischer Ordnung erleichtern. + +Gerade für heterogene Ressourcensammlungen sind Metadaten wichtig, weil Inhalte oft aus sehr unterschiedlichen Quellen stammen. Während ein wissenschaftlicher Artikel über umfangreiche bibliographische Angaben verfügt, enthält ein kurzer Social-Media-Beitrag zunächst nur wenige strukturierte Felder. Umso wichtiger ist es, aus den vorhandenen Informationen eine abstrahierte thematische Beschreibung abzuleiten. Die automatische Vergabe solcher Beschreibungen ist damit ein naheliegender Anwendungsfall des Informationsretrievals und zugleich eine Voraussetzung für spätere Such-, Filter- und Exportfunktionen. + +== Tagging-Systeme und ihre Herausforderungen + +Tagging-Systeme ordnen Ressourcen Schlagwörter zu, um sie thematisch zu gruppieren und leichter auffindbar zu machen. Im Unterschied zu streng kontrollierten Vokabularen oder Taxonomien sind Tags oft flexibel, offen und nutzergetrieben. Diese Offenheit ist praktisch, weil neue Begriffe schnell eingeführt werden können und Nutzerinnen und Nutzer ihre eigene Sicht auf Inhalte abbilden können. Gleichzeitig entstehen dadurch typische Probleme, etwa uneinheitliche Schreibweisen, Synonyme, Mehrdeutigkeiten und stark schwankende Detailgrade @guy2006. + +Eine Ressource über Programmiersprachen könnte beispielsweise mit `rust`, `systems_programming`, `compiler`, `memory` oder `performance` markiert werden. Jede dieser Bezeichnungen ist potenziell sinnvoll, doch nicht jede repräsentiert dieselbe Ebene oder denselben Fokus. Ohne Strukturierung entstehen Sammlungen, in denen Tags nebeneinanderstehen, obwohl sie hierarchisch oder semantisch miteinander verbunden sind. Für Retrieval-Aufgaben ist das problematisch, weil die Suche entweder zu breit oder zu eng ausfallen kann. + +Im vorliegenden Projekt wird deshalb kein flaches Tagging verwendet, sondern ein hierarchischer Tag-Baum. Dieser verbindet die Flexibilität einzelner Schlagwörter mit der Ordnungsfunktion einer Taxonomie. Ein Tag wie `cs/programming_languages/rust` ist aussagekräftiger als ein isoliertes `rust`, weil es den Begriff in einen größeren Kontext einordnet. Die Herausforderung verschiebt sich dadurch jedoch: Das System muss nicht nur ein passendes Thema erkennen, sondern auch die richtige Ebene innerhalb der Hierarchie wählen. + +== Klassische Verfahren des Machine Learning + +Vor dem Aufkommen großer Sprachmodelle wurde die automatische Textklassifikation überwiegend mit klassischen Verfahren des Machine Learning umgesetzt. Diese Methoden basieren meist auf einer expliziten Merkmalsdarstellung von Texten, etwa durch Worthäufigkeiten, und einem darauf trainierten Klassifikator. Sie sind rechnerisch oft effizient und in gut definierten Szenarien leistungsfähig. Ihr Erfolg hängt aber stark davon ab, wie gut Texte vorverarbeitet, Merkmale ausgewählt und Trainingsdaten gelabelt wurden @manning2008. + +Für das Verständnis der späteren LLM-basierten Lösung ist ein kurzer Blick auf die zentralen klassischen Ansätze sinnvoll. Besonders relevant sind Bag-of-Words- und TF-IDF-Repräsentationen als Grundlage, Naive Bayes als probabilistisches Verfahren und Support Vector Machines als starke lineare Klassifikatoren für hochdimensionale Textdaten. + +=== Bag-of-Words und TF-IDF + +Beim Bag-of-Words-Ansatz wird ein Text als ungeordnete Menge von Wörtern modelliert. Entscheidend ist dabei nicht die genaue Wortreihenfolge, sondern welche Terme vorkommen und mit welcher Häufigkeit. Auf diese Weise lässt sich jeder Text in einen Vektor überführen, dessen Dimensionen einzelnen Wörtern oder Token entsprechen. Das Verfahren ist konzeptionell einfach und für viele frühe Textklassifikationssysteme grundlegend gewesen @manning2008. + +Eine wichtige Verfeinerung stellt TF-IDF dar, also die Gewichtung durch Term Frequency und Inverse Document Frequency. Häufige Begriffe innerhalb eines Dokuments werden stärker gewichtet, während Wörter, die in nahezu allen Dokumenten vorkommen, an Bedeutung verlieren. Dadurch wird die Repräsentation informativer, weil charakteristische Begriffe stärker hervortreten. Salton und Buckley zeigen, dass unterschiedliche Gewichtungsschemata erheblichen Einfluss auf Retrieval- und Klassifikationsergebnisse haben können @salton1988. + +Trotz ihres Nutzens bleibt die Repräsentation jedoch überwiegend oberflächenorientiert. Zwei inhaltlich ähnliche Texte können sehr unterschiedlich erscheinen, wenn sie unterschiedliche Vokabeln verwenden. Umgekehrt können Texte mit ähnlichen Begriffen thematisch verschieden sein. Genau diese Begrenzung wird bei kurzen und kontextabhängigen Ressourcen besonders sichtbar. + +=== Naive Bayes + +Naive Bayes ist ein probabilistisches Klassifikationsverfahren, das auf dem Satz von Bayes beruht und die Merkmale eines Dokuments als bedingt unabhängig voneinander annimmt. Diese Annahme ist in realen Texten zwar stark vereinfacht, führt in der Praxis jedoch häufig zu brauchbaren und robusten Ergebnissen. Vor allem in frühen Anwendungen der Dokumentklassifikation war Naive Bayes wegen seiner Einfachheit und Effizienz weit verbreitet @manning2008. + +Für Textdaten existieren unterschiedliche Ereignismodelle, insbesondere multinomiale und Bernoulli-Varianten. McCallum und Nigam zeigen, dass die Wahl des Ereignismodells die Klassifikationsleistung deutlich beeinflussen kann und dokumentieren die Eignung von Naive Bayes für Textklassifikation trotz der starken Unabhängigkeitsannahme @mccallum1998. Die Stärke des Verfahrens liegt darin, mit vergleichsweise wenig Rechenaufwand Wahrscheinlichkeiten für Klassen zu schätzen. + +Für komplexe Tagging-Systeme stößt Naive Bayes jedoch an Grenzen. Die Methode modelliert keine tieferen semantischen Beziehungen zwischen Begriffen und bildet Kontext nur indirekt über Worthäufigkeiten ab. Gerade bei mehrdeutigen oder sehr kurzen Texten kann das zu unscharfen Zuordnungen führen. + +=== Support Vector Machines + +Support Vector Machines, kurz SVM, gehören zu den klassischen Verfahren, die für Textklassifikation lange als besonders leistungsfähig galten. Ihr Grundprinzip besteht darin, im Merkmalsraum eine trennende Hyperebene zu finden, die Klassen möglichst gut voneinander separiert. Gerade in hochdimensionalen, spärlich besetzten Textvektoren funktioniert dieser Ansatz oft zuverlässig, weil viele Dokumente trotz großer Vokabularräume linear trennbar oder nahezu trennbar sind @joachims2002. + +Für Textklassifikation ist die Methode deshalb attraktiv, weil sie mit TF-IDF- oder ähnlichen Repräsentationen gut kombinierbar ist. Joachims zeigt, dass SVMs in diesem Bereich hohe Genauigkeit erreichen und insbesondere bei großen Merkmalsräumen robust arbeiten @joachims2002. Im Vergleich zu einfacheren Verfahren wie Naive Bayes ist das Modell jedoch schwerer interpretierbar und erfordert ebenfalls annotierte Trainingsdaten. + +Auch SVMs lösen nicht das Grundproblem fehlender Semantik. Sie trennen Klassen auf Basis der vorgegebenen Merkmalsdarstellung, erzeugen diese Darstellung aber nicht selbst. Wenn relevante Inhalte in einem Text implizit bleiben oder nur durch Weltwissen erschließbar sind, kann auch ein starker Klassifikator nur begrenzt helfen. + +== Grenzen klassischer Ansätze + +Die beschriebenen Verfahren haben wesentlich zur Entwicklung moderner Textklassifikation beigetragen, doch ihre Grenzen werden im Kontext dieser Arbeit deutlich. Erstens setzen sie in der Regel ein gelabeltes Korpus voraus. Für ein persönliches oder projektbezogenes Tagging-System liegt ein solches Korpus häufig nicht vor, und seine Erstellung wäre zeitaufwendig. Zweitens hängen klassische Verfahren stark von der Qualität der Merkmalsrepräsentation ab. Werden Synonyme, Mehrdeutigkeiten oder thematische Beziehungen nicht bereits in den Merkmalen erfasst, kann das Modell sie kaum nachträglich rekonstruieren @manning2008. + +Hinzu kommt, dass kurze Social-Media-Texte oft nur bruchstückhafte Informationen enthalten. Ein Beitrag kann auf eine Debatte verweisen, implizites Fachwissen voraussetzen oder wesentliche Teile seiner Bedeutung aus Links, Autorenschaft und Diskurskontext beziehen. Ein Bag-of-Words-Modell sieht in einem solchen Fall nur wenige Terme. Für die Auswahl eines spezifischen Tags innerhalb einer Hierarchie ist dies oft nicht ausreichend. + +Schließlich ist das Ziel dieses Projekts nicht die Vorhersage genau einer Klasse, sondern die Zuweisung mehrerer möglichst spezifischer Tags in einer hierarchischen Struktur. Klassische Modelle können zwar für Multi-Label- oder Hierarchieaufgaben erweitert werden, doch dies erhöht Modellierungsaufwand und Komplexität. Für ein prototypisches System ohne große Trainingsdatenbasis liegt daher ein anderer Ansatz näher: die Nutzung eines bereits breit vortrainierten Sprachmodells. + +== Large Language Models + +=== Grundprinzipien und Training + +Large Language Models sind neuronale Sprachmodelle, die auf sehr großen Textmengen vortrainiert werden und auf dieser Grundlage statistische Regularitäten von Sprache, Wissen und typischen Textmustern erfassen. Im Kern lernen sie, welches Token mit welcher Wahrscheinlichkeit auf einen gegebenen Kontext folgt. Durch die enorme Menge an Trainingsdaten und Parametern entstehen Modelle, die nicht nur lokale Wortfolgen, sondern auch komplexere sprachliche und semantische Zusammenhänge verarbeiten können @bommasani2021. + +Der entscheidende Unterschied zu klassischen Textklassifikationsverfahren liegt darin, dass LLMs nicht speziell für eine einzelne Klassifikationsaufgabe trainiert werden müssen, um nützliche Ergebnisse zu liefern. Vielmehr können sie durch Anweisungen im Prompt auf neue Aufgaben ausgerichtet werden. Diese Fähigkeit macht sie insbesondere für Szenarien interessant, in denen keine große, sauber annotierte Trainingsmenge vorhanden ist, aber dennoch eine inhaltlich differenzierte Beurteilung nötig ist. + +=== Transformer-Architektur (konzeptionell) + +Die gegenwärtige Leistungsfähigkeit großer Sprachmodelle ist eng mit der Transformer-Architektur verbunden. Vaswani et al. führen mit dem Transformer ein Modell ein, das auf Self-Attention basiert und dadurch Abhängigkeiten zwischen Wörtern eines Textes parallel und kontextsensitiv verarbeiten kann @vaswani2017. Anstatt Wörter nur sequenziell zu betrachten, gewichtet das Modell, welche Teile des Eingabetextes für die Interpretation eines bestimmten Tokens besonders relevant sind. + +Konzeptionell ist diese Architektur für Textklassifikation deshalb bedeutsam, weil sie Beziehungen über größere Distanzen hinweg erfassen kann. Wenn in einem Text erst spät klar wird, worauf sich ein Begriff bezieht, kann ein Transformer diese Information besser einbeziehen als klassische Modelle mit starren Vektorrepräsentationen. Für die hier betrachtete Aufgabe bedeutet das, dass nicht nur einzelne Schlüsselwörter, sondern auch ihre Einbettung in den Gesamtzusammenhang berücksichtigt werden können. + +=== Semantisches Verständnis und Kontext + +Im Zusammenhang mit LLMs wird häufig von semantischem Verständnis gesprochen. Fachlich präziser ist es, von kontextsensitiver Bedeutungsverarbeitung zu sprechen. Das Modell besitzt kein menschliches Verstehen, kann aber auf Basis seines Trainings sehr viele sprachliche Muster, thematische Bezüge und typische Diskurszusammenhänge in seine Vorhersagen einfließen lassen @bommasani2021. Für praktische Aufgaben wie Tagging ist genau diese Fähigkeit entscheidend, weil sie über rein oberflächenbasierte Schlüsselworterkennung hinausgeht. + +Ein kurzer Text über Compilerbau kann beispielsweise Begriffe wie `intermediate representation`, `optimization passes` oder `toolchain` enthalten, ohne das Oberthema explizit zu benennen. Ein LLM kann solche Signale in Beziehung setzen und daraus eine spezifische thematische Einordnung ableiten. Gerade in technischen Wissenssammlungen ist diese Eigenschaft wertvoll, weil viele Ressourcen nur für Personen mit Vorwissen unmittelbar eindeutig sind. + +=== Zero-Shot- und Few-Shot-Lernen + +Ein weiterer wichtiger Aspekt ist die Fähigkeit großer Sprachmodelle zum Zero-Shot- und Few-Shot-Lernen. Brown et al. zeigen am Beispiel von GPT-3, dass große Sprachmodelle neue Aufgaben allein auf Basis sprachlicher Instruktionen und weniger oder sogar ganz ohne Beispiele bearbeiten können @brown2020. Das Modell muss dabei nicht durch Gradientenupdates auf die konkrete Aufgabe nachtrainiert werden, sondern reagiert direkt auf die Formulierung des Prompts. + +Für das vorliegende Projekt ist insbesondere Zero-Shot-Lernen relevant. Die Klassifikation erfolgt nicht mithilfe eines separat trainierten Tagging-Modells, sondern durch einen Prompt, der die Tag-Hierarchie, die Ressource und das gewünschte Ausgabeformat enthält. Wenige oder gar keine Beispiele im Prompt senken den Vorbereitungsaufwand und machen das System flexibel. Gleichzeitig steigt damit die Bedeutung einer präzisen Aufgabenbeschreibung, weil die Qualität der Ausgabe stark von der Formulierung des Prompts abhängt. + += Methodik + +== Problemformulierung der automatisierten Verschlagwortung + +Methodisch lässt sich das in dieser Arbeit behandelte Problem als hierarchische Multi-Label-Textklassifikation formulieren. Gegeben ist eine digitale Ressource, im aktuellen Prototyp repräsentiert durch eine URL zu einem X-Beitrag sowie den daraus extrahierten Textinhalt. Gesucht ist eine Menge von einem bis drei möglichst spezifischen Tags aus einem vorgegebenen Tag-Baum. Die Tags sollen thematisch passen, hierarchisch korrekt eingeordnet sein und die Ressource so beschreiben, dass sie später wiedergefunden und in Beziehung zu anderen Ressourcen gesetzt werden kann. + +Im Unterschied zu einer einfachen Ein-Klassen-Klassifikation müssen mehrere Bedingungen gleichzeitig erfüllt werden. Eine Ressource kann mehreren Themenbereichen zugeordnet sein, etwa `distributed_systems` und `databases`. Gleichzeitig soll das System eher spezifische Blattknoten als sehr allgemeine Oberkategorien wählen. Wenn kein passender Tag existiert, soll das System außerdem einen begründeten Vorschlag für einen neuen Tag liefern. Die Methodik umfasst somit nicht nur Zuordnung, sondern auch kontrollierte Erweiterbarkeit der Tag-Struktur. + +== Ansatz mit Large Language Models + +Der gewählte Ansatz nutzt ein Large Language Model als generischen Klassifikator, der ohne projektspezifisches Training auskommt. Statt ein Modell mit annotierten Beispielen auf den vorhandenen Tag-Baum zu trainieren, wird die Aufgabe zur Laufzeit in natürlicher Sprache formuliert. Das Modell erhält die aktuelle Tag-Hierarchie, die textuelle Beschreibung der Ressource und klare Regeln zur Ausgabe. Aus methodischer Sicht handelt es sich damit um eine instruktionale Zero-Shot-Klassifikation. + +Dieser Ansatz ist für das Projekt aus drei Gründen sinnvoll. Erstens entfällt die aufwendige Erstellung eines Trainingsdatensatzes. Zweitens kann das Modell Weltwissen und semantische Relationen nutzen, um auch kurze oder indirekte Texte einzuordnen. Drittens lässt sich das System leicht anpassen, indem der Tag-Baum oder die Prompt-Regeln verändert werden, ohne dass ein neues Modell trainiert werden muss. Die methodische Flexibilität ist damit höher als bei einem klassisch trainierten Spezialmodell. + +== Prompt-basierte Klassifikation + +Die Klassifikation wird durch einen strukturierten Prompt gesteuert, der dem Sprachmodell die Aufgabe explizit vorgibt. Im Projekt enthält dieser Prompt erstens eine Rollenbeschreibung als Resource Classifier, zweitens Regeln zur Auswahl möglichst spezifischer Tags, drittens die aktuelle Tag-Hierarchie und viertens eine formatierte Ressourcendarstellung. Zusätzlich wird ein JSON-Ausgabeformat vorgegeben, das die Felder `tags`, `confidence`, `new_tags` und `reasoning` enthält. Der Prompt reduziert damit die Offenheit der Modellantwort und fördert eine maschinenlesbare Ausgabe. + +Methodisch ist diese Form der Prompt-Gestaltung zentral, weil LLMs zwar flexibel, aber nicht deterministisch sind. Ein ungenauer Prompt könnte zu freien Textantworten, zu allgemeinen Kategorisierungen oder zu uneinheitlichen Formaten führen. Deshalb wird die Ausgabe auf ein klar beschriebenes JSON-Schema beschränkt. Die spätere Verarbeitung im Programm setzt nämlich voraus, dass die Modellantwort von `serde` geparst werden kann. Die Prompt-basierte Klassifikation ist in diesem Projekt somit nicht nur eine sprachliche Instruktion, sondern Teil des technischen Schnittstellendesigns. + +== Mehrfachklassifikation (Multi-Label-Tagging) + +Ein zentrales methodisches Merkmal des Systems ist die Mehrfachklassifikation. Viele technische Ressourcen behandeln mehrere Themen gleichzeitig. Ein Beitrag über eine Datenbank-Engine kann sich zugleich auf Architekturentscheidungen, Persistenz, Performance und Nebenläufigkeit beziehen. Eine Ein-Klassen-Entscheidung würde diese Mehrdimensionalität unzulässig reduzieren. Das Modell wird daher angewiesen, ein bis drei passende Tags zurückzugeben. + +Damit die Mehrfachklassifikation nicht zu unspezifischen Sammellisten führt, werden pro Tag zusätzlich Konfidenzwerte erwartet. Diese Werte sind im Prototyp keine mathematisch kalibrierten Wahrscheinlichkeiten, sondern modellgenerierte Einschätzungen, die als heuristische Stärke der Zuordnung interpretiert werden. In der Anwendung dienen sie dazu, besonders unsichere Fälle zu markieren. Zusätzlich sieht das Schema vor, neue Tags mit Elternkategorie und Begründung vorzuschlagen, wenn der bestehende Baum keine zufriedenstellende Zuordnung erlaubt. + +== Evaluationskriterien + +Auch wenn in dieser Fassung noch keine systematische Ergebnisdiskussion vorgenommen wird, lassen sich klare Evaluationskriterien formulieren. Ein erstes Kriterium ist die inhaltliche Angemessenheit: Die vergebenen Tags sollen den Gegenstand einer Ressource korrekt und nicht nur oberflächlich beschreiben. Ein zweites Kriterium ist die Spezifität. Ein System, das systematisch nur allgemeine Oberkategorien wie `cs` oder `software_development` auswählt, wäre praktisch wenig nützlich, selbst wenn die Zuordnungen formal nicht falsch wären. + +Ein drittes Kriterium betrifft die hierarchische Konsistenz. Die vorgeschlagenen Tags müssen in die bestehende Struktur passen und dürfen keine semantisch widersprüchlichen Kombinationen erzeugen. Viertens ist die Robustheit der Ausgabe wichtig: Für die technische Nutzbarkeit muss die Antwort zuverlässig im erwarteten JSON-Format vorliegen. Fünftens ist die praktische Anschlussfähigkeit zu nennen. Ein brauchbares System muss seine Ergebnisse speichern, exportieren und bei wiederholter Verarbeitung konsistent behandeln können. Diese Kriterien verbinden damit semantische, strukturelle und technische Qualitätsanforderungen. + += Umsetzung + +== Datenerhebung + +=== Datenquelle (Twitter) + +Die Datenerhebung des vorliegenden Prototyps basiert auf einer einfachen, kontrollierbaren Eingabequelle: einer Datei mit URLs namens `test-classification-list`. Jede Zeile dieser Datei enthält eine Ressource, die klassifiziert werden soll. Im aktuellen Entwicklungsstand werden ausschließlich URLs von X beziehungsweise Twitter verarbeitet. Die Entscheidung für diese Datenquelle ist pragmatisch begründet. Beiträge auf X sind leicht verlinkbar, technisch klar identifizierbar und zugleich inhaltlich anspruchsvoll, weil sie kurz, kontextreich und oft stark verdichtet formuliert sind. + +Die Wahl dieser Quelle passt zum Ziel der Arbeit, ein praxisnahes System für persönliche Wissenssammlungen zu untersuchen. Viele technisch orientierte Beiträge, Diskussionen oder Hinweise auf Werkzeuge und Fachthemen werden heute in Social-Media-Form verbreitet. Solche Ressourcen gehen ohne systematische Organisation leicht verloren. Der Prototyp nutzt X-Beiträge daher als realistische Testumgebung für ein später erweiterbares Klassifikationssystem. + +=== Eigenschaften und Herausforderungen der Daten + +Die verwendeten Daten weisen mehrere Eigenschaften auf, die ihre automatische Verarbeitung erschweren. Zunächst sind die Texte kurz. Anders als wissenschaftliche Artikel oder längere Blogbeiträge enthalten Tweets meist nur wenige Sätze. Relevante Informationen bleiben häufig implizit oder sind auf externe Links ausgelagert. Zusätzlich wird in Social-Media-Beiträgen oft informelle Sprache verwendet, einschließlich Abkürzungen, Fachjargon, Ironie oder verkürzter Formulierungen. + +Hinzu kommen strukturelle Herausforderungen. Die Eingabeliste kann Duplikate enthalten, was im Testdatensatz tatsächlich vorkommt. Außerdem ist ein Beitrag nicht nur durch seinen Text charakterisiert, sondern auch durch Metadaten wie Autor, Beitrag-ID und eingebettete URLs. Der Prototyp verarbeitet daher nicht allein den reinen Text, sondern verbindet ihn mit einer knappen kontextgebenden Titelform. Aus methodischer Sicht wird dadurch versucht, den Informationsverlust kurzer Texte teilweise auszugleichen, ohne den Aufwand eines vollständigen Diskurskontextes übernehmen zu müssen. + +== Datenverarbeitungspipeline + +Die Verarbeitung der Ressourcen erfolgt in einer mehrstufigen Pipeline, die in [@fig:pipeline] schematisch dargestellt ist. Ausgangspunkt ist eine Liste von URLs. Nach der Quelle-Erkennung wird der jeweilige Beitrag extrahiert, in eine einheitliche Textrepräsentation überführt, durch das Sprachmodell klassifiziert und schließlich zusammen mit seinen Tag-Zuordnungen in einer SQLite-Datenbank gespeichert. Zusätzlich unterstützt das System einen Export in JSON sowie eine einfache Statistikabfrage über den aktuellen Datenbestand. + +#figure( + [ + #align(center)[ + #pipeline-box([`test-classification-list` \ URL-Liste]) + #v(0.15cm) + [↓] + #v(0.15cm) + #pipeline-box([Python-Scraper \ `scrape_user_tweet_contents.py`]) + #v(0.15cm) + [↓] + #v(0.15cm) + #pipeline-box([TOML-Parsing \ und Normalisierung]) + #v(0.15cm) + [↓] + #v(0.15cm) + #pipeline-box([LLM-Klassifikation \ `codex e` mit JSON-Ausgabe]) + #v(0.15cm) + [↓] + #v(0.15cm) + #pipeline-box([SQLite-Speicherung \ sowie Export und Statistik]) + ] + ], + caption: [Vereinfachte Datenverarbeitungspipeline des implementierten Prototyps.] +) + +=== Extraktion + +Die eigentliche Verarbeitung beginnt in `src/main.rs`. Dort wird zunächst die Eingabedatei eingelesen und zeilenweise verarbeitet. Für jede URL bestimmt die Funktion `determine_resource_source`, ob es sich um eine unterstützte Quelle handelt. Gegenwärtig werden nur Adressen erkannt, die `twitter.com` oder `x.com` enthalten. Alle anderen Quellen werden mit einem Hinweis übersprungen. Diese Beschränkung macht deutlich, dass der Prototyp noch kein allgemeiner Web-Scraper ist, sondern zunächst einen klar abgegrenzten Ressourcentyp implementiert. + +Für Twitter- beziehungsweise X-Links ruft das Programm die Funktion `scrapers::twitter::scrape(url)` auf. Diese extrahiert aus der URL die Tweet-ID und startet anschließend über `Command::new("python")` das externe Skript `scrape_user_tweet_contents.py`. Wenn die Extraktion erfolgreich war, liegt im Verzeichnis `scraped-tweets/` eine TOML-Datei mit dem Namen `tweet-.toml` vor. Diese Datei dient als Übergabeformat zwischen Scraper und weiterer Rust-Verarbeitung. Die Extraktion ist damit technisch ausgelagert, aber sauber in die Hauptpipeline integriert. + +=== Vorverarbeitung + +Nach der Extraktion wird die erzeugte TOML-Datei mit `parse_scraped_tweet` eingelesen. Die Implementierung versucht zunächst, das erwartete Format mittels `serde` in eine interne Struktur zu deserialisieren. Dabei werden Felder wie `id`, `full_text` und der Autorenname unter `author.screen_name` berücksichtigt. Falls diese direkte Deserialisierung scheitert, greift eine Fallback-Routine, die relevante Zeilen manuell aus dem TOML-Text extrahiert. Diese zweistufige Verarbeitung erhöht die Robustheit gegenüber kleineren Formatabweichungen der Scraper-Ausgabe. + +Das Ergebnis der Vorverarbeitung ist ein vereinheitlichtes Objekt vom Typ `ScrapedTweet`, das die drei wesentlichen Informationen `id`, `text` und `author` enthält. Für die Klassifikation wird daraus ein formatierter String erzeugt: `Title: Tweet by @...` und `Content: ...`. Diese Darstellung ist bewusst einfach gehalten. Sie liefert dem Sprachmodell sowohl den eigentlichen Inhalt als auch einen minimalen Kontext über die Autorschaft, ohne zusätzliche Komplexität einzuführen. In methodischer Hinsicht ist die Vorverarbeitung damit kein schweres Text-Cleaning, sondern eine zielgerichtete Normalisierung in eine LLM-freundliche Form. + +=== Klassifikation + +Die Klassifikation nutzt den in der Datei `tag-tree` abgelegten hierarchischen Tag-Baum als formale Zielstruktur. Dieser Baum wird zu Beginn des Programms eingelesen und anschließend an den Klassifikationsprompt übergeben. Die Funktion `classify_with_retry` in `src/classifiers.rs` übernimmt die Orchestrierung des Klassifikationsaufrufs. Intern ruft sie `classify` auf, welches über ein Subprozesskommando `codex e` mit dem konstruierten Prompt startet. Damit ist das LLM nicht direkt als Bibliothek eingebunden, sondern über eine externe Prozessschnittstelle angebunden. + +Die Modellantwort soll ausschließlich JSON enthalten. Dieses JSON wird in die Struktur `ClassificationResult` geparst, die die Felder `tags`, `confidence`, `new_tags` und `reasoning` umfasst. Um mit unvollständigen Antworten umgehen zu können, sind die Felder mit `#[serde(default)]` versehen. Zusätzlich enthält die Funktion eine Retry-Logik: Scheitert entweder der Modellaufruf oder das Parsen der JSON-Antwort, wird der Vorgang bis zur maximalen Versuchszahl wiederholt. Diese Fehlerbehandlung ist für die Praxis wesentlich, weil freie Sprachmodelle zwar strukturierte Ausgaben erzeugen können, ihre Formatdisziplin aber nicht absolut garantiert ist. + +=== Speicherung der Tags + +Bereits beim Programmstart wird eine SQLite-Datenbank unter dem Pfad `resources.db` geöffnet und über `init_schema()` mit den benötigten Tabellen initialisiert. Das Datenbankschema umfasst die Tabellen `resources`, `tags`, `resource_tags` und `classification_log`. Dadurch werden sowohl die eigentlichen Ressourcen als auch ihre Zuordnungen, Konfidenzwerte und Begründungen der Klassifikation gespeichert. Die Wahl von SQLite ist für einen lokalen Prototyp zweckmäßig, weil keine separate Server-Infrastruktur nötig ist und relationale Tabellen dennoch zuverlässig angelegt werden können @sqlite2026. + +Vor einer neuen Verarbeitung prüft `resource_exists(url)`, ob die betreffende URL bereits bekannt ist. So kann verhindert werden, dass identische Ressourcen bei wiederholten Läufen erneut klassifiziert werden, sofern nicht der `--force`-Schalter gesetzt ist. Nach erfolgreicher Klassifikation wird die Ressource zunächst über `insert_resource` in die Tabelle `resources` eingetragen oder aktualisiert. Anschließend erzeugt `store_classification` die Tag-Zuordnungen in `resource_tags`, legt fehlende Hierarchieeinträge in `tags` an und schreibt die Begründung sowie Tag-Vorschläge in `classification_log`. Die Speicherung ist damit nicht nur Ergebnisablage, sondern strukturelle Grundlage für spätere Auswertung, Export und Weiterverarbeitung. + +== Implementierungsdetails + +=== Projektstruktur + +Das Projekt ist bewusst modular gehalten. Die zentrale Ablaufsteuerung liegt in `src/main.rs`, wo die Kommandozeilenschnittstelle mit `clap` definiert wird. Dort werden die drei Subkommandos `classify`, `export` und `stats` bereitgestellt. `classify` verarbeitet Ressourcen aus einer Eingabedatei, `export` schreibt gespeicherte Ressourcen in eine JSON-Datei und `stats` gibt eine einfache Übersicht über Ressourcen- und Tag-Anzahlen aus. Diese Aufteilung macht die Anwendung als Werkzeug bedienbar und trennt Steuerlogik von Fachlogik. + +Die Klassifikationslogik selbst liegt in `src/classifiers.rs`, die Datenbankanbindung in `src/db.rs` und die Scraper-spezifische Verarbeitung in `src/scrapers/twitter.rs`. Ergänzt wird diese Struktur durch externe Projektdateien wie `tag-tree`, `test-classification-list` und das Verzeichnis `scraped-tweets/`. Die Architektur folgt damit einer klaren Rollenverteilung: Eingabe und Ablaufsteuerung, Datenextraktion, semantische Klassifikation sowie Persistenz sind voneinander getrennt, aber über einfache Funktionsschnittstellen gekoppelt. + +=== Klassifikationslogik + +Die interne Klassifikationslogik des Programms ist auf idempotente und nachvollziehbare Verarbeitung ausgelegt. Für jede URL wird zunächst geprüft, ob sie bereits in der Datenbank vorhanden ist. Existiert ein Eintrag und wurde kein `--force`-Flag gesetzt, wird die Ressource übersprungen. Damit bleibt der Lauf effizient und wiederholbar. Wird `--force` verwendet, wird eine bestehende Ressource erneut klassifiziert, was insbesondere bei verändertem Prompt oder erweitertem Tag-Baum sinnvoll ist. + +Nach dem eigentlichen Modellaufruf werden die vom LLM gelieferten Informationen nicht blind übernommen, sondern zusätzlich ausgewertet. Das Programm gibt gefundene Tags, Konfidenzen und die textuelle Begründung auf der Konsole aus. Für neue Tag-Vorschläge wird der Elternknoten samt Begründung gesondert angezeigt. Außerdem wird mit `confident_tags(0.5)` eine einfache Schwelle angewandt, um sehr unsichere Klassifikationen sichtbar zu machen. Diese Schwelle ersetzt keine wissenschaftliche Evaluation, zeigt aber, wie modellinterne Sicherheitsschätzungen bereits im Prototyp in praktische Entscheidungslogik überführt werden können. + +=== Scraper-Implementierung + +Die Scraper-Implementierung ist ein gutes Beispiel für die pragmatische, prototypische Natur des Projekts. Anstatt eine vollständige Scraper-Logik direkt in Rust nachzubauen, nutzt das System ein bereits vorhandenes Python-Skript zur Extraktion von Tweet-Inhalten. Die Rust-Seite übernimmt die Rolle eines Wrappers: Sie extrahiert die Tweet-ID aus der URL, ruft das Skript mit dieser ID auf und prüft anschließend, ob die erwartete TOML-Datei erzeugt wurde. Dieser Ansatz beschleunigt die Entwicklung, weil vorhandene Werkzeuge wiederverwendet werden können. + +Gleichzeitig zeigt die Implementierung, an welchen Stellen praktische Robustheit wichtig wird. Die durch den Scraper erzeugten TOML-Dateien folgen zwar einem erwarteten Grundschema, enthalten aber verschachtelte Felder und potenzielle Formatvarianten. Deshalb kombiniert der Parser eine reguläre `serde`-Deserialisierung mit einer manuellen Fallback-Auswertung einzelner Schlüssel wie `id`, `full_text` und `screen_name`. Für den Zweck des Prototyps ist dies ein sinnvoller Kompromiss zwischen Eleganz und Fehlertoleranz. Perspektivisch wäre denkbar, die Datenextraktion noch stärker zu standardisieren oder auf weitere Ressourcentypen auszudehnen. In der aktuellen Projektphase ist die bestehende Lösung jedoch ausreichend, um eine durchgängige Klassifikationspipeline realistisch zu demonstrieren. + +#bibliography("refs.bib", style: "ieee", title: [Literaturverzeichnis]) diff --git a/docs/refs.bib b/docs/refs.bib new file mode 100644 index 0000000..7d0e90c --- /dev/null +++ b/docs/refs.bib @@ -0,0 +1,86 @@ +@book{manning2008, + author = {Christopher D. Manning and Prabhakar Raghavan and Hinrich Schütze}, + title = {Introduction to Information Retrieval}, + publisher = {Cambridge University Press}, + year = {2008}, + doi = {10.1017/CBO9780511809071}, + isbn = {9780511809071} +} + +@article{salton1988, + author = {Gerard Salton and Christopher Buckley}, + title = {Term-weighting approaches in automatic text retrieval}, + journal = {Information Processing \& Management}, + volume = {24}, + number = {5}, + pages = {513--523}, + year = {1988}, + doi = {10.1016/0306-4573(88)90021-0} +} + +@inproceedings{mccallum1998, + author = {Andrew McCallum and Kamal Nigam}, + title = {A Comparison of Event Models for Naive Bayes Text Classification}, + booktitle = {AAAI-98 Workshop on Learning for Text Categorization}, + pages = {41--48}, + year = {1998} +} + +@book{joachims2002, + author = {Thorsten Joachims}, + title = {Learning to Classify Text Using Support Vector Machines}, + publisher = {Springer}, + address = {Boston, MA}, + year = {2002}, + doi = {10.1007/978-1-4615-0907-3} +} + +@inproceedings{vaswani2017, + author = {Ashish Vaswani and others}, + title = {Attention Is All You Need}, + booktitle = {Advances in Neural Information Processing Systems 30 (NeurIPS 2017)}, + year = {2017}, + url = {https://arxiv.org/abs/1706.03762} +} + +@article{brown2020, + author = {Tom B. Brown and others}, + title = {Language Models are Few-Shot Learners}, + journal = {arXiv preprint arXiv:2005.14165}, + year = {2020}, + url = {https://arxiv.org/abs/2005.14165} +} + +@article{bommasani2021, + author = {Rishi Bommasani and others}, + title = {On the Opportunities and Risks of Foundation Models}, + journal = {arXiv preprint arXiv:2108.07258}, + year = {2021}, + url = {https://arxiv.org/abs/2108.07258} +} + +@article{liu2023, + author = {Rundong Liu and others}, + title = {Recent Advances in Hierarchical Multi-label Text Classification: A Survey}, + journal = {arXiv preprint arXiv:2307.16265}, + year = {2023}, + url = {https://arxiv.org/abs/2307.16265} +} + +@article{guy2006, + author = {Marieke Guy and Emma Tonkin}, + title = {Folksonomies: Tidying up Tags?}, + journal = {D-Lib Magazine}, + volume = {12}, + number = {1}, + year = {2006}, + url = {https://mirror.dlib.org/dlib/january06/guy/01guy.html} +} + +@misc{sqlite2026, + author = {{SQLite Documentation}}, + title = {CREATE TABLE}, + year = {2026}, + url = {https://www.sqlite.org/lang_createtable.html}, + note = {Abgerufen am 25.03.2026} +} diff --git a/docs/template.typ b/docs/template.typ new file mode 100644 index 0000000..1f542e1 --- /dev/null +++ b/docs/template.typ @@ -0,0 +1,57 @@ +#let conf( + title: "Titel der Facharbeit", + authors: ("Vorname Nachname",), + teacher: none, + due_date: none, + abstract: none, + doc, +) = [ + #set text( + font: "New Computer Modern", + lang: "de", + size: 10pt + ) + #set par(justify: true, leading: 0.65em) + #set heading(numbering: "1.") + + // Title + author block (arXiv-like) + #set align(center) + #v(1.2cm) + #block(width: 70%)[ + #set align(center) + #text(17pt, weight: "bold", [#title]) + #v(0.2cm) + #text(12pt, [#authors.join(", ")]) + #if teacher != none or due_date != none { + v(0.3cm) + set text(size: 9pt) + let meta = ( + if teacher != none { [Betreuung: #teacher] } else { none }, + if due_date != none { [Abgabetermin: #due_date] } else { none }, + ).filter(x => x != none) + text(meta.join(" · ")) + set text(size: 10pt) + } + #v(0.3cm) + + #if abstract != none and abstract != "" { + set align(center) + text(11pt, weight: "bold", [Abstract]) + v(0cm) + set align(center) + set text(size: 9.5pt) + abstract + set text(size: 10pt) + } + ] + + // Inhaltsverzeichnis on a new page, then continue without page break + #pagebreak() + #set align(left) + #heading(level: 1, numbering: none, outlined: false)[Inhaltsverzeichnis] + #outline() + + // Document body + #pagebreak() + #doc +] diff --git a/src/classifiers.rs b/src/classifiers.rs index 3510872..14f93f2 100644 --- a/src/classifiers.rs +++ b/src/classifiers.rs @@ -1,8 +1,8 @@ -use std::process::Command; -use serde::{Deserialize, Serialize}; use anyhow::{Context, Result}; +use serde::{Deserialize, Serialize}; +use std::process::Command; -pub fn classify(input: &str, current_tag_tree: String) -> Result { +pub fn classify(tag_tree: &str, content: String) -> Result { let prompt = format!("You are a resource classifier. Given a hierarchical tag tree and a resource, classify it into 1-3 most specific applicable tags. # RULES: @@ -12,10 +12,10 @@ pub fn classify(input: &str, current_tag_tree: String) -> Result { - Output JSON only # CURRENT TAG TREE: -{current_tag_tree} +{tag_tree} # RESOURCE INFORMATION: -{input} +{content} # OUTPUT FORMAT: {{ @@ -35,19 +35,56 @@ pub fn classify(input: &str, current_tag_tree: String) -> Result { .arg("e") .arg(prompt) .output() - .with_context(|| "Failed to execute tweet scraping command")?; + .with_context(|| "Failed to execute classification command")?; println!("Output: {:?}", out); Ok(String::from_utf8_lossy(&out.stdout).to_string()) } +pub fn classify_with_retry( + tag_tree: &str, + content: String, + max_attempts: u32, +) -> Result { + for attempt in 1..=max_attempts { + match classify(tag_tree, content.clone()) { + Ok(json) => match ClassificationResult::from_json(&json) { + Ok(result) => return Ok(result), + Err(e) => { + eprintln!( + "Attempt {}/{}: Failed to parse: {}", + attempt, max_attempts, e + ); + eprintln!("Raw response: {}", json); + if attempt == max_attempts { + return Err(e.into()); + } + } + }, + Err(e) => { + eprintln!( + "Attempt {}/{}: LLM call failed: {}", + attempt, max_attempts, e + ); + if attempt == max_attempts { + return Err(e); + } + } + } + } + unreachable!() +} + // Yeah #[derive(Debug, Serialize, Deserialize)] pub struct ClassificationResult { + #[serde(default)] pub tags: Vec, + #[serde(default)] pub confidence: Vec, #[serde(default)] pub new_tags: Vec, + #[serde(default)] pub reasoning: String, } @@ -66,7 +103,8 @@ impl ClassificationResult { /// Get the most confident tag (if any exist) pub fn primary_tag(&self) -> Option<(&str, f32)> { - self.tags.iter() + self.tags + .iter() .zip(self.confidence.iter()) .max_by(|a, b| a.1.partial_cmp(b.1).unwrap()) .map(|(tag, conf)| (tag.as_str(), *conf)) @@ -79,7 +117,8 @@ impl ClassificationResult { /// Get tags above confidence threshold pub fn confident_tags(&self, threshold: f32) -> Vec<&str> { - self.tags.iter() + self.tags + .iter() .zip(self.confidence.iter()) .filter(|&(_, &conf)| conf >= threshold) .map(|(tag, _)| tag.as_str()) diff --git a/src/db.rs b/src/db.rs new file mode 100644 index 0000000..445f5fd --- /dev/null +++ b/src/db.rs @@ -0,0 +1,340 @@ +use std::collections::HashMap; +use std::collections::hash_map::DefaultHasher; +use std::hash::{Hash, Hasher}; + +use anyhow::{Context, Result}; +use rusqlite::{Connection, params}; +use serde::Serialize; + +use crate::classifiers::ClassificationResult; + +#[derive(Debug, Serialize)] +pub struct Resource { + pub id: String, + #[serde(rename = "type")] + pub resource_type: String, + pub url: String, + pub title: Option, + pub content: Option, + pub saved_at: Option, + pub metadata: Option, +} + +#[derive(Debug, Serialize)] +pub struct TagAssignment { + pub tag_path: String, + pub confidence: f32, +} + +#[derive(Debug, Serialize)] +pub struct ExportedResource { + pub id: String, + #[serde(rename = "type")] + pub resource_type: String, + pub url: String, + pub title: Option, + pub content: Option, + pub saved_at: Option, + pub metadata: Option, + pub tags: Vec, +} + +pub struct Database { + conn: Connection, +} + +impl Database { + pub fn new(path: &str) -> Result { + let conn = Connection::open(path) + .with_context(|| format!("Failed to open database at {}", path))?; + conn.execute("PRAGMA foreign_keys = ON", []) + .context("Failed to enable foreign keys")?; + Ok(Self { conn }) + } + + pub fn init_schema(&self) -> Result<()> { + let schema = r#" + CREATE TABLE IF NOT EXISTS resources ( + id TEXT PRIMARY KEY, + type TEXT NOT NULL, + url TEXT NOT NULL UNIQUE, + title TEXT, + content TEXT, + saved_at DATETIME DEFAULT CURRENT_TIMESTAMP, + metadata TEXT + ); + + CREATE TABLE IF NOT EXISTS tags ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + full_path TEXT NOT NULL UNIQUE, + parent_path TEXT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP + ); + + CREATE TABLE IF NOT EXISTS resource_tags ( + resource_id TEXT NOT NULL, + tag_path TEXT NOT NULL, + confidence REAL NOT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (resource_id, tag_path), + FOREIGN KEY (resource_id) REFERENCES resources(id) + ); + + CREATE TABLE IF NOT EXISTS classification_log ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + resource_id TEXT NOT NULL, + timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, + reasoning TEXT, + new_tag_suggestions TEXT, + FOREIGN KEY (resource_id) REFERENCES resources(id) + ); + "#; + + self.conn + .execute_batch(schema) + .context("Failed to initialize database schema") + } + + pub fn insert_resource(&self, url: &str, resource_type: &str, content: &str) -> Result { + let resource_id = stable_id_for_url(url); + self.conn + .execute( + r#" + INSERT INTO resources (id, type, url, content) + VALUES (?1, ?2, ?3, ?4) + ON CONFLICT(url) DO UPDATE + SET type = excluded.type, content = excluded.content + "#, + params![resource_id, resource_type, url, content], + ) + .context("Failed to insert resource")?; + Ok(resource_id) + } + + pub fn resource_exists(&self, url: &str) -> Result { + let exists: i64 = self + .conn + .query_row( + "SELECT EXISTS(SELECT 1 FROM resources WHERE url = ?1)", + params![url], + |row| row.get(0), + ) + .context("Failed to query resource existence")?; + Ok(exists == 1) + } + + pub fn ensure_tag_exists(&self, tag_path: &str) -> Result<()> { + let parts: Vec<&str> = tag_path + .split('/') + .filter(|part| !part.is_empty()) + .collect(); + let mut current_parts: Vec<&str> = Vec::new(); + + for part in parts { + current_parts.push(part); + let full_path = current_parts.join("/"); + let parent_path = if current_parts.len() > 1 { + Some(current_parts[..current_parts.len() - 1].join("/")) + } else { + None + }; + + self.conn + .execute( + "INSERT OR IGNORE INTO tags (full_path, parent_path) VALUES (?1, ?2)", + params![full_path, parent_path], + ) + .context("Failed to insert tag")?; + } + + Ok(()) + } + + pub fn get_all_tags(&self) -> Result> { + let mut stmt = self + .conn + .prepare("SELECT full_path FROM tags ORDER BY full_path") + .context("Failed to prepare tag query")?; + let tags = stmt + .query_map([], |row| row.get(0)) + .context("Failed to fetch tags")? + .collect::, _>>() + .context("Failed to collect tags")?; + Ok(tags) + } + + pub fn store_classification( + &self, + resource_id: &str, + result: &ClassificationResult, + ) -> Result<()> { + if result.tags.len() != result.confidence.len() { + eprintln!( + "Warning: tag/confidence count mismatch ({} tags, {} confidences)", + result.tags.len(), + result.confidence.len() + ); + } + + for (tag, confidence) in result.tags.iter().zip(result.confidence.iter()) { + self.ensure_tag_exists(tag)?; + self.conn + .execute( + r#" + INSERT INTO resource_tags (resource_id, tag_path, confidence) + VALUES (?1, ?2, ?3) + ON CONFLICT(resource_id, tag_path) DO UPDATE + SET confidence = excluded.confidence + "#, + params![resource_id, tag, confidence], + ) + .context("Failed to insert resource tag")?; + } + + let new_tag_suggestions = serde_json::to_string(&result.new_tags) + .context("Failed to serialize new tag suggestions")?; + self.conn + .execute( + r#" + INSERT INTO classification_log (resource_id, reasoning, new_tag_suggestions) + VALUES (?1, ?2, ?3) + "#, + params![resource_id, result.reasoning, new_tag_suggestions], + ) + .context("Failed to insert classification log")?; + + Ok(()) + } + + pub fn get_resources_by_tag(&self, tag_path: &str) -> Result> { + let mut stmt = self + .conn + .prepare( + r#" + SELECT r.id, r.type, r.url, r.title, r.content, r.saved_at, r.metadata + FROM resources r + INNER JOIN resource_tags rt ON r.id = rt.resource_id + WHERE rt.tag_path = ?1 + "#, + ) + .context("Failed to prepare resource-by-tag query")?; + let resources = stmt + .query_map(params![tag_path], row_to_resource) + .context("Failed to fetch resources by tag")? + .collect::, _>>() + .context("Failed to collect resources by tag")?; + Ok(resources) + } + + pub fn get_unclassified_resources(&self) -> Result> { + let mut stmt = self + .conn + .prepare( + r#" + SELECT r.id, r.type, r.url, r.title, r.content, r.saved_at, r.metadata + FROM resources r + LEFT JOIN resource_tags rt ON r.id = rt.resource_id + WHERE rt.resource_id IS NULL + "#, + ) + .context("Failed to prepare unclassified resource query")?; + let resources = stmt + .query_map([], row_to_resource) + .context("Failed to fetch unclassified resources")? + .collect::, _>>() + .context("Failed to collect unclassified resources")?; + Ok(resources) + } + + pub fn get_resources_with_tags(&self) -> Result> { + let mut stmt = self + .conn + .prepare( + r#" + SELECT r.id, r.type, r.url, r.title, r.content, r.saved_at, r.metadata, + rt.tag_path, rt.confidence + FROM resources r + LEFT JOIN resource_tags rt ON r.id = rt.resource_id + ORDER BY r.saved_at + "#, + ) + .context("Failed to prepare export query")?; + + let mut rows = stmt.query([]).context("Failed to query resources")?; + let mut resources: HashMap = HashMap::new(); + + while let Some(row) = rows.next().context("Failed to read resource row")? { + let resource_id: String = row.get(0)?; + let resource_type: String = row.get(1)?; + let url: String = row.get(2)?; + let title: Option = row.get(3)?; + let content: Option = row.get(4)?; + let saved_at: Option = row.get(5)?; + let metadata: Option = row.get(6)?; + let tag_path: Option = row.get(7)?; + let confidence: Option = row.get(8)?; + + let entry = resources + .entry(resource_id.clone()) + .or_insert_with(|| ExportedResource { + id: resource_id.clone(), + resource_type, + url, + title, + content, + saved_at, + metadata, + tags: Vec::new(), + }); + + if let (Some(tag_path), Some(confidence)) = (tag_path, confidence) { + entry.tags.push(TagAssignment { + tag_path, + confidence: confidence as f32, + }); + } + } + + Ok(resources.into_values().collect()) + } + + pub fn count_resources(&self) -> Result { + self.conn + .query_row("SELECT COUNT(*) FROM resources", [], |row| row.get(0)) + .context("Failed to count resources") + } + + pub fn count_tags(&self) -> Result { + self.conn + .query_row("SELECT COUNT(*) FROM tags", [], |row| row.get(0)) + .context("Failed to count tags") + } + + pub fn count_classified_resources(&self) -> Result { + self.conn + .query_row( + "SELECT COUNT(DISTINCT resource_id) FROM resource_tags", + [], + |row| row.get(0), + ) + .context("Failed to count classified resources") + } +} + +fn row_to_resource(row: &rusqlite::Row<'_>) -> rusqlite::Result { + Ok(Resource { + id: row.get(0)?, + resource_type: row.get(1)?, + url: row.get(2)?, + title: row.get::<_, Option>(3)?, + content: row.get::<_, Option>(4)?, + saved_at: row.get::<_, Option>(5)?, + metadata: row.get::<_, Option>(6)?, + }) +} + +fn stable_id_for_url(url: &str) -> String { + let mut hasher = DefaultHasher::new(); + url.hash(&mut hasher); + format!("{:x}", hasher.finish()) +} diff --git a/src/main.rs b/src/main.rs index 1f20a98..0ccc8a8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,14 @@ use std::fs; use anyhow::{Context, Result}; +use clap::{Parser, Subcommand}; + mod classifiers; +mod db; mod scrapers; +use db::Database; + enum Source { Twitter, Other, @@ -17,75 +22,162 @@ fn determine_resource_source(line: &str) -> Source { } } +#[derive(Parser)] +#[command(name = "classifier")] +#[command(about = "Resource classifier with hierarchical tags")] +struct Cli { + #[command(subcommand)] + command: Commands, +} + +#[derive(Subcommand)] +enum Commands { + /// Classify resources from a file + Classify { + /// Path to file with URLs + #[arg(short, long, default_value = "test-classification-list")] + input: String, + + /// Force re-classification of existing resources + #[arg(short, long)] + force: bool, + }, + + /// Export resources to JSON + Export { + /// Output file + #[arg(short, long)] + output: String, + }, + + /// Show statistics + Stats, +} + fn main() -> Result<()> { - // Read the file - let contents = fs::read_to_string("test-classification-list") - .expect("Something went wrong reading the file"); - let current_tag_tree = - fs::read_to_string("tag-tree").expect("Something went wrong reading the tag tree file"); + let cli = Cli::parse(); + + println!("Opening database..."); + let db = Database::new("resources.db").context("Failed to open database")?; + db.init_schema() + .context("Failed to initialize database schema")?; + + match cli.command { + Commands::Classify { input, force } => classify_resources(&db, &input, force), + Commands::Export { output } => export_resources(&db, &output), + Commands::Stats => show_stats(&db), + } +} + +fn classify_resources(db: &Database, input: &str, force: bool) -> Result<()> { + let contents = fs::read_to_string(input) + .with_context(|| format!("Failed to read input file: {}", input))?; + let tag_tree = fs::read_to_string("tag-tree").context("Failed to read tag tree file")?; - // Determine source for line in contents.lines() { - let source = determine_resource_source(line); + let url = line.trim(); + if url.is_empty() { + continue; + } + let exists = db.resource_exists(url)?; + if exists && !force { + println!("Skipping already-classified resource: {}", url); + continue; + } + + if exists && force { + println!("Re-classifying existing resource: {}", url); + } + + let source = determine_resource_source(url); match source { Source::Twitter => { - println!("Classifying Twitter resource: {}", line); + println!("Classifying Twitter resource: {}", url); - // Scrape the Tweet - let tweet_file = scrapers::twitter::scrape(line); - let tweet_scrape_contents = match fs::read_to_string(tweet_file.unwrap()) - .with_context(|| "Something went wrong reading the scraped tweet file") - { + let tweet_file = match scrapers::twitter::scrape(url) { + Ok(path) => path, Err(e) => { - eprintln!("Error reading scraped tweet file: {:?}", e); + eprintln!("Error scraping tweet {}: {}", url, e); continue; } - Ok(contents) => contents, }; - let classifier_output = - classifiers::classify(¤t_tag_tree, tweet_scrape_contents); - - match classifier_output { - Ok(json_string) => { - match classifiers::ClassificationResult::from_json(&json_string) { - Ok(result) => { - println!("Tags: {:?}", result.tags); - println!("Confidence: {:?}", result.confidence); - println!("Reasoning: {}", result.reasoning); - - // Check if we need to review new tags - if !result.new_tags.is_empty() { - println!("\n🆕 New tag suggestions:"); - for suggestion in &result.new_tags { - println!( - " - {} (under {})", - suggestion.name, suggestion.parent - ); - println!(" Reason: {}", suggestion.reason); - } - } - - // Only use high-confidence tags - let confident = result.confident_tags(0.5); - if confident.is_empty() { - println!("⚠️ Low confidence classification - review needed"); - } else { - println!("✅ Confident tags: {:?}", confident); - } - } - Err(e) => eprintln!("Failed to parse classification: {}", e), - } + let tweet = match scrapers::twitter::parse_scraped_tweet(&tweet_file) { + Ok(tweet) => tweet, + Err(e) => { + eprintln!("Error parsing tweet {}: {}", url, e); + continue; } - Err(e) => eprintln!("Classification failed: {}", e), + }; + + let content = format!("Title: Tweet by @{}\nContent: {}", tweet.author, tweet.text); + let resource_id = db.insert_resource(url, "twitter", &content)?; + + let result = match classifiers::classify_with_retry(&tag_tree, content, 3) { + Ok(result) => result, + Err(e) => { + eprintln!("Classification failed for {}: {}", url, e); + continue; + } + }; + + println!("Tags: {:?}", result.tags); + println!("Confidence: {:?}", result.confidence); + println!("Reasoning: {}", result.reasoning); + + if !result.new_tags.is_empty() { + println!("\nNew tag suggestions:"); + for suggestion in &result.new_tags { + println!(" - {} (under {})", suggestion.name, suggestion.parent); + println!(" Reason: {}", suggestion.reason); + } + } + + let confident = result.confident_tags(0.5); + if confident.is_empty() { + println!("Low confidence classification - review needed"); + } else { + println!("Confident tags: {:?}", confident); + } + + if let Err(e) = db.store_classification(&resource_id, &result) { + eprintln!("Failed to store classification for {}: {}", url, e); } } Source::Other => { - eprintln!("Classification of this source/website is not covered yet!"); + eprintln!( + "Classification of this source/website is not covered yet: {}", + url + ); } } } Ok(()) } + +fn export_resources(db: &Database, output: &str) -> Result<()> { + let resources = db + .get_resources_with_tags() + .context("Failed to fetch resources for export")?; + let json = serde_json::to_string_pretty(&resources) + .context("Failed to serialize resources to JSON")?; + fs::write(output, json).with_context(|| format!("Failed to write export file: {}", output))?; + println!("Exported {} resources to {}", resources.len(), output); + Ok(()) +} + +fn show_stats(db: &Database) -> Result<()> { + let total_resources = db.count_resources()?; + let classified_resources = db.count_classified_resources()?; + let tag_count = db.count_tags()?; + let unclassified = total_resources.saturating_sub(classified_resources); + + println!("Resources: {}", total_resources); + println!("Classified resources: {}", classified_resources); + println!("Unclassified resources: {}", unclassified); + println!("Tags: {}", tag_count); + + Ok(()) +} diff --git a/src/scrapers/twitter.rs b/src/scrapers/twitter.rs index d0f4e68..a2d23c8 100644 --- a/src/scrapers/twitter.rs +++ b/src/scrapers/twitter.rs @@ -1,5 +1,83 @@ use anyhow::{Context, Result, bail}; -use std::{path::PathBuf, process::Command}; +use serde::Deserialize; +use std::{fs, path::PathBuf, process::Command}; + +#[derive(Debug, Deserialize)] +pub struct ScrapedTweet { + pub id: String, + pub text: String, + pub author: String, +} + +#[derive(Debug, Deserialize)] +struct RawScrapedTweet { + pub id: String, + #[serde(rename = "full_text")] + pub text: String, + pub author: Option, +} + +#[derive(Debug, Deserialize)] +struct TweetAuthor { + #[serde(rename = "screen_name")] + pub handle: String, +} + +pub fn parse_scraped_tweet(path: &PathBuf) -> Result { + let contents = fs::read_to_string(path) + .with_context(|| format!("Failed to read scraped tweet file: {}", path.display()))?; + + if let Ok(raw) = toml::from_str::(&contents) { + let author = raw + .author + .map(|author| author.handle) + .unwrap_or_else(|| "unknown".to_string()); + return Ok(ScrapedTweet { + id: raw.id, + text: raw.text, + author, + }); + } + + parse_scraped_tweet_fallback(&contents, path) +} + +fn parse_scraped_tweet_fallback(contents: &str, path: &PathBuf) -> Result { + let mut id = None; + let mut text = None; + let mut author = None; + + for line in contents.lines() { + let trimmed = line.trim(); + if trimmed.starts_with("id = ") && id.is_none() { + id = parse_quoted_value(trimmed); + } else if trimmed.starts_with("full_text = ") && text.is_none() { + text = parse_quoted_value(trimmed).map(unescape_toml_string); + } else if trimmed.starts_with("screen_name = ") && author.is_none() { + author = parse_quoted_value(trimmed); + } + } + + let id = id.with_context(|| format!("Missing id in scraped tweet: {}", path.display()))?; + let text = + text.with_context(|| format!("Missing full_text in scraped tweet: {}", path.display()))?; + let author = author.unwrap_or_else(|| "unknown".to_string()); + + Ok(ScrapedTweet { id, text, author }) +} + +fn parse_quoted_value(line: &str) -> Option { + let start = line.find('"')?; + let end = line.rfind('"')?; + if end <= start { + return None; + } + Some(line[start + 1..end].to_string()) +} + +fn unescape_toml_string(value: String) -> String { + value.replace("\\n", "\n").replace("\\\"", "\"") +} pub fn scrape(url: &str) -> Result { let tweet_id = url.split('/').next_back().unwrap(); diff --git a/tag-tree b/tag-tree index c95d97e..a121057 100644 --- a/tag-tree +++ b/tag-tree @@ -149,3 +149,5 @@ - window_managers - web_technologies - wasm +- Ai + - LLMs diff --git a/test-classification-list b/test-classification-list index 7146a9a..20adab6 100644 --- a/test-classification-list +++ b/test-classification-list @@ -1 +1,8 @@ https://x.com/fleetwood___/status/1987527758558228809 +https://x.com/thegeneralist01/status/2017960363099107400 +https://x.com/thegeneralist01/status/2007161972442145086 +https://x.com/TigerBeetleDB/status/2019013589705916447 +https://x.com/TigerBeetleDB/status/2019013589705916447 +https://x.com/mitchellh/status/2020252149117313349 +https://x.com/amitpr/status/2020263065745519001 +https://x.com/claudeai/status/2020207322124132504