PR

【FAISS】駅メモ とは?(ゲーム)の初心者のために「検索エンジンwith LLM」を作ってみた話

Rintaが書いた

皆さんこんにちは。
久しぶりの更新です。
何かとコンテストとテストが被ってしばらく更新できていませんでした
久しぶりの今回はFAISSを使ってみたときの感想です

前からえっちなサイトだけの検索エンジンが作ってみたいと思っていたのですが
中々機会がなかったので作っていなかったのですが今回駅メモが身内ではやって、いろいろなゲームの仕様が検索したくなったので作ってみました
その時のメモです

ちなみに今ちょうど聞いているのは「青空のラプソディ 」です

使ったコード、データベースはGithubに上がってます
完成形が見たい方は一番下行ってください

スポンサーリンク
スポンサーリンク
スポンサーリンク
  1. FAISS とは
  2. データ集めよう
    1. GitHub Copilot
    2. 1. サイトマップの取得と解析
    3. 2. ローカル状態の読み書き
    4. 3. 差分チェック
    5. 4. ページのスクレイピング
    6. 5. テキストのチャンク分割
    7. 6. ベクトル化とFAISSインデックス作成
    8. 7. 状態の保存
      1. その他の特徴
  3. app.py
    1. GitHub Copilot
  4. 主な構成・処理の流れ
    1. 1. UIのセットアップ
    2. 2. RAGエージェントの構築
    3. 3. チャット履歴管理
    4. 4. ユーザー入力受付・AI応答
    5. 5. エラー処理・ガード
  5. 特徴・工夫点
  6. まとめ
  7. 動作!
  8. まとめ!
  9. 書いてたときのBGMたち
    1. Yunomi & nicamoq – インドア系ならトラックメイカー
    2. 切り絵でキルミーダンス -Kill Me Baby Dance
    3. 《キルミーベイベー》 NCED ふたりのきもちのほんとのひみつ Full Version
    4. テトリス / 重音テトSV
    5. 【洛天依翻唱】元素週期表之歌(2014高考獻禮系列)
    6. Nightcore – Sweet Little Bumblebee (Lyrics)
    7. キャンディークッキーチョコレート 🍭🍪🍫初音ミク・重音テトSV
    8. 裏命ちゃんのフクオカトリップ奇騒曲 / 南ノ南 FEAT りめちゃんとキャストの皆様
    9. 【初音ミク】 ビノミ 【オリジナル】
    10. レビテト / LOL feat. 重音テトSV
    11. お返事まだカナ💦❓おじさん構文😁❗️ / 雨衣
    12. 【ロシデレ】「気まぐれロマンティック」|ノンクレジットエンディング(第11話)
    13. 月島きらり「バラライカ」 ビデオクリップ
    14. ano「許婚っきゅん」Music Video
    15. SAKURAスキップ
    16. fhána / 青空のラプソディ – MUSIC VIDEO

FAISS とは

一言で言うと、FAISSは「大量のデータの中から、似ているものを超高速で見つけるためのツール」です。正式名称は「Facebook AI Similarity Search」と言います。Facebook(元meta)が作って、みんなが使えるように公開してくれています。

「似ているものを見つける」って、例えばどんな時に使うかというと…

  • 画像検索: 「この画像に似た画像はどれ?」って探すとき
  • レコメンデーション: 「この曲が好きなら、こんな曲もどうですか?」っておすすめするとき
  • 自然言語処理: 「この文章と似た意味の文章はどれ?」って見つけ出すとき

などなど、色々な場面で役に立つんです。

データ集めよう

とりあず読み込ますためのデータを集めましょう。
今回はhttps://ek1mem0.wiki.fc2.com をインデックスしました。
サーバー負荷回避(←重要)のために3秒間隔でアクセスして夜間に行っています。
いつも通りクソコードをchatgptに整形してもらってます。

# prepare.py (コメント追加&変数名最適化版)

import requests
from bs4 import BeautifulSoup
import time
import os
import pickle
import json
from lxml import etree
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain.docstore.document import Document

# --- 設定項目 ---
# 1. サイトマップのURL
WIKI_SITEMAP_URL = "https://ek1mem0.wiki.fc2.com/sitemap.xml"

# 2. 埋め込みに使用するローカルモデル名
EMBEDDING_MODEL_NAME = "intfloat/multilingual-e5-large" 

# 3. FAISSインデックスの保存先ディレクトリ名
FAISS_INDEX_PATH = "faiss_index"

# 4. サイトへの負荷対策設定
REQUEST_DELAY = 3.0  # 各リクエストの間に3.0秒の待機を入れる
HTTP_HEADERS = {
    'User-Agent': 'WikiChatbotScraper/1.0 (for personal AI project)'
}

# 5. 状態保存用のファイルパス
STATE_FILES = {
    "documents": "documents.pkl",
    "metadata": "metadata.json"
}

# --- 関数定義 ---

def load_sitemap(url):
    """サイトマップを解析し、URLと最終更新日の辞書を返す"""
    print(f"サイトマップを取得中: {url}")
    try:
        response = requests.get(url, headers=HTTP_HEADERS)
        response.raise_for_status()
        sitemap_xml = etree.fromstring(response.content)
        
        # XML名前空間を取得
        ns = {'s': sitemap_xml.nsmap.get(None, '')}
        
        url_data = {}
        for url_element in sitemap_xml.xpath('//s:url', namespaces=ns):
            loc = url_element.xpath('s:loc/text()', namespaces=ns)[0]
            lastmod = url_element.xpath('s:lastmod/text()', namespaces=ns)
            url_data[loc] = lastmod[0] if lastmod else 'N/A'
        
        print(f"サイトマップから {len(url_data)} 件のURLを検出しました。")
        return url_data
    except requests.RequestException as e:
        print(f"エラー: サイトマップの取得に失敗しました。 {e}")
        return {}
    except etree.XMLSyntaxError as e:
        print(f"エラー: サイトマップの解析に失敗しました。 {e}")
        return {}

def load_local_state():
    """ローカルに保存されたドキュメントとメタデータを読み込む"""
    if os.path.exists(STATE_FILES["documents"]) and os.path.exists(STATE_FILES["metadata"]):
        print("ローカルに保存された状態ファイルを読み込んでいます...")
        with open(STATE_FILES["documents"], "rb") as f:
            local_docs = pickle.load(f)
        with open(STATE_FILES["metadata"], "r") as f:
            local_meta = json.load(f)
        return local_docs, local_meta
    return [], {}

def save_local_state(docs, meta):
    """現在のドキュメントとメタデータをローカルに保存する"""
    print("現在の状態をローカルファイルに保存しています...")
    with open(STATE_FILES["documents"], "wb") as f:
        pickle.dump(docs, f)
    with open(STATE_FILES["metadata"], "w") as f:
        json.dump(meta, f, indent=2)

def scrape_single_page(url):
    """単一のページをスクレイピングしてDocumentオブジェクトを返す"""
    try:
        response = requests.get(url, headers=HTTP_HEADERS)
        response.raise_for_status()
        
        soup = BeautifulSoup(response.content, 'html.parser')
        main_content = soup.select_one('div#main')
        
        if main_content:
            title = soup.select_one('h2.title').get_text(strip=True) if soup.select_one('h2.title') else "無題"
            content_text = main_content.get_text(separator='\n', strip=True)
            return Document(
                page_content=content_text,
                metadata={'source': url, 'title': title}
            )
    except requests.RequestException as e:
        print(f"  - エラー: {url} の取得に失敗。スキップします。 ({e})")
    return None

# --- メイン処理 ---
if __name__ == "__main__":
    # 1. サイトマップとローカルの状態を読み込む
    sitemap_data = load_sitemap(WIKI_SITEMAP_URL)
    local_docs, local_meta = load_local_state()

    # 2. 更新が必要なURLを特定する
    urls_to_update = []
    for url, lastmod in sitemap_data.items():
        if url not in local_meta or local_meta.get(url) != lastmod:
            urls_to_update.append(url)
            
    print(f"\n差分チェックの結果: {len(urls_to_update)} 件の新規・更新ページが見つかりました。")

    if not urls_to_update and os.path.exists(FAISS_INDEX_PATH):
        print("すべてのデータは最新です。処理を終了します。")
    else:
        # 3. 変更があったページのみスクレイピング
        if urls_to_update:
            print("ページの取得と更新を開始します...")
            updated_docs_map = {doc.metadata['source']: doc for doc in local_docs}
            
            for i, url in enumerate(urls_to_update):
                print(f"  [{i+1}/{len(urls_to_update)}] {url} を取得中...")
                new_doc = scrape_single_page(url)
                if new_doc:
                    updated_docs_map[url] = new_doc
                time.sleep(REQUEST_DELAY) # 負荷対策
            
            # サイトマップに存在するページのみに絞り込む (削除されたページを除外)
            final_docs = [doc for url, doc in updated_docs_map.items() if url in sitemap_data]
            print("ページの更新が完了しました。")
        else:
            final_docs = local_docs

        # 4. テキストをチャンクに分割
        print("\nテキストのチャンク分割を開始します...")
        text_splitter = RecursiveCharacterTextSplitter(
            chunk_size=1000,
            chunk_overlap=100,
        )
        chunked_docs = text_splitter.split_documents(final_docs)
        print(f"チャンク分割が完了しました。合計 {len(chunked_docs)} チャンク。")

        # 5. FAISSインデックスを再構築・保存
        if chunked_docs:
            print("\nFAISSインデックスの構築を開始します...")
            embeddings = HuggingFaceEmbeddings(model_name=EMBEDDING_MODEL_NAME)
            vectorstore = FAISS.from_documents(chunked_docs, embeddings)
            vectorstore.save_local(FAISS_INDEX_PATH)
            print(f"FAISSインデックスを '{FAISS_INDEX_PATH}' に保存しました。")
            
            # 6. 最新の状態でローカルファイルを更新
            save_local_state(final_docs, sitemap_data)
        else:
            print("処理するドキュメントがないため、インデックスは作成されませんでした。")

        print("\n事前準備がすべて完了しました。 `app.py` を実行してください。")

余談ですが、gemini cli流行ってますよね。私はnanoでプログラミングはあまりしなく(大体アップロードしてwgetで落としてる)、github copilotとの兼用が面倒そうなのでまだ試していません。友達の使用感聞いて使ってみます。

実行すると保存されていくので放置してください。
実行されるとなんかファイルが出るはずです。

このコードについて解説して

screiping.py:142

GitHub Copilot

1 参照 使用済み

このscreiping.pyは、特定のWikiサイト(FC2 Wiki)の全ページを自動でクロール・スクレイピングし、テキストを分割・埋め込みベクトル化し、FAISSインデックスとして保存する「事前準備スクリプト」です。
LangChainやFAISS、HuggingFaceの埋め込みモデルを使い、AIチャットボットなどの検索用データベースを作る用途が想定されています。

主な処理の流れは以下の通りです。


1. サイトマップの取得と解析

  • load_sitemap()でサイトマップXMLを取得し、全ページのURLと最終更新日を辞書として返します。

2. ローカル状態の読み書き

  • 以前保存したドキュメント(ページ内容)とメタデータ(URLごとの更新日)をpickle/jsonで保存・読み込みします。

3. 差分チェック

  • サイトマップとローカル状態を比較し、更新・新規ページのみを抽出します。

4. ページのスクレイピング

  • scrape_single_page()で各ページを取得し、本文テキストとタイトルを抽出、LangChainのDocumentとして格納します。

5. テキストのチャンク分割

6. ベクトル化とFAISSインデックス作成

  • HuggingFaceの多言語埋め込みモデルで各チャンクをベクトル化し、FAISSで検索用インデックスを作成・保存します。

7. 状態の保存

  • 最新のドキュメントとメタデータをローカルファイルに保存します。

その他の特徴

  • サイトへの負荷軽減のため、リクエスト間に1秒の遅延を入れています。
  • 途中でエラーが発生してもスキップして続行します。
  • すべての処理が完了したら「app.pyを実行してください」と案内が出ます。

まとめ
このスクリプトは、Wikiサイトの内容をAI検索用に自動で最新化・ベクトル化するための「データ準備自動化ツール」です。
AIチャットボットや検索システムの前処理として使われます。

AI生成を記事に貼り付けるのは賛否ありますが少なくとも私の解説よりはマシなはずです。

app.py

裏で、qwen3乗っけてLMstudio動かしてます。

# app.py (コメント追加&変数名最適化版)

import streamlit as st
import pickle
from langchain_community.vectorstores import FAISS
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain.tools.retriever import create_retriever_tool
from langchain_core.prompts import ChatPromptTemplate

# --- 設定項目 ---
EMBEDDING_MODEL_NAME = "intfloat/multilingual-e5-large"
FAISS_INDEX_PATH = "faiss_index"
LMSTUDIO_BASE_URL = "http://localhost:1234/v1"

# --- アプリの基本設定 ---
st.set_page_config(page_title="Station Remember - 駅メモWikiチャットボット", layout="wide")

# --- サイドバー ---
with st.sidebar:
    st.header("Station Remember - 駅メモWikiチャットボット")
    st.write("このチャットボットは、駅メモ!wikiの情報を元に、あなたの質問にAIが回答します。")
    st.write("---")
    st.header("使い方")
    st.info("下のチャット入力欄に質問を入力してください。AIがWikiの情報を検索・分析し、その思考プロセスと共に回答を生成します。")
    if st.button("チャット履歴をクリア", use_container_width=True):
        st.session_state.messages = []
        st.rerun()

# --- RAG Agentのセットアップ ---
@st.cache_resource
def load_agent():
    """FAISSのみで検索するAgentをロードします。"""
    try:
        # FAISS (ベクトル検索) Retrieverの準備
        embeddings = HuggingFaceEmbeddings(model_name=EMBEDDING_MODEL_NAME)
        vectorstore = FAISS.load_local(FAISS_INDEX_PATH, embeddings, allow_dangerous_deserialization=True)
        faiss_retriever = vectorstore.as_retriever(search_kwargs={"k": 30})  # ← ここを20に変更
    except Exception as e:
        st.error(f"データベースの読み込みに失敗しました。`prepare.py`を正常に実行しましたか?\nエラー: {e}")
        return None

    llm = ChatOpenAI(base_url=LMSTUDIO_BASE_URL, api_key="not-needed", temperature=0.2, streaming=True)

    retriever_tool = create_retriever_tool(
        faiss_retriever,  # FAISSのみ
        name="faiss_ekimemo_wiki_search",
        description="駅メモ!のでんこ、スキル、イベント、編成などに関する情報をベクトル検索で探します。"
    )
    tools = [retriever_tool]

    prompt = ChatPromptTemplate.from_messages([
        ("system", """
あなたは「駅メモ!」の専門家AIアシスタントです。
ユーザーの質問に答えるために、提供されたツールを駆使して情報を収集・分析し、統合して最終的な回答を生成してください。

### 行動原則:
1. **段階的調査:** 複雑な質問は、複数のステップに分解して、段階的にツールを使って調査してください。
2. **情報集約と結論:** ツールを複数回使用して情報を十分に収集した後は、**必ず全ての情報を統合し、最終的な回答を生成して思考を終了してください。**
3. **自己修正:** もし同じような検索を繰り返していることに気づいた場合は、検索キーワードを変えるか、すでにある情報だけで回答を作成してください。**無限に検索を続けないでください。**
4. **日本語での回答:** 回答は必ず日本語で行ってください。
5. **不明な場合:** Wikiの情報にないことは「分かりません」と正直に回答してください。
6. **簡潔な回答:** ユーザーに最終的な回答を返す時、思考プロセスやツール使用について言及する必要はありません。結論だけを述べてください。
"""),
        ("placeholder", "{chat_history}"),
        ("human", "{input}"),
        ("placeholder", "{agent_scratchpad}"),
    ])

    agent = create_tool_calling_agent(llm, tools, prompt)
    agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, handle_parsing_errors=True, max_iterations=10)
    return agent_executor

# --- メイン画面 ---
st.title("チャット")

agent_executor = load_agent()
if "messages" not in st.session_state:
    st.session_state.messages = []

for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])

if user_question := st.chat_input("「でんこ」や編成について質問してください"):
    st.session_state.messages.append({"role": "user", "content": user_question})
    with st.chat_message("user"):
        st.markdown(user_question)

    with st.chat_message("assistant"):
        final_answer = ""
        workflow_steps = []
        think_steps = []

        # 思考プロセスと最終回答の表示場所を明確に分離
        workflow_placeholder = st.empty()
        answer_placeholder = st.empty()
        think_placeholder = st.empty()

        try:
            with st.spinner("AIが思考中です..."):
                response_generator = agent_executor.stream(
                    {"input": user_question, "chat_history": st.session_state.messages}
                )

                for chunk in response_generator:
                    # <think>パートの抽出
                    if "actions" in chunk:
                        for action in chunk["actions"]:
                            # <think>タグが含まれている場合は分離
                            tool_input = str(action.tool_input)
                            if "<think>" in tool_input and "</think>" in tool_input:
                                before, think_part = tool_input.split("<think>", 1)
                                think_content, after = think_part.split("</think>", 1)
                                workflow_steps.append(f"**検索実行:** `{before.strip() + after.strip()}`")
                                think_steps.append(think_content.strip())
                            else:
                                workflow_steps.append(f"**検索実行:** `{tool_input}`")
                    elif "steps" in chunk:
                        workflow_steps.append("**結果分析:** (関連情報を取得し、次の行動を検討中...)")
                    # <think>パートがoutputに含まれる場合も分離
                    if "output" in chunk:
                        output = chunk.get("output", "")
                        if "<think>" in output and "</think>" in output:
                            before, think_part = output.split("<think>", 1)
                            think_content, after = think_part.split("</think>", 1)
                            final_answer = before.strip() + after.strip()
                            think_steps.append(think_content.strip())
                        else:
                            final_answer = output

                    # 思考プロセスをリアルタイムで更新
                    with workflow_placeholder.expander("AIの思考プロセスを見る", expanded=True):
                        st.markdown("\n\n---\n\n".join(f"- {s}" for s in workflow_steps))
                    # <think>パートを別枠で表示・非表示切替
                    if think_steps:
                        with think_placeholder.expander("AIの<think>パート(思考の詳細)", expanded=False):
                            st.markdown("\n\n---\n\n".join(think_steps))

        except Exception as e:
            final_answer = f"申し訳ありません、処理中にエラーが発生しました。\n`{e}`"

        if not final_answer:
            final_answer = "申し訳ありません、思考がループしたか、時間内に結論に達しませんでした。質問を変えるか、より具体的にしてみてください。"

        answer_placeholder.markdown(final_answer)
        st.session_state.messages.append({"role": "assistant", "content": final_answer})

ちなみに、streamlitで作ってるのにHTMLで一部書いてて30分くらいなんでだ~と格闘していました;;

こっちもお願い

app.py:1-148

GitHub Copilot

1 参照 使用済み

このapp.pyは、先ほどのscreiping.pyで作成したFAISSインデックスを使い、駅メモ!Wikiの情報をAIチャット形式で検索・回答できるStreamlit製Webアプリです。
LangChainのRAG(検索拡張生成)エージェントを活用し、ユーザーの質問にAIがWiki情報を元に日本語で答えます。


主な構成・処理の流れ

1. UIのセットアップ

  • streamlitでWebアプリのページ設定、サイドバー(使い方・説明・履歴クリアボタン)を作成。
  • メイン画面にチャットUIを表示。

2. RAGエージェントの構築

  • load_agent()でFAISSインデックスをロードし、HuggingFace埋め込みモデルでベクトル検索できるretrieverを作成。
  • OpenAI互換API(例: LM Studio)経由でローカルLLMを呼び出し、LangChainのエージェントとして組み立て。
  • エージェントには「複数回検索→情報統合→日本語で簡潔に回答」などの行動原則をプロンプトで指示。

3. チャット履歴管理

  • st.session_state.messagesでユーザーとAIのやりとりを保存・表示。
  • 履歴クリアボタンでリセット可能。

4. ユーザー入力受付・AI応答

  • チャット入力欄で質問を受け付け、履歴に追加。
  • AIエージェントが質問を受けて、FAISS検索→情報分析→回答生成を段階的に実行。
  • AIの「思考プロセス」や「<think>パート」(内部推論)を展開式で表示可能。
  • 最終回答のみをユーザーに返す(プロセスは省略)。

5. エラー処理・ガード

  • インデックスやモデルのロード失敗時はエラーメッセージを表示。
  • AIが結論に達しない場合やループ時もメッセージを返す。

特徴・工夫点

  • RAG(検索拡張生成):AIがWiki情報を検索しながら回答するため、最新・正確な情報を返せる。
  • 思考プロセスの可視化:AIがどんな検索・推論をしたかをユーザーが確認できる。
  • 日本語特化:プロンプトで日本語回答を徹底。
  • ローカルLLM対応:LM Studioなどのローカル大規模言語モデルをAPI経由で利用可能。
  • Streamlitで手軽にWeb化:インストール後すぐにWebチャットボットとして使える。

まとめ

このapp.pyは、駅メモ!Wikiの情報をAIが検索・分析し、チャット形式で日本語回答するRAG型AIチャットボットWebアプリです。
screiping.pyで作ったデータベースを活用し、ユーザーの質問に専門的かつ簡潔に答えることができます。

あ、LLMのcontext lengthは2万トークン以上にしといたほうがいいですよ。

動作!

フットバースは、駅メモ!におけるアイテムで、「相手の妨害を受けず確実にリンクする」機能を持ちます。以下が主な特徴です:

  1. 効果
    • 相手を「吹っ飛ばす」として、ほぼ確実にリンク成功(リンク成功時ボーナス100pt(+α)を得られる)。
    • ダメージを与えないため、アクセス時の経験値は得られず、新駅ボーナスのみ取得可能。
    • 特定の状況(ダル太夫の無敵スキル発動時など)では使用回数が消費されない。
  2. 入手方法
    • ガチャ(一部)、奪取erポイント交換、ダッチューのスキル(1日1回)。
    • 過去には「弱」「中」「強」の3種類がありましたが、2017年以降は「弱相当」に統一され、「中」「強」は廃止。
  3. 弱体化と評価
    • 効果が大幅に修正(使用回数制限など)され、ガチャでの排出率も削減。
    • 一部ユーザーからは過剰な強さへの批判があるが、運営は「これ以上の弱体化はない」と公式で明言。
  4. 注意点
    • 誰もいない駅にアクセスした場合、使用回数が消費されない。
    • スキル(例:あるはのカウンター)により無効化される可能性あり。

詳細なステータスやスキルアップ情報も含まれています

まとめ!

結構精度はいいと思いますよ
推論モデルなので相性もいいですし。

物理と国語の授業中に頑張ったかいがありますね。

ちなみに今はインドア系ならトラックメイカー聞いてます。
カラオケで歌ってみたい
そういえば最近DAM4windwos買ったんですよ。
前のが壊れてウーファー付きのスピーカー買ったのでせっかくなのでと思って。
けっこういいですよ

書いてたときのBGMたち

3:17再生中

Yunomi & nicamoq – インドア系ならトラックメイカー

airi

3938万 回視聴Japanese & EDM songs will now be on the new channel: airomusic – https://www.youtube.com/@airomusic Soundcloud – https://soundcloud.com/tkrism/indoor Genre: J-Pop Artwork (http://bit.ly/2N9M…1:30再生中

切り絵でキルミーダンス -Kill Me Baby Dance

ポセイドン太郎

838万 回視聴キルミーベイベー アニメ10周年!! これを作っていたら、年末年始がすべて溶けました。 偉大なる元ネタ様:キルミーベイベー ED2:57再生中

《キルミーベイベー》 NCED ふたりのきもちのほんとのひみつ Full Version

♡工藤新にゃん♡

624万 回視聴2:23再生中

テトリス / 重音テトSV

柊マグネタイト

7392万 回視聴Listen & Download : https://nex-tone.link/A00168049 柊マグネタイトと申します。 どうしてこんな目に。 作詞作編曲MIX&Mastering映像 / 柊マグネタイト https://twitt…3:51再生中

【洛天依翻唱】元素週期表之歌(2014高考獻禮系列)

1.7万 回視聴原影片:http://www.bilibili.com/video/av1187880 版權皆屬於原作者,如有侵權,會立即刪除 原搬運者把影片刪除了!這邊幫忙搬運~☆2:54再生中

Nightcore – Sweet Little Bumblebee (Lyrics)

RubyChan’s Nightcore

1472万 回視聴✔ Bambee – Bumble Bee ✔ Lyrics in video! ▶️ Previous video: https://youtu.be/5UsQHChmA-4 ──────────────────────────────…2:49再生中

キャンディークッキーチョコレート 🍭🍪🍫初音ミク・重音テトSV

はろける・HALLO CEL

3191万 回視聴Streaming→ https://linkco.re/P7dcz7dA (Spotify, Apple music, etc.) Happy Halloween🍭🍪🍫 この曲は、お菓子大好き☆なテトミクが かわいいダンスを踊る、とっ…3:31再生中

裏命ちゃんのフクオカトリップ奇騒曲 / 南ノ南 FEAT りめちゃんとキャストの皆様

南ノ南

289万 回視聴りめ「フクオカに住も♡」 ↓※以下作品の続編です https://youtu.be/hXabKIYl_Yo?si=vuwSqbHhkJNTrDez https://youtu.be/978nElfOkL8?si=oixCWZ7fpnmoav4v — クリエイターフ…3:04再生中

【初音ミク】 ビノミ 【オリジナル】

MARETU

2307万 回視聴・off vocal https://www.dropbox.com/scl/fi/65gm9foa13wm05fzts8tx/binomi-off-vocal.zip?rlkey=fnqfcimlk3ng4hy4mevz7n4p9&dl=02:18再生中

レビテト / LOL feat. 重音テトSV

PPP Sounds

382万 回視聴Streaming→ https://linkco.re/39qdS4vA ■Title / レビテト ■Vocal / 重音テトSV ■Music & Animation / LOL x:https://x.com/hitorikanawo ■ニコニコ / https://www.nicovideo.jp/…3:08再生中

お返事まだカナ💦❓おじさん構文😁❗️ / 雨衣

吉本おじさん

847万 回視聴#お返事まだカナおじさん構文 #雨衣 #雨衣のおうた 🎙️ Vocal : 雨衣 🎶 Music / Movie : 吉本おじさん 🎨 Illustration : 透真 ( X : https://x.com/tomayuki_09…1:31再生中

【ロシデレ】「気まぐれロマンティック」|ノンクレジットエンディング(第11話)

【公式】ロシデレチャンネル

72万 回視聴エンディングテーマ(第11話):アーリャ(CV:上坂すみれ)「気まぐれロマンティック」 配信:https://nex-tone.link/HKAaTfBWh #11エンディングアニメーシ…3:43再生中

月島きらり「バラライカ」 ビデオクリップ

アップフロントチャンネル

2806万 回視聴月島きらり starring 久住小春(モーニング娘。) 「バラライカ」 ビデオクリップ 2006年10月25日発売 EPCE-5427【初回生…3:09再生中

ano「許婚っきゅん」Music Video

ano official channel

740万 回視聴streaming & download / https://tf.lnk.to/iinazukkyun 「許婚っきゅん」 (読み:いいなずっきゅん) 作詞: あの / 真部脩一 作曲: 真部脩一 編曲: TAKU INOUE ==========…3:53再生中

SAKURAスキップ

fourfolium – Topic

91万 回視聴Provided to YouTube by KADOKAWA CORPORATION SRAV SAKURAスキップ · fourfolium TVアニメ「NEW GAME!」オープニングテーマ ℗ KADOKAWA Released on: 2024-03-27 Auto-generated…4:38再生中

fhána / 青空のラプソディ – MUSIC VIDEO

fhána Official Channel

5820万 回視聴

コメント

タイトルとURLをコピーしました