[敗家紀錄] 如何在台灣啟動 Rayban Meta Glasses 的 AI 功能 (2025 更新)

image-20241107112526283

前言

其實我一直都蠻喜歡 Rayban 的太陽眼鏡,之前也有買一副。 這次的 Meta AI 太陽眼鏡之前就蠻想買的,但是想說不知道會不會有新的。還好看到近期沒有出新的太陽眼鏡後,就下手買了。 這一篇會稍微紀錄相關的購買方式跟如何在台灣啟動 Meta AI 在太陽眼鏡上。

怎麼有效購買: (透過托運)

Amazon: [Ray-Ban Meta Smart Glasses, Wayfarer](https://www.amazon.com/dp/B0CGXYNWBH)

用代購買的,兩週可以到台灣。用這個邀請連結,可以用 Amazon Prime 價格,還有 150 運費可以折抵 https://www.buyandship.com.tw/invite/7510252302 用我邀請鏈結,可以各拿 150 免運,從美國運回來只需要 170 台幣,等於 20 就可以運回 Rayban Meta 眼鏡。

購買相關流程:

  • 先開立 BuyAndShip 帳戶
  • 選取 Portland 倉庫位址:
    • 13822 NE AIRPORT WAY
    • PORTLAND, OR 97251-9614
    • United States
  • 在 Amazon 設定下填寫倉庫(因為 Portland 不用收稅)
  • 等 Amazon 發貨之後,會到 BuyAndShip 網頁上看到入庫。
  • BuyAndShip 會自動轉貼到 香港倉庫
  • BuyAndShip 設定平台合併貨物寄到台灣地址(跟付運費)

基本功能:

雷朋智能眼鏡 Rayban Smart Glasses 購買開箱心得:

- 聽音樂很不錯

- 拍照跟影片效果很不錯

- AI 功能台灣還不能用(修正: 可以用 VPN 啟動

接下來就等著出去玩可以拍風景 🙂

image-20241107153844070

如何在台灣啟動 MetaAI 功能

image-20241107153858264

更新:  2025/01/17 
因為在版本 V193 之後(大概是 2024/11 月底) 他好像會抓取 Meta 帳戶的區域,而非你安裝 App 的網路 IP 。所以原來方法無法正常成功,需要透過以下修改。
  • 我的方式是在 VPN 連著的情況下開一個新的 meta account。 (需要 VPN 連線到美國)
  • 這樣就是要把眼鏡 factory reset 之後重新用新的 meta account 來配對綁定。

[遊戲天國] 血源詛咒 一 PS5全破紀錄

image-20241031235812644

前言

一年前自從買了 PlayStation Portal 之後,其實就逐漸有在打「血源詛咒」,但是老實說血源詛咒難度真的不小。就算我「隻狼」也全破了,還是很不習慣血源詛咒的拼血模式。

因為血源詛咒比較難找人一起打(我是都沒招過啦),所以打起來卡關的時候真的很絕望的勒。

必須得說,血源詛咒真的光線太暗。由於用 HDR 電視,根本都看不到怪。一定要拿火把,不然都沒有的看。

攻略重點

記得「鋸矛」是最好用的武器,攻速快(沒有之一),傷害又相當嚇人。 還是記得要刷金杯去把「血岩」刷滿。

比較難打魔王 - 愚笨蜘蛛羅姆

真的覺得很難打,因為每次都抓不到訣竅。 正常來說,如果你可以在第一階段變身的時候,把他打斷。他就會讓你打到死。 但是「鋸矛」唯一的缺點就是失衡值不夠高,所以每次都讓他的小蜘蛛一湧而上。

第一階段要清完小蜘蛛,然後想辦法打斷。如果沒有……

第二階段開始就打完就跑,然後鏡頭要對著。避免遠程魔法。

受詛咒的路德維希 - 月光大劍我來啦

真的不好打,撕咬攻擊真的很麻煩。傷害又很大。記得繞在腳後面去打。第一下一定要躲掉,儘量往你的右前方躲,然後在後面刷他。 要小心連環技,這是比較麻煩的部分。 第二階段很好打,直接上去應剛。重擊馬上就可以處決,我遇到的第二階段是初見殺。

比尾王更難打的 - 星辰鐘塔的瑪麗亞女士

槍反是唯一解方 + 鋸矛10

  • 爪印 兩個 15% + 10%
  • 內臟爆擊 恢復 HP

第一階段: 往你的右前方閃 第二階段之後: 往你的左前方閃 一個重點: 第二階段轉變的時候可以打兩三刀,血噴完之後可以衝上去再給兩三刀

[Google Cloud] 如何在 GCP Cloud Run 上面透過 LangChain 取得 YouTube 的相關資訊

image-20241006183038597

前言

之前提到過,我開發了一個「透過 IFTTT 與 LangChain 打造的科技時事 LINE Bot」,這個 Bot 可以幫助我獲取所需的資訊。然而,最近我發現許多 YouTube 影片中包含了豐富的技術資訊,這促使我思考如何有效地獲取 YouTube 字幕資訊以便整理和分析。

雖然 LangChain YouTube Transcripts Loader 可以快速獲取字幕資訊,但在部署到 Google CloudRun 時卻遇到了一些問題。本文將分享我遇到的問題及解決過程,希望能對大家有所幫助。

使用 LangChain YouTube Transcripts Loader 獲取影片字幕

根據 LangChain 的教學文件 (YouTube transcripts),我們可以使用這個套件來獲取帶有字幕的 YouTube 影片,並通過分析快速了解影片內容。以下是一些範例程式碼:

%pip install --upgrade --quiet pytube
loader = YoutubeLoader.from_youtube_url(
    "https://www.youtube.com/watch?v=QsYGlZkevEg", add_video_info=True
)
loader.load()

在 GCP CloudRun 上無法獲取資料

當我嘗試將程式碼部署到 Google CloudRun 時,使用以下程式碼:

def summarized_from_youtube(youtube_url: str) -> str:
    """
    Summarize a YouTube video using the YoutubeLoader and Google Generative AI model.
    """
    try:
        print("Youtube URL: ", youtube_url)
        # Load the video content using YoutubeLoader
        loader = YoutubeLoader.from_youtube_url(
            youtube_url, add_video_info=True, language=["zh-Hant", "zh-Hans", "ja", "en"])
        docs = loader.load()

        print("Pages of Docs: ", len(docs))
        # Extract the text content from the loaded documents
        text_content = docs_to_str(docs)
        print("Words: ", len(text_content.split()),
              "First 1000 chars: ", text_content[:1000])

        # Summarize the extracted text
        summary = summarize_text(text_content)

        return summary
    except Exception as e:
        # Log the exception if needed
        print(f"An error occurred: {e}")
        return ""

結果卻完全無法獲取任何資料,沒有錯誤訊息,但結果是空的。

image-20241005230731585

Debug 1: 在 Colab 上測試相同程式碼

可以參考以下的 gist 程式碼

image-20241005230200026

起初我以為是程式碼的問題,但在 Colab 上卻能正常執行。看來需要使用 GoogleApiYoutubeLoader

在 Google CloudRun 上使用 LangChain Youtube Loader

要在 CloudRun 上使用 GoogleApiYoutubeLoader,需要遵循以下步驟:

image-20241005231500039

image-20241006001807126

使用 Secret Manager 獲取資料

  • 首先需要在環境變數中設定 Project ID PROJECT_ID
  • 然後將資料預先存放在 youtube_api_credentials 中。
def get_secret(secret_id):
    logging.debug(f"Fetching secret for: {secret_id}")
    client = secretmanager.SecretManagerServiceClient()
    name = f"projects/{os.environ['PROJECT_ID']}/secrets/{secret_id}/versions/latest"
    response = client.access_secret_version(request={"name": name})
    secret_data = response.payload.data.decode("UTF-8")
    logging.debug(f"Secret fetched successfully for: {secret_id}, {secret_data[:50]}")
    return secret_data

這樣就可以安全地透過 Secret Manager 獲取資料。

在 GCP CloudRun 上部署範例程式碼

程式碼: https://github.com/kkdai/gcp-test-youtuber

def load_youtube_data():
    try:
        logging.debug("Loading YouTube data")
        google_api_client = init_google_api_client()

        # Use a Channel
        youtube_loader_channel = GoogleApiYoutubeLoader(
            google_api_client=google_api_client,
            channel_name="Reducible",
            captions_language="en",
        )

        # Use Youtube Ids
        youtube_loader_ids = GoogleApiYoutubeLoader(
            google_api_client=google_api_client,
            video_ids=["TrdevFK_am4"],
            add_video_info=True,
        )

        # Load data
        logging.debug("Loading data from channel")
        channel_data = youtube_loader_channel.load()
        logging.debug("Loading data from video IDs")
        ids_data = youtube_loader_ids.load()

        logging.debug("Data loaded successfully")
        return jsonify({"channel_data": str(channel_data), "ids_data": str(ids_data)})
    except Exception as e:
        logging.error(f"An error occurred: {str(e)}", exc_info=True)
        return jsonify({"error": str(e)}), 500

成果

影片來源:https://www.youtube.com/watch?v=TrdevFK_am4

image-20241006004150943

[好書分享] 我的孩子是霸凌者

Image

作者: しろやぎ秋吾  譯者: 葉門  出版社:台灣角川 
出版日期: 2024/08/23 語言:繁體中文 

買書推薦網址:

前言:

這是 2024 年第 10 本讀完的書。週末的時候,我經常與小孩在誠品書局閱讀,有時候我會看一些雜誌與月刊,這一本書忽然在書架上吸引我的目光,內容不長但是影響很深遠。

內容摘要:

★當知道自己的孩子是「加害者」,父母該如何面對?
★留下的傷口絕無法輕易消逝。被道歉了,難道就一定要選擇原諒嗎?

「我可不記得有把妳教成這種孩子!」

最疼愛的女兒,竟與自己最憎恨的存在劃上等號。
不可原諒的霸凌事件,女兒居然是「加害者」。

關於孩子,原來我一無所知──

▍本書內容
新聞內容播報著「校園霸凌事件」的惡行。

『我的孩子絕不會這樣。』
──赤木加奈子認為那只是與自己無關的社會事件。
然而某天,她竟得知自己的女兒.愛,
在班上對同班同學.馬場小春有霸凌行為。
國中時曾遭受同儕霸凌的加奈子,對此難以忍受。
對自己孩子的厭惡、憤怒,不斷湧上心頭。

另一方面,接受了赤木家道歉後的小春卻開始拒絕上學。
對孩子的不捨、獨自承受外界的壓力,
小春的母親.千春,選擇向第三者傾訴煩惱。

此時網路上的一則匿名貼文成了導火線,
將這件事推向無法預測的方向──

對孩子的不信任感、夫妻間意見的相左、社群媒體上的批判…
被各式問題折磨的雙方家庭。
同時描寫「加害者」與「受害者」雙視角,以不同的視角探討校園霸凌!

心得:

如果你的小孩霸凌別人怎麼辦? 身為爸媽,或許第一句都會是「怎麼可能? 我才不會教導出這樣的小孩」。

小春跟小愛是從小的好朋友,忽然有一天小愛開始欺負小春,甚至在班上鼓勵與霸凌他。

小春後來不敢上課,他的媽媽打給小愛的爸媽報價道歉也沒有改善。小春完全不敢上課,小愛的媽媽雖然是霸凌者的爸媽,但是她小時候也是被霸凌的人。讓小愛的媽媽無地自容。

這本書深入從霸凌事件的兩個家庭去深入描繪。後來霸凌的影片被張貼出來,整件事情更是爆鍋。

小愛在路上被其他人霸凌,他的爸媽上了學校檢討會。 但是小春還是沒有辦法去上學。 或許這個時候、如果是電影,我們會說霸凌者活該。但是這是一個真實是一事件取材。霸凌者被揭開後,他有說要生活的。後來他們也是被逼迫離開學校。小愛同樣也受到不可抹滅的傷害。

[Gemini/Firebase] 個人資訊流 - 透過 IFTTT 與 LangChain 打造科技時事 LINE Bot

PlantUML diagram

近期

最近弄了一個資訊流,我覺得很有趣:- IFTTT 抓取 HN, HuggingFace 熱門文章

  • 發到 Webhook 裡面有 LangChain 抓爬蟲 + LangChain Summary
  • 發 LINE Bot 給自己看
  • 挑選自己喜歡的,直接貼到 Twitter (目前沒有付費 API 沒辦法發長文

程式碼: https://github.com/kkdai/gh-summarized-scheduler

IFTTT 設定

image-20240911222516788

image-20240911222612321

成果

image-20240911222313402

  • 有詳細的文章連結跟文章摘要。
  • 可以快速決定要不要進去看。
  • 整個格式也改成可以直接複製貼到 Twitter (我有付費發長文)

未來發展:

  • 照理說應該要可以一鍵發文到 twitter ,但是 X API 好貴(一百美)
  • 有想過用 IFTTT 來發文 Twitter 但是有長度限制 (128) ,不能用。
  • 不過 Threads API 似乎可以發長文。

[Gemini/Firebase] 土炮打造- 透過 Firebase 作為 Embedding Vector DB 透過 Gemini 來幫你的 Github Page Blog 做 RAG 服務

RAG

前提

我是一個很喜歡寫作的人,經常學習到東西都會記錄在這一個網站裡面。陸陸續續從 2002 年也寫了 20 多年的部落格。但是經常自己也是會忘記我曾經寫過什麼樣的內容。

之前我也曾經說明過:將我會的東西整理成部落格,主要不是為了幫助其他人,最重要是可以幫助到未來的自己。總有一天你會遇到類似的問題,而你的文章的思緒跟脈絡就跟你想的一樣。只要透過類似的關鍵字,馬上就可以找到你的回憶。

伴隨著 LLM 生成式 AI 的爆發性成長,其實再也不需要透過 Google Search 來搜尋自己寫過的內容。 其實你可以透過 RAG 的方式來直接去詢問一個聊天機器人來達到類似的工作。

相關的內容其實有很多,透過 LangChain 要來做一個 RAG 的功能更是相當的簡潔。 但是本篇文章將反樸歸真,透過 Python 與 Google Gemini 與 Firebase DB 直接告訴你如何打造 Embedding DB ,做成一個可以簡易的查詢功能的 RAG 應用。

完整相關程式碼:

https://github.com/kkdai/jekyll-rag-firebase

透過 Firebase Realtime DB 來當成 Vector DB

程式碼: https://github.com/kkdai/jekyll-rag-firebase/blob/main/embedding.py

image-20240823122740989

之前就一直在思考,究竟有沒有方式可以透過 Firebase 來做 Vector DB。其實是可以的,方式如下:

def generate_embedding(text):
    result = genai.embed_content(
        model="models/text-embedding-004",
        content=text,
        task_type="retrieval_document",
        title="Embedding of single string"
    )
    embedding = result['embedding']
    return embedding

這是一個透過 tex-embedding-004 的 gemini model ,可以幫助你直接將一串文字直接產生 Embedding 的 Vector Value 。

如果要將資料儲存在 Firebase 的資料庫內,其實也沒有那麼複雜。以下程式碼可以很快速了解。

def store_embedding(embedding_data):
    ref = db.reference('embeddings')
    ref.child(embedding_data['id']).set(embedding_data)
    print(f"Embedding data for {embedding_data['id']} stored successfully.")

完整範例大概如下:


# 範例文字
text_1 = "What is the meaning of life?"
text_2 = "How to learn Python programming?"
text_3 = "The quick brown fox jumps over the lazy dog."

# Embedding Vector Value
embedding_vector_1 = generate_embedding(text_1)
embedding_vector_2 = generate_embedding(text_2)
embedding_vector_3 = generate_embedding(text_3)

# 產生 DB data
embedding_data_1 = {
    'id': 'embedding_1',
    'vector': embedding_vector_1
}

embedding_data_2 = {
    'id': 'embedding_2',
    'vector': embedding_vector_2
}

embedding_data_3 = {
    'id': 'embedding_3',
    'vector': embedding_vector_3
}

# 儲存資料
store_embedding(embedding_data_1)
store_embedding(embedding_data_2)
store_embedding(embedding_data_3)

如何解析 Github Page 的 Blog 資料? (以 Jekyll 打造的 Blog 為例子)

範例部落格資料: https://github.com/kkdai/kkdai.github.io

程式碼:

image-20240823124515723

裡面會看到,公開的文章會放在 _posts 資料夾下面。 雖然裡面是 Markdown 的內容,但是這樣的內容往往更適合給 LLM 作為總結與 Embedding 使用。 接下來來看一下程式碼。

取得 Github Token

請參考這篇文章,有完整教學該如何取的 Github Token 作為 Github 操作之用。

讀取 Github 上面的檔案資料

def git_article(github_token, repo_owner, repo_name, directory_path):
    g = Github(github_token)
    repo = g.get_repo(f"{repo_owner}/{repo_name}")
    contents = repo.get_contents(directory_path)
    
    files_data = []
    
    while contents:
        file_content = contents.pop(0)
        if file_content.type == "dir":
            contents.extend(repo.get_contents(file_content.path))
        else:
            files_data.append({
                'file_name': file_content.name,
                'content': file_content.decoded_content.decode('utf-8')
            })
            print(f"Downloaded {file_content.name}")
    
    result = {
        'files': files_data,
        'total_count': len(files_data)
    }
    return result

這段程式碼,可以指定給他 tokenrepo_owner, repo_namedirectory_path 之後。他會將裡面的檔案一個個存取出來。

整個打包 - 讀取 Github Page 然後製作 Embedding Vector 在 Firebase 上

程式碼: https://github.com/kkdai/jekyll-rag-firebase/blob/main/blog_embedding.py

直接單獨執行這個程式碼即可。但是整個資料夾時間會比較久,也會花費相當程度的費用。

這部分程式碼中,請記得要將以下資料改成你的資料夾資料。

  • REPO_OWNER = ‘kkdai’
  • REPO_NAME = ‘kkdai.github.io’
  • DIRECTORY_PATH = ‘_posts’
def git_article(github_token, repo_owner, repo_name, directory_path):
    g = Github(github_token)
    repo = g.get_repo(f"{repo_owner}/{repo_name}")
    contents = repo.get_contents(directory_path)
    
    files_data = []

    while contents:
        file_content = contents.pop(0)
        if file_content.type == "dir":
            contents.extend(repo.get_contents(file_content.path))
        else:
            file_id = file_content.name.split('.')[0]
            if check_if_exists(file_id):
                print(f"File {file_id} already exists in the database. Skipping.")
                continue
            
            file_content_decoded = file_content.decoded_content.decode('utf-8')
            cleaned_content = remove_html_tags(file_content_decoded)
            embedding = generate_embedding(cleaned_content)
            
            embedding_data = {
                'id': file_id,
                'vector': embedding.tolist(),  # 将 numpy 数组转换为列表
                'content': cleaned_content
            }
            store_embedding(embedding_data)
            
            files_data.append({
                'file_name': file_id,
                'content': cleaned_content
            })
            print(f"Downloaded and processed {file_id}")
    
    result = {
        'files': files_data,
        'total_count': len(files_data)
    }
    return result

這邊稍微解釋一下,整個邏輯:

  • 讀取 jekyll 目錄底下 _posts
  • 取出檔案名稱,比如 2024-08-10-reading-elon-musk 作為 firebase 的 I
  • 先檢查是否已經在 Firebase Realtime DB 中有存在的物件
    • 如果有,就 skip 到下一個檔案
  • 取出裡面 utf-8 編碼的內容 file_content.decoded_content.decode('utf-8')
  • 並且去除掉所有 HTML tags remove_html_tags
  • 透過 generate_embedding 來產生整篇文章的 embedding vector value
  • 存入資料庫

透過 Firebase Realtime DB詢問資料

程式碼: https://github.com/kkdai/jekyll-rag-firebase/blob/main/blog_query.py

直接單獨執行這個程式碼即可。

# 示例调用
if __name__ == "__main__":    
    # 示例查询和生成响应
    question = "我哪一天架設 oracle8i 的?"
    response = query_and_generate_response(question, top_k=1)
    for res in response:
        print(f"File ID: {res['file_id']}, Similarity: {res['similarity']}")
        # print(f"Content: {res['content']}")
        content_str = res['content']
        # print(f'content_str: {content_str}')
        prompt = f"""
        use the following CONTEXT to answer the QUESTION at the end.
        If you don't know the answer, just say that you don't know, don't try to make up an answer.

        CONTEXT: {content_str}
        QUESTION: {question}

        reply in zh_tw
        """

        # print(f'prompt: {prompt}')
        completion = generate_gemini_text_complete(prompt)
        print(completion.text)
  • 先產生問句 question 的 Embedding

  • query_and_generate_response(question, top_k=1) 其中 k=1 只要最接近的一個答案。

    • def query_embedding(question, top_k=1):
          # 生成问句的嵌入
          question_embedding = generate_embedding(question)
              
          # 从数据库中获取所有存储的嵌入
          ref = db.reference('blog_embeddings')
          all_embeddings = ref.get()
              
          if not all_embeddings:
              return "No embeddings found in the database."
              
          # 计算问句嵌入与存储嵌入之间的相似度
          similarities = []
          for file_id, embedding_data in all_embeddings.items():
              stored_embedding = np.array(embedding_data['vector'])
              similarity = 1 - cosine(question_embedding, stored_embedding)
              similarities.append((file_id, similarity))
              
          # 按相似度排序并返回最相关的结果
          similarities.sort(key=lambda x: x[1], reverse=True)
          top_results = similarities[:top_k]
              
          results = []
          for file_id, similarity in top_results:
              file_content = ref.child(file_id).get()
              results.append({
                  'file_id': file_id,
                  'similarity': similarity,
                  'content': file_content
              })
              
          return results
      
    • 講問題與每一個資料的比對結果存入 similarities

    • 透過排序之後,挑選最相似的答案

      • similarities.sort(key=lambda x: x[1], reverse=True)
        top_results = similarities[:top_k]
        
  • 取出資料之後,將問句跟參考資料放入 prompt 之中來詢問。

    • prompt = f"""
      use the following CONTEXT to answer the QUESTION at the end.
      If you don't know the answer, just say that you don't know, don't try to make up an answer.
          
      CONTEXT: {content_str}
      QUESTION: {question}
          
      reply in zh_tw
      """
      
  • 回覆答案。

未來發展

本篇文章透過一些簡單的程式碼,來了解如何透過 Gemini 與 Firebase Realtime DB 來打造一個 RAG 的應用。程式碼儘可能使用原生的一些套件,儘量不使用比較複雜的 LangChain 或是 LlamaIndex 。

雖然使用 LangChain 或是 LlamaIndex 打造出來的 RAG 會更加的效果好甚至程式碼更少。但是我們還是需要知道 RAG 的原理是什麼,這樣才能針對相關的細節來優化與改善。

接下來我們可以持續改善的方向如下:

  • Embedding 的方式與演算法
  • 如果文章修改了,可以自動來更新 Embedding DB
  • 透過 LINE 官方帳號的應對與回覆,可以有更漂亮的畫面與相關應用。