Sebastien Rousseau

Àkàndé: GPT-Powered Voice Assistant for Executives

Arkitektura ng isang open-source na Python voice assistant: Whisper, GPT-4, SQLite cache, at fpdf2

7 min basahin
Banner for: Àkàndé: GPT-Powered Voice Assistant for Executives

Buod para sa mga Ehekutibo / Mga Pangunahing Aral

  • Ang Àkàndé ⧉ ay isang open-source na Python voice assistant na nagbubuklod ng OpenAI Whisper speech-to-text, GPT-4 chat completions, isang lokal na SQLite response cache, at fpdf2 PDF export sa iisang voice-driven na daloy ng trabaho na hindi nangangailangan ng cloud storage at lokal na mga timbang ng AI model.
  • Ang SQLite cache ay nag-iimbak ng mga SHA-256 hash ng mga normalized na query string na naka-mapa sa raw na teksto ng tugon ng API; ang mga cache hit ay walang gastos na token at bumabalik sa loob ng 10 ms, na ginagawang halos libre ang mga paulit-ulit na query (tulad ng pagsusuri ng isang desisyon mula sa naunang bahagi ng pulong).
  • Ang multi-turn na pag-uusap ay pinapanatili sa pamamagitan ng pagbuo ng listahan ng messages sa memorya at ipinasa ito sa bawat tawag sa Chat Completions API — tinatanggap ng modelo ang buong kasaysayan ng session upang makatuon sa mga naunang palitan, sa gastos ng unti-unting pagtaas ng paggamit ng token bawat liko.
  • Ang pagbuo ng PDF summary ay nagse-serialize ng listahan ng messages ng session sa isang naka-format na dokumento ng fpdf2: ang mga liko ng user at assistant ay naka-label, ang mga timestamp ay inisertan, at ang awtomatikong pagination ay humahawak ng mga session ng anumang haba; ang file ay isinusulat sa lokal na filesystem, hindi na-upload.
  • Hangganan ng privacy: tanging ang live na query (at kasaysayan ng session hanggang sa limitasyon ng context window) lamang ang umaalis sa device — walang mga recording ng audio, transcript, at mga cached na tugon ang ipinapadala sa anumang remote na serbisyo maliban sa API ng OpenAI.

Ang Àkàndé ⧉ ay isang open-source na Python voice assistant na itinayo sa paligid ng tatlong composable na bahagi: OpenAI Whisper para sa pagkilala ng pagsasalita, ang GPT-4 Chat Completions API para sa pag-unawa at pagbuo ng wika, at isang lokal na SQLite database para sa pag-cache ng tugon at pagpapanatili ng session. Ang resulta ay isang voice-driven na daloy ng trabaho na maaaring patakbuhin sa isang laptop nang walang lokal na mga timbang ng modelo, imprastraktura ng offline na imbakan, o stack ng container.

Inilalarawan ng artikulong ito ang teknikal na arkitektura ng bawat bahagi, ang mga desisyon sa disenyo tungkol sa caching at multi-turn na konteksto, at ang pipeline ng PDF export.

Pangkalahatang-ideya ng Pipeline #

Ang isang solong pakikipag-ugnayan ng Àkàndé ay sumusunod sa pagkakasunod-sunod na ito:

  1. Pagkuha ng audio — nagsasalita ang user; ini-record ng application ang audio sa isang pansamantalang WAV file gamit ang sounddevice o isang katugmang library ng audio.
  2. Speech-to-text — isinusumite ang WAV file sa openai.audio.transcriptions.create() (Whisper API); ibinabalik ang transcript bilang isang plain string.
  3. Paghahanap sa cache — na-normalize ang transcript (lower-case, na-collapse ang whitespace) at na-hash ng SHA-256; hinahanap ang hash sa lokal na SQLite na talahanayan ng response_cache.
  4. Tawag sa API o cache hit — kapag walang natagpuan, ang transcript ay idaragdag sa listahan ng messages ng session at ipapadala sa openai.chat.completions.create(); ang teksto ng tugon ay iniimbak sa cache.
  5. Text-to-speech — ang teksto ng tugon ay iko-convert sa audio gamit ang endpoint na openai.audio.speech.create() (TTS) o isang lokal na library ng TTS, at ii-play back.
  6. PDF export (sa kahilingan) — ang buong listahan ng messages ay ise-serialize sa isang naka-format na dokumento ng fpdf2 at isusulat sa disk.

Integrasyon ng OpenAI: Chat Completions at Whisper #

Gumagamit ang Àkàndé ng Python SDK na openai para sa pagkilala ng pagsasalita at pagbuo ng teksto. Ang tawag sa transcription ng Whisper:

with open(audio_file_path, "rb") as f:
    transcript = openai.audio.transcriptions.create(
        model="whisper-1",
        file=f,
        language=None  # auto-detect
    )
user_text = transcript.text

Pinapanatili ng tawag sa Chat Completions ang isang listahan ng messages na may saklaw ng session:

messages.append({"role": "user", "content": user_text})

response = openai.chat.completions.create(
    model="gpt-4-turbo-preview",
    messages=messages,
    temperature=0.2,
    max_tokens=1024
)

assistant_text = response.choices[0].message.content
messages.append({"role": "assistant", "content": assistant_text})

Ang system prompt ay isina-prepend nang isang beses sa simula ng session at kinokontrol ang persona, format ng output, at anumang mga hadlang na tukoy sa domain ng Àkàndé:

messages = [
    {
        "role": "system",
        "content": (
            "You are Àkàndé, a concise executive assistant. "
            "Respond in plain prose. Do not use markdown. "
            "If asked to summarise, produce three bullet points maximum."
        )
    }
]

Ang pagtatakda ng temperature=0.2 ay nagpapalitan ng malikhaing pagkakaiba-iba para sa determinismo — mahalaga para sa mga factual na query tulad ng pag-alala sa isang desisyon mula sa mas maagang bahagi ng session.

SQLite Response Cache #

Minimal ang schema ng cache:

CREATE TABLE IF NOT EXISTS response_cache (
    query_hash  TEXT PRIMARY KEY,
    response    TEXT NOT NULL,
    created_at  INTEGER NOT NULL  -- Unix timestamp
);

Ang lookup at write path:

import hashlib, sqlite3, time

def _normalise(text: str) -> str:
    return " ".join(text.lower().split())

def cache_get(conn: sqlite3.Connection, query: str) -> str | None:
    h = hashlib.sha256(_normalise(query).encode()).hexdigest()
    row = conn.execute(
        "SELECT response FROM response_cache WHERE query_hash = ?", (h,)
    ).fetchone()
    return row[0] if row else None

def cache_set(conn: sqlite3.Connection, query: str, response: str) -> None:
    h = hashlib.sha256(_normalise(query).encode()).hexdigest()
    conn.execute(
        "INSERT OR REPLACE INTO response_cache VALUES (?, ?, ?)",
        (h, response, int(time.time()))
    )
    conn.commit()

Tinitiyak ng INSERT OR REPLACE na ang isang cached na tugon ay ia-update kung ang parehong query ay isusumite pagkatapos ng pag-upgrade ng modelo. Ang isang TTL-based na query sa pagpapatalsik (DELETE WHERE created_at < ?) ay maaaring i-schedule sa startup upang limitahan ang laki ng cache.

Pagganap ng cache hit: ang isang SQLite lookup sa isang lokal na SSD ay bumabalik sa loob ng 1 ms para sa mga talahanayan na hanggang ~100,000 row. Ang round-trip latency para sa isang live na tawag sa API ng GPT-4 ay karaniwang 600–900 ms para sa maikli na mga tugon. Para sa isang pang-araw-araw na briefing na may kaunting paulit-ulit na query, inaalis ng cache ang karamihan sa mga tawag sa API pagkatapos ng unang session.

Pagbuo ng PDF Summary #

Gumagamit ang PDF export ng fpdf2, isang pinapanatiling Python PDF library na walang binary dependencies:

from fpdf import FPDF
from datetime import datetime

def export_session_pdf(messages: list[dict], output_path: str) -> None:
    pdf = FPDF()
    pdf.add_page()
    pdf.set_font("Helvetica", size=11)
    pdf.set_margins(20, 20, 20)

    pdf.set_font("Helvetica", "B", 14)
    pdf.cell(0, 10, f"Àkàndé Session — {datetime.now():%Y-%m-%d %H:%M}", ln=True)
    pdf.ln(4)

    for msg in messages:
        if msg["role"] == "system":
            continue
        label = "You" if msg["role"] == "user" else "Àkàndé"
        pdf.set_font("Helvetica", "B", 10)
        pdf.cell(0, 6, label, ln=True)
        pdf.set_font("Helvetica", size=10)
        pdf.multi_cell(0, 5, msg["content"])
        pdf.ln(3)

    pdf.output(output_path)

Hinahawakan ng multi_cell() ang line-wrapping at awtomatikong page break, kaya ang mga session ng anumang haba ay nagbibigay ng maayos na naka-format na dokumento nang walang manual na lohika ng pagination. Ang output ay isang file na katugma sa PDF/A na walang naka-embed na font na lampas sa karaniwang sukatan ng Helvetica.

Modelo ng Privacy #

Ang hangganan ng privacy sa Àkàndé ay tinukoy ng tatlong katotohanan:

  1. Isinusumite ang audio sa Whisper API sa pamamagitan ng HTTPS at hindi pinapanatili ng OpenAI pagkatapos ng tawag sa API (ayon sa patakaran sa paggamit ng data ng API ng OpenAI simula Pebrero 2024).
  2. Ang mga tawag sa Chat Completions API ay nagpapadala ng listahan ng messages ng session — na maaaring naglalaman ng buong kasaysayan ng pag-uusap para sa mga multi-turn na session.
  3. Ang database ng SQLite at mga PDF file ay ganap na nananatili sa lokal na filesystem; walang background sync sa anumang serbisyo ng cloud ang nangyayari.

Para sa mga kaso ng paggamit ng ehekutibo na kinabibilangan ng mga sensitibong paksa — mga talakayan sa M&A, mga usapin ng tauhan, regulatoryo na estratehiya — ang kasaysayan ng session na ipinapadala sa API ay dapat na suriin laban sa patakaran sa paggamit ng AI ng organisasyon bago mag-deploy. Ang limitasyon ng max_tokens sa system prompt ay maaaring gamitin upang maiwasan ang hindi sinasadyang pagpapadala ng konteksto na lumagpas sa nilalayong saklaw ng pagsisiwalat.

Mga Madalas Itanong #

Pinapanatili ba ng Àkàndé ang kasaysayan ng pag-uusap pagkatapos matapos ang session? Ang in-memory na listahan ng messages ay itatapon kapag lumabas ang proseso. Ang kasaysayan ng pag-uusap ay pinapanatili lamang kung ang user ay nag-trigger ng PDF export o kung ang isang custom na layer ng pagpapanatili ay idaragdag. Nag-iimbak ang SQLite cache ng mga hash ng query at teksto ng tugon, hindi ang buong konteksto ng pag-uusap.

Paano hinahawakan ng cache ang mga query na magkakatulad ngunit hindi magkapareho? Gumagamit ang cache ng exact-match hashing sa normalized na query string. Ang dalawang query na nagkakaiba ng isang salita ay magbibigay ng iba't ibang hash at magreresulta sa magkakahiwalay na tawag sa API. Ang semantic caching (gamit ang embedding similarity upang itugma ang mga halos magkaparehong query) ay mangangailangan ng karagdagang hakbang sa vector lookup at hindi bahagi ng base na implementasyon.

Anong GPT model ang ginagamit ng Àkàndé bilang default? Ang default ay gpt-4-turbo-preview simula Pebrero 2024. Ang pangalan ng modelo ay isang parameter ng configuration, kaya anumang modelo ng OpenAI chat completion ay maaaring palitan. Ang paglipat sa gpt-3.5-turbo ay nagbabawas ng gastos sa API ng humigit-kumulang 20× bawat token ngunit nagbabawas ng kalidad ng pag-iisip para sa mga kumplikadong multi-step na query.

Maaari bang i-customize ang format ng PDF export? Oo. Ang function ng export ng fpdf2 ay tinatanggap ang listahan ng messages bilang tanging kinakailangang input, kaya ang font, margin, laki ng pahina, nilalaman ng header, at labeling ay maaaring baguhin lahat sa pamamagitan ng pag-edit ng function ng export. Sinusuportahan din ng fpdf2 ang pagdaragdag ng mga larawan, talahanayan, at Unicode na font, na nagbibigay-daan sa mas mayamang mga layout ng dokumento para sa mga organisasyong may mga tiyak na kinakailangan sa branding.

Mga Sanggunian #

  1. OpenAI. Audio Transcriptions — Whisper API. OpenAI Platform Documentation, 2024. https://platform.openai.com/docs/api-reference/audio/createTranscription
  2. OpenAI. Chat Completions API. OpenAI Platform Documentation, 2024. https://platform.openai.com/docs/api-reference/chat/create
  3. Voss, J. et al. fpdf2: Modern PDF generation for Python. GitHub, 2024. https://github.com/py-pdf/fpdf2
  4. SQLite Consortium. SQLite Documentation. sqlite.org, 2024. https://www.sqlite.org/docs.html

Huling sinuri .

Huling sinuri .