Finto on ontologinen sanasto. Tai oikeammin se on palvelu, joka tarjoaa myös sanastoja. Jos kiinnostaa ontologian filosofinen aspekti, niin käytä hakua. Siinä ollaan metafyysissä asioissa, joista (minusta) kannattaa keskustella hämyisessä huoneessa suitsukkeiden tuoksussa teekuppi kädessä. Sanastojen kohdalla kyse on siitä miten jokin asia luokitellaan. Kirjaston luokittelu on ontologinen järjestelmä ja ontologinen sanasto tekee sanoille ja käsitteille hieman samaa.
Melkoisen usein ontologia jaetaan kahteen osaan: filosofiseen pohdiskeluun kaiken olevan perimmäisestä olemuksesta ja sen paikasta hierarkiassa, tai tietotekniikan ja varsinkin tekoälyjen tarpeeseen saada termille hierarkia ja luokitus.
Kun minä tarvitsen ontologisen sanaston määritelmän vaikkapa (koti)koirasta, niin minua ei kiinnosta sen filosofinen suhde kaikkeen olevaan — joka sekin on toki mielenkiintoinen aihe. Minua kiinnostaa silloin ontologian teknisempi puoli: rakenne + merkitys + suhteet.
Tapaus kotikoira
Biologiassa tieteellinen taksonomia on enemmänkin erikoistapaus, tai typistetty, ontologiaan liittyvästä luetteloinnista. Taksonomia esittää vain kategoriat, alaspäin tarkentuvan rakenteen.
Taso | Luokitus |
---|---|
Domaini | Eukaryota |
Kunta | Animalia |
Pääjakso | Chordata |
Luokka | Mammalia |
Lahko | Carnivora |
Alalahko | Caniformia |
Heimo | Canidae |
Suku | Canis |
Laji | Canis lupus |
Alalaji | Canis lupus familiaris |
Jos tuohon ympätään mukaan enemmän ontologinen ote, niin taulukko voisi olla taksonomian lisäksi myös tätä:
Käsite / Taso | Arvo / Esimerkki | Suhde / Ominaisuus |
---|---|---|
Domaini | Eukaryota | aitotumallinen eliö |
Kunta | Animalia | kuuluu eläinkuntaan |
Pääjakso | Chordata | selkäjänteiset |
Luokka | Mammalia | nisäkäs, jolla karva ja maidontuotanto |
Lahko | Carnivora | petoeläin |
Alalahko | Caniformia | ”koiramainen” petoeläin |
Heimo | Canidae | koiraeläimet |
Suku | Canis | sisältää sudet, kojootit, sakaalit ja koirat |
Laji | Canis lupus | harmaasusi |
Alalaji | Canis lupus familiaris | kesykoira, suden alalaji |
Sukulaisuussuhteet | Canis latrans (kojootit), Canis aureus (sakaali) | läheisiä lajeja |
Suhde ihmiseen | Domestikoitu n. 50 000 vuotta sitten | elää ihmisen lemmikkinä ja hyötyeläimenä |
Ominaisuuksia | Nelijalkainen, laumaeläin, sosiaalinen, vaihtuva turkki | käyttäytymis- ja fyysiset piirteet |
Ekologinen rooli | Peto, raadonsyöjä, ihmisen apu | vaikuttaa ekosysteemeihin ja kulttuuriin |
Finto.fi
Finto on Kansalliskirjaston projekti, joka tarjoaa ontologia- ja käsitesanastoja. Vahvimmin ymmärtääkseni tietoteknisiin luokittelutarpeisiin, mutta voi niitä ihminenkin käyttää.
Minulla sen käyttö on enemmältikin uteliaisuutta jonkun käsitteen suhteen, ja mihin yhteyksiin se kuuluu. Useimmiten tarve on kylläkin englanninkielelle, mutta ei suomenkaan käyttö minulle ole poikkeuksellista.
Koska sanastoilla on niin vahva tietotekninen kysyntä, niin se useimmiten tarjoavat myös API:n. Ja jos tai kun syöte on sellaista JSON-muotoa, jota SearXNG pystyy käyttämään, niin sain työkalun hieman korostaa ontologisia hakuosumia.
Asennus
Minä käytän Kansalliskirjaston Fintoa. Se tarjoaa REST API:n, joten oletin sen olevan kohtuullisen helposti kytkettävissä SearXNG json engineen. Oletus oli väärä. Heidän käyttämänsä JSON ei ole sellainen, jota SearXNG söisi suoraan. Jos sinua kiinnostaa aihe syvemmältä, niin osannet vilkaista syötteen, mutta ongelma tuli sellaisessa kuin @context
. Se listaa asiat, mutta json engine pysähtyy siihen, eikä anna tuloksia.
curl -s "https://api.finto.fi/rest/v1/search?vocab=ysa&query=koira&lang=fi" | jq
❯ Näytä koodi
{
"@context": {
"skos": "http://www.w3.org/2004/02/skos/core#",
"isothes": "http://purl.org/iso25964/skos-thes#",
"onki": "http://schema.onki.fi/onki#",
"uri": "@id",
"type": "@type",
"results": {
"@id": "onki:results",
"@container": "@list"
},
"prefLabel": "skos:prefLabel",
"altLabel": "skos:altLabel",
"hiddenLabel": "skos:hiddenLabel",
"@language": "fi"
},
"uri": "",
"results": [
{
"uri": "http://www.yso.fi/onto/ysa/Y96272",
"type": [
"skos:Concept"
],
"localname": "Y96272",
"prefLabel": "koira",
"lang": "fi",
"vocab": "ysa"
}
]
}
Olin omituisessa tilanteessa. Sain komentoriviltä osumat, myös testatessani haku-urlia, mutta engine antoi logeissa 200 OK vastauksen ja hakutuloksissa ei löydy. Minulla kesti taasen useampi tunti, kun ensin yritin ymmärtää mitä tapahtuu ja sen jälkeen miten korjata tilanne.
Minun piti saada siivottua @context
pois. Tai rakentaa kokonaan uusi engine, joka ylittää taatusti vaatimattomat kykyni. Pitkä tarina lyhyesti on, että tuo poisto on tehtävä web-serverillä, ei SearXNG:ssä, koska en osaa ja siihen minulla olisi kaksi tapaa:
- käyttää Nginxin javascript moduulia (njs) — tuo kaatui siihen, että en saanut sitä asennettua versio- ja riippuvuussuhteiden takia, joka ilmeisesti oli seuraus itse käännetystä Nginxistä ja että yritin asennusta väärästä reposta. En riidellyt tuon kanssa enempää, koska en kuitenkaan olisi osannut tehdä vaadittavaa JS-scriptiä.
- käyttää Plesk-serveriä välissä ja siivota asiat siellä hieman perinteisempään tapaan.
Ketju olisi siis
- Asiakas tulee Nginxiin porttiin 443 ja tekee normaalisti vhostissa
haku.eksis.eu
hakunsa - Haku lähtee searxng-containeriin ja annetaan enginelle
- Engine lähettää pyynnön
-
- hostin sisäiselle Nginx vhostille, joka proxyaa sen Flaskille, tai
- sisäisen verkon kautta
- Flask-containerista pyyntö lähetetään
finto.fi
ja saadaan json, josta python-scripti siivoaa@context
pois kiusaamasta
Pientä pyörimistä, mutta kaikkiaan tarvitaan vain joko yksi proxymääritys lisää sisäistä liikennettä hoitavassa Nginxin vhostissa — minulla on se jo valmiina, koska tarvitsin moisen omille WordPresseilleni — tai containereihin parin rivin networks-määrittely. Plus yksi docker container ja python scripti.
Kaikki muu ratkesi kohtuullisen kivuttomasti Googlella, mutta koska en ole koodari, vaan kopypeistaaja, niin pythonin kanssa meinasi tulla itku.
Asennus alkaa tarvittavan hakemiston luomisella: mkdir /opt/finto-adapter
. Ja kuten aina, niin polku ja nimi on vapaavalintaiset.
Tarvitaan myös kolme tiedostoa:
docker-compose.yml
käynnistämään containerinDockerfile
luomaan gunicornin containerin sisäistä liikennettä varten ja Pleskin, jossa työt tekevät python-scripti pyöriiapp.yml
joka korjaa JSON vastauksen (ja on ehkä samalla se Plesk; vaikka käytän Pleskiä muuallakin, niin se on minulle hyvin mystinen konsepti)
Container
Tarvitaan container:
❯ Näytä koodi
nano /opt/finto-adapter/docker-compose.yml
❯ Näytä koodi
services:
finto-adapter:
container_name: finto-adapter
build: .
restart: unless-stopped
networks: [searxnet]
networks:
searxnet:
external: true
Network täytyy lisätä myös /opt/searxng/docker-compose.yml
:
❯ Näytä koodi
services:
searxng:
...
networks: [searxnet]
...
networks:
searxnet:
external: true
Jos Valkey on käytössä, niin myös se tarvitsee networks: [searxnet]
maininnan.
Verkon nimi on vapaavalintainen. Minä käytän tuota vain siksi, että se liittyy SearXNG:n toimintaan ja on… no, net.
Kun käytetään verkkoa, niin ports:
saattaa muuttua tarpeettomaksi. Siksi sitä ei ole määritelty containerissa finto-adapter. Mutta SearXNG:n olisi perinjuurin vaikeaa kommunikoida Nginxin kanssa ilman portteja — paitsi ehkä jos Nginx on myös docker-versio. Siinä siirrytään minulle täysin tuntemattomalle alueelle.
Verkko täytyy myös luoda. Tämä on ymmärtääkseni ainutkertainen komento, eikä tarvita toiste, ellei varsinaisesti ja tarkoituksella poista networksiä.
❯ Näytä koodi
docker network create searxnet
Nginx
Networksiä ei ole pakko luoda. Pyynnön voi tehdä myös Nginxin vhostin kautta. En uppoa suuremmin sen varsinaiseen rakentamiseen, koska se on tehty jutussa Searxng ja WordPress.
Kannattaako tämä suunta vai networks? Kokeilin kumpaakin. Molemmissa on komponentti, joka voi mennä rikki. Joko Nginxin vhost ja sen yhteydet, tai dockerin network. En minä tiedä kumpi on suurempi riski. Mutta näiden välillä on kolme minulle jossain määrin merkityksellistä eroa.
Nginxin vhost tarjoaa minulle sellaisen login, jota osaan suoraan lukea ja josta näen historian. Helpottaa ongelmien selvittämistä. Mutta kuinka usein tarvitsen moista? En kovinkaan usein, mutta kun tarvitsen, niin koen tarvitsemani. Dockerin kohdalla koen olevani samassa tilanteessa kuin Varnishin kanssa, jossa tilanteessa ”mitä hemmettiä äsken tapahtui” tai ”mikä hajosi viime yönä” minulla ei ole mitään suoraa logia ihmettelyä varten. Kaikki on kyettävä toistamaan ja seuraamaan livenä.
Tai sitten en vaan osaa.
Toinen on mahdottomuus käyttää curlia testaamisessa mihinkään muuhun kuin tutkittaessa ymmärtääkö containerit liikenteen hostin suunnasta. SearXNG:n image ei tunne curlia (eikä nanoa tai muuta editoria, joten cat on ainoa tapa tutkia tekstiä). Mutta wget löytyy, joten containerin sisällä täytyy käyttää sitä.
Kolmas tekijä on nopeus. Molemmat toimivat hostin sisällä, jolla ei ole varsinaista tolkutonta kuormaa ja vapaata rammia löytyy. Ytimet eivät ole todellakaan parasta kastia, mutta niitä pitäisi olla sinällään riittävästi, eikä mikään logitus paljasta kiusaa tekevää pullonkaulaa. Sitä en tiedä miten Nginx hoitaa oman sisäisen työnjakonsa hostien välillä.
Networks on nopeampi. Nyt ei puhuta mistään isoista viiveistä, mutta silminnähtävistä. Sen verran selvästä erosta, että purin Nginx-virityksen ja siirsin yhteydet networksille.
Jos haluaakin tehdä asiat Nginxin kautta, niin tämä on oma vhost searxng-internal.conf
. Joten muuta oleelliset asiat, älä kopioi suoraan. Jätin siitä pois WordPress-osat.
❯ Näytä koodi
server {
listen 172.18.0.1:9000;
server_name _;
# Lokit erilliseen tiedostoon
access_log /var/log/nginx/xng-access.log main;
error_log /var/log/nginx/xng-error.log;
location = /finto/search {
# salli vain searxng:n IP tai localhost
allow 127.0.0.1;
allow 172.18.0.0/16; # docker-verkko jos tarpeen
deny all;
proxy_pass http://127.0.0.1:9001/finto/search;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 15s;
}
# Kaikki muu 403:lla kiinni
location / {
return 403;
}
}
Jep. Koska tuo vhost on sisäinen, niin IP-rajoitukset ovat turhia. Mutta on hyvää käytäntöä laittaa ne, niin siitä tulee tapa. Miksikö sallin niin hostin localhostin kuin dockerin/containerin käyttämät? Koska olen ihan pihalla siitä mikä IP milloinkin liikkuu docker-maailmassa.
Muuta enginen urlissa domain oikeaksi. Se, että mikä on ”oikea” riippuu siitä mitä kaikkea olet tehnyt containerissa, mutta tässä esimerkissä se on 172.18.0.1:9000 eli containerin hostiin näkyvä IP. Bridgen IP ei toimi, vaikka monet dokumentit sitä esimerkkinä käyttävätkin.
Plesk
Tämä todellakin kirjoitetaan isolla alkukirjaimella. Se ei ole typo tai laitteen automaattisesti asettama iso alkukirjan. Kantapään kautta opittu asia.
❯ Näytä koodi
nano /opt/finto-adapter/Dockerfile
❯ Näytä koodi
FROM python:3.12-slim
RUN pip install --no-cache-dir flask requests gunicorn
WORKDIR /app
COPY app.py /app/
CMD ["gunicorn","-b","0.0.0.0:8080","app:app"]
- sisäinen järjestelmä, niin en jaksanut alkaa pohtimaan IP-osoitteita
Ja itse scripti:
❯ Näytä koodi
nano /opt/finto-adapter/app.yml
❯ Näytä koodi
from flask import Flask, request, jsonify
import requests
app = Flask(__name__)
@app.get("/finto/search")
def finto_search():
upstream = "https://api.finto.fi/rest/v1/search"
r = requests.get(upstream, params=request.args, headers={"Accept":"application/json"}, timeout=10)
r.raise_for_status()
data = r.json()
data.pop("@context", None) # tämä on se joka sotkee json_engineä
if "results" not in data or not isinstance(data["results"], list):
data["results"] = []
return jsonify(data)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8080)
Container, ja nuo muutkin, täytyy käynnistää:
❯ Näytä koodi
docker compose up -d --build
Sammuttamiselle ei ole suuremmin tarvetta, mutta minä teen sen aina ja poikkeuksetta ennen uudelleenkäynnistystä: docker compose down
. Käynnistäminen --build
kanssa tarvitaan, jos app.py
tai Dockerfile
on muokattu. Jos on muuttanut docker-compose.yml
niin tavallinen käynnistys riittää. Joten tässäkin menen aina varovaisuus edellä ja jokainen kerta uudelleenkäynnistys tehdään docker compose up -d --build
komennolla.
json engine
Minä en ole aivan varma tämän ”oikeellisuudesta” ja vaikuttaako oikeastaan mikään mihinkään muuhun kuin hiukan UI puolelle. Silloin url on se ainoa määräävä asia kyselylle. Finto kun ei palauta varsinaisesti sisältöä varsinaisena sivuna, jota esiteltäisiin hakutuloksissa. Esitettävä sisältö pitäisi rakentaa enginessä, ja sellaista en osaa tehdä.
Tämä hakee Fintosta käyttäen sanastoa YSO (Yleinen Suomalainen Ontologia):
❯ Näytä koodi
- name: finto.yso
#inactive: true
engine: json_engine
shortcut: yso
categories: [general, science]
enable_http: true
search_url: http://finto-adapter:8080/finto/search?vocab=yso&query={query}&lang=fi
results_query: results
title_query: prefLabel
url_query: uri
content_query: vocab
weight: 2
Koska networks on käytössä, niin ei tarvitse sinällään miettiä IP-osoitteita, mutta hostin, tässä containerin, nimi tarvitaan. Se on se mikä on asetettu container_name:
kohdassa, mutta sen saa selville tälläkin:
❯ Näytä koodi
docker compose ps -a
Lopuksi searxng:n uudelleenkäynnistys ja logi kuuntelemaan:
❯ Näytä koodi
docker compose down && docker compose up -d && docker logs -f searxng
Useampi engine
Minulla on käytössä Finton suuntaan kolme engineä: ilman määriteltyä sanastoa, YSO ja YSO-paikat. Haku kaikista sanastoista on minulle oikeastaan tarpeeton, koska yleensä YSO riittää. Se on kuitenkin jäänyt, vaikka pidentääkin hakutuloksia.
Se, että mistä Finton tarjoamasta sanastosta haetaan, kerrotaan haku-urlin kohdassa vocab=
Tämä hakee kaikista, koska sanastoa ei ole määritelty:
❯ Näytä koodi
search_url: http://finto-adapter:8080/finto/search?vocab=&query={query}&lang=fi
Tämä taasen käyttää sanastoa YSO-paikat:
❯ Näytä koodi
search_url: http://finto-adapter:8080/finto/search?vocab=yso-paikat&query={query}&lang=fi
Tietysti myös shotcut:
täytyy vaihtaa.
Testaaminen
Nopein tapa testata on tietysti tehdä haku !bang avulla, niin muut tulokset eivät sotke.
❯ Näytä koodi
!yso koira
Ja vastaus on varmaan tällainen:
Aivan. Ei se tuon kummallisempaa hakutulosta anna.
Jos saat
- timeoutin, niin joku ei kuuntele jossain tai haku-url on väärin
- jos tulee http-error, niin se on luultavasti 403 (siksi container jätettiin logittamaan) ja silloin hostin määrityksissä on jotain pielessä
Siinä vaiheessa alkaa selvittäminen, ja aivan ensimmäiseksi tarkistetaan molempien containerien tiedostot, ja että ne ylipäätään ovat käynnissä. Minulla yleisin syy on typo jossain kohtaa.
Tällä voi kokeilla searxng-containerin sisältä (vaihda url siihen mitä käytät):
❯ Näytä koodi
docker exec -it searxng sh -lc 'wget -qO- "http://finto-adapter:8080/finto/search?vocab=ysa&query=koira&lang=fi" | head'
Vastauksen pitäisi olla tällainen:
❯ Näytä koodi
{"results":[{"lang":"fi","localname":"Y96272","prefLabel":"koira","type":["skos:Concept"],"uri":"http://www.yso.fi/onto/ysa/Y96272","vocab":"ysa"}],"uri":""}
Esimerkkinä tietysti perin typerä, koska tuo on toimivasta systeemistä ja toimivuuden näkisi jo UI:n kautta. Mutta jos mitään ei tule, niin tarkista ainakin search_url
.
Jos ratkaisu ei ole noin helppo, niin alkaa savotta. Minä en tunne näiden sielunelämää niin vahvasti, että pystyisin selvittämään miten debuggausta kannattaa lähteä rakentamaan.
Ja paljon riippuu siitäkin millaista ratkaisua on ylipäätään käytetty. Hakeeko SearXNG esimerkiksi Nginxin kautta, IP:llä containerien välillä ja networksiä hyödyntämämällä. Mutta ensin täytynee päättää toimivatko eri palvelut ylipäätään tai oletettavasti toimivat, koska silloin on joku tolpanväli ilman lankaa. Tai jos verkot ja yhteydet pitäisi olla kunnossa, niin jokin palvelu ei tee sitä mitä pitäisi.
Hakukoneiden käyttö tulee tuossa tutuksi.
Kannattaako Finton käyttö enginellä?
Se mikä kannattaa ja mikä ei, on tarve- ja makukysymys. Jos minulla on akuutti ja aito tarve ontologiselle sanastolle, niin menen suoraan Fintoon. Tai jonnekin muualla, en minä aina suomea käytä. Mutta jos et milloinkaan tarvitse sanojen luokittelua, niin et tarvitse kuin uteliaisuutta varten.
Omassa käytössä 95 % on uteliaisuutta ja 4 % on jotain, joka haiskahtaa tarpeelta tai käsitykseltä tarpeesta. Se puuttuva 1 % on niitä tapauksia, joissa tökkään osumaa ja kun se aukeaa, niin en tiedä miksi moisen aukaisin. Kaipa se on eräänlaista keittiön ovi -oireilua, jota myös uuden tilan ongelmaksi voidaan kutsua.
Jos et tunnista millään tasolla sanojen luokittelun tarvetta (tai uteliaisuutta), niin älä asenna. Jos hakukoneella on muitakin käyttäjiä, niin ehkä sen voi asentaa. Ainahan enginen saa otettua pois käytöstä hakukoneet-välilehdessä — asia, jonka jokaisen olisi syytä muistaa ja joka ilmeisesti pitäisi erikseen opettaa käyttäjille. Aivan kuten somessa syöte kannattaa laittaa sellaiseksi, että ei ihan joka päivä vituta sen lukeminen, niin hakukoneet kannattaa rajoittaa sellaisiin, joista saa haluamansa ja tarvitsemansa vastaukset (niissä rajoissa kuin mitä hakukoneet ylipäätään pystyvät toimimaan).
Keskustele foorumilla Katiskan foorumi