iT邦幫忙

2025 iThome 鐵人賽

DAY 28
0
AI & Data

AI 營養師 + Web3 數位健康護照系列 第 28

Day28. 個人專屬 AI 營養顧問 EP. 2:實作 FAQ + RAG

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20251005/201292209Nzc2csX7m.jpg
(圖片來源:由 Perplexity 協助生圖)

正如圖片所示,儲存飲食紀錄的目的之一,就是希望 AI 提供意見的時候,是根據自己的實際情況,而不是網路上漫無邊際的營養資訊,尤其是希望 AI 幫自己均衡一下飲食,給出具體的規劃時,更是如此

一、專案實作:

整個 RAG 流程的核心在於「索引」和「查詢」。當一筆新的飲食紀錄儲存時,系統會更新索引;當使用者提問時,系統會查詢索引,找到相關資料,最後交給 AI 生成答案。

步驟一:資料來源與即時索引 (main_controller.py)

一切始於使用者儲存一筆飲食分析紀錄。

https://ithelp.ithome.com.tw/upload/images/20251011/20129220A9uS7ukXyx.jpg

  • 觸發點: main_controller.py 中的 /save_record 路由。
  • 行為: 當使用者在結果頁按下「儲存這筆分析」按鈕時,前端會發送一個 AJAX 請求到這個路由。
  • 關鍵程式碼:
 # controllers/main_controller.py -> save_record()
 # 儲存紀錄到資料庫
  db.session.add(record)
  db.session.commit()

  # 將新儲存的紀錄加入 RAG 索引
  try:
      index_record(record) 
  except Exception as e:
      print(f"RAG index failed: {e}")
  • 補充說明: 在將紀錄存入資料庫後,程式會立刻呼叫 rag_service.py 中的 index_record(record) 函式,將這筆新資料即時地加入到 RAG 的「知識庫」中。

步驟二:混合式索引 (rag_service.py)

  1. 倒排索引 (Inverted Index) - 關鍵字檢索

    • 結構: INVERTED_INDEX = defaultdict(set)
    • 原理: 這是一個經典的搜尋引擎技術。它儲存的是「哪個詞出現在哪些文件裡」。例如 {'牛肉': {1, 5}, '蛋白質': {1, 8, 9}},表示ID為1和5的文件包含「牛肉」。
    • 關鍵程式碼 (index_record):
      # in services/rag_service.py -> index_record()
      def index_record(record):
          doc_id = record.id
          text = f"{record.summary or ''} {record.nutrients_json or ''}"
      
          # 將文字切成字元
          tokens = tokenize(text) 
          DOC_TOKENS[doc_id] = tokens
          for t in tokens:
      
              # 更新倒排索引
              INVERTED_INDEX[t].add(doc_id) 
      
  2. 向量索引 (Vector Index) - 語意檢索

    • 結構: DOC_EMBS = {}
    • 原理: 將每筆紀錄的文字轉換成一個由數字組成的「向量」(Embedding)。語意上相似的文字,其向量在空間中的距離也更近。
    • 關鍵程式碼 (index_record_vector):
      # services/rag_service.py -> index_record_vector()
      def index_record_vector(record):
          text = f"{record.summary or ''} {record.nutrients_json or ''}"
      
          # 呼叫 Gemini API 取得向量
          emb = embeddings_service.get_embedding(text) 
      
          # 儲存向量
          DOC_EMBS[record.id] = emb 
      
    • Embedding 服務 (embeddings_service.py): 這個檔案負責呼叫 Google Gemini API,將文字轉為向量。

步驟三:查詢與檢索 (main_controller.py & rag_service.py)

  1. 填入想問的問題

https://ithelp.ithome.com.tw/upload/images/20251010/20129220CEwrblaJMr.jpg

2. 詢問:可以看到主要的參考資料(引用來源)

https://ithelp.ithome.com.tw/upload/images/20251010/201292209LFS1UTGtq.jpg

3. 當使用者在 RAG 頁面提問時,查詢流程啟動。

  • 觸發點: main_controller.py 中的 /rag/query 路由。

  • 程式碼:

    # controllers/main_controller.py -> rag_query()
    def rag_query():
        question = data.get('question', '').strip()
    
        # 1. 檢索 (Retrieval)
        from services.rag_service import query_records
    
        # 使用關鍵字檢索
        search_results = query_records(question, top_k=top_k) 
    
        # 2. 增強 (Augmented):將檢索到的資料組合成上下文
        context_text = "\n".join(contexts) 
    
        # 3. 生成 (Generation)
        from services.gemini_service import generate_answer_from_context
    
        # 交給 LLM
        answer = generate_answer_from_context(question, context_text) 
    
        return jsonify({'answer': answer, 'sources': sources})
    

    4. 根據個人飲食紀錄給出建議

https://ithelp.ithome.com.tw/upload/images/20251010/20129220eBBO4CggvE.jpg


上一篇
Day27. 個人專屬 AI 營養顧問 EP. 1:FAQ
下一篇
Day29. 專案完成後,為什麼要放到 GitHub?(不一定要 Public,也可以選擇 Private)
系列文
AI 營養師 + Web3 數位健康護照29
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言