Hei koodaajat! Minä olen Harri ja tervetuloa tekemään mun kanssa yhdessä AI-agentteja.
Katso ensin edellinen artikkeli Python-ympäristön asentamisesta omalle koneellesi, jos sinulla ei vielä ole Pythonia asennettuna.
Tässä projektissa rakennamme yhdessä älykkään AI-agentin, joka yhdistää useita tekoälypalveluita yhteen helppokäyttöiseen sovellukseen. Kyseessä on täydellinen Python-projekti aloittelijoista edistyneempiin koodaajiin.
Mitä teemme?
Luomme SmartAgentin – desktop-sovelluksen, joka osaa:
- Keskustella useiden eri AI-mallien kanssa (ChatGPT, Claude, Gemini, jne.)
- Käyttää äänikomentoja ja puhua takaisin
- Ajastaa ja suorittaa tehtäviä automaattisesti
- Hakea tietoa netistä ja YouTubesta
- Lukea PDF-tiedostoja ja luoda raportteja
- Kääntää tekstejä eri kielille
Projektin vaiheet
Ympäristön valmistelu
- Python-riippuvuuksien kartoitus
- Automaattinen asennusskripti
- Ympäristön testaus
Perus-AI-agentti
- Yksinkertainen ChatGPT-keskustelu
- GUI tkinterillä
- Web-haku ja PDF-luku
Äänominaisuudet
- Puhesyntetisointi (teksti → puhe)
- Puheentunnistus (puhe → teksti)
- Herätyssana-aktivointi
Multi-AI tuki
- Tuki useille AI-palveluille
- API-avainten hallinta
- Palvelun vaihto lennossa
Tehtävien ajastus
- Kalenteripohjainen ajastus
- Taustatehtävien suoritus
- PDF-raporttien generointi
- Tehtävien hallintapaneeli
Riippuvuuksien asentaminen ja asennusskripti
Tarvittavat Python-kirjastot (asennetaan yleensä pip install komennoilla)
Projektimme tarvitsee useita kirjastoja. Tässä ne kategorioittain:
Ydinkirjastot (PAKOLLISIA):
pip install openai requests PyPDF2
Äänominaisuudet:
pip install pyttsx3 SpeechRecognition pyaudio
Käännökset ja haku:
pip install deep-translator youtube-search-python
Tehtävien ajastus:
pip install schedule reportlab tkcalendar
Lisä-AI-palvelut (valinnaiset):
pip install anthropic google-generativeai cohere
Automaattinen asennusskripti
Jos haluat helpottaa riippuvuuksien asennusta, sä voit myös tehdä pienen Python-scriptin (ohjelman), joka kartoittaa sun koneen ja asentaa python kirjastot automaattisesti, kun sä käynnistät sen.
Asennus skripti, Python koodi:
#!/usr/bin/env python3
"""
AI Agent Setup Script
Automaattinen asennusohjelma AI-agentille
"""
import subprocess
import sys
import os
def install_package(package):
"""Install a package using pip"""
try:
subprocess.check_call([sys.executable, "-m", "pip", "install", package])
return True
except subprocess.CalledProcessError:
return False
def check_import(module_name):
"""Check if a module can be imported"""
try:
__import__(module_name)
return True
except ImportError:
return False
def main():
print("🚀 AI-Agentti Asennusohjelma")
print("=" * 40)
# Core packages
core_packages = [
"openai",
"requests",
"PyPDF2"
]
# Translation packages (try both)
translation_packages = [
"deep-translator", # Better for Python 3.13
"googletrans==4.0.0rc1" # Alternative
]
# YouTube search
youtube_packages = [
"youtube-search-python",
"youtube-search" # Alternative
]
# Speech packages
speech_packages = [
"pyttsx3",
"SpeechRecognition"
]
optional_packages = [
"pyaudio" # Might need manual installation on some systems
]
print("\n📦 Asennetaan peruspaketit...")
for package in core_packages:
print(f"Asennetaan {package}...", end=" ")
if install_package(package):
print("✅")
else:
print("❌")
print("\n🌍 Asennetaan käännöspalvelu...")
translation_installed = False
for package in translation_packages:
if not translation_installed:
print(f"Kokeiletaan {package}...", end=" ")
if install_package(package):
print("✅")
translation_installed = True
else:
print("❌")
print("\n🎬 Asennetaan YouTube-haku...")
youtube_installed = False
for package in youtube_packages:
if not youtube_installed:
print(f"Kokeiletaan {package}...", end=" ")
if install_package(package):
print("✅")
youtube_installed = True
else:
print("❌")
print("\n🔊 Asennetaan puheominaisuudet...")
for package in speech_packages:
print(f"Asennetaan {package}...", end=" ")
if install_package(package):
print("✅")
else:
print("❌")
print("\n🎤 Asennetaan valinnaisia paketteja...")
for package in optional_packages:
print(f"Kokeiletaan {package}...", end=" ")
if install_package(package):
print("✅")
else:
print("⚠️ (Voi vaatia manuaalista asennusta)")
print("\n" + "=" * 40)
print("🧪 Testataan asennuksia...")
# Test imports
tests = [
("openai", "OpenAI API"),
("requests", "HTTP-pyynnöt"),
("PyPDF2", "PDF-luku"),
("deep_translator", "Käännös (deep-translator)"),
("googletrans", "Käännös (googletrans)"),
("youtubesearchpython", "YouTube-haku"),
("pyttsx3", "Puhesynteesi"),
("speech_recognition", "Puheentunnistus")
]
working_features = []
for module, description in tests:
if check_import(module):
print(f"✅ {description}")
working_features.append(description)
else:
print(f"❌ {description}")
print("\n" + "=" * 40)
print("📋 YHTEENVETO")
print(f"Toimivat ominaisuudet ({len(working_features)}):")
for feature in working_features:
print(f" ✅ {feature}")
print(f"\n🔑 TÄRKEÄÄ: OpenAI API-avain")
print("1. Mene: https://platform.openai.com/api-keys")
print("2. Luo uusi API-avain")
print("3. Aseta se agentin asetuksissa (⚙️-nappi)")
print(f"\n🚀 Valmis! Käynnistä agentti: python AI-agentti.py")
# Create a simple config file
config = {
"setup_completed": True,
"installed_features": working_features
}
try:
import json
with open("agent_setup.json", "w", encoding="utf-8") as f:
json.dump(config, f, ensure_ascii=False, indent=2)
print("📄 Asetustiedosto luotu: agent_setup.json")
except:
pass
if __name__ == "__main__":
main()
input("\nPaina Enter lopettaaksesi...")
Huom! Luo tämä tiedosto nimellä setup.py
ja aja komennolla python setup.py
:
Asennusskriptin käyttö
- Tallenna yllä oleva koodi nimellä
setup.py
- Aja komentorivillä:
python setup.py

- Seuraa tulostettuja ohjeita ja mahdollisia virheitä
- Korjaa manuaalisesti paketit jotka eivät asentuneet automaattisesti
Miksi tämä projekti on loistava oppimiskokemus?
Aloittelijoille:
- Opit Python GUI-ohjelmoinnin perusteet
- Näet miten API:ja käytetään käytännössä
- Ymmärrät modulaarisen koodin rakentamisen
Edistyneemmille:
- Monimutkaisen sovelluksen arkkitehtuuri
- Async-ohjelmointi ja taustatehtävät
- Virheiden käsittely ja robust-koodin kirjoittaminen
Kaikille:
- Valmis työkalu omaan käyttöön
- Helppo laajentaa uusilla ominaisuuksilla
- Portfolio-projekti CV:hen
Seuraava askel
Kun olet ajanut asennusskriptin / tai asentanut tarvittavat kirjastot pip install komennoilla ja kaikki paketit on asennettu, olet valmis aloittamaan varsinaisen koodauksen!
Monipuolinen AI-agentti python koodilla:
Tehdään yhdessä monipuolinen AI-agentti, joka osaa:
- Tehdä web-hakuja
- Kommunikoida verkkopalvelujen kanssa (API)
- Lukea ja jäsentää PDF-dokumentteja
- Toimia ikkunasovelluksena (graafinen käyttöliittymä)
Käytämme tuttuja Python-kirjastoja: openai
, requests
, PyPDF2
, tkinter
ja webbrowser
. Tässä koko agentin runko selityksineen:
Python koodi 3:
import openai
import requests
import PyPDF2
import tkinter as tk
from tkinter import filedialog
import webbrowser
class MultiToolAgent:
def __init__(self, name, api_key):
self.name = name
openai.api_key = api_key
self.memory = []
def chat_with_user(self, user_input):
self.memory.append({"role": "user", "content": user_input})
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[{"role": "system", "content": "Olet monipuolinen apuagentti."}] + self.memory
)
reply = response.choices[0].message["content"]
self.memory.append({"role": "assistant", "content": reply})
return reply
def web_search(self, query):
print(f"Tehdään haku: {query}")
webbrowser.open(f"https://www.google.com/search?q={query}")
def call_api(self, url):
try:
response = requests.get(url)
return response.json() if response.headers["Content-Type"] == "application/json" else response.text
except Exception as e:
return f"Virhe API-kutsussa: {e}"
def read_pdf(self, path):
try:
with open(path, 'rb') as file:
reader = PyPDF2.PdfReader(file)
text = ''
for page in reader.pages:
text += page.extract_text()
return text
except Exception as e:
return f"PDF-lukuvirhe: {e}"
# Graafinen käyttöliittymä (GUI):
def run_gui():
agent = MultiToolAgent("TekoBot", api_key="YOUR_OPENAI_API_KEY")
def handle_input():
user_input = entry.get()
result = agent.chat_with_user(user_input)
output.delete(1.0, tk.END)
output.insert(tk.END, result)
def handle_pdf():
path = filedialog.askopenfilename(filetypes=[("PDF Files", "*.pdf")])
text = agent.read_pdf(path)
output.delete(1.0, tk.END)
output.insert(tk.END, text)
def handle_search():
query = entry.get()
agent.web_search(query)
def handle_api():
url = entry.get()
result = agent.call_api(url)
output.delete(1.0, tk.END)
output.insert(tk.END, str(result))
window = tk.Tk()
window.title("TekoBot Agentti")
entry = tk.Entry(window, width=50)
entry.pack()
tk.Button(window, text="Puhu agentin kanssa", command=handle_input).pack()
tk.Button(window, text="Tee Google-haku", command=handle_search).pack()
tk.Button(window, text="Lue PDF-tiedosto", command=handle_pdf).pack()
tk.Button(window, text="Soita API-osoitteeseen", command=handle_api).pack()
output = tk.Text(window, height=20, width=60)
output.pack()
window.mainloop()
# Käynnistä käyttöliittymä
run_gui()
Mitä agentti osaa tehdä?
Ominaisuus | Toiminta |
---|---|
Keskustelu | Käyttää GPT-mallia vastauksiin käyttäjän viesteihin |
Web-haku | Avaa selaimessa Google-haun |
API-kutsu | Hakee sisältöä ulkoisesta verkkopalvelusta |
PDF-luku | Poimii tekstin PDF-tiedostosta ja näyttää sen |
GUI | Tarjoaa graafisen käyttöliittymän nappeineen |
Selitykset peruskäyttäjille
- openai.ChatCompletion.create käyttää kielimallia – syötät viestin, saat älykkään vastauksen.
- webbrowser.open avaa Googlen haun selaimessa.
- requests.get kutsuu verkkopalvelun (API), esim. säätiedot tai uutisfeedin.
- PyPDF2 lukee PDF-tiedoston sisältä ja poimii tekstin.
- tkinter luo ikkunan, jossa voit syöttää viestejä, klikata nappeja ja nähdä vastaukset.
Vielä hiukan kehittyneempi esimerkki Python koodilla:
Nyt päivitetään meidän monitoimi-AI-agentti niin, että se osaa:
- Puhua ääneen käyttäjän kanssa (tekstistä puhetta)
- Kääntää tekstiä eri kielille (esim. suomi ↔ englanti)
- Hakea YouTube-videoita hakusanan perusteella
Käytettävät lisäkirjastot
Kirjasto | Tarkoitus |
---|---|
pyttsx3 | Puheominaisuus (tekstistä puheeksi) |
googletrans | Käännökset eri kielille |
youtube_search | YouTube-haku hakusanalla |
Ennen käyttöä:
Asenna vielä lisäksi nämä kirjastot komennolla:
pip install pyttsx3 googletrans==4.0.0-rc1 youtube-search-python
Python koodi 4:
import openai
import requests
import PyPDF2
import tkinter as tk
from tkinter import filedialog
import webbrowser
import pyttsx3
from googletrans import Translator
from youtubesearchpython import VideosSearch
# AI-agentin määrittely
class MultiToolAgent:
def __init__(self, name, api_key):
self.name = name
openai.api_key = api_key
self.memory = []
self.speaker = pyttsx3.init()
self.translator = Translator()
def chat_with_user(self, user_input):
self.memory.append({"role": "user", "content": user_input})
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[{"role": "system", "content": "Olet monipuolinen apuagentti."}] + self.memory
)
reply = response.choices[0].message["content"]
self.memory.append({"role": "assistant", "content": reply})
return reply
def speak(self, text):
self.speaker.say(text)
self.speaker.runAndWait()
def translate_text(self, text, target_lang="en"):
translated = self.translator.translate(text, dest=target_lang)
return translated.text
def web_search(self, query):
webbrowser.open(f"https://www.google.com/search?q={query}")
def call_api(self, url):
try:
response = requests.get(url)
return response.json() if response.headers["Content-Type"] == "application/json" else response.text
except Exception as e:
return f"Virhe API-kutsussa: {e}"
def read_pdf(self, path):
try:
with open(path, 'rb') as file:
reader = PyPDF2.PdfReader(file)
text = ''
for page in reader.pages:
text += page.extract_text()
return text
except Exception as e:
return f"PDF-lukuvirhe: {e}"
def youtube_search(self, query):
search = VideosSearch(query, limit=3)
results = search.result()["result"]
links = [f"{vid['title']}: {vid['link']}" for vid in results]
return "\n".join(links)
# Graafinen käyttöliittymä
def run_gui():
agent = MultiToolAgent("TekoBot", api_key="YOUR_OPENAI_API_KEY")
def handle_input():
user_input = entry.get()
result = agent.chat_with_user(user_input)
output.delete(1.0, tk.END)
output.insert(tk.END, result)
def handle_speak():
text = output.get(1.0, tk.END)
agent.speak(text)
def handle_translate():
user_input = entry.get()
translated = agent.translate_text(user_input, target_lang="en")
output.delete(1.0, tk.END)
output.insert(tk.END, translated)
def handle_pdf():
path = filedialog.askopenfilename(filetypes=[("PDF Files", "*.pdf")])
text = agent.read_pdf(path)
output.delete(1.0, tk.END)
output.insert(tk.END, text)
def handle_search():
query = entry.get()
agent.web_search(query)
def handle_api():
url = entry.get()
result = agent.call_api(url)
output.delete(1.0, tk.END)
output.insert(tk.END, str(result))
def handle_youtube():
query = entry.get()
result = agent.youtube_search(query)
output.delete(1.0, tk.END)
output.insert(tk.END, result)
window = tk.Tk()
window.title("TekoBot Agentti")
entry = tk.Entry(window, width=50)
entry.pack()
tk.Button(window, text="Keskustele agentin kanssa", command=handle_input).pack()
tk.Button(window, text="Lue PDF", command=handle_pdf).pack()
tk.Button(window, text="Google-haku", command=handle_search).pack()
tk.Button(window, text="Soita API", command=handle_api).pack()
tk.Button(window, text="Käännä englanniksi", command=handle_translate).pack()
tk.Button(window, text="Puhu vastaus ääneen", command=handle_speak).pack()
tk.Button(window, text="Etsi YouTube-videoita", command=handle_youtube).pack()
output = tk.Text(window, height=20, width=60)
output.pack()
window.mainloop()
# Käynnistä GUI
run_gui()
Kuinka agentin uudet ominaisuudet toimivat?
Toiminto | Kuvaus |
---|---|
Puhu teksti ääneen | pyttsx3 muuntaa vastauksen puheeksi |
Käännä teksti | googletrans kääntää käyttäjän syötteen englanniksi |
YouTube-haku | Hakee 3 videolinkkiä hakusanan perusteella |
Lisätään vielä puheentunnistus ja äänikomentoaktivointi mukaan python koodiin
Lisätään agentille puheentunnistus ja äänikomentoaktivointi – eli:
- Agentti kuuntelee puhetta ja muuntaa sen tekstiksi
- Agentti aktivoituu tietyllä “herätyssanalla”, kuten “Hei TekoBot”
Toteutetaan tämä Pythonissa käyttäen speech_recognition
-kirjastoa:
Asennettavat lisäkirjastot
pip install SpeechRecognition pyaudio
Huom: Windows-käyttäjillä pyaudio
voi vaatia asennuksen binääristä. Jos tulee virhe, käytä valmiiksi koottua versiota esim. täältä: https://www.lfd.uci.edu/~gohlke/pythonlibs/#pyaudio
Päivitetty agenttikoodi: puheentunnistus + herätyskomento
Lisätään nämä meidän aiempaan MultiToolAgent
-luokkaan ja graafiseen käyttöliittymään:
Python koodi 5:
import speech_recognition as sr
class MultiToolAgent:
def __init__(self, name, api_key):
self.name = name
openai.api_key = api_key
self.memory = []
self.speaker = pyttsx3.init()
self.translator = Translator()
self.recognizer = sr.Recognizer()
self.microphone = sr.Microphone()
def listen(self, wake_word="Hei TekoBot"):
print("🎧 Agentti odottaa herätyssanaa...")
while True:
with self.microphone as source:
self.recognizer.adjust_for_ambient_noise(source)
audio = self.recognizer.listen(source)
try:
text = self.recognizer.recognize_google(audio, language="fi-FI")
print(f"Kuultu: {text}")
if wake_word.lower() in text.lower():
print("Herätyssana havaittu – agentti aktivoituu!")
return text
except sr.UnknownValueError:
print("Ei tunnistettavaa puhetta")
except sr.RequestError as e:
print(f" Tunnistusvirhe: {e}")
def speech_to_text(self):
print("Sano jotain agentille...")
with self.microphone as source:
self.recognizer.adjust_for_ambient_noise(source)
audio = self.recognizer.listen(source)
try:
return self.recognizer.recognize_google(audio, language="fi-FI")
except sr.UnknownValueError:
return "En kuullut selkeästi"
except sr.RequestError as e:
return f"Virhe tunnistuksessa: {e}"
GUI-painikkeet (täydennys aiempaan käyttöliittymään):
Lisää nämä tk.Button(...)
-rivit run_gui
-funktioon:
tk.Button(window, text="Kuuntele herätyssanaa", command=lambda:
output.insert(tk.END, agent.listen())).pack()
tk.Button(window, text="Muunna puhe tekstiksi", command=lambda:
output.insert(tk.END, agent.speech_to_text())).pack()
Miten nämä toiminnot toimivat?
Toiminto | Kuvaus |
---|---|
Herätyssana (“Hei TekoBot”) | Agentti kuuntelee jatkuvasti mikrofonia ja aktivoituu kun herätyssana kuullaan |
Puheentunnistus | Käyttäjä puhuu → agentti muuntaa puheen tekstiksi Google Speech API:n avulla |
Melunsäätö & odotus | Agentti kalibroi taustamelun ja odottaa puhetta selkeästi |
TekoBot reagoi | Herätyssanan jälkeen agentti voi tehdä vaikkapa automaattisen vastauksen tai kysyä lisätietoa |
Mihin tätä voi käyttää?
- Handsfree-käyttö → puhut agentille ilman näppäimistöä
- Älykäs kodin agentti (pienellä säätämisellä )
- Interaktiiviset esittelyt, koulutusohjelmat tai tapahtuma-agentit
Kehitetään vielä koodia hiukan paremmaksi:
1. Parempi arkkitehtuuri
- Erotettu agentti-logiikka (
AdvancedAIAgent
) ja käyttöliittymä (AgentGUI
) - Modulaarinen rakenne – helppo laajentaa uusilla ominaisuuksilla
- Virheenkäsittely kaikissa funktioissa
2. Asetukset ja pysyvyys
- Asetukset tallentuvat JSON-tiedostoon
- Keskusteluhistoria säilyy ja voidaan tallentaa
- API-avain tallennetaan turvallisesti
3. Käyttöliittymän parannukset
- Selkeämpi, värillinen nappi-layout
- Tilarivia joka näyttää mitä tapahtuu
- Asetusikkuna API-avaimen ja herätyssanan muuttamiseen
- Scroll-mahdollisuus pitkille vastauksille
4. Toiminnallisuuden laajennukset
- Älykkäämpi herätyssana-toiminto
- Automaattinen komennon kuuntelu herätyssanan jälkeen
- Parempi PDF-luku sivunumeroilla
- YouTube-haku näyttää useita tuloksia
- API-kutsut paremmalla virheenkäsittelyllä
Miten agentti toimii:
Perusrakenne:
- AdvancedAIAgent-luokka – Agentin “aivot”
- Hoitaa kaikki AI-toiminnot
- Hallinnoi muistia ja asetuksia
- Tarjoaa API:n eri toiminnoille
- AgentGUI-luokka – Käyttöliittymä
- Luo visuaalisen käyttöliittymän
- Yhdistää napit agentin toimintoihin
- Hallinnoi käyttäjän syötteitä ja vastausten näyttämistä
Tärkeimmät ominaisuudet:
- Keskustelu: Käyttää OpenAI GPT:tä älykkäisiin vastauksiin
- Puhe: Muuttaa tekstin puheeksi
- Kuuntelu: Muuttaa puheen tekstiksi
- Herätyssana: Kuuntelee “Hei TekoBot” ja aktivoituu automaattisesti
- Web-haku: Avaa Google-haun selaimessa
- PDF-luku: Lukee PDF-tiedostoja ja näyttää sisällön
- YouTube: Hakee videoita ja näyttää linkit
- Käännös: Kääntää tekstiä eri kielille
- API-kutsut: Hakee dataa ulkoisista palveluista
4. OpenAI API-avain
- Mene: https://platform.openai.com/api-keys
- Rekisteröidy jos tarvitsee
- Luo uusi avain (“Create new secret key”)
- Kopioi avain (alkaa
sk-...
) - Käynnistä agentti ja klikkaa ⚙️-nappia
- Liitä avain asetuksiin
5. Jos pyaudio ei asennu (yleinen ongelma Windows):
Windowsille:
# Lataa sopiva wheel-tiedosto:
# https://www.lfd.uci.edu/~gohlke/pythonlibs/#pyaudio
pip install pyaudio-0.2.11-cp313-cp313-win_amd64.whl
Vaihtoehtoisesti:
# Conda-asennus (jos Anaconda käytössä)
conda install pyaudio
Sitten jos varsinainen AI-agentti.py tiedosto löytyy myös esimerkiksi C:\temp\ kansiosta, voidaan komentaa:
python AI-agentti.py

Graafinen Python ohjelma aukeaa ja Asetukset-painikkeesta voidaan asettaa OpenAI API-avain.
Ohjelma kuuntelee jo käyttäjää, jos painetaan Puhu-painiketta.
Muokataan koodia vielä niin, että voisi valita muitakin tekoäly api yhteyksiä kuin ChatGPT:
Kokeillaan tehdä Multi-AI agentti joka tukee useita AI-palveluita. Tässä mitä uutta lisättiin:
Tuetut AI-palvelut:
1. OpenAI
- GPT-3.5-turbo, GPT-4, GPT-4-turbo
- Tarvitsee: OpenAI API-avaimen
2. Groq
- Llama 3 (70B/8B), Mixtral, Gemma
- Tarvitsee: Groq API-avaimen
- Erittäin nopea inference
3. Anthropic Claude
- Claude 3 Haiku, Sonnet, Opus
- Tarvitsee: Anthropic API-avaimen
4. Google Gemini
- Gemini Pro, Gemini Pro Vision
- Tarvitsee: Google AI API-avaimen
5. Ollama (Paikallinen)
- Llama 3, Mistral, CodeLlama, Phi3, Qwen2
- Ei tarvitse API-avainta, toimii paikallisesti
- Asennus:
ollama pull llama3
6. Cohere
- Command-R, Command-R-Plus
- Tarvitsee: Cohere API-avaimen
7. Hugging Face
- DialoGPT, BlenderBot ja muut mallit
- Tarvitsee: HF Token
8. Together AI
- Llama 2, Mixtral ja muut avoimen lähdekoodin mallit
- Tarvitsee: Together AI API-avaimen
Uudet ominaisuudet:
AI-palvelun valinta
- Dropdown-valikko AI-palvelun valintaan
- Automaattinen mallien listaus valitun palvelun mukaan
- Reaaliaikainen vaihtaminen eri palveluiden välillä
Parannetut asetukset
- Välilehtipohjainen asetusikkuna
- API-avaimet kaikille palveluille
- AI-parametrit (max_tokens, temperature)
- Puhe-asetukset
- Helpot linkit API-avainten hankkimiseen
Ollama-tuki
- Paikallinen AI ilman API-avaimia
- Tuki kaikille Ollama-malleille
- Automaattinen yhteys localhost:11434
Asennusohjeet:
1. Peruspaketit
pip install requests PyPDF2 deep-translator youtube-search-python pyttsx3 SpeechRecognition
2. AI-palvelujen kirjastot (valinnaiset)
# OpenAI
pip install openai
# Anthropic
pip install anthropic
# Google Gemini
pip install google-generativeai
# Ollama (ei tarvitse Python-pakettia, vain Ollama-ohjelman)
# Lataa: https://ollama.ai
3. Ollama-asennus (paikallinen AI)
# Lataa ja asenna Ollama
# Windows/Mac: https://ollama.ai/download
# Käynnistä Ollama
ollama serve
# Lataa malli (esim. Llama 3)
ollama pull llama3
API-avainten hankkiminen:
Agentissa on suorat linkit kaikkien palveluiden API-avainten hankkimiseen:
- Klikkaa ⚙️ Asetukset
- “API-avaimet” -välilehti
- Klikkaa linkkejä → Avautuu selaimen kautta
- Kopioi API-avain ja liitä asetuksiin
- Tallenna
Käyttöönotto:
python AI-agentti.py
Ohjelma näyttää käynnistyessä mitkä AI-palvelut ovat saatavilla:
🚀 Käynnistetään Multi-AI Agentti...
Tuetut AI-palvelut:
✅ 🔑 OpenAI (GPT-3.5/4)
✅ 🔑 Groq (Llama/Mixtral)
✅ 🆓 Ollama (Local)
❌ 🔑 Anthropic (Claude)
...
Vinkki: Mikä AI-palvelu valita?
- Groq: Nopein vastaus, ilmainen tier
- OpenAI: Laadukkaita vastauksia, maksullinen
- Ollama: Täysin ilmainen, toimii offline
- Claude: Hyviä pitkiä vastauksia
- Gemini: Googlen malli, kohtuuhintainen
Voit vaihtaa AI-palvelua kesken keskustelun ilman että menetät keskusteluhistoriaa!
Python koodi 8:
import requests
import PyPDF2
import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext, ttk
import webbrowser
import threading
import json
import os
from datetime import datetime
import time
# Conditional imports with fallbacks
try:
import pyttsx3
SPEECH_AVAILABLE = True
except ImportError:
SPEECH_AVAILABLE = False
print("⚠️ pyttsx3 ei saatavilla - puhesynteesi poistettu käytöstä")
try:
from deep_translator import GoogleTranslator
TRANSLATE_AVAILABLE = True
TRANSLATOR_TYPE = "deep_translator"
except ImportError:
try:
from googletrans import Translator
TRANSLATE_AVAILABLE = True
TRANSLATOR_TYPE = "googletrans"
except ImportError:
TRANSLATE_AVAILABLE = False
TRANSLATOR_TYPE = None
print("⚠️ Käännöspalvelu ei saatavilla - asenna: pip install deep-translator")
try:
from youtubesearchpython import VideosSearch
YOUTUBE_AVAILABLE = True
except ImportError:
YOUTUBE_AVAILABLE = False
print("⚠️ youtube-search-python ei saatavilla - YouTube-haku poistettu käytöstä")
try:
import speech_recognition as sr
SPEECH_RECOGNITION_AVAILABLE = True
except ImportError:
SPEECH_RECOGNITION_AVAILABLE = False
print("⚠️ SpeechRecognition ei saatavilla - puheentunnistus poistettu käytöstä")
# AI Provider imports
try:
import openai
OPENAI_AVAILABLE = True
except ImportError:
OPENAI_AVAILABLE = False
print("⚠️ OpenAI ei saatavilla - asenna: pip install openai")
try:
import anthropic
ANTHROPIC_AVAILABLE = True
except ImportError:
ANTHROPIC_AVAILABLE = False
try:
import google.generativeai as genai
GEMINI_AVAILABLE = True
except ImportError:
GEMINI_AVAILABLE = False
class AIProviderManager:
"""Manages different AI providers and their APIs"""
def __init__(self):
self.providers = {
"openai": {
"name": "OpenAI (GPT-3.5/4)",
"models": ["gpt-3.5-turbo", "gpt-4", "gpt-4-turbo"],
"requires_key": True,
"available": OPENAI_AVAILABLE
},
"groq": {
"name": "Groq (Llama/Mixtral)",
"models": ["llama3-70b-8192", "llama3-8b-8192", "mixtral-8x7b-32768", "gemma-7b-it"],
"requires_key": True,
"available": True # Uses OpenAI-compatible API
},
"anthropic": {
"name": "Anthropic (Claude)",
"models": ["claude-3-haiku-20240307", "claude-3-sonnet-20240229", "claude-3-opus-20240229"],
"requires_key": True,
"available": ANTHROPIC_AVAILABLE
},
"gemini": {
"name": "Google Gemini",
"models": ["gemini-pro", "gemini-pro-vision"],
"requires_key": True,
"available": GEMINI_AVAILABLE
},
"ollama": {
"name": "Ollama (Local)",
"models": ["llama3", "mistral", "codellama", "llama2", "phi3", "qwen2"],
"requires_key": False,
"available": True,
"base_url": "http://localhost:11434"
},
"cohere": {
"name": "Cohere",
"models": ["command-r", "command-r-plus", "command"],
"requires_key": True,
"available": True
},
"huggingface": {
"name": "Hugging Face",
"models": ["microsoft/DialoGPT-large", "microsoft/DialoGPT-medium", "facebook/blenderbot-400M-distill"],
"requires_key": True,
"available": True
},
"together": {
"name": "Together AI",
"models": ["meta-llama/Llama-2-70b-chat-hf", "mistralai/Mixtral-8x7B-Instruct-v0.1"],
"requires_key": True,
"available": True
}
}
def get_available_providers(self):
"""Get list of available providers"""
return {k: v for k, v in self.providers.items() if v["available"]}
def call_ai_api(self, provider, model, messages, api_key=None, **kwargs):
"""Generic AI API caller"""
try:
if provider == "openai":
return self._call_openai(model, messages, api_key, **kwargs)
elif provider == "groq":
return self._call_groq(model, messages, api_key, **kwargs)
elif provider == "anthropic":
return self._call_anthropic(model, messages, api_key, **kwargs)
elif provider == "gemini":
return self._call_gemini(model, messages, api_key, **kwargs)
elif provider == "ollama":
return self._call_ollama(model, messages, **kwargs)
elif provider == "cohere":
return self._call_cohere(model, messages, api_key, **kwargs)
elif provider == "huggingface":
return self._call_huggingface(model, messages, api_key, **kwargs)
elif provider == "together":
return self._call_together(model, messages, api_key, **kwargs)
else:
return "Tuntematon AI-palveluntarjoaja"
except Exception as e:
return f"AI API -virhe ({provider}): {str(e)}"
def _call_openai(self, model, messages, api_key, **kwargs):
"""Call OpenAI API"""
if not OPENAI_AVAILABLE:
return "OpenAI-kirjasto ei ole saatavilla"
openai.api_key = api_key
response = openai.ChatCompletion.create(
model=model,
messages=messages,
max_tokens=kwargs.get('max_tokens', 500),
temperature=kwargs.get('temperature', 0.7)
)
return response.choices[0].message["content"]
def _call_groq(self, model, messages, api_key, **kwargs):
"""Call Groq API (OpenAI compatible)"""
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
data = {
"model": model,
"messages": messages,
"max_tokens": kwargs.get('max_tokens', 500),
"temperature": kwargs.get('temperature', 0.7)
}
response = requests.post(
"https://api.groq.com/openai/v1/chat/completions",
headers=headers,
json=data,
timeout=30
)
if response.status_code == 200:
return response.json()["choices"][0]["message"]["content"]
else:
return f"Groq API virhe: {response.status_code} - {response.text}"
def _call_anthropic(self, model, messages, api_key, **kwargs):
"""Call Anthropic Claude API"""
if not ANTHROPIC_AVAILABLE:
return "Anthropic-kirjasto ei ole saatavilla"
client = anthropic.Anthropic(api_key=api_key)
# Convert OpenAI format to Anthropic format
system_msg = "Olet ystävällinen ja hyödyllinen AI-agentti."
user_messages = []
for msg in messages:
if msg["role"] == "system":
system_msg = msg["content"]
elif msg["role"] in ["user", "assistant"]:
user_messages.append(msg)
response = client.messages.create(
model=model,
max_tokens=kwargs.get('max_tokens', 500),
system=system_msg,
messages=user_messages
)
return response.content[0].text
def _call_gemini(self, model, messages, api_key, **kwargs):
"""Call Google Gemini API"""
if not GEMINI_AVAILABLE:
return "Gemini-kirjasto ei ole saatavilla"
genai.configure(api_key=api_key)
model_instance = genai.GenerativeModel(model)
# Convert messages to Gemini format
prompt = "\n".join([f"{msg['role']}: {msg['content']}" for msg in messages])
response = model_instance.generate_content(prompt)
return response.text
def _call_ollama(self, model, messages, **kwargs):
"""Call Ollama local API"""
try:
# Convert to Ollama format
prompt = "\n".join([f"{msg['role']}: {msg['content']}" for msg in messages])
data = {
"model": model,
"prompt": prompt,
"stream": False
}
response = requests.post(
"http://localhost:11434/api/generate",
json=data,
timeout=60
)
if response.status_code == 200:
return response.json()["response"]
else:
return f"Ollama virhe: {response.status_code} - Varmista että Ollama on käynnissä"
except requests.exceptions.ConnectionError:
return "Ollama ei ole käynnissä. Käynnistä se komennolla: ollama serve"
def _call_cohere(self, model, messages, api_key, **kwargs):
"""Call Cohere API"""
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
# Get the last user message as prompt
prompt = messages[-1]["content"] if messages else "Hei"
data = {
"model": model,
"message": prompt,
"max_tokens": kwargs.get('max_tokens', 500),
"temperature": kwargs.get('temperature', 0.7)
}
response = requests.post(
"https://api.cohere.ai/v1/chat",
headers=headers,
json=data,
timeout=30
)
if response.status_code == 200:
return response.json()["text"]
else:
return f"Cohere API virhe: {response.status_code}"
def _call_huggingface(self, model, messages, api_key, **kwargs):
"""Call Hugging Face API"""
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
prompt = messages[-1]["content"] if messages else "Hei"
data = {
"inputs": prompt,
"parameters": {
"max_new_tokens": kwargs.get('max_tokens', 500),
"temperature": kwargs.get('temperature', 0.7)
}
}
response = requests.post(
f"https://api-inference.huggingface.co/models/{model}",
headers=headers,
json=data,
timeout=30
)
if response.status_code == 200:
result = response.json()
if isinstance(result, list) and result:
return result[0].get("generated_text", "Ei vastausta")
return str(result)
else:
return f"Hugging Face API virhe: {response.status_code}"
def _call_together(self, model, messages, api_key, **kwargs):
"""Call Together AI API"""
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
data = {
"model": model,
"messages": messages,
"max_tokens": kwargs.get('max_tokens', 500),
"temperature": kwargs.get('temperature', 0.7)
}
response = requests.post(
"https://api.together.xyz/v1/chat/completions",
headers=headers,
json=data,
timeout=30
)
if response.status_code == 200:
return response.json()["choices"][0]["message"]["content"]
else:
return f"Together AI virhe: {response.status_code}"
class AdvancedAIAgent:
"""
An advanced AI agent with multiple AI provider support and capabilities:
- Support for OpenAI, Groq, Anthropic, Gemini, Ollama, and more
- Text-to-speech and speech recognition
- Web search and API calls
- PDF document reading
- Language translation
- YouTube search
- Memory persistence
- Wake word activation
"""
def __init__(self, name="TekoBot"):
self.name = name
self.memory = []
self.conversation_history = []
self.settings = self.load_settings()
self.ai_manager = AIProviderManager()
# Initialize components
self.init_speech_components()
self.init_translator()
# State management
self.is_listening = False
self.listening_thread = None
def load_settings(self):
"""Load agent settings from file or create defaults"""
settings_file = "agent_settings.json"
default_settings = {
"ai_provider": "openai",
"ai_model": "gpt-3.5-turbo",
"api_keys": {},
"wake_word": "hei tekobot",
"language": "fi-FI",
"voice_rate": 200,
"voice_volume": 0.9,
"max_tokens": 500,
"temperature": 0.7
}
if os.path.exists(settings_file):
try:
with open(settings_file, 'r', encoding='utf-8') as f:
loaded = json.load(f)
# Merge with defaults to ensure all keys exist
default_settings.update(loaded)
return default_settings
except:
return default_settings
return default_settings
def save_settings(self):
"""Save current settings to file"""
with open('agent_settings.json', 'w', encoding='utf-8') as f:
json.dump(self.settings, f, ensure_ascii=False, indent=2)
def init_speech_components(self):
"""Initialize text-to-speech and speech recognition"""
self.speaker = None
self.recognizer = None
self.microphone = None
if SPEECH_AVAILABLE:
try:
self.speaker = pyttsx3.init()
self.speaker.setProperty('rate', self.settings.get('voice_rate', 200))
self.speaker.setProperty('volume', self.settings.get('voice_volume', 0.9))
except Exception as e:
print(f"⚠️ Puhesynteesi alustus epäonnistui: {e}")
self.speaker = None
if SPEECH_RECOGNITION_AVAILABLE:
try:
self.recognizer = sr.Recognizer()
self.microphone = sr.Microphone()
# Calibrate for ambient noise
with self.microphone as source:
self.recognizer.adjust_for_ambient_noise(source, duration=1)
except Exception as e:
print(f"⚠️ Puheentunnistus alustus epäonnistui: {e}")
self.recognizer = None
self.microphone = None
def init_translator(self):
"""Initialize translation service"""
self.translator = None
if TRANSLATE_AVAILABLE:
try:
if TRANSLATOR_TYPE == "deep_translator":
self.translator = "deep_translator"
elif TRANSLATOR_TYPE == "googletrans":
self.translator = Translator()
except Exception as e:
print(f"⚠️ Käännöspalvelun alustus epäonnistui: {e}")
self.translator = None
def chat_with_user(self, user_input):
"""Generate AI response using selected provider"""
provider = self.settings.get('ai_provider', 'openai')
model = self.settings.get('ai_model', 'gpt-3.5-turbo')
api_key = self.settings.get('api_keys', {}).get(provider)
if not api_key and self.ai_manager.providers[provider]["requires_key"]:
return f"API-avain puuttuu palvelulle: {provider}. Aseta se asetuksista."
try:
# Add user input to memory
self.memory.append({"role": "user", "content": user_input})
# Keep memory manageable (last 20 messages)
if len(self.memory) > 20:
self.memory = self.memory[-20:]
# Create system message
system_message = {
"role": "system",
"content": f"Olet {self.name}, ystävällinen ja hyödyllinen AI-agentti. Vastaa suomeksi."
}
# Prepare messages
messages = [system_message] + self.memory
# Generate response using selected provider
reply = self.ai_manager.call_ai_api(
provider=provider,
model=model,
messages=messages,
api_key=api_key,
max_tokens=self.settings.get('max_tokens', 500),
temperature=self.settings.get('temperature', 0.7)
)
self.memory.append({"role": "assistant", "content": reply})
# Save to conversation history
self.conversation_history.append({
"timestamp": datetime.now().isoformat(),
"user": user_input,
"agent": reply,
"provider": provider,
"model": model
})
return reply
except Exception as e:
return f"Virhe AI-vastauksessa: {e}"
def speak(self, text):
"""Convert text to speech"""
if self.speaker:
try:
self.speaker.say(text)
self.speaker.runAndWait()
except Exception as e:
print(f"Puhevirhe: {e}")
else:
print("Puhesynteesi ei ole käytettävissä")
def speech_to_text(self, timeout=5):
"""Convert speech to text"""
if not self.microphone:
return "Mikrofoni ei ole käytettävissä"
try:
with self.microphone as source:
print("🎤 Kuuntelen...")
audio = self.recognizer.listen(source, timeout=timeout)
text = self.recognizer.recognize_google(
audio,
language=self.settings.get('language', 'fi-FI')
)
print(f"Kuultu: {text}")
return text
except sr.WaitTimeoutError:
return "Aikakatkaisu - ei kuultu puhetta"
except sr.UnknownValueError:
return "En ymmärtänyt puhetta"
except sr.RequestError as e:
return f"Puheentunnistusvirhe: {e}"
def listen_for_wake_word(self, callback=None):
"""Listen continuously for wake word"""
if not self.microphone:
print("Mikrofoni ei ole käytettävissä")
return
self.is_listening = True
wake_word = self.settings.get('wake_word', 'hei tekobot').lower()
print(f"🎧 Kuuntelen herätyssanaa: '{wake_word}'")
while self.is_listening:
try:
with self.microphone as source:
audio = self.recognizer.listen(source, timeout=1, phrase_time_limit=5)
text = self.recognizer.recognize_google(
audio,
language=self.settings.get('language', 'fi-FI')
).lower()
if wake_word in text:
print("✅ Herätyssana havaittu!")
if callback:
callback(f"Herätyssana havaittu: {text}")
break
except sr.WaitTimeoutError:
continue
except sr.UnknownValueError:
continue
except sr.RequestError as e:
print(f"Virhe: {e}")
break
def stop_listening(self):
"""Stop listening for wake word"""
self.is_listening = False
if self.listening_thread and self.listening_thread.is_alive():
self.listening_thread.join()
def translate_text(self, text, target_lang="en"):
"""Translate text to target language"""
if not TRANSLATE_AVAILABLE or not self.translator:
# Fallback translation using a simple web API
return self.simple_translate(text, target_lang)
try:
if TRANSLATOR_TYPE == "deep_translator":
# Using deep-translator
translator = GoogleTranslator(source='auto', target=target_lang)
translated_text = translator.translate(text)
return f"Käännös ({target_lang}): {translated_text}"
elif TRANSLATOR_TYPE == "googletrans":
# Using googletrans
translated = self.translator.translate(text, dest=target_lang)
return f"Käännös ({target_lang}): {translated.text}"
except Exception as e:
return f"Käännösvirhe: {e}"
def simple_translate(self, text, target_lang="en"):
"""Simple translation fallback using web API"""
try:
# Using a simple translation API as fallback
url = f"https://api.mymemory.translated.net/get?q={requests.utils.quote(text)}&langpair=fi|{target_lang}"
response = requests.get(url, timeout=10)
data = response.json()
if data.get('responseStatus') == 200:
translated_text = data['responseData']['translatedText']
return f"Käännös ({target_lang}): {translated_text}"
else:
return "Käännöspalvelu ei ole käytettävissä"
except Exception as e:
return f"Käännösvirhe: {e}"
def web_search(self, query):
"""Open web search in browser"""
try:
search_url = f"https://www.google.com/search?q={query}"
webbrowser.open(search_url)
return f"Avattiin haku: {query}"
except Exception as e:
return f"Hakuvirhe: {e}"
def youtube_search(self, query, limit=5):
"""Search YouTube videos"""
if not YOUTUBE_AVAILABLE:
# Fallback: open YouTube search in browser
try:
search_url = f"https://www.youtube.com/results?search_query={requests.utils.quote(query)}"
webbrowser.open(search_url)
return f"YouTube-haku avattu selaimessa: {query}"
except Exception as e:
return f"YouTube-hakuvirhe: {e}"
try:
search = VideosSearch(query, limit=limit)
results = search.result()["result"]
if not results:
return "Ei tuloksia löytynyt"
video_list = []
for i, video in enumerate(results, 1):
video_list.append(f"{i}. {video['title']}\n {video['link']}\n")
return f"YouTube-tulokset haulle '{query}':\n\n" + "\n".join(video_list)
except Exception as e:
return f"YouTube-hakuvirhe: {e}"
def call_api(self, url):
"""Make API call to external service"""
try:
headers = {'User-Agent': 'Mozilla/5.0 (compatible; AIAgent/1.0)'}
response = requests.get(url, headers=headers, timeout=10)
if response.status_code == 200:
if 'application/json' in response.headers.get('Content-Type', ''):
return json.dumps(response.json(), indent=2, ensure_ascii=False)
else:
return response.text[:1000] + "..." if len(response.text) > 1000 else response.text
else:
return f"HTTP {response.status_code}: {response.reason}"
except Exception as e:
return f"API-virhe: {e}"
def read_pdf(self, file_path):
"""Extract text from PDF file"""
try:
text_content = []
with open(file_path, 'rb') as file:
reader = PyPDF2.PdfReader(file)
for page_num, page in enumerate(reader.pages, 1):
text = page.extract_text()
if text.strip():
text_content.append(f"--- Sivu {page_num} ---\n{text}\n")
if text_content:
return "".join(text_content)
else:
return "PDF-tiedosto on tyhjä tai teksti ei ole luettavissa"
except Exception as e:
return f"PDF-lukuvirhe: {e}"
def save_conversation(self):
"""Save conversation history to file"""
if self.conversation_history:
filename = f"conversation_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
with open(filename, 'w', encoding='utf-8') as f:
json.dump(self.conversation_history, f, ensure_ascii=False, indent=2)
return f"Keskustelu tallennettu: {filename}"
return "Ei keskustelua tallennettavaksi"
class AgentGUI:
"""Graphical user interface for the AI Agent"""
def __init__(self):
self.agent = AdvancedAIAgent()
self.setup_gui()
def setup_gui(self):
"""Create the main GUI window"""
self.window = tk.Tk()
self.window.title(f"{self.agent.name} - Multi-AI Agentti")
self.window.geometry("900x800")
self.window.configure(bg='#f0f0f0')
# Main frame
main_frame = tk.Frame(self.window, bg='#f0f0f0')
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# Title with AI provider info
self.title_var = tk.StringVar()
self.update_title()
title_label = tk.Label(
main_frame,
textvariable=self.title_var,
font=("Arial", 16, "bold"),
bg='#f0f0f0'
)
title_label.pack(pady=(0, 10))
# AI Provider selection frame
ai_frame = tk.LabelFrame(main_frame, text="AI-palveluntarjoaja", bg='#f0f0f0')
ai_frame.pack(fill=tk.X, pady=(0, 10))
provider_frame = tk.Frame(ai_frame, bg='#f0f0f0')
provider_frame.pack(fill=tk.X, padx=5, pady=5)
tk.Label(provider_frame, text="Palvelu:", bg='#f0f0f0').pack(side=tk.LEFT)
self.provider_var = tk.StringVar()
self.provider_combo = ttk.Combobox(
provider_frame,
textvariable=self.provider_var,
state="readonly",
width=20
)
self.provider_combo.pack(side=tk.LEFT, padx=(5, 10))
self.provider_combo.bind('<<ComboboxSelected>>', self.on_provider_change)
tk.Label(provider_frame, text="Malli:", bg='#f0f0f0').pack(side=tk.LEFT)
self.model_var = tk.StringVar()
self.model_combo = ttk.Combobox(
provider_frame,
textvariable=self.model_var,
state="readonly",
width=25
)
self.model_combo.pack(side=tk.LEFT, padx=(5, 0))
self.model_combo.bind('<<ComboboxSelected>>', self.on_model_change)
# Populate provider combo
self.update_provider_combos()
# Input frame
input_frame = tk.Frame(main_frame, bg='#f0f0f0')
input_frame.pack(fill=tk.X, pady=(0, 10))
tk.Label(input_frame, text="Syöte:", bg='#f0f0f0').pack(anchor=tk.W)
self.entry = tk.Entry(input_frame, font=("Arial", 11), width=70)
self.entry.pack(fill=tk.X, pady=(5, 0))
self.entry.bind('<Return>', lambda e: self.handle_chat())
# Button frame 1 - Main functions
btn_frame1 = tk.Frame(main_frame, bg='#f0f0f0')
btn_frame1.pack(fill=tk.X, pady=(0, 5))
tk.Button(btn_frame1, text="💬 Keskustele", command=self.handle_chat, bg='#4CAF50', fg='white').pack(side=tk.LEFT, padx=(0, 5))
# Add speech buttons only if speech components are available
if SPEECH_AVAILABLE and self.agent.speaker:
tk.Button(btn_frame1, text="🔊 Puhu", command=self.handle_speak, bg='#2196F3', fg='white').pack(side=tk.LEFT, padx=(0, 5))
if SPEECH_RECOGNITION_AVAILABLE and self.agent.microphone:
tk.Button(btn_frame1, text="🎤 Kuuntele", command=self.handle_speech_to_text, bg='#FF9800', fg='white').pack(side=tk.LEFT, padx=(0, 5))
# Button frame 2 - Utility functions
btn_frame2 = tk.Frame(main_frame, bg='#f0f0f0')
btn_frame2.pack(fill=tk.X, pady=(0, 5))
tk.Button(btn_frame2, text="🌐 Google-haku", command=self.handle_web_search, bg='#9C27B0', fg='white').pack(side=tk.LEFT, padx=(0, 5))
tk.Button(btn_frame2, text="📄 Lue PDF", command=self.handle_pdf, bg='#607D8B', fg='white').pack(side=tk.LEFT, padx=(0, 5))
tk.Button(btn_frame2, text="🎬 YouTube", command=self.handle_youtube, bg='#F44336', fg='white').pack(side=tk.LEFT, padx=(0, 5))
# Button frame 3 - Advanced functions
btn_frame3 = tk.Frame(main_frame, bg='#f0f0f0')
btn_frame3.pack(fill=tk.X, pady=(0, 5))
tk.Button(btn_frame3, text="🌍 Käännä EN", command=self.handle_translate, bg='#795548', fg='white').pack(side=tk.LEFT, padx=(0, 5))
tk.Button(btn_frame3, text="🔗 API-kutsu", command=self.handle_api, bg='#3F51B5', fg='white').pack(side=tk.LEFT, padx=(0, 5))
# Add wake word button only if speech recognition is available
if SPEECH_RECOGNITION_AVAILABLE:
tk.Button(btn_frame3, text="🎧 Herätyssana", command=self.handle_wake_word, bg='#E91E63', fg='white').pack(side=tk.LEFT, padx=(0, 5))
# Control frame
ctrl_frame = tk.Frame(main_frame, bg='#f0f0f0')
ctrl_frame.pack(fill=tk.X, pady=(0, 10))
tk.Button(ctrl_frame, text="💾 Tallenna keskustelu", command=self.handle_save, bg='#009688', fg='white').pack(side=tk.LEFT, padx=(0, 5))
tk.Button(ctrl_frame, text="🗑️ Tyhjennä", command=self.handle_clear, bg='#757575', fg='white').pack(side=tk.LEFT, padx=(0, 5))
tk.Button(ctrl_frame, text="⚙️ Asetukset", command=self.show_settings, bg='#FFC107').pack(side=tk.LEFT, padx=(0, 5))
tk.Button(ctrl_frame, text="🔄 Päivitä AI-lista", command=self.update_provider_combos, bg='#00BCD4', fg='white').pack(side=tk.LEFT, padx=(0, 5))
# Output area
tk.Label(main_frame, text="Vastaus:", bg='#f0f0f0').pack(anchor=tk.W)
self.output = scrolledtext.ScrolledText(
main_frame,
height=20,
font=("Arial", 10),
wrap=tk.WORD,
bg='white'
)
self.output.pack(fill=tk.BOTH, expand=True, pady=(5, 0))
# Status bar
self.status_var = tk.StringVar()
self.status_var.set("Valmis")
status_bar = tk.Label(
self.window,
textvariable=self.status_var,
relief=tk.SUNKEN,
anchor=tk.W,
bg='#e0e0e0'
)
status_bar.pack(side=tk.BOTTOM, fill=tk.X)
def update_title(self):
"""Update title with current AI provider info"""
provider = self.agent.settings.get('ai_provider', 'openai')
model = self.agent.settings.get('ai_model', 'gpt-3.5-turbo')
provider_name = self.agent.ai_manager.providers.get(provider, {}).get('name', provider)
self.title_var.set(f"🤖 {self.agent.name} - {provider_name} ({model})")
def update_provider_combos(self):
"""Update provider and model combo boxes"""
available_providers = self.agent.ai_manager.get_available_providers()
# Update provider combo
provider_names = [f"{k} - {v['name']}" for k, v in available_providers.items()]
self.provider_combo['values'] = provider_names
# Set current provider
current_provider = self.agent.settings.get('ai_provider', 'openai')
for i, (k, v) in enumerate(available_providers.items()):
if k == current_provider:
self.provider_combo.current(i)
break
# Update model combo
self.update_model_combo()
def update_model_combo(self):
"""Update model combo based on selected provider"""
current_provider = self.get_current_provider_key()
if current_provider and current_provider in self.agent.ai_manager.providers:
models = self.agent.ai_manager.providers[current_provider]['models']
self.model_combo['values'] = models
# Set current model
current_model = self.agent.settings.get('ai_model', models[0] if models else '')
if current_model in models:
self.model_combo.set(current_model)
elif models:
self.model_combo.current(0)
def get_current_provider_key(self):
"""Get current provider key from combo selection"""
selection = self.provider_var.get()
if ' - ' in selection:
return selection.split(' - ')[0]
return selection
def on_provider_change(self, event=None):
"""Handle provider selection change"""
provider_key = self.get_current_provider_key()
if provider_key:
self.agent.settings['ai_provider'] = provider_key
self.update_model_combo()
self.update_title()
self.agent.save_settings()
def on_model_change(self, event=None):
"""Handle model selection change"""
model = self.model_var.get()
if model:
self.agent.settings['ai_model'] = model
self.update_title()
self.agent.save_settings()
def update_status(self, message):
"""Update status bar"""
self.status_var.set(f"{datetime.now().strftime('%H:%M:%S')} - {message}")
self.window.update()
def display_result(self, result):
"""Display result in output area"""
self.output.delete(1.0, tk.END)
self.output.insert(tk.END, result)
self.output.see(tk.END)
def handle_chat(self):
"""Handle chat input"""
user_input = self.entry.get().strip()
if not user_input:
return
provider = self.agent.settings.get('ai_provider', 'openai')
self.update_status(f"Luodaan vastausta käyttäen {provider}...")
# Run AI call in separate thread to prevent GUI freezing
def ai_call():
result = self.agent.chat_with_user(user_input)
self.window.after(0, lambda: [
self.display_result(f"Sinä: {user_input}\n\n{self.agent.name}: {result}"),
self.update_status("Valmis")
])
threading.Thread(target=ai_call, daemon=True).start()
self.entry.delete(0, tk.END)
def handle_speak(self):
"""Handle text-to-speech"""
if not SPEECH_AVAILABLE or not self.agent.speaker:
messagebox.showwarning("Varoitus", "Puhesynteesi ei ole käytettävissä")
return
text = self.output.get(1.0, tk.END).strip()
if text:
self.update_status("Puhun...")
threading.Thread(target=lambda: [
self.agent.speak(text),
self.update_status("Valmis")
], daemon=True).start()
def handle_speech_to_text(self):
"""Handle speech recognition"""
if not SPEECH_RECOGNITION_AVAILABLE or not self.agent.microphone:
messagebox.showwarning("Varoitus", "Puheentunnistus ei ole käytettävissä")
return
self.update_status("Kuuntelen puhetta...")
def speech_recognition():
result = self.agent.speech_to_text()
self.window.after(0, lambda: [
self.entry.delete(0, tk.END),
self.entry.insert(0, result),
self.update_status("Valmis")
])
threading.Thread(target=speech_recognition, daemon=True).start()
def handle_web_search(self):
"""Handle web search"""
query = self.entry.get().strip()
if query:
result = self.agent.web_search(query)
self.display_result(result)
self.update_status("Haku avattu selaimessa")
def handle_pdf(self):
"""Handle PDF reading"""
file_path = filedialog.askopenfilename(
title="Valitse PDF-tiedosto",
filetypes=[("PDF files", "*.pdf")]
)
if file_path:
self.update_status("Luetaan PDF-tiedostoa...")
def read_pdf():
result = self.agent.read_pdf(file_path)
self.window.after(0, lambda: [
self.display_result(result),
self.update_status("PDF luettu")
])
threading.Thread(target=read_pdf, daemon=True).start()
def handle_youtube(self):
"""Handle YouTube search"""
query = self.entry.get().strip()
if query:
self.update_status("Haetaan YouTube-videoita...")
def youtube_search():
result = self.agent.youtube_search(query)
self.window.after(0, lambda: [
self.display_result(result),
self.update_status("YouTube-haku valmis")
])
threading.Thread(target=youtube_search, daemon=True).start()
def handle_translate(self):
"""Handle translation"""
text = self.entry.get().strip()
if text:
self.update_status("Käännetään...")
def translate():
result = self.agent.translate_text(text)
self.window.after(0, lambda: [
self.display_result(result),
self.update_status("Käännös valmis")
])
threading.Thread(target=translate, daemon=True).start()
def handle_api(self):
"""Handle API call"""
url = self.entry.get().strip()
if url:
self.update_status("Soitetaan API...")
def api_call():
result = self.agent.call_api(url)
self.window.after(0, lambda: [
self.display_result(result),
self.update_status("API-kutsu valmis")
])
threading.Thread(target=api_call, daemon=True).start()
def handle_wake_word(self):
"""Handle wake word listening"""
if not SPEECH_RECOGNITION_AVAILABLE or not self.agent.microphone:
messagebox.showwarning("Varoitus", "Puheentunnistus ei ole käytettävissä")
return
if not self.agent.is_listening:
self.update_status("Kuunnellaan herätyssanaa...")
self.agent.listening_thread = threading.Thread(
target=self.agent.listen_for_wake_word,
args=(self.wake_word_callback,),
daemon=True
)
self.agent.listening_thread.start()
else:
self.agent.stop_listening()
self.update_status("Kuuntelu pysäytetty")
def wake_word_callback(self, message):
"""Callback for wake word detection"""
self.window.after(0, lambda: [
self.display_result(message),
self.update_status("Herätyssana havaittu!")
])
# Automatically listen for command after wake word
def listen_command():
command = self.agent.speech_to_text()
if command and "ei kuultu" not in command.lower():
self.window.after(0, lambda: [
self.entry.delete(0, tk.END),
self.entry.insert(0, command)
])
# Auto-execute if it's a valid command
if len(command.split()) > 2: # Only if substantial input
self.window.after(100, self.handle_chat)
threading.Thread(target=listen_command, daemon=True).start()
def handle_save(self):
"""Handle conversation saving"""
result = self.agent.save_conversation()
messagebox.showinfo("Tallennus", result)
def handle_clear(self):
"""Clear output and entry"""
self.output.delete(1.0, tk.END)
self.entry.delete(0, tk.END)
self.update_status("Tyhjennetty")
def show_settings(self):
"""Show settings dialog"""
settings_window = tk.Toplevel(self.window)
settings_window.title("Asetukset")
settings_window.geometry("600x700")
settings_window.transient(self.window)
settings_window.configure(bg='#f0f0f0')
# Create notebook for tabbed interface
notebook = ttk.Notebook(settings_window)
notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# API Keys tab
api_frame = tk.Frame(notebook, bg='#f0f0f0')
notebook.add(api_frame, text="API-avaimet")
api_keys = self.agent.settings.get('api_keys', {})
api_entries = {}
# Create API key entries for each provider
providers = self.agent.ai_manager.providers
row = 0
for provider_key, provider_info in providers.items():
if provider_info['requires_key']:
tk.Label(api_frame, text=f"{provider_info['name']} API-avain:", bg='#f0f0f0').grid(
row=row, column=0, sticky=tk.W, padx=10, pady=5
)
entry = tk.Entry(api_frame, width=60, show="*")
entry.grid(row=row, column=1, padx=10, pady=5)
if provider_key in api_keys:
entry.insert(0, api_keys[provider_key])
api_entries[provider_key] = entry
row += 1
# Links for getting API keys
links_frame = tk.LabelFrame(api_frame, text="API-avainten hankkiminen:", bg='#f0f0f0')
links_frame.grid(row=row, column=0, columnspan=2, sticky=tk.W+tk.E, padx=10, pady=20)
links = [
("OpenAI", "https://platform.openai.com/api-keys"),
("Groq", "https://console.groq.com/keys"),
("Anthropic", "https://console.anthropic.com/"),
("Google AI", "https://makersuite.google.com/app/apikey"),
("Cohere", "https://dashboard.cohere.ai/api-keys"),
("Hugging Face", "https://huggingface.co/settings/tokens"),
("Together AI", "https://api.together.xyz/settings/api-keys")
]
for i, (name, url) in enumerate(links):
link_label = tk.Label(
links_frame,
text=f"• {name}: {url}",
fg="blue",
cursor="hand2",
bg='#f0f0f0'
)
link_label.grid(row=i, column=0, sticky=tk.W, padx=5, pady=2)
link_label.bind("<Button-1>", lambda e, url=url: webbrowser.open(url))
# General settings tab
general_frame = tk.Frame(notebook, bg='#f0f0f0')
notebook.add(general_frame, text="Yleiset asetukset")
# Wake word
tk.Label(general_frame, text="Herätyssana:", bg='#f0f0f0').grid(row=0, column=0, sticky=tk.W, padx=10, pady=5)
wake_entry = tk.Entry(general_frame, width=30)
wake_entry.grid(row=0, column=1, padx=10, pady=5)
wake_entry.insert(0, self.agent.settings.get('wake_word', 'hei tekobot'))
# Language
tk.Label(general_frame, text="Kieli (puheentunnistus):", bg='#f0f0f0').grid(row=1, column=0, sticky=tk.W, padx=10, pady=5)
lang_entry = tk.Entry(general_frame, width=30)
lang_entry.grid(row=1, column=1, padx=10, pady=5)
lang_entry.insert(0, self.agent.settings.get('language', 'fi-FI'))
# AI parameters tab
ai_params_frame = tk.Frame(notebook, bg='#f0f0f0')
notebook.add(ai_params_frame, text="AI-parametrit")
# Max tokens
tk.Label(ai_params_frame, text="Maksimi tokeneja:", bg='#f0f0f0').grid(row=0, column=0, sticky=tk.W, padx=10, pady=5)
tokens_var = tk.StringVar(value=str(self.agent.settings.get('max_tokens', 500)))
tokens_spin = tk.Spinbox(ai_params_frame, from_=100, to=4000, textvariable=tokens_var, width=10)
tokens_spin.grid(row=0, column=1, padx=10, pady=5)
# Temperature
tk.Label(ai_params_frame, text="Lämpötila (0-1):", bg='#f0f0f0').grid(row=1, column=0, sticky=tk.W, padx=10, pady=5)
temp_var = tk.StringVar(value=str(self.agent.settings.get('temperature', 0.7)))
temp_entry = tk.Entry(ai_params_frame, textvariable=temp_var, width=10)
temp_entry.grid(row=1, column=1, padx=10, pady=5)
# Voice settings tab (if speech available)
if SPEECH_AVAILABLE:
voice_frame = tk.Frame(notebook, bg='#f0f0f0')
notebook.add(voice_frame, text="Ääni-asetukset")
# Voice rate
tk.Label(voice_frame, text="Puhenopeus:", bg='#f0f0f0').grid(row=0, column=0, sticky=tk.W, padx=10, pady=5)
rate_var = tk.StringVar(value=str(self.agent.settings.get('voice_rate', 200)))
rate_spin = tk.Spinbox(voice_frame, from_=50, to=400, textvariable=rate_var, width=10)
rate_spin.grid(row=0, column=1, padx=10, pady=5)
# Voice volume
tk.Label(voice_frame, text="Äänenvoimakkuus (0-1):", bg='#f0f0f0').grid(row=1, column=0, sticky=tk.W, padx=10, pady=5)
volume_var = tk.StringVar(value=str(self.agent.settings.get('voice_volume', 0.9)))
volume_entry = tk.Entry(voice_frame, textvariable=volume_var, width=10)
volume_entry.grid(row=1, column=1, padx=10, pady=5)
def save_settings():
# Save API keys
new_api_keys = {}
for provider_key, entry in api_entries.items():
key = entry.get().strip()
if key:
new_api_keys[provider_key] = key
self.agent.settings['api_keys'] = new_api_keys
self.agent.settings['wake_word'] = wake_entry.get()
self.agent.settings['language'] = lang_entry.get()
self.agent.settings['max_tokens'] = int(tokens_var.get())
self.agent.settings['temperature'] = float(temp_var.get())
if SPEECH_AVAILABLE:
self.agent.settings['voice_rate'] = int(rate_var.get())
self.agent.settings['voice_volume'] = float(volume_var.get())
# Update speech engine settings
if self.agent.speaker:
self.agent.speaker.setProperty('rate', int(rate_var.get()))
self.agent.speaker.setProperty('volume', float(volume_var.get()))
self.agent.save_settings()
messagebox.showinfo("Asetukset", "Asetukset tallennettu!")
settings_window.destroy()
# Save button
save_btn = tk.Button(
settings_window,
text="💾 Tallenna asetukset",
command=save_settings,
bg='#4CAF50',
fg='white',
font=("Arial", 12, "bold")
)
save_btn.pack(pady=10)
def run(self):
"""Start the GUI"""
try:
self.window.mainloop()
finally:
self.agent.stop_listening()
# Main execution
if __name__ == "__main__":
print("🚀 Käynnistetään Multi-AI Agentti...")
print("Tuetut AI-palvelut:")
# Show available providers
manager = AIProviderManager()
for key, info in manager.get_available_providers().items():
status = "✅" if info["available"] else "❌"
key_req = "🔑" if info["requires_key"] else "🆓"
print(f" {status} {key_req} {info['name']}")
print("\n💡 Vinkki: Aseta API-avaimet ⚙️-napista saadaksesi parhaan kokemuksen!")
# Create and run the agent GUI
app = AgentGUI()
app.run()
Tallennetaan koodi taas esimerkiksi C:\temp\AI-agentti.py tekstitiedostoon.
Windows start valikossa, cmd, ja siirrytään kansioon; cd C:\temp komennolla ja käynnistetään ohjelma komennolla:
python AI-agentti.py


Smart AI-Agent 2.0 – vielä hiukan parannettu versio
Muokataan koodia vielä niin, että voit antaa ajastettuja (kalenterinäkymä) tehtäviä suoritettavaksi taustalle:
Luodaan ja näytetään lista näistä tehtävistä, ja niiden edistyminen. Lisätään koodiin myös .pdf generointi, valmistuneista tehtävistä. Lisätään napit, joista voi näyttää ja ladata valmistuneet tehtävät katseltavaksi.
Python koodi 9:
import requests
import PyPDF2
import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext, ttk
import webbrowser
import threading
import json
import os
from datetime import datetime, timedelta
import time
import uuid
import schedule
from collections import defaultdict
import subprocess
import sys
# PDF generation import
try:
from reportlab.lib.pagesizes import letter, A4
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import inch
from reportlab.lib import colors
PDF_GENERATION_AVAILABLE = True
except ImportError:
PDF_GENERATION_AVAILABLE = False
print("⚠️ reportlab ei saatavilla - asenna: pip install reportlab")
# Calendar widget import
try:
from tkcalendar import Calendar, DateEntry
CALENDAR_AVAILABLE = True
except ImportError:
CALENDAR_AVAILABLE = False
print("⚠️ tkcalendar ei saatavilla - asenna: pip install tkcalendar")
# Schedule import
try:
import schedule
SCHEDULE_AVAILABLE = True
except ImportError:
SCHEDULE_AVAILABLE = False
print("⚠️ schedule ei saatavilla - asenna: pip install schedule")
# Conditional imports with fallbacks
try:
import pyttsx3
SPEECH_AVAILABLE = True
except ImportError:
SPEECH_AVAILABLE = False
print("⚠️ pyttsx3 ei saatavilla - puhesynteesi poistettu käytöstä")
try:
from deep_translator import GoogleTranslator
TRANSLATE_AVAILABLE = True
TRANSLATOR_TYPE = "deep_translator"
except ImportError:
try:
from googletrans import Translator
TRANSLATE_AVAILABLE = True
TRANSLATOR_TYPE = "googletrans"
except ImportError:
TRANSLATE_AVAILABLE = False
TRANSLATOR_TYPE = None
print("⚠️ Käännöspalvelu ei saatavilla - asenna: pip install deep-translator")
try:
from youtubesearchpython import VideosSearch
YOUTUBE_AVAILABLE = True
except ImportError:
YOUTUBE_AVAILABLE = False
print("⚠️ youtube-search-python ei saatavilla - YouTube-haku poistettu käytöstä")
try:
import speech_recognition as sr
SPEECH_RECOGNITION_AVAILABLE = True
except ImportError:
SPEECH_RECOGNITION_AVAILABLE = False
print("⚠️ SpeechRecognition ei saatavilla - puheentunnistus poistettu käytöstä")
# AI Provider imports
try:
import openai
OPENAI_AVAILABLE = True
except ImportError:
OPENAI_AVAILABLE = False
print("⚠️ OpenAI ei saatavilla - asenna: pip install openai")
try:
import anthropic
ANTHROPIC_AVAILABLE = True
except ImportError:
ANTHROPIC_AVAILABLE = False
try:
import google.generativeai as genai
GEMINI_AVAILABLE = True
except ImportError:
GEMINI_AVAILABLE = False
class TaskStatus:
"""Task status constants"""
PENDING = "pending"
RUNNING = "running"
COMPLETED = "completed"
FAILED = "failed"
CANCELLED = "cancelled"
class ScheduledTask:
"""Represents a scheduled task"""
def __init__(self, task_id, name, description, scheduled_time, task_type,
parameters=None, recurring=False, interval=None):
self.id = task_id
self.name = name
self.description = description
self.scheduled_time = scheduled_time
self.task_type = task_type
self.parameters = parameters or {}
self.recurring = recurring
self.interval = interval
self.status = TaskStatus.PENDING
self.created_at = datetime.now()
self.started_at = None
self.completed_at = None
self.result = None
self.error = None
self.progress = 0
self.logs = []
def add_log(self, message):
"""Add log entry"""
self.logs.append({
"timestamp": datetime.now().isoformat(),
"message": message
})
def to_dict(self):
"""Convert task to dictionary"""
return {
"id": self.id,
"name": self.name,
"description": self.description,
"scheduled_time": self.scheduled_time.isoformat() if self.scheduled_time else None,
"task_type": self.task_type,
"parameters": self.parameters,
"recurring": self.recurring,
"interval": self.interval,
"status": self.status,
"created_at": self.created_at.isoformat(),
"started_at": self.started_at.isoformat() if self.started_at else None,
"completed_at": self.completed_at.isoformat() if self.completed_at else None,
"result": self.result,
"error": self.error,
"progress": self.progress,
"logs": self.logs
}
@classmethod
def from_dict(cls, data):
"""Create task from dictionary"""
task = cls(
data["id"],
data["name"],
data["description"],
datetime.fromisoformat(data["scheduled_time"]) if data["scheduled_time"] else None,
data["task_type"],
data.get("parameters", {}),
data.get("recurring", False),
data.get("interval")
)
task.status = data.get("status", TaskStatus.PENDING)
task.created_at = datetime.fromisoformat(data["created_at"])
task.started_at = datetime.fromisoformat(data["started_at"]) if data.get("started_at") else None
task.completed_at = datetime.fromisoformat(data["completed_at"]) if data.get("completed_at") else None
task.result = data.get("result")
task.error = data.get("error")
task.progress = data.get("progress", 0)
task.logs = data.get("logs", [])
return task
class TaskScheduler:
"""Manages scheduled tasks"""
def __init__(self, agent):
self.agent = agent
self.tasks = {}
self.running_tasks = {}
self.scheduler_thread = None
self.is_running = False
self.load_tasks()
self.start_scheduler()
def start_scheduler(self):
"""Start the task scheduler"""
if not self.is_running:
self.is_running = True
self.scheduler_thread = threading.Thread(target=self._scheduler_loop, daemon=True)
self.scheduler_thread.start()
def stop_scheduler(self):
"""Stop the task scheduler"""
self.is_running = False
if self.scheduler_thread:
self.scheduler_thread.join(timeout=1)
def _scheduler_loop(self):
"""Main scheduler loop"""
while self.is_running:
try:
# Check for tasks to run
current_time = datetime.now()
for task_id, task in list(self.tasks.items()):
if (task.status == TaskStatus.PENDING and
task.scheduled_time and
current_time >= task.scheduled_time):
self._execute_task(task)
# Clean up completed threads
for task_id in list(self.running_tasks.keys()):
thread = self.running_tasks[task_id]
if not thread.is_alive():
del self.running_tasks[task_id]
time.sleep(1) # Check every second
except Exception as e:
print(f"Scheduler error: {e}")
time.sleep(5)
def _execute_task(self, task):
"""Execute a scheduled task"""
if task.id in self.running_tasks:
return # Task already running
task.status = TaskStatus.RUNNING
task.started_at = datetime.now()
task.add_log("Task execution started")
def task_runner():
try:
task.progress = 10
result = self._run_task_by_type(task)
task.status = TaskStatus.COMPLETED
task.completed_at = datetime.now()
task.result = result
task.progress = 100
task.add_log("Task completed successfully")
# Schedule next occurrence if recurring
if task.recurring and task.interval:
self._schedule_next_occurrence(task)
except Exception as e:
task.status = TaskStatus.FAILED
task.completed_at = datetime.now()
task.error = str(e)
task.progress = 0
task.add_log(f"Task failed: {str(e)}")
finally:
self.save_tasks()
thread = threading.Thread(target=task_runner, daemon=True)
self.running_tasks[task.id] = thread
thread.start()
def _run_task_by_type(self, task):
"""Run task based on its type"""
task_type = task.task_type
params = task.parameters
task.progress = 25
if task_type == "ai_chat":
task.add_log("Generating AI response")
result = self.agent.chat_with_user(params.get("prompt", ""))
elif task_type == "web_search":
task.add_log("Performing web search")
task.progress = 50
result = self.agent.web_search(params.get("query", ""))
elif task_type == "youtube_search":
task.add_log("Searching YouTube")
task.progress = 50
result = self.agent.youtube_search(params.get("query", ""))
elif task_type == "translate":
task.add_log("Translating text")
task.progress = 50
result = self.agent.translate_text(
params.get("text", ""),
params.get("target_lang", "en")
)
elif task_type == "api_call":
task.add_log("Making API call")
task.progress = 50
result = self.agent.call_api(params.get("url", ""))
elif task_type == "pdf_read":
task.add_log("Reading PDF file")
task.progress = 50
result = self.agent.read_pdf(params.get("file_path", ""))
elif task_type == "custom_command":
task.add_log("Executing custom command")
task.progress = 50
# Execute custom Python command
command = params.get("command", "")
try:
result = eval(command) if command else "No command specified"
except Exception as e:
result = f"Command execution failed: {str(e)}"
else:
result = f"Unknown task type: {task_type}"
task.progress = 90
return result
def _schedule_next_occurrence(self, task):
"""Schedule next occurrence for recurring task"""
if not task.interval:
return
# Create new task instance for next occurrence
next_time = None
if task.interval == "daily":
next_time = task.scheduled_time + timedelta(days=1)
elif task.interval == "weekly":
next_time = task.scheduled_time + timedelta(weeks=1)
elif task.interval == "monthly":
next_time = task.scheduled_time + timedelta(days=30)
elif task.interval == "hourly":
next_time = task.scheduled_time + timedelta(hours=1)
if next_time:
new_task = ScheduledTask(
str(uuid.uuid4()),
task.name,
task.description,
next_time,
task.task_type,
task.parameters,
task.recurring,
task.interval
)
self.add_task(new_task)
def add_task(self, task):
"""Add a new task"""
self.tasks[task.id] = task
self.save_tasks()
return task.id
def cancel_task(self, task_id):
"""Cancel a task"""
if task_id in self.tasks:
task = self.tasks[task_id]
if task.status == TaskStatus.PENDING:
task.status = TaskStatus.CANCELLED
task.add_log("Task cancelled by user")
self.save_tasks()
return True
return False
def get_task(self, task_id):
"""Get task by ID"""
return self.tasks.get(task_id)
def get_tasks_by_status(self, status):
"""Get tasks by status"""
return [task for task in self.tasks.values() if task.status == status]
def get_all_tasks(self):
"""Get all tasks"""
return list(self.tasks.values())
def save_tasks(self):
"""Save tasks to file"""
tasks_data = {task_id: task.to_dict() for task_id, task in self.tasks.items()}
with open('scheduled_tasks.json', 'w', encoding='utf-8') as f:
json.dump(tasks_data, f, ensure_ascii=False, indent=2)
def load_tasks(self):
"""Load tasks from file"""
try:
if os.path.exists('scheduled_tasks.json'):
with open('scheduled_tasks.json', 'r', encoding='utf-8') as f:
tasks_data = json.load(f)
self.tasks = {
task_id: ScheduledTask.from_dict(task_data)
for task_id, task_data in tasks_data.items()
}
except Exception as e:
print(f"Error loading tasks: {e}")
self.tasks = {}
class PDFGenerator:
"""Generate PDF reports for completed tasks"""
@staticmethod
def generate_task_report(tasks, filename=None):
"""Generate PDF report for tasks"""
if not PDF_GENERATION_AVAILABLE:
return "PDF generation not available - install reportlab"
if not filename:
filename = f"task_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.pdf"
try:
# Create document
doc = SimpleDocTemplate(filename, pagesize=A4)
styles = getSampleStyleSheet()
story = []
# Title
title_style = ParagraphStyle(
'CustomTitle',
parent=styles['Heading1'],
fontSize=24,
spaceAfter=30,
textColor=colors.darkblue,
alignment=1 # Center
)
story.append(Paragraph("Tehtäväraportti", title_style))
story.append(Spacer(1, 20))
# Summary
summary_style = ParagraphStyle(
'Summary',
parent=styles['Normal'],
fontSize=12,
spaceAfter=15,
textColor=colors.black
)
total_tasks = len(tasks)
completed_tasks = len([t for t in tasks if t.status == TaskStatus.COMPLETED])
failed_tasks = len([t for t in tasks if t.status == TaskStatus.FAILED])
summary_text = f"""
<b>Yhteenveto:</b><br/>
• Tehtäviä yhteensä: {total_tasks}<br/>
• Valmistuneita: {completed_tasks}<br/>
• Epäonnistuneita: {failed_tasks}<br/>
• Raportin luontiaika: {datetime.now().strftime('%d.%m.%Y %H:%M:%S')}<br/>
"""
story.append(Paragraph(summary_text, summary_style))
story.append(Spacer(1, 20))
# Task details
for task in tasks:
# Task header
task_header_style = ParagraphStyle(
'TaskHeader',
parent=styles['Heading2'],
fontSize=16,
spaceAfter=10,
textColor=colors.darkgreen if task.status == TaskStatus.COMPLETED else colors.red
)
status_emoji = {
TaskStatus.COMPLETED: "✅",
TaskStatus.FAILED: "❌",
TaskStatus.RUNNING: "🔄",
TaskStatus.PENDING: "⏳",
TaskStatus.CANCELLED: "🚫"
}
story.append(Paragraph(
f"{status_emoji.get(task.status, '❓')} {task.name}",
task_header_style
))
# Task details
details_style = ParagraphStyle(
'TaskDetails',
parent=styles['Normal'],
fontSize=10,
spaceAfter=15,
leftIndent=20
)
details_text = f"""
<b>Kuvaus:</b> {task.description}<br/>
<b>Tyyppi:</b> {task.task_type}<br/>
<b>Tila:</b> {task.status}<br/>
<b>Luotu:</b> {task.created_at.strftime('%d.%m.%Y %H:%M:%S')}<br/>
"""
if task.scheduled_time:
details_text += f"<b>Ajastettu:</b> {task.scheduled_time.strftime('%d.%m.%Y %H:%M:%S')}<br/>"
if task.started_at:
details_text += f"<b>Aloitettu:</b> {task.started_at.strftime('%d.%m.%Y %H:%M:%S')}<br/>"
if task.completed_at:
details_text += f"<b>Valmistunut:</b> {task.completed_at.strftime('%d.%m.%Y %H:%M:%S')}<br/>"
# Calculate duration
if task.started_at:
duration = task.completed_at - task.started_at
details_text += f"<b>Kesto:</b> {str(duration).split('.')[0]}<br/>"
if task.result:
result_preview = task.result[:200] + "..." if len(task.result) > 200 else task.result
details_text += f"<b>Tulos:</b> {result_preview}<br/>"
if task.error:
details_text += f"<b>Virhe:</b> {task.error}<br/>"
story.append(Paragraph(details_text, details_style))
# Logs
if task.logs:
logs_style = ParagraphStyle(
'Logs',
parent=styles['Normal'],
fontSize=8,
spaceAfter=20,
leftIndent=40,
textColor=colors.grey
)
logs_text = "<b>Lokit:</b><br/>"
for log in task.logs[-5:]: # Show last 5 logs
log_time = datetime.fromisoformat(log["timestamp"]).strftime('%H:%M:%S')
logs_text += f"• {log_time}: {log['message']}<br/>"
story.append(Paragraph(logs_text, logs_style))
story.append(Spacer(1, 10))
# Build PDF
doc.build(story)
return filename
except Exception as e:
return f"PDF generation error: {str(e)}"
class AIProviderManager:
"""Manages different AI providers and their APIs"""
def __init__(self):
self.providers = {
"openai": {
"name": "OpenAI (GPT-3.5/4)",
"models": ["gpt-3.5-turbo", "gpt-4", "gpt-4-turbo"],
"requires_key": True,
"available": OPENAI_AVAILABLE
},
"groq": {
"name": "Groq (Llama/Mixtral)",
"models": ["llama3-70b-8192", "llama3-8b-8192", "mixtral-8x7b-32768", "gemma-7b-it"],
"requires_key": True,
"available": True # Uses OpenAI-compatible API
},
"anthropic": {
"name": "Anthropic (Claude)",
"models": ["claude-3-haiku-20240307", "claude-3-sonnet-20240229", "claude-3-opus-20240229"],
"requires_key": True,
"available": ANTHROPIC_AVAILABLE
},
"gemini": {
"name": "Google Gemini",
"models": ["gemini-pro", "gemini-pro-vision"],
"requires_key": True,
"available": GEMINI_AVAILABLE
},
"ollama": {
"name": "Ollama (Local)",
"models": ["llama3", "mistral", "codellama", "llama2", "phi3", "qwen2"],
"requires_key": False,
"available": True,
"base_url": "http://localhost:11434"
},
"cohere": {
"name": "Cohere",
"models": ["command-r", "command-r-plus", "command"],
"requires_key": True,
"available": True
},
"huggingface": {
"name": "Hugging Face",
"models": ["microsoft/DialoGPT-large", "microsoft/DialoGPT-medium", "facebook/blenderbot-400M-distill"],
"requires_key": True,
"available": True
},
"together": {
"name": "Together AI",
"models": ["meta-llama/Llama-2-70b-chat-hf", "mistralai/Mixtral-8x7B-Instruct-v0.1"],
"requires_key": True,
"available": True
}
}
def get_available_providers(self):
"""Get list of available providers"""
return {k: v for k, v in self.providers.items() if v["available"]}
def call_ai_api(self, provider, model, messages, api_key=None, **kwargs):
"""Generic AI API caller"""
try:
if provider == "openai":
return self._call_openai(model, messages, api_key, **kwargs)
elif provider == "groq":
return self._call_groq(model, messages, api_key, **kwargs)
elif provider == "anthropic":
return self._call_anthropic(model, messages, api_key, **kwargs)
elif provider == "gemini":
return self._call_gemini(model, messages, api_key, **kwargs)
elif provider == "ollama":
return self._call_ollama(model, messages, **kwargs)
elif provider == "cohere":
return self._call_cohere(model, messages, api_key, **kwargs)
elif provider == "huggingface":
return self._call_huggingface(model, messages, api_key, **kwargs)
elif provider == "together":
return self._call_together(model, messages, api_key, **kwargs)
else:
return "Tuntematon AI-palveluntarjoaja"
except Exception as e:
return f"AI API -virhe ({provider}): {str(e)}"
def _call_openai(self, model, messages, api_key, **kwargs):
"""Call OpenAI API"""
if not OPENAI_AVAILABLE:
return "OpenAI-kirjasto ei ole saatavilla"
openai.api_key = api_key
response = openai.ChatCompletion.create(
model=model,
messages=messages,
max_tokens=kwargs.get('max_tokens', 500),
temperature=kwargs.get('temperature', 0.7)
)
return response.choices[0].message["content"]
def _call_groq(self, model, messages, api_key, **kwargs):
"""Call Groq API (OpenAI compatible)"""
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
data = {
"model": model,
"messages": messages,
"max_tokens": kwargs.get('max_tokens', 500),
"temperature": kwargs.get('temperature', 0.7)
}
response = requests.post(
"https://api.groq.com/openai/v1/chat/completions",
headers=headers,
json=data,
timeout=30
)
if response.status_code == 200:
return response.json()["choices"][0]["message"]["content"]
else:
return f"Groq virhe: {response.status_code}"
class SmartAgent:
def __init__(self):
"""Initialize the Smart Agent"""
self.conversation_history = []
self.api_keys = {}
self.current_ai_provider = "groq"
self.current_ai_model = "llama3-70b-8192"
self.ai_manager = AIProviderManager()
self.scheduler = TaskScheduler(self)
self.load_config()
# Initialize speech components if available
if SPEECH_AVAILABLE:
self.tts_engine = pyttsx3.init()
self.setup_voice()
if SPEECH_RECOGNITION_AVAILABLE:
self.recognizer = sr.Recognizer()
self.microphone = sr.Microphone()
def setup_voice(self):
"""Setup text-to-speech voice"""
if SPEECH_AVAILABLE:
voices = self.tts_engine.getProperty('voices')
# Try to find Finnish voice, fallback to first available
for voice in voices:
if 'finnish' in voice.name.lower() or 'fi' in voice.id.lower():
self.tts_engine.setProperty('voice', voice.id)
break
self.tts_engine.setProperty('rate', 150)
self.tts_engine.setProperty('volume', 0.8)
def speak_text(self, text):
"""Convert text to speech"""
if SPEECH_AVAILABLE:
try:
self.tts_engine.say(text)
self.tts_engine.runAndWait()
except Exception as e:
print(f"Speech error: {e}")
def listen_speech(self):
"""Listen to speech and convert to text"""
if not SPEECH_RECOGNITION_AVAILABLE:
return "Speech recognition not available"
try:
with self.microphone as source:
self.recognizer.adjust_for_ambient_noise(source)
print("Kuuntelen...")
audio = self.recognizer.listen(source, timeout=5)
print("Tunnistan puhetta...")
text = self.recognizer.recognize_google(audio, language='fi-FI')
return text
except sr.WaitTimeoutError:
return "Aikakatkaisut - ei ääntä havaittu"
except sr.UnknownValueError:
return "Puhetta ei voitu tunnistaa"
except sr.RequestError as e:
return f"Virhe puheentunnistuksessa: {e}"
def load_config(self):
"""Load configuration from file"""
try:
if os.path.exists('agent_config.json'):
with open('agent_config.json', 'r') as f:
config = json.load(f)
self.api_keys = config.get('api_keys', {})
self.current_ai_provider = config.get('ai_provider', 'groq')
self.current_ai_model = config.get('ai_model', 'llama3-70b-8192')
except Exception as e:
print(f"Config load error: {e}")
def save_config(self):
"""Save configuration to file"""
try:
config = {
'api_keys': self.api_keys,
'ai_provider': self.current_ai_provider,
'ai_model': self.current_ai_model
}
with open('agent_config.json', 'w') as f:
json.dump(config, f, indent=2)
except Exception as e:
print(f"Config save error: {e}")
def set_api_key(self, provider, key):
"""Set API key for provider"""
self.api_keys[provider] = key
self.save_config()
def get_api_key(self, provider):
"""Get API key for provider"""
return self.api_keys.get(provider)
def web_search(self, query):
"""Perform web search using DuckDuckGo"""
try:
# Simple web search implementation
search_url = f"https://duckduckgo.com/html/?q={query}"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
response = requests.get(search_url, headers=headers, timeout=10)
if response.status_code == 200:
return f"Hakutulokset haulle '{query}' löydetty. Status: {response.status_code}"
else:
return f"Haku epäonnistui. Status: {response.status_code}"
except Exception as e:
return f"Hakuvirhe: {str(e)}"
def youtube_search(self, query):
"""Search YouTube videos"""
if not YOUTUBE_AVAILABLE:
return "YouTube-haku ei ole saatavilla"
try:
videos_search = VideosSearch(query, limit=5)
results = videos_search.result()
if results['result']:
output = f"YouTube-hakutulokset haulle '{query}':\n\n"
for i, video in enumerate(results['result'], 1):
output += f"{i}. {video['title']}\n"
output += f" Kanava: {video['channel']['name']}\n"
output += f" Kesto: {video['duration']}\n"
output += f" URL: {video['link']}\n\n"
return output
else:
return f"Ei tuloksia haulle: {query}"
except Exception as e:
return f"YouTube-hakuvirhe: {str(e)}"
def translate_text(self, text, target_lang='en'):
"""Translate text using Google Translate"""
if not TRANSLATE_AVAILABLE:
return "Käännöspalvelu ei ole saatavilla"
try:
if TRANSLATOR_TYPE == "deep_translator":
translator = GoogleTranslator(source='auto', target=target_lang)
result = translator.translate(text)
else: # googletrans
translator = Translator()
result = translator.translate(text, dest=target_lang)
result = result.text
return f"Käännös ({target_lang}): {result}"
except Exception as e:
return f"Käännösvirhe: {str(e)}"
def read_pdf(self, file_path):
"""Read PDF file content"""
try:
with open(file_path, 'rb') as file:
pdf_reader = PyPDF2.PdfReader(file)
text_content = ""
for page_num in range(len(pdf_reader.pages)):
page = pdf_reader.pages[page_num]
text_content += page.extract_text() + "\n"
return f"PDF sisältö ({len(pdf_reader.pages)} sivua):\n\n{text_content[:1000]}..."
except Exception as e:
return f"PDF-lukuvirhe: {str(e)}"
def call_api(self, url, method='GET', data=None, headers=None):
"""Make HTTP API call"""
try:
if headers is None:
headers = {'Content-Type': 'application/json'}
if method.upper() == 'GET':
response = requests.get(url, headers=headers, timeout=10)
elif method.upper() == 'POST':
response = requests.post(url, json=data, headers=headers, timeout=10)
else:
return f"Tuntematon HTTP-metodi: {method}"
return f"API-vastaus ({response.status_code}):\n{response.text[:500]}..."
except Exception as e:
return f"API-virhe: {str(e)}"
def chat_with_user(self, user_input):
"""Chat with user using AI"""
try:
# Add user message to history
self.conversation_history.append({"role": "user", "content": user_input})
# Limit conversation history
if len(self.conversation_history) > 10:
self.conversation_history = self.conversation_history[-10:]
# Prepare messages for AI
messages = [
{"role": "system", "content": "Olet ystävällinen ja hyödyllinen suomenkielinen AI-agentti. Vastaa aina suomeksi ja ole ytimekäs mutta informatiivinen."}
] + self.conversation_history
# Get AI response
api_key = self.get_api_key(self.current_ai_provider)
response = self.ai_manager.call_ai_api(
self.current_ai_provider,
self.current_ai_model,
messages,
api_key
)
# Add AI response to history
self.conversation_history.append({"role": "assistant", "content": response})
return response
except Exception as e:
return f"Chat-virhe: {str(e)}"
def analyze_sentiment(self, text):
"""Simple sentiment analysis"""
positive_words = ['hyvä', 'loistava', 'mahtava', 'upea', 'erinomainen', 'positiivinen']
negative_words = ['huono', 'kauhea', 'hirveä', 'negatiivinen', 'surullinen', 'vihainen']
text_lower = text.lower()
positive_count = sum(1 for word in positive_words if word in text_lower)
negative_count = sum(1 for word in negative_words if word in text_lower)
if positive_count > negative_count:
return "Positiivinen"
elif negative_count > positive_count:
return "Negatiivinen"
else:
return "Neutraali"
def get_weather(self, city="Helsinki"):
"""Get weather information (mock implementation)"""
try:
# This is a simple mock - in real implementation, use weather API
weather_data = {
"Helsinki": {"temp": "5°C", "condition": "Pilvistä", "humidity": "75%"},
"Tampere": {"temp": "3°C", "condition": "Sateinen", "humidity": "80%"},
"Turku": {"temp": "4°C", "condition": "Aurinkoinen", "humidity": "70%"}
}
if city in weather_data:
data = weather_data[city]
return f"Sää kaupungissa {city}:\nLämpötila: {data['temp']}\nTila: {data['condition']}\nKosteus: {data['humidity']}"
else:
return f"Säätietoja ei löytynyt kaupungille: {city}"
except Exception as e:
return f"Säävirhe: {str(e)}"
def shutdown(self):
"""Shutdown the agent"""
print("Suljetaan AI-agentti...")
if hasattr(self, 'scheduler'):
self.scheduler.stop_scheduler()
self.save_config()
class SmartAgentGUI:
def __init__(self):
"""Initialize the GUI"""
self.agent = SmartAgent()
self.setup_gui()
# Start GUI update thread
self.update_thread = threading.Thread(target=self.update_task_display, daemon=True)
self.update_thread.start()
def setup_gui(self):
"""Setup the main GUI"""
self.root = tk.Tk()
self.root.title("🤖 Smart AI Agent v2.0")
self.root.geometry("1200x800")
self.root.configure(bg='#2b2b2b')
# Configure styles
style = ttk.Style()
style.theme_use('clam')
style.configure('Dark.TFrame', background='#2b2b2b')
style.configure('Dark.TLabel', background='#2b2b2b', foreground='#ffffff')
style.configure('Dark.TButton', background='#404040', foreground='#ffffff')
self.setup_notebook()
self.setup_chat_tab()
self.setup_tasks_tab()
self.setup_scheduler_tab()
self.setup_settings_tab()
self.setup_menu()
def setup_notebook(self):
"""Setup main notebook with tabs"""
self.notebook = ttk.Notebook(self.root)
self.notebook.pack(fill='both', expand=True, padx=10, pady=10)
# Create tabs
self.chat_frame = ttk.Frame(self.notebook, style='Dark.TFrame')
self.tasks_frame = ttk.Frame(self.notebook, style='Dark.TFrame')
self.scheduler_frame = ttk.Frame(self.notebook, style='Dark.TFrame')
self.settings_frame = ttk.Frame(self.notebook, style='Dark.TFrame')
self.notebook.add(self.chat_frame, text="💬 Chat")
self.notebook.add(self.tasks_frame, text="📋 Tehtävät")
self.notebook.add(self.scheduler_frame, text="⏰ Ajastus")
self.notebook.add(self.settings_frame, text="⚙️ Asetukset")
def setup_chat_tab(self):
"""Setup chat tab"""
# Chat display
self.chat_display = scrolledtext.ScrolledText(
self.chat_frame,
height=20,
bg='#1e1e1e',
fg='#ffffff',
font=('Consolas', 10)
)
self.chat_display.pack(fill='both', expand=True, padx=10, pady=10)
# Input frame
input_frame = ttk.Frame(self.chat_frame, style='Dark.TFrame')
input_frame.pack(fill='x', padx=10, pady=5)
self.chat_input = tk.Entry(
input_frame,
bg='#404040',
fg='#ffffff',
font=('Consolas', 10)
)
self.chat_input.pack(side='left', fill='x', expand=True, padx=(0, 5))
self.chat_input.bind('<Return>', self.send_message)
# Buttons
ttk.Button(
input_frame,
text="Lähetä",
command=self.send_message,
style='Dark.TButton'
).pack(side='right', padx=(0, 5))
if SPEECH_RECOGNITION_AVAILABLE:
ttk.Button(
input_frame,
text="🎤",
command=self.start_voice_input,
style='Dark.TButton'
).pack(side='right', padx=(0, 5))
# Quick actions frame
actions_frame = ttk.Frame(self.chat_frame, style='Dark.TFrame')
actions_frame.pack(fill='x', padx=10, pady=5)
ttk.Button(actions_frame, text="🌐 Web-haku", command=self.quick_web_search).pack(side='left', padx=2)
ttk.Button(actions_frame, text="📺 YouTube", command=self.quick_youtube_search).pack(side='left', padx=2)
ttk.Button(actions_frame, text="🌡️ Sää", command=self.quick_weather).pack(side='left', padx=2)
ttk.Button(actions_frame, text="🔄 Käännä", command=self.quick_translate).pack(side='left', padx=2)
ttk.Button(actions_frame, text="📄 PDF", command=self.quick_pdf_read).pack(side='left', padx=2)
def setup_tasks_tab(self):
"""Setup tasks management tab"""
# Task list
self.task_tree = ttk.Treeview(self.tasks_frame, columns=('status', 'type', 'scheduled', 'progress'), show='tree headings')
self.task_tree.heading('#0', text='Nimi')
self.task_tree.heading('status', text='Tila')
self.task_tree.heading('type', text='Tyyppi')
self.task_tree.heading('scheduled', text='Ajastettu')
self.task_tree.heading('progress', text='Edistyminen')
# Configure column widths
self.task_tree.column('#0', width=200)
self.task_tree.column('status', width=100)
self.task_tree.column('type', width=120)
self.task_tree.column('scheduled', width=150)
self.task_tree.column('progress', width=100)
self.task_tree.pack(fill='both', expand=True, padx=10, pady=10)
# Task control buttons
task_buttons_frame = ttk.Frame(self.tasks_frame, style='Dark.TFrame')
task_buttons_frame.pack(fill='x', padx=10, pady=5)
ttk.Button(task_buttons_frame, text="🔄 Päivitä", command=self.refresh_tasks).pack(side='left', padx=2)
ttk.Button(task_buttons_frame, text="❌ Peruuta", command=self.cancel_selected_task).pack(side='left', padx=2)
ttk.Button(task_buttons_frame, text="📊 Raportti", command=self.generate_task_report).pack(side='left', padx=2)
ttk.Button(task_buttons_frame, text="🗑️ Tyhjennä", command=self.clear_completed_tasks).pack(side='left', padx=2)
def setup_scheduler_tab(self):
"""Setup task scheduler tab"""
# Scheduler form
form_frame = ttk.LabelFrame(self.scheduler_frame, text="Uusi ajastettu tehtävä", style='Dark.TFrame')
form_frame.pack(fill='x', padx=10, pady=10)
# Task name
ttk.Label(form_frame, text="Tehtävän nimi:", style='Dark.TLabel').grid(row=0, column=0, sticky='w', padx=5, pady=5)
self.task_name_entry = tk.Entry(form_frame, bg='#404040', fg='#ffffff')
self.task_name_entry.grid(row=0, column=1, sticky='ew', padx=5, pady=5)
# Task description
ttk.Label(form_frame, text="Kuvaus:", style='Dark.TLabel').grid(row=1, column=0, sticky='w', padx=5, pady=5)
self.task_desc_entry = tk.Entry(form_frame, bg='#404040', fg='#ffffff')
self.task_desc_entry.grid(row=1, column=1, sticky='ew', padx=5, pady=5)
# Task type
ttk.Label(form_frame, text="Tyyppi:", style='Dark.TLabel').grid(row=2, column=0, sticky='w', padx=5, pady=5)
self.task_type_combo = ttk.Combobox(form_frame, values=[
'ai_chat', 'web_search', 'youtube_search', 'translate',
'api_call', 'pdf_read', 'custom_command'
])
self.task_type_combo.grid(row=2, column=1, sticky='ew', padx=5, pady=5)
# Schedule time
ttk.Label(form_frame, text="Ajastus:", style='Dark.TLabel').grid(row=3, column=0, sticky='w', padx=5, pady=5)
schedule_frame = ttk.Frame(form_frame, style='Dark.TFrame')
schedule_frame.grid(row=3, column=1, sticky='ew', padx=5, pady=5)
if CALENDAR_AVAILABLE:
self.date_entry = DateEntry(schedule_frame, background='darkblue', foreground='white', borderwidth=2)
else:
self.date_entry = tk.Entry(schedule_frame, bg='#404040', fg='#ffffff')
self.date_entry.insert(0, datetime.now().strftime('%Y-%m-%d'))
self.date_entry.pack(side='left', padx=(0, 5))
self.time_entry = tk.Entry(schedule_frame, bg='#404040', fg='#ffffff', width=8)
self.time_entry.pack(side='left')
self.time_entry.insert(0, datetime.now().strftime('%H:%M'))
# Recurring options
ttk.Label(form_frame, text="Toisto:", style='Dark.TLabel').grid(row=4, column=0, sticky='w', padx=5, pady=5)
recurring_frame = ttk.Frame(form_frame, style='Dark.TFrame')
recurring_frame.grid(row=4, column=1, sticky='ew', padx=5, pady=5)
self.recurring_var = tk.BooleanVar()
recurring_check = ttk.Checkbutton(recurring_frame, text="Toistuva", variable=self.recurring_var)
recurring_check.pack(side='left', padx=(0, 5))
self.interval_combo = ttk.Combobox(recurring_frame, values=['hourly', 'daily', 'weekly', 'monthly'], width=10)
self.interval_combo.pack(side='left')
# Parameters
ttk.Label(form_frame, text="Parametrit (JSON):", style='Dark.TLabel').grid(row=5, column=0, sticky='w', padx=5, pady=5)
self.params_entry = tk.Text(form_frame, height=3, bg='#404040', fg='#ffffff')
self.params_entry.grid(row=5, column=1, sticky='ew', padx=5, pady=5)
self.params_entry.insert('1.0', '{"prompt": "Hei AI!"}')
# Configure grid weights
form_frame.columnconfigure(1, weight=1)
# Schedule button
ttk.Button(form_frame, text="📅 Ajasta tehtävä", command=self.schedule_task).grid(row=6, column=0, columnspan=2, pady=10)
def setup_settings_tab(self):
"""Setup settings tab"""
# AI Provider settings
ai_frame = ttk.LabelFrame(self.settings_frame, text="AI-asetukset", style='Dark.TFrame')
ai_frame.pack(fill='x', padx=10, pady=10)
ttk.Label(ai_frame, text="AI-palveluntarjoaja:", style='Dark.TLabel').grid(row=0, column=0, sticky='w', padx=5, pady=5)
available_providers = list(self.agent.ai_manager.get_available_providers().keys())
self.ai_provider_combo = ttk.Combobox(ai_frame, values=available_providers)
self.ai_provider_combo.set(self.agent.current_ai_provider)
self.ai_provider_combo.grid(row=0, column=1, sticky='ew', padx=5, pady=5)
self.ai_provider_combo.bind('<<ComboboxSelected>>', self.on_provider_change)
ttk.Label(ai_frame, text="Malli:", style='Dark.TLabel').grid(row=1, column=0, sticky='w', padx=5, pady=5)
self.ai_model_combo = ttk.Combobox(ai_frame)
self.ai_model_combo.set(self.agent.current_ai_model)
self.ai_model_combo.grid(row=1, column=1, sticky='ew', padx=5, pady=5)
self.update_model_list()
# API Keys
keys_frame = ttk.LabelFrame(self.settings_frame, text="API-avaimet", style='Dark.TFrame')
keys_frame.pack(fill='x', padx=10, pady=10)
self.api_key_entries = {}
row = 0
for provider, info in self.agent.ai_manager.get_available_providers().items():
if info["requires_key"]:
ttk.Label(keys_frame, text=f"{info['name']}:", style='Dark.TLabel').grid(row=row, column=0, sticky='w', padx=5, pady=2)
entry = tk.Entry(keys_frame, show="*", bg='#404040', fg='#ffffff')
entry.grid(row=row, column=1, sticky='ew', padx=5, pady=2)
# Load existing key
existing_key = self.agent.get_api_key(provider)
if existing_key:
entry.insert(0, existing_key)
self.api_key_entries[provider] = entry
ttk.Button(keys_frame, text="Tallenna",
command=lambda p=provider: self.save_api_key(p)).grid(row=row, column=2, padx=5, pady=2)
row += 1
keys_frame.columnconfigure(1, weight=1)
ai_frame.columnconfigure(1, weight=1)
# System info
info_frame = ttk.LabelFrame(self.settings_frame, text="Järjestelmätiedot", style='Dark.TFrame')
info_frame.pack(fill='both', expand=True, padx=10, pady=10)
self.system_info = scrolledtext.ScrolledText(info_frame, height=10, bg='#1e1e1e', fg='#ffffff')
self.system_info.pack(fill='both', expand=True, padx=5, pady=5)
self.update_system_info()
def setup_menu(self):
"""Setup application menu"""
menubar = tk.Menu(self.root)
self.root.config(menu=menubar)
# File menu
file_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="Tiedosto", menu=file_menu)
file_menu.add_command(label="Lataa keskustelu", command=self.load_conversation)
file_menu.add_command(label="Tallenna keskustelu", command=self.save_conversation)
file_menu.add_separator()
file_menu.add_command(label="Lopeta", command=self.quit_application)
# Tools menu
tools_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="Työkalut", menu=tools_menu)
tools_menu.add_command(label="Tyhjennä keskustelu", command=self.clear_chat)
tools_menu.add_command(label="Vie tehtävät PDF:ksi", command=self.export_tasks_pdf)
# Help menu
help_menu = tk.Menu(menubar, tearoff=0)
menubar.add_cascade(label="Ohje", menu=help_menu)
help_menu.add_command(label="Tietoja", command=self.show_about)
def send_message(self, event=None):
"""Send message to AI"""
message = self.chat_input.get().strip()
if not message:
return
self.chat_input.delete(0, tk.END)
# Display user message
self.add_to_chat(f"👤 Sinä: {message}\n", '#00ff00')
# Get AI response in thread
def get_response():
try:
response = self.agent.chat_with_user(message)
self.root.after(0, lambda: self.add_to_chat(f"🤖 AI: {response}\n\n", '#00aaff'))
# Optional: speak response
if SPEECH_AVAILABLE and hasattr(self, 'speak_responses') and self.speak_responses:
threading.Thread(target=lambda: self.agent.speak_text(response), daemon=True).start()
except Exception as e:
self.root.after(0, lambda: self.add_to_chat(f"❌ Virhe: {str(e)}\n\n", '#ff0000'))
threading.Thread(target=get_response, daemon=True).start()
def add_to_chat(self, text, color='#ffffff'):
"""Add text to chat display"""
self.chat_display.config(state='normal')
self.chat_display.insert(tk.END, text)
# Color the last inserted text
start_pos = f"{self.chat_display.index(tk.END)}-{len(text)}c"
self.chat_display.tag_add("colored", start_pos, tk.END)
self.chat_display.tag_config("colored", foreground=color)
self.chat_display.config(state='disabled')
self.chat_display.see(tk.END)
def start_voice_input(self):
"""Start voice input"""
if not SPEECH_RECOGNITION_AVAILABLE:
messagebox.showwarning("Varoitus", "Puheentunnistus ei ole saatavilla")
return
def voice_thread():
try:
self.root.after(0, lambda: self.add_to_chat("🎤 Kuuntelen...\n", '#ffff00'))
text = self.agent.listen_speech()
self.root.after(0, lambda: self.chat_input.insert(0, text))
self.root.after(0, lambda: self.add_to_chat(f"🎤 Kuultiin: {text}\n", '#ffff00'))
except Exception as e:
self.root.after(0, lambda: self.add_to_chat(f"🎤 Virhe: {str(e)}\n", '#ff0000'))
threading.Thread(target=voice_thread, daemon=True).start()
def quick_web_search(self):
"""Quick web search"""
query = tk.simpledialog.askstring("Web-haku", "Syötä hakutermi:")
if query:
self.chat_input.insert(0, f"Hae webistä: {query}")
def quick_youtube_search(self):
"""Quick YouTube search"""
query = tk.simpledialog.askstring("YouTube-haku", "Syötä hakutermi:")
if query:
self.chat_input.insert(0, f"Hae YouTubesta: {query}")
def quick_weather(self):
"""Quick weather check"""
city = tk.simpledialog.askstring("Sää", "Syötä kaupunki:", initialvalue="Helsinki")
if city:
self.chat_input.insert(0, f"Mikä on sää kaupungissa {city}?")
def quick_translate(self):
"""Quick translate"""
text = tk.simpledialog.askstring("Käännös", "Syötä käännettävä teksti:")
if text:
lang = tk.simpledialog.askstring("Kieli", "Kohdekieli (esim. en, fi, de):", initialvalue="en")
if lang:
self.chat_input.insert(0, f"Käännä '{text}' kielelle {lang}")
def quick_pdf_read(self):
"""Quick PDF read"""
file_path = filedialog.askopenfilename(
title="Valitse PDF-tiedosto",
filetypes=[("PDF files", "*.pdf")]
)
if file_path:
self.chat_input.insert(0, f"Lue PDF: {file_path}")
def update_task_display(self):
"""Update task display periodically"""
while True:
try:
time.sleep(2) # Update every 2 seconds
self.root.after(0, self.refresh_tasks)
except Exception as e:
print(f"Task update error: {e}")
time.sleep(5)
def refresh_tasks(self):
"""Refresh the task list"""
try:
# Clear current items
for item in self.task_tree.get_children():
self.task_tree.delete(item)
# Add tasks
tasks = self.agent.scheduler.get_all_tasks()
for task in sorted(tasks, key=lambda t: t.created_at, reverse=True):
scheduled_str = task.scheduled_time.strftime('%d.%m.%Y %H:%M') if task.scheduled_time else "Ei ajastettu"
progress_str = f"{task.progress}%"
self.task_tree.insert(
'',
'end',
text=task.name,
values=(task.status, task.task_type, scheduled_str, progress_str)
)
except Exception as e:
print(f"Task refresh error: {e}")
def cancel_selected_task(self):
"""Cancel selected task"""
selection = self.task_tree.selection()
if not selection:
messagebox.showwarning("Varoitus", "Valitse peruutettava tehtävä")
return
item = selection[0]
task_name = self.task_tree.item(item, 'text')
# Find task by name (not ideal, but works for demo)
for task in self.agent.scheduler.get_all_tasks():
if task.name == task_name and task.status == TaskStatus.PENDING:
if self.agent.scheduler.cancel_task(task.id):
messagebox.showinfo("Onnistui", f"Tehtävä '{task_name}' peruutettu")
self.refresh_tasks()
return
messagebox.showwarning("Varoitus", "Tehtävää ei voitu peruuttaa")
def generate_task_report(self):
"""Generate PDF report of tasks"""
if not PDF_GENERATION_AVAILABLE:
messagebox.showwarning("Varoitus", "PDF-generointi ei ole saatavilla")
return
tasks = self.agent.scheduler.get_all_tasks()
if not tasks:
messagebox.showinfo("Info", "Ei tehtäviä raportoitavaksi")
return
filename = PDFGenerator.generate_task_report(tasks)
if filename.endswith('.pdf'):
messagebox.showinfo("Onnistui", f"Raportti luotu: {filename}")
# Try to open the PDF
try:
webbrowser.open(filename)
except:
pass
else:
messagebox.showerror("Virhe", filename)
def clear_completed_tasks(self):
"""Clear completed tasks"""
if messagebox.askyesno("Vahvistus", "Haluatko poistaa kaikki valmiit tehtävät?"):
completed_tasks = [t for t in self.agent.scheduler.get_all_tasks()
if t.status in [TaskStatus.COMPLETED, TaskStatus.FAILED, TaskStatus.CANCELLED]]
for task in completed_tasks:
if task.id in self.agent.scheduler.tasks:
del self.agent.scheduler.tasks[task.id]
self.agent.scheduler.save_tasks()
self.refresh_tasks()
messagebox.showinfo("Onnistui", f"Poistettu {len(completed_tasks)} tehtävää")
def schedule_task(self):
"""Schedule a new task"""
try:
# Get form data
name = self.task_name_entry.get().strip()
description = self.task_desc_entry.get().strip()
task_type = self.task_type_combo.get()
if not name or not task_type:
messagebox.showwarning("Varoitus", "Syötä tehtävän nimi ja tyyppi")
return
# Parse date and time
if CALENDAR_AVAILABLE:
date_str = self.date_entry.get()
else:
date_str = self.date_entry.get()
time_str = self.time_entry.get()
datetime_str = f"{date_str} {time_str}"
try:
scheduled_time = datetime.strptime(datetime_str, '%Y-%m-%d %H:%M')
except ValueError:
try:
scheduled_time = datetime.strptime(datetime_str, '%d.%m.%Y %H:%M')
except ValueError:
messagebox.showerror("Virhe", "Virheellinen päivämäärä/aika-muoto")
return
# Parse parameters
try:
params_text = self.params_entry.get('1.0', tk.END).strip()
parameters = json.loads(params_text) if params_text else {}
except json.JSONDecodeError:
messagebox.showerror("Virhe", "Virheellinen JSON-muoto parametreissa")
return
# Create task
task = ScheduledTask(
str(uuid.uuid4()),
name,
description,
scheduled_time,
task_type,
parameters,
self.recurring_var.get(),
self.interval_combo.get() if self.recurring_var.get() else None
)
# Add to scheduler
task_id = self.agent.scheduler.add_task(task)
messagebox.showinfo("Onnistui", f"Tehtävä ajastettu! ID: {task_id}")
# Clear form
self.task_name_entry.delete(0, tk.END)
self.task_desc_entry.delete(0, tk.END)
self.task_type_combo.set('')
self.params_entry.delete('1.0', tk.END)
self.params_entry.insert('1.0', '{"prompt": "Hei AI!"}')
self.refresh_tasks()
except Exception as e:
messagebox.showerror("Virhe", f"Tehtävän ajastus epäonnistui: {str(e)}")
def on_provider_change(self, event=None):
"""Handle AI provider change"""
provider = self.ai_provider_combo.get()
self.agent.current_ai_provider = provider
self.agent.save_config()
self.update_model_list()
def update_model_list(self):
"""Update model list based on selected provider"""
provider = self.ai_provider_combo.get()
if provider in self.agent.ai_manager.providers:
models = self.agent.ai_manager.providers[provider]["models"]
self.ai_model_combo['values'] = models
if models:
self.ai_model_combo.set(models[0])
self.agent.current_ai_model = models[0]
self.agent.save_config()
def save_api_key(self, provider):
"""Save API key for provider"""
if provider in self.api_key_entries:
key = self.api_key_entries[provider].get().strip()
if key:
self.agent.set_api_key(provider, key)
messagebox.showinfo("Onnistui", f"API-avain tallennettu: {provider}")
else:
messagebox.showwarning("Varoitus", "Syötä API-avain")
def update_system_info(self):
"""Update system information display"""
info_text = f"""
🤖 Smart AI Agent v2.0
📅 Käynnistetty: {datetime.now().strftime('%d.%m.%Y %H:%M:%S')}
📊 Järjestelmätiedot:
• Python versio: {sys.version.split()[0]}
• Käyttöjärjestelmä: {os.name}
• Työhakemisto: {os.getcwd()}
🔧 Saatavilla olevat ominaisuudet:
• Puhesynteesi: {'✅' if SPEECH_AVAILABLE else '❌'}
• Puheentunnistus: {'✅' if SPEECH_RECOGNITION_AVAILABLE else '❌'}
• PDF-generointi: {'✅' if PDF_GENERATION_AVAILABLE else '❌'}
• Kalenteri: {'✅' if CALENDAR_AVAILABLE else '❌'}
• YouTube-haku: {'✅' if YOUTUBE_AVAILABLE else '❌'}
• Käännöspalvelu: {'✅' if TRANSLATE_AVAILABLE else '❌'}
🤖 AI-palveluntarjoajat:
"""
for provider, info in self.agent.ai_manager.get_available_providers().items():
status = '✅' if info['available'] else '❌'
key_status = '🔑' if self.agent.get_api_key(provider) else '🔓'
info_text += f"• {info['name']}: {status} {key_status}\n"
info_text += f"""
📋 Tehtävätilastot:
• Odottavia: {len(self.agent.scheduler.get_tasks_by_status(TaskStatus.PENDING))}
• Käynnissä: {len(self.agent.scheduler.get_tasks_by_status(TaskStatus.RUNNING))}
• Valmistuneita: {len(self.agent.scheduler.get_tasks_by_status(TaskStatus.COMPLETED))}
• Epäonnistuneita: {len(self.agent.scheduler.get_tasks_by_status(TaskStatus.FAILED))}
• Peruutettuja: {len(self.agent.scheduler.get_tasks_by_status(TaskStatus.CANCELLED))}
💡 Vinkkejä:
• Käytä 🎤-nappia puheentunnistukseen
• Ajasta tehtäviä Ajastus-välilehdellä
• Luo PDF-raportteja Tehtävät-välilehdellä
• Tallenna API-avaimet Asetukset-välilehdellä
"""
self.system_info.delete('1.0', tk.END)
self.system_info.insert('1.0', info_text)
def load_conversation(self):
"""Load conversation from file"""
try:
file_path = filedialog.askopenfilename(
title="Lataa keskustelu",
filetypes=[("JSON files", "*.json")]
)
if file_path:
with open(file_path, 'r', encoding='utf-8') as f:
conversation = json.load(f)
self.agent.conversation_history = conversation
# Display loaded conversation
self.clear_chat()
for msg in conversation:
role = "👤 Sinä" if msg["role"] == "user" else "🤖 AI"
color = '#00ff00' if msg["role"] == "user" else '#00aaff'
self.add_to_chat(f"{role}: {msg['content']}\n", color)
messagebox.showinfo("Onnistui", "Keskustelu ladattu")
except Exception as e:
messagebox.showerror("Virhe", f"Keskustelun lataus epäonnistui: {str(e)}")
def save_conversation(self):
"""Save conversation to file"""
try:
if not self.agent.conversation_history:
messagebox.showinfo("Info", "Ei keskustelua tallennettavaksi")
return
file_path = filedialog.asksaveasfilename(
title="Tallenna keskustelu",
defaultextension=".json",
filetypes=[("JSON files", "*.json")]
)
if file_path:
with open(file_path, 'w', encoding='utf-8') as f:
json.dump(self.agent.conversation_history, f, ensure_ascii=False, indent=2)
messagebox.showinfo("Onnistui", "Keskustelu tallennettu")
except Exception as e:
messagebox.showerror("Virhe", f"Keskustelun tallennus epäonnistui: {str(e)}")
def clear_chat(self):
"""Clear chat display and history"""
self.chat_display.config(state='normal')
self.chat_display.delete('1.0', tk.END)
self.chat_display.config(state='disabled')
self.agent.conversation_history = []
messagebox.showinfo("Onnistui", "Keskustelu tyhjennetty")
def export_tasks_pdf(self):
"""Export all tasks to PDF"""
self.generate_task_report()
def show_about(self):
"""Show about dialog"""
about_text = """
🤖 Smart AI Agent v2.0
Monipuolinen AI-agentti tehtävien ajastuksella ja automaatiolla.
Ominaisuudet:
• Keskustelu useilla AI-palveluntarjoajilla
• Tehtävien ajastus ja automaatio
• Web-haku ja YouTube-haku
• PDF-lukeminen ja -generointi
• Puheentunnistus ja -synteesi
• Käännöspalvelut
Kehittäjä: AI Assistant
Versio: 2.0
Lisenssi: MIT
© 2024 Smart AI Agent Project
"""
messagebox.showinfo("Tietoja", about_text)
def quit_application(self):
"""Quit the application"""
if messagebox.askyesno("Lopetus", "Haluatko varmasti lopettaa?"):
self.agent.shutdown()
self.root.quit()
def run(self):
"""Run the GUI application"""
try:
self.root.protocol("WM_DELETE_WINDOW", self.quit_application)
self.root.mainloop()
except KeyboardInterrupt:
self.quit_application()
if __name__ == "__main__":
# Install required packages if missing
required_packages = [
"requests", "PyPDF2", "schedule",
"reportlab", "tkcalendar", "pyttsx3",
"deep-translator", "youtube-search-python",
"SpeechRecognition", "openai", "anthropic",
"google-generativeai"
]
print("🤖 Smart AI Agent v2.0 käynnistyy...")
print("📦 Tarkistetaan riippuvuudet...")
# Show startup info
print(f"✅ Python {sys.version.split()[0]}")
print(f"✅ Työhakemisto: {os.getcwd()}")
try:
# Create and run the GUI
app = SmartAgentGUI()
print("🚀 GUI käynnistetty!")
app.run()
except Exception as e:
print(f"❌ Virhe käynnistyksessä: {e}")
import traceback
traceback.print_exc()
finally:
print("👋 Smart AI Agent lopetettu")
Uudet ominaisuudet:
Ajastetut tehtävät
- Kalenterinäkymä tehtävien ajastamiseen (tkcalendar)
- Toistuvat tehtävät (tunneittain, päivittäin, viikoittain, kuukausittain)
- Reaaliaikainen seuranta tehtävien edistymisestä
- Tehtävätyypit:
- 💬 AI-keskustelu
- 🌐 Web-haku
- 🎬 YouTube-haku
- 🌍 Käännös
- 🔗 API-kutsu
- 📄 PDF-luku
- ⚙️ Mukautettu Python-komento
Tehtävien hallinta
- Tehtävälista TreeView-komponentilla
- Reaaliaikainen päivitys (5s välein)
- Yksityiskohtaiset tiedot jokaisesta tehtävästä
- Lokit tehtävien suorituksesta
- Tehtävien poisto ja peruutus
PDF-raportointi
- Automaattinen PDF-generointi valmiista tehtävistä
- Yksityiskohtaiset raportit sisältäen:
- Yhteenveto tilastoista
- Tehtävien yksityiskohdat
- Suoritusajat ja kestot
- Tulokset ja virheet
- Lokitiedot
- Raporttien hallinta – lataa ja katsele vanhoja raportteja
GUI-parannukset
- “📅 Tehtävien hallinta” -nappi pääikkunassa
- “➕ Ajasta tehtävä” -pikakuvake
- Raporttien selaus ja avaus suoraan sovelluksesta
Tarvittavat riippuvuudet:
Asenna nämä kirjastot saadaksesi kaikki ominaisuudet käyttöön:
bash
pip install requests PyPDF2 schedule reportlab tkcalendar pyttsx3 deep-translator youtube-search-python SpeechRecognition openai anthropic google-generativeai
Käyttöohjeet:
- Luo tehtävä: Klikkaa “📅 Tehtävien hallinta” → “➕ Lisää tehtävä”
- Valitse ajankohta: Käytä kalenteria ja kellonaikaa
- Määritä parametrit: Syötä tehtävän tiedot (esim. AI-prompt)
- Seuraa edistymistä: Tehtävälista päivittyy automaattisesti
- Luo raportit: “📄 Luo raportti” -nappi generoi PDF:n
- Selaa raportteja: “📁 Näytä raportit” avaa raporttiselaimen
Tallennettavat tiedostot:
scheduled_tasks.json
– Tehtävien tiedottask_report_YYYYMMDD_HHMMSS.pdf
– PDF-raportitagent_settings.json
– Agenttiasetukset
Järjestelmä toimii taustalla ja suorittaa tehtäviä automaattisesti aikataulun mukaan!
Keskeiset ominaisuudet:
- Usean AI-palveluntarjoajan tuki – OpenAI, Anthropic, Google Gemini, Groq, Ollama, Cohere jne.
- Tehtävien ajastus – Ajasta tehtävät käynnistymään tiettyinä aikoina, myös toistuvina
- Graafinen käyttöliittymä – Moderni, tummasävyinen monivälilehtinen käyttöliittymä
- Puheentunnistus ja -syntetisointi – Äänisyöte ja -lähtötoiminnot
- PDF-käsittely – Lue PDF-tiedostoja ja luo raportteja
- Haku verkossa ja YouTubessa – Sisäänrakennettu hakutoiminto
- Käännöspalvelut – Tekstin käännöstuki
- Tehtävien hallinta – Näytä, peruuta ja seuraa ajastettuja tehtäviä
Pääkomponentit:
- SmartAgent – Ydintoiminnallisuudet: AI-keskustelu ja apuohjelmat
- TaskScheduler – Hallitsee ajastettujen tehtävien suorittamista
- SmartAgentGUI – Täydellinen käyttöliittymä keskusteluun, tehtävien hallintaan, ajastukseen ja asetuksiin
- AIProviderManager – Hallitsee useita AI-palveluntarjoajia
- PDFGenerator – Luo yksityiskohtaisia tehtäväraportteja
Sovelluksen käynnistäminen:
Sovellus käynnistyy graafisella käyttöliittymällä, jossa voit keskustella tekoälyn kanssa, ajastaa tehtäviä, hallita asetuksia ja tarkastella tehtäväraportteja. Kaikki ominaisuudet toimivat joustavilla vararatkaisuilla, jos jotkin valinnaiset riippuvuudet puuttuvat.
Käynnistys tapahtuu samalla komennolla esim. C:\temp\ kansiossa kuten ennenkin:
python AI-agentti.py




SmartAgent – älykäs apurisi
SmartAgent yhdistää useiden tekoälypalveluiden voiman yhteen helppokäyttöiseen ja moderniin käyttöliittymään. Voit keskustella tekoälyn kanssa, ajastaa ja hallita tehtäviä, hakea tietoa verkosta ja YouTubesta, kääntää tekstejä, käsitellä PDF-tiedostoja sekä käyttää puheentunnistusta ja -syntetisointia.
Olitpa sitten työnteossa, opiskelussa tai luovassa projektissa, SmartAgent auttaa sinua pysymään järjestyksessä ja saavuttamaan tavoitteesi.
Päätoiminnot lyhyesti:
- Usean AI-palveluntarjoajan tuki (OpenAI, Anthropic, Google Gemini jne.)
- Tehtävien ajastus ja hallinta
- Moderni tumma käyttöliittymä
- Äänisyöte ja -lähtö
- PDF-luku ja raporttien luonti
- Verkkohaku ja YouTube-haku
- Tekstien käännökset