Global Trend Radar
Dev.to US tech 2026-06-27 00:07

PythonとStreamlitを使ってAI搭載の映画キュレーターを作成しました — 学んだこと

原題: I built an AI-powered movie curator with Python and Streamlit — here's what I learned

元記事を開く →

分析結果

カテゴリ
AI
重要度
65
トレンドスコア
27
要約
PythonとStreamlitを用いてAI映画キュレーターを開発した経験を共有します。プロジェクトを通じて、データ収集、機械学習モデルの構築、ユーザーインターフェースの設計など、多くの技術的な課題に直面しました。特に、映画の推薦システムの精度向上や、ユーザーの好みに応じたカスタマイズの重要性を学びました。このプロジェクトは、AI技術の実践的な応用と、ユーザーエクスペリエンスの向上に向けた貴重な教訓を提供しました。
キーワード
I've been a movie person my whole life. Growing up in the 80s and 90s, I'd go to the video store without knowing what I wanted and walk out with five tapes. Browsing shelves, reading back covers, picking something just because the title was weird. It was slow and it worked. Today you open Netflix with 10,000 options and spend 40 minutes picking nothing. The problem isn't lack of content. It's lack of curation. So I built something about it. What I built CineAntologia AI — a public catalog of movies and TV shows from the 80s to today, with an AI chat that works like a film curator instead of a search engine. You describe what you want to feel while watching: "Something like Stranger Things, that 80s atmosphere and suspense" "Heavy crime drama like The Wire" "Philosophical sci-fi in the style of Blade Runner" And it gives you suggestions with actual cultural context — not just a list. Live demo: ( https://cineantologia-ai.streamlit.app ) The stack Nothing exotic: Python 3.11 Streamlit — for the UI and deploy TMDb API — free, great data, covers posters, genres, ratings, where to watch OpenAI GPT-4o-mini or Groq for the AI chat Streamlit Community Cloud — free deploy for public repos No database in v1. No auth. No over-engineering. Three decisions worth talking about Ship on day one I pushed the repo public and deployed the moment it minimally worked. No months of polishing in private. When it's a drawer project you optimize for the code. When it's public you optimize for the person using it. That shift in perspective is worth the discomfort of shipping something imperfect. Users bring their own API key The AI Chat uses GPT-4o-mini or Groq. Instead of covering API costs for everyone — unpredictable at scale — users paste their own key in the sidebar. It lives only in their browser session, never hits my server. Three wins: zero cost to scale, real privacy, and the user actually learns how an AI API works. For anyone without a key, I point them to Groq — free, no credit card, fast as hell: Dumb pages, smart services app/ ├── pages/ # only renders, no logic ├── services/ # all data and AI logic lives here └── utils/ # config, CSS, shared sidebar Each page is as dumb as possible — it just calls a service and renders the result. All TMDb calls, normalization, and AI prompting stay in services/. Made the whole thing much easier to iterate on. What was actually hard The system prompt. Getting the AI to respond like a real curator took more iteration than I expected. The fix that worked was enforcing a strict output format: SYSTEM_PROMPT = """You are CineAntologia AI, a film curator specialized in movies and TV shows from every decade. Always end your response with suggestions in this exact format: Title (Year) — reason in one line Minimum 4, maximum 6 suggestions.""" That fixed format made a huge difference in consistency. Normalizing TMDb data. Movies have title and release_date. Shows have name and first_air_date. I wrote a _normalize() function that flattens everything into the same schema before any page touches the data: def _normalize ( items : list ) -> list [ dict ]: out = [] for item in items : mt = item . get ( " media_type " , " movie " ) if mt == " person " : continue title = item . get ( " title " ) or item . get ( " name " ) or " — " date = item . get ( " release_date " ) or item . get ( " first_air_date " ) or "" year = int ( date [: 4 ]) if date and len ( date ) >= 4 else None out . append ({ " id " : item . get ( " id " ), " title " : title , " year " : year , " type " : " Movie " if mt == " movie " else " Series " , " genres " : [...], " synopsis " : item . get ( " overview " , "" ), " poster " : poster_url ( item . get ( " poster_path " )), " rating " : round ( item . get ( " vote_average " , 0 ), 1 ), }) return out CSS in Streamlit. Streamlit has strong opinions about styling. Custom dark theme with Bebas Neue + Inter required injecting CSS via st.markdown() with unsafe_allow_html=True. Not pretty, but it works. What's next Anime and K-drama — huge audience, badly served by recommendation tools Oscar Hall of Fame since 1929 — every category, with poster and where to watch today Semantic search — embeddings to search by vibe, not just title Specialized agents — horror agent, sci-fi agent, drama agent Try it 🎬 ( https://cineantologia-ai.streamlit.app ) For the AI Chat, grab a free Groq key at https://console.groq.com — no credit card, takes 2 minutes. Feedback, suggestions, and PRs are very welcome. Still early, lots of room to grow. Built with Python, Streamlit, TMDb API, and coffee. I've been a movie person my whole life. Growing up in the 80s and 90s, I'd go to the video store without knowing what I wanted and walk out with five tapes. Browsing shelves, reading back covers, picking something just because the title was weird. It was slow and it worked. Today you open Netflix with 10,000 options and spend 40 minutes picking nothing. The problem isn't lack of content. It's lack of curation. So I built something about it. What I built CineAntologia AI — a public catalog of movies and TV shows from the 80s to today, with an AI chat that works like a film curator instead of a search engine. You describe what you want to feel while watching: "Something like Stranger Things, that 80s atmosphere and suspense" "Heavy crime drama like The Wire" "Philosophical sci-fi in the style of Blade Runner" And it gives you suggestions with actual cultural context — not just a list. Live demo: ( https://cineantologia-ai.streamlit.app ) The stack Nothing exotic: Python 3.11 Streamlit — for the UI and deploy TMDb API — free, great data, covers posters, genres, ratings, where to watch OpenAI GPT-4o-mini or Groq for the AI chat Streamlit Community Cloud — free deploy for public repos No database in v1. No auth. No over-engineering. Three decisions worth talking about Ship on day one I pushed the repo public and deployed the moment it minimally worked. No months of polishing in private. When it's a drawer project you optimize for the code. When it's public you optimize for the person using it. That shift in perspective is worth the discomfort of shipping something imperfect. Users bring their own API key The AI Chat uses GPT-4o-mini or Groq. Instead of covering API costs for everyone — unpredictable at scale — users paste their own key in the sidebar. It lives only in their browser session, never hits my server. Three wins: zero cost to scale, real privacy, and the user actually learns how an AI API works. For anyone without a key, I point them to Groq — free, no credit card, fast as hell: Dumb pages, smart services app/ ├── pages/ # only renders, no logic ├── services/ # all data and AI logic lives here └── utils/ # config, CSS, shared sidebar Each page is as dumb as possible — it just calls a service and renders the result. All TMDb calls, normalization, and AI prompting stay in services/. Made the whole thing much easier to iterate on. What was actually hard The system prompt. Getting the AI to respond like a real curator took more iteration than I expected. The fix that worked was enforcing a strict output format: SYSTEM_PROMPT = """You are CineAntologia AI, a film curator specialized in movies and TV shows from every decade. Always end your response with suggestions in this exact format: Title (Year) — reason in one line Minimum 4, maximum 6 suggestions.""" That fixed format made a huge difference in consistency. Normalizing TMDb data. Movies have title and release_date. Shows have name and first_air_date. I wrote a _normalize() function that flattens everything into the same schema before any page touches the data: def _normalize ( items : list ) -> list [ dict ]: out = [] for item in items : mt = item . get ( " media_type " , " movie " ) if mt == " person " : continue title = item . get ( " title " ) or item . get ( " name " ) or " — " date = item . get ( " release_date " ) or item . get ( " first_air_date " ) or "" year = int ( date [: 4 ]) if date and len ( date ) >= 4 else None out . append ({ " id " : item . get ( " id " ), " title " : title , " year " : year , " type " : " Movie " if mt == " movie " else " Series " , " genres " : [...], " synopsis " : item . get ( " overview " , "" ), " poster " : poster_url ( item . get ( " poster_path " )), " rating " : round ( item . get ( " vote_average " , 0 ), 1 ), }) return out CSS in Streamlit. Streamlit has strong opinions about styling. Custom dark theme with Bebas Neue + Inter required injecting CSS via st.markdown() with unsafe_allow_html=True. Not pretty, but it works. What's next Anime and K-drama — huge audience, badly served by recommendation tools Oscar Hall of Fame since 1929 — every category, with poster and where to watch today Semantic search — embeddings to search by vibe, not just title Specialized agents — horror agent, sci-fi agent, drama agent Try it 🎬 ( https://cineantologia-ai.streamlit.app ) For the AI Chat, grab a free Groq key at https://console.groq.com — no credit card, takes 2 minutes. Feedback, suggestions, and PRs are very welcome. Still early, lots of room to grow. Built with Python, Streamlit, TMDb API, and coffee.