Mochi: pieni sinä, joka kirjoittaa muistiinpanosi
macOS-sovellus joka muuttaa puheesi muistiinpanoiksi Obsidian-holviisi. Tunnistus ja puhujien erottelu pyörivät Macilla, joten ääni ei lähde koneelta eikä yksikään minuutti maksa mitään.

- 0
- tavua ääntä lähetetty
- 3
- paikallista tunnistustasoa
- 4
- puhujaa eroteltuna per puhelu
Konteksti
Mochi on macOS-sovellus joka asettuu näytön reunalle ja kirjoittaa ylös mitä sanot. Paina Optionia ja välilyöntiä, puhu, ja muistiinpano laskeutuu Obsidian-holviisi ennen kuin olet saanut ajatuksen loppuun.
Se näyttää sinulta. Lataat käyttöönotossa yhden valokuvan, ja Mochi piirtää sinusta pienen karikatyyrin: iso pää, isot silmät, lyhyet raajat. Se hahmo on käyttöliittymä. Se nukkuu kun teet töitä ja herää kun puhut.
Tunnistus ja puhujien erottelu pyörivät Macilla. Ääntä ei lähetetä mihinkään, eikä yksikään litterointiminuutti maksa mitään.
Ongelma
Ääniviesti on helppo nauhoittaa eikä sitä lueta juuri koskaan. Nauhoitus on prosessin loppu eikä sen alku, joten se jää kansioon aikaleima nimenään eikä kukaan avaa sitä enää.
Sanelutyökalut ratkaisevat puolet ongelmasta tuottamalla tekstiä, ja ojentavat tekstin sitten sinulle sen sijaan että veisivät sen sinne missä muistiinpanosi oikeasti asuvat. Kokousten litterointityökalut ratkaisevat toisen puolen lähettämällä koko puhelun jonkun palvelimelle, mikä on kohtuullinen vaihtokauppa myyntitiimille ja huono vaihtokauppa yksityiselle keskustelulle.
Molemmat jättävät saman aukon. En halunnut litterointia. Halusin että ääneen sanottu lause on jo holvissa, siinä kansiossa josta katsoisin, ilman että olin päättänyt käydä laittamassa sen sinne.
Oma roolini
Kaikki. Nuits on yksi ihminen.
Swift-sovellus, tunnistusputki, holviin kirjoittaminen, Worker-backend, markkinointisivusto, brändi, maskotti, hinnoittelu, lakisivut ja tietosuojalinja. Kenellekään ei voi ojentaa epämukavaa valintaa, mikä näkyy ennen kaikkea niissä kohdissa joissa rehellinen vastaus maksaa yhden ominaisuuden.
Lähestymistapa
Päätin kirjoitusten järjestyksen ennen mitään muuta, ja se ratkaisi kaikki myöhemmät kiistat. Sanatarkka litterointi menee holviin ensin, aina, ennen kuin kielimallia edes kutsutaan. Siistitty versio liitetään samaan muistiinpanoon jälkikäteen toisena lohkona.
Se järjestys on tuote. Jos verkko on poikki, jos avain on väärä, jos palveluntarjoajalla on huono iltapäivä, sinulla on silti sanasi. Mallin kaatuminen heikentää muistiinpanoa; se ei voi koskaan hukata sitä. Kaikki muu seuraa siitä että tuo kirjoitus on ensimmäisenä. Tunnistuksen on oltava paikallinen, koska pilvitunnistin tekisi ensimmäisestä kirjoituksesta verkosta riippuvan. Mallin on oltava sinun, koska toinen kirjoitus on vapaaehtoinen ja se kuuluu laskuttaa siltä joka sen valitsi.
Maskotti on sama päätös tunteen tasolla. Valikkorivin kuvake on apuväline, eikä sen katoamista huomaisi. Pieni hahmo joka näyttää sinulta ja reagoi kymmenesosasekunnissa pikanäppäimen painallukseen on jotain jota jäisit kaipaamaan. Sen vuoksi kannatti rakentaa animaatiojärjestelmä.
Toteutus
Natiivi Swift. SwiftUI paneeleihin, AppKit päällyskerrokseen, koska ikkuna joka kelluu kaiken päällä, ohittaa klikkaukset pyydettäessä ja käyttäytyy oikein Spaceissa ja usealla näytöllä ei ole sellainen jonka SwiftUI haluaisi antaa. Hahmo on vektorisprite-kompositointia keyframe- ja jousianimaatiolla, ei pelimoottori.
Tunnistus on Parakeet FluidAudion kautta, vietynä CoreML:ään kolmena tasona: 227,5 Mt englanniksi, 335,7 Mt 25 kielelle ja 483,1 Mt aksenteille ja meluisille huoneille. Nuo luvut on mitattu, ei muistettu. Skripti lukee mallivaraston tiedostopuun, laskee vain ne täsmälliset tiedostot jotka äänikirjasto oikeasti hakee, ja generoi Swift-tiedoston jota käyttöönoton kortit lukevat. Kukaan ei voi kirjoittaa kokoa käsin käyttöliittymään. Aiempi versio määrittelystä kantoi arvioita jotka olivat joka ainoalla rivillä noin kaksinkertaisesti tuotteen eduksi, mikä on täsmälleen se suunta johon arviot lipsuvat, ja skripti on olemassa jotta se ei toistu.
Tärkein löytöni oli hiljaisuusbugi, ja se muutti arkkitehtuurin. Dekoodausikkuna joka päättyy keskelle hiljaisuutta palauttaa tyhjän merkkijonon, ilman virhettä ja korkealla ilmoitetulla luottamuksella. Kaksitoista sekuntia puhetta litteroituu oikein. Samat kaksitoista sekuntia kolmen sekunnin hiljaisuus perässä litteroituu tyhjäksi. Tuotteelle jonka koko lupaus on sanatarkka tallenne, onnistumiselta näyttävä epäonnistuminen on pahin mahdollinen. Siksi hiljaisuus ei koskaan päädy tunnistimelle. Silero VAD pilkkoo nauhoituksen ensin puhealueiksi, jokainen alue litteroidaan erikseen, ja lyhyitä levennetään ympäröivällä äänellä eikä nollatäytöllä, koska perään liimattu hiljaisuus on juuri se laukaisin. Puheentunnistusmalli on aina asennettuna eikä koskaan valinnainen, ja se on oikeellisuuspäätös eikä suorituskykypäätös.
Kokoustila nauhoittaa järjestelmän äänen ja mikrofonin erillisille raidoille, jottei ohjelmiston tarvitse koskaan arvailla omaa ääntäsi. Suoratoistodiarisaatio erottaa jopa neljä puhujaa reaaliajassa. Saman ihmisen tunnistaminen kahden eri kokouksen välillä vaatii kokonaan toisen moottorin, minkä hyväksyin liian myöhään: suoratoistodiarisoija ei säilytä embeddingejä istuntojen välillä, joten tämän päivän puhuja yksi ja ensi viikon puhuja yksi ovat toisilleen tuntemattomia ääniä, eikä mikään viritys muuta sitä. Nyt puhelun jälkeen ajetaan toinen vaihe. Se ottaa kahdeksasta viiteentoista sekuntia kunkin äänen pisimmistä puheenvuoroista, poimii 256-ulotteisen embeddingin ja vertaa sitä Macille tallennettuihin profiileihin kosinisamankaltaisuudella. Kynnys on tarkoituksella varovaisella puolella, koska osumatta jäänyt vastaavuus maksaa yhden uudelleennimeämisen kun taas väärä osuma laittaa kollegan nimen tuntemattoman sanojen päälle. Kohdennus ratkaistaan kaikille äänille yhdellä kertaa, joten kahden hengen puhelussa ensimmäinen puhuja ei voi napata profiilia jonka toinen osui paremmin.
Holviin kirjoittaminen on ainoa osa jolla on oikea testisarja: 29 tapausta markdownin rakentamisesta ja levykäyttäytymisestä. Se kirjoittaa atomisesti, liittää perään sen sijaan että korvaisi, eikä koskaan kirjoita uusiksi muistiinpanoa jota olet muokannut, koska iCloudin synkkaama holvi rankaisee kaikista kolmesta virheestä konfliktitiedostoilla.
Kielimallit ovat sinun. Yksi rekisteri, yksi OpenAI-yhteensopiva pyyntömuoto, palveluntarjoajakohtaiset poikkeukset siellä missä muoto eroaa, ja mukana OpenRouter, Anthropic, Gemini, DeepSeek ja muita. Liität avaimen, se menee avainnippuun, eikä se koskaan koske backendiini. Päättely ei siis ole koskaan kulu liiketoiminnalle, ja juuri se sallii yhden 12 dollarin paketin sisältää rajattoman litteroinnin ilman tähteä.
Hahmon generointi on ainoa asia joka maksaa minulle rahaa käyttäjää kohti, ja siitä tuli projektin kiinnostavin tekninen osuus. Se etenee kahdessa vaiheessa yhdellä toimittajalla OpenRouterin kautta. Ensimmäinen vaihe muuttaa valokuvasi neljäksi hahmoehdokkaaksi noin kolmentoista sentin kappalehintaan, ja sinä valitset yhden. Toinen vaihe piirtää valitun hahmon yhdeksän tila-asua aitona SVG:nä noin kahdeksan sentin kappalehintaan. Neljän vaihtoehdon valinta on tietoinen kuluerä: kolme piirrosta heitetään pois, ja vaihtoehto olisi pakottaa joku hyväksymään ensimmäinen asia jonka malli piirsi hänen omista kasvoistaan.
Vektorimalli ei tarjoa läpinäkyvyysparametria, mikä olisi tarkoittanut myös taustanpoistopalvelun maksamista. Sen sijaan jokainen kutsu pakottaa tasaisen tunnetun taustavärin, ja palautuneesta SVG:stä poistetaan jälkikäteen se yksi koko kankaan kokoinen suorakulmio. Poisto katsoo vain juuren suoria lapsia, joten leikkauspolun sisällä olevaa suorakulmiota ei koskaan luulla taustaksi. Yksi toimittaja, ei taustanpoistopalvelua, aito läpinäkyvyys.
Tuo rajapinta opetti minulle suurimman osan siitä mitä tiedän tuloksen todentamisesta tilakoodin sijaan. Kuvamallit puuttuvat päälistauksesta kokonaan ja näkyvät vain erillisellä listalla. Pyyntöskeemassa dokumentoitu muotoparametri ei valitse mitään, koska vektoritulostus valitaan ottamalla vektorimalli eikä koskaan täyttämällä kenttää. Ja tunnistamattomat toimittaja-avaimet pudotetaan hiljaisesti sen sijaan että ne hylättäisiin, joten väärin kirjoitettu kentän nimi palauttaa täysin onnistuneen vastauksen ilman yhtäkään asetettua kontrollia. Toimittaja on kiinnitetty eksplisiittisesti siihen kutsuun joka kantaa valokuvan, ja se on vaatimustenmukaisuutta eikä reitityshygieniaa: kuvatoimittaja on se joka on nimetty alihankkijaluettelossa, ja hiljainen varareitti lähettäisi kuvan jonkun kasvoista yritykselle jota siellä ei ole.
Clerk hoitaa kirjautumisen macOS-isännöidyllä virrallaan. Polar toimii myyntiedustajana, joten maailmanlaajuinen vero on jonkun muun murhe, ja se antaa lisenssiavaimen aktivointirajalla joka sallii Macin vaihdon ilman tukipyyntöä. Sparkle toimittaa päivitykset, eikä koskaan käynnistä uudelleen kesken nauhoituksen. Sivusto on Astro 7 Cloudflare Workersilla, ja API ja D1 ovat sen vieressä.
Sovelluksessa ei ole kaatumisraportointia, analytiikkaa eikä telemetriaa. Se on päätös eikä unohdus, ja se on se päätös jota odotan katuvani operatiivisesti enkä millään muulla tavalla. Mochi viettää päivänsä auki oleva mikrofoni, käynnissä oleva kokousnauhoitus ja juuri sanotun teksti hallussaan. Kaatumisraportoija tuon prosessin sisällä lähettää kutsupinoja, säikeiden nimiä ja huonona päivänä sen mitä puskurissa sattui olemaan kun prosessi kuoli. Ei voi luvata etteivät nauhoitukset koskaan poistu Macilta ja samalla toimittaa agenttia jonka työ on lähettää minulle dataa prosessista jolla oli nauhoitus auki. Vastineeksi saan lauseen joka ei tarvitse tähteä sivustolla, sovelluksessa eikä tietosuojaselosteessa. Hintana on se ettei mitään saavu itsestään: bugi on aina täsmälleen niin näkyvä kuin se raportti jonka joku päättää lähettää. Lievennyksenä on painike joka kopioi leikepöydälle sovelluksen oman lokin viimeiset viisitoista minuuttia, sen version ja macOS-koontiversion, eikä kosketa mihinkään muuhun. Sinä luet sen ja päätät itse.
Lopputulos
Ei vielä julkaistu. Sivusto on pystyssä osoitteessa mochiformac.com ja kerää jonotuslistaa, ja latasivu sanoo suoraan ettei tänään ole koontiversiota annettavaksi enkä aio arvailla päivämäärää.
Polku puheesta holviin toimii päästä päähän, ja juuri sen halusin saada valmiiksi ennen kuin koskin tunnistautumiseen, laskutukseen tai hahmon generointiin.
Mitä tekisin toisin
Kirjoitin mallien koot määrittelyyn muistista ja jokainen niistä oli väärin noin kaksinkertaisesti, aina omaksi edukseni. Nyt skripti mittaa ne ja käyttöliittymä voi näyttää vain sen mitä skripti kirjoitti. Tuon opetuksen yleinen versio tuli kalliiksi: jokainen luku jonka käyttäjä lukee pitäisi generoida, ei koskaan kirjoittaa käsin.
Rakensin myös suoratoistodiarisoijan ensin ja oletin että palaavan äänen tunnistaminen olisi viritysongelma sen päällä. Se on eri ongelma joka vaatii eri mallin, ja hukkasin oikeita viikkoja yrittäessäni saada embeddingejä jostakin jolla niitä ei ole.




