我媽媽經營一家小企業已經有一段時間了,她從客戶、供應商和經銷商那裡收到了大量的發票。我一直想知道為什麼我年輕時她總是對我生氣。不久我就意識到她一直在摸索這些發票。
因此,我想,為什麼不建立一個人工智慧機器人來自動檢索電子郵件、處理電子郵件並將必要的詳細資訊組織到電子表格中呢?
以下是我如何建造這個機器人的過程,它讓我媽媽免去了 20 個小時的揪頭髮和對每個人大喊大叫的麻煩。
使用Composio 的Gmail 整合從收件匣檢索發票電子郵件。
使用LLM提取相關資料點。
將資料點新增至 Google 工作表。

這是關於我們的快速介紹。
Composio 是一個開源工具基礎設施,用於建立強大且可靠的 AI 應用程式。我們提供超過 100 多個跨行業垂直領域的工具和集成,從 CRM、HRM 和銷售到生產力、開發和社交媒體。
Composio 處理所有這些應用程式的使用者身份驗證和授權,使 API 端點與各種 AI 模型和框架的連接變得簡單。

請幫我們加一顆星。 🥹
這將幫助我們建立更多這樣的文章💖
{% cta https://dub.composio.dev/gmailgen %}為 Composio 儲存庫加註星標 ⭐{% endcta %}
該專案簡化了從 Gmail 檢索發票電子郵件、下載發票附件以及將關鍵元素提取到 Google 表格的過程。

註冊 GmailGenius(應用程式的名稱)並將您的 Gmail 帳戶和 Google 試算表關聯起來。
輸入您要在 Gmail 中搜尋的關鍵字。
GmailGenius 會從 Gmail 中尋找與您的關鍵字條件相符的電子郵件和附件。
附件中的有用資訊將被提取並儲存在連結的 Google 表格中。
在幕後,人工智慧工具將任務分為多個步驟並執行它們:
從 Gmail 中檢索與關鍵字/短語條件相符的電子郵件。
下載相關附件。
使用 Nanonet 從附件中提取有價值的屬性。
將提取的資料儲存在連結的 Google Sheet 中
前端: React、Vite 和 TailwindCSS。
身份驗證:Firebase。
後端:Python、FastAPI。
AI 代理:CrewAI、Composition、Gemini。
Composio :用於將應用程式與 AI 代理程式整合的工具包。
CrewAI :用於建構協作式多個人工智慧機器人系統的開源框架。
Firebase :一個 Google 平台,為網頁和行動應用程式提供資料庫、身份驗證和託管等後端服務。
React + Vite :用於建立 UI 的 React 和用於快速開發和建構工具的 Vite 的組合。
FastAPI :用於更快建立 REST API 的 Python 框架。
雙子座:來自 Google 的LLMs。
要快速開始,請分叉並克隆此存儲庫。
此專案分為兩部分:後端和前端。後端由使用 CrewAI、Composio 和 Gemini 建立的 AI 工具組成,前端具有互動式 UI。
建置開發環境。使setup.sh可執行並執行它。
cd GmailGenius/backend
chmod +x setup.sh
,/setup.sh
作為參考,這是setup.sh檔。
#!/bin/bash
# Create a virtual environment
echo "Creating virtual environment..."
python3 -m venv ~/.venvs/gmail_agent
# Activate the virtual environment
echo "Activating virtual environment..."
source ~/.venvs/gmail_agent/bin/activate
# Install libraries from requirements.txt
echo "Installing libraries from requirements.txt..."
pip install -r requirements.txt
# Login to your account
echo "Login to your Composio acount"
composio login
# Add calendar tool
echo "Add Gmail tools. Finish the flow"
composio add gmail
composio add googlesheets
#Enable Gmail trigger
composio triggers enable gmail_new_gmail_message
# Copy env backup to .env file
if [ -f ".env.example" ]; then
echo "Copying .env.example to .env..."
cp .env.example .env
else
echo "No .env.example file found. Creating a new .env file..."
touch .env
fi
# Prompt user to fill the .env file
echo "Please fill in the .env file with the necessary environment variables."
echo "Setup completed successfully!"
這將建立一個Python虛擬環境並安裝requirements.txt中的函式庫。系統也會提示您登入 Composio。這會將您重新導向到 Composio 登入頁面。
在 Composio 上建立帳戶,並將顯示的金鑰貼到終端機中以登入您的 Composio 帳戶。

然後,您將被重定向到 Google 驗證頁面以新增 Gmail 和 Google Sheet 整合。

完成集成後。您可以存取 Composio 儀表板並監控您的整合。

現在我們已經完成了集成,讓我們建立後端。
您將需要 Nanonets 和 Google Gemini 的 API 來完成專案。
奈米網
這將有助於從發票 PDF 中提取相關資料。因此,使用 Nanonet 建立一個帳戶和一個免費的 API 金鑰。

複製密鑰並將其新增至.env檔案。
另外,將 Nanonet URL https://app.nanonets.com/api/v2/OCR/FullText設定到.env檔案中。
另外,請前往Google AI 工作室並建立 API 金鑰。

將密鑰也儲存到.env檔案中。
讓我們先建立負責從 Gmail 收件匣檢索發票、處理 PDF 並將其寫入 Google 試算表的 AI 機器人。
首先導入模組
import os
import re
import glob
import json
from composio.client.collections import TriggerEventData
from composio_crewai import Action, ComposioToolSet
from crewai import Agent, Crew, Task, Process
from crewai_tools.tools.base_tool import BaseTool
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv
from typing import Any, Dict
import requests
load_dotenv()
為 Gemini 建立一個實例。
from langchain_google_genai import ChatGoogleGenerativeAI
llm = ChatGoogleGenerativeAI(model="gemini-1.5-flash",
verbose=True, temperature=0.5,
google_api_key=os.environ.get("GEMINI_API_KEY"))
定義一個使用 Nanonets 擷取電子郵件的工具。
#Get the attachment that was recently downloaded
def get_recent_attachment() -> str:
pdf_files = glob.glob(os.path.join("/Users/abhishekpatil/.composio/output/", "*.pdf")) #modify path as per need
if not pdf_files:
return None
most_recent_pdf = max(pdf_files, key=os.path.getctime)
return most_recent_pdf
#Extract useful attributes from the attachment
class extractorTool(BaseTool):
name: str = "ExtractorTool"
description: str = "This tool extracts useful attributes from pdf document/attachments"
def _run(self) -> Dict[str, Any]:
attachment = get_recent_attachment()
url = os.environ.get("NANO_URL")
FilePath = {'file': open(attachment, 'rb')}
response = requests.post(url, auth=requests.auth.HTTPBasicAuth(os.environ.get("NANO_API_KEY"), ''), files=FilePath)
return json.loads(response.text)["result"][0]["prediction"]
在上面的程式碼區塊中,
我們定義了一個 CrewAI 工具extractor tool ,它將解析 PDF 並從中提取資訊。
get_recent_attachment()函數檢索最近下載的 PDF。
我們還需要一個工具來追蹤 Google Sheet 中插入的行。
#Get the current counter value
def read_counter():
try:
with open("counter.json", "r") as file:
data = json.load(file)
return data["counter"]
except FileNotFoundError:
initial_data = {"counter": 0}
with open("counter.json", "w") as file:
json.dump(initial_data, file)
return 0
#Tool to increment counter value
class incrementCounter(BaseTool):
name: str = "Increment Counter"
description: str = "This tool increments the counter value"
def _run(self):
current_value = read_counter()
new_value = current_value + 1
data = {"counter": new_value}
with open("counter.json", "w") as file:
json.dump(data, file)
它會追蹤 Google Sheets 中的行位置以準確加入資料。由於我們需要指定資料輸入的確切儲存格(例如,A1),因此計數器有助於確定下一個可用行,特別是在某些行已填充的情況下。只有在成功新增資料時計數器才會更新,以確保其反映正確的位置,從而防止在未新增資料時進行不必要的更新。
初始化 Gmail、Google Sheets 的 Composio 工具以及我們剛剛定義的工具。
#Tools
IncrementCounter = incrementCounter()
Extractor_tool=extractorTool()
composio_toolset = ComposioToolSet()
google_tools = composio_toolset.get_actions(
actions=[
# Action.GMAIL_FETCH_MESSAGE_BY_THREAD_ID,
Action.GMAIL_GET_ATTACHMENT,
Action.GMAIL_FETCH_EMAILS,
Action.GOOGLESHEETS_BATCH_UPDATE
]
)
tools = google_tools + [Extractor_tool, IncrementCounter]
我們之前定義了 Extractor Tool,現在定義了具有三個操作的 Google 工具:
Action.GMAIL_GET_ATTACHMENT :從 Gmail 電子郵件擷取附件。
Action.GMAIL_FETCH_EMAILS :根據特定條件從 Gmail 取得電子郵件。
Action.GOOGLESHEETS_BATCH_UPDATE :批次更新 Google 試算表中的資料。
接下來,定義一個 CrewAI 代理程式。
google_assistant = Agent(
role="Gmail Assistant",
goal="""Get five recent emails/messages and check if the thread ID matches, download attachments & extract attributes/information from it & store attributes in Google sheet (Store just the values & not the attribute names)""",
backstory=f"""You're an AI assistant that makes use of google tools/APIs and does work on behalf of user. You can extract attributes/information from attachments & Store them in Google sheet""",
verbose=True,
llm=llm,
tools=tools,
allow_delegation=False,
)
現在,我們將定義一個 CrewAI 代理程式。代理負責執行任務。
使用下列命令建立 Agent 實例
角色、目標和背景故事:這為LLMs提供了完成工作的額外背景。
詳細:記錄執行追蹤。
LLM:LLM 實例。
工具:我們之前定義的所有工具。提取工具和谷歌工具。
允許委託:設定為 false,以便代理程式不會將控制流傳遞給其他代理程式(如果可用)
接下來,定義事件監聽器。
事件偵聽器將繼續監視 Gmail 收件匣並在電子郵件到達時檢索它們。您可以使用觸發器篩選器定義事件偵聽器。
在這裡,我們在 Composio 設定期間啟用了 Gmail 觸發器,該觸發器從 Gmail 收件匣獲取新電子郵件。
每個事件監聽器都會伴隨一個回呼函數,當透過觸發器接收到事件時呼叫函數。
@listener.callback(filters={"trigger_name": "GMAIL_NEW_GMAIL_MESSAGE"})
def callback_new_message(event: TriggerEventData) -> None:
print("Received email")
payload = event.payload
thread_id = payload.get("threadId")
keywords = "Invoice, Apple TV"
attributes = "Invoice date, invoice number, invoice amount, currency"
sheetId = "1UEzR3FG9Jk6Vl_2RvTgHJuDbuzZXHDMl6yAIX3NDwJU"
sheetName = "Sheet1"
find_invoice_from_gmail = Task(
description=f"""
Find emails with thread id {thread_id}, check if it contains keywords like {keywords}, if so then extract attachment id & attachment name associated with that email and download the attachment & store the following attributes: {attributes} in google sheet with id {sheetId} & sheet name {sheetName} and cell A{read_counter()}
""",
agent=google_assistant,
expected_output="If email matches criteria ({keywords}) then download attachment & store attributes on google sheet & increment counter, otherwise indicate email isnt related",
)
gmail_processing_crew = Crew(
agents=[google_assistant],
tasks=[find_invoice_from_gmail],
verbose=1,
process=Process.sequential,
)
result = gmail_processing_crew.kickoff()
return result
print("Subscription created!")
listener.listen()
在回呼函數callvack_new_message中,
我們提取了要在電子郵件中尋找的關鍵字以查找發票、要保存在 Google 工作表上的屬性以及工作表名稱。
為google_assistant代理程式定義了一個 CrewAI 任務,具有清晰的描述和預期輸出。
最後,定義具有代理和任務的 Crew。
接下來,我們將建立一個API端點來接收來自前端的資訊。正如我之前提到的,我們將使用 FastAPI 和 Pydantic。
匯入所需的模組並設定日誌記錄。
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from agent import run_crew
import logging
# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
建立 FastAPI 應用程式並使用CORSMiddleware設定 CORS。
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
*`allow_origins=[""]`** :允許來自任何來源的請求。這對於開發很有用,但在生產中應該受到限制。
allow_credentials=True :允許在請求中包含 cookie 和 HTTP 驗證。
*`allow_methods=[""]`** :允許所有 HTTP 方法(GET、POST、PUT、DELETE 等)。
*`allow_headers=[""]`** :允許請求中的所有標頭。
現在,為 Message 定義一個 Pydantic 類別。
class Message(BaseModel):
emailKeywords: str
attributes: str
sheetId: str
最後,定義 POST 端點。
@app.post("/fetch")
async def fetch(message: Message):
try:
logger.info(f"Received request with email keywords: {message.emailKeywords} and attributes: {message.attributes}")
result, status_code = run_crew(message.emailKeywords, message.attributes, message.sheetId)
logger.info(f"Agent result: {result}")
return {"result": result}
except Exception as e:
logger.error(f"Error occurred: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
端點從前端接收使用者輸入並使用輸入資料執行工作人員。
該應用程式的前端是使用 React 和 Vite 建立的。
進入src目錄並安裝相依性。
npm install
Firebase 處理身份驗證。因此,請確保將必要的值新增至環境變數。
VITE_FIREBASE_API_KEY=your-firebase-api-key
VITE_FIREBASE_AUTH_DOMAIN=your-firebase-auth-domain
VITE_FIREBASE_PROJECT_ID=your-firebase-project-id
VITE_FIREBASE_STORAGE_BUCKET=your-firebase-storage-bucket
VITE_FIREBASE_MESSAGING_SENDER_ID=your-firebase-messaging-sender-id
VITE_FIREBASE_APP_ID=your-firebase-app-id
VITE_FIREBASE_MEASUREMENT_ID=your-firebase-measurement-id
inside the config folder建立一個Firebase js檔案並定義 FireBase 設定。
import { initializeApp } from "firebase/app";
import { getAnalytics } from "firebase/analytics";
import { getAuth, signOut } from "firebase/auth";
const firebaseConfig = {
apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
appId: import.meta.env.VITE_FIREBASE_APP_ID,
measurementId: import.meta.env.VITE_FIREBASE_MEASUREMENT_ID
};
const app = initializeApp(firebaseConfig);
const analytics = getAnalytics(app);
export const auth = getAuth(app);
export const logOut = () => {
signOut(auth).then(() => {
console.log("user signed out");
}).catch((error) => {
console.log("error signing user out");
});
}
該文件執行以下操作。
初始化 Firebase :Firebase 使用專案設定進行設定。它將應用程式連接到 Firebase。
設定分析:然後,程式碼會設定 Firebase Analytics,它可以幫助您追蹤使用者行為並了解人們如何使用您的應用程式。
啟用身份驗證:這將啟用身份驗證,這有助於管理使用者登入應用程式。
建立註銷功能:最後,程式碼提供了一個將使用者登出應用程式的功能。呼叫時,此函數會登出使用者。
對於這個專案,我們有三個頁面。
登入:登入頁面。
首頁:首頁列出常見問題等。
儀表板:主應用程式使用者介面。
這是一個帶有 Firebase 身份驗證的簡單登入頁面。
import { signInWithRedirect, GoogleAuthProvider, signInWithPopup } from "firebase/auth";
import { auth } from "../config/firebase";
import { useNavigate } from "react-router-dom";
const Login = () => {
const provider = new GoogleAuthProvider();
const navigate = useNavigate();
const signUpWithGoogle = async () => {
try {
const result = await signInWithPopup(auth, provider);
const credential = GoogleAuthProvider.credentialFromResult(result);
navigate("/dashboard");
} catch (error) {
alert(error);
const errorCode = error.code;
const errorMessage = error.message;
// The email of the user's account used.
const email = error.customData.email;
// The AuthCredential type that was used.
const credential = GoogleAuthProvider.credentialFromError(error);
}
}
return <div className="min-h-screen text-gray-900 flex justify-center">
<div className="flex justify-center flex-1">
<div className="lg:w-1/2 xl:w-5/12 p-24 sm:p-12">
<div className="flex flex-col items-center mt-24">
<button
onClick={signUpWithGoogle}
className="w-full max-w-xs font-bold shadow-sm rounded-lg py-3 bg-indigo-100 text-gray-800 flex items-center justify-center transition-all duration-300 ease-in-out focus:outline-none hover:shadow focus:shadow-sm focus:shadow-outline">
<div className="bg-white p-2 rounded-full">
<svg className="w-4" viewBox="0 0 533.5 544.3">
<path
d="M533.5 278.4c0-18.5-1.5-37.1-4.7-55.3H272.1v104.8h147c-6.1 33.8-25.7 63.7-54.4 82.7v68h87.7c51.5-47.4 81.1-117.4 81.1-200.2z"
fill="#4285f4" />
<path
d="M272.1 544.3c73.4 0 135.3-24.1 180.4-65.7l-87.7-68c-24.4 16.6-55.9 26-92.6 26-71 0-131.2-47.9-152.8-112.3H28.9v70.1c46.2 91.9 140.3 149.9 243.2 149.9z"
fill="#34a853" />
<path
d="M119.3 324.3c-11.4-33.8-11.4-70.4 0-104.2V150H28.9c-38.6 76.9-38.6 167.5 0 244.4l90.4-70.1z"
fill="#fbbc04" />
<path
d="M272.1 107.7c38.8-.6 76.3 14 104.4 40.8l77.7-77.7C405 24.6 339.7-.8 272.1 0 169.2 0 75.1 58 28.9 150l90.4 70.1c21.5-64.5 81.8-112.4 152.8-112.4z"
fill="#ea4335" />
</svg>
</div>
<span className="ml-4">
Login/Sign Up with Google
</span>
</button>
</div>
</div>
</div>
</div>
}
export default Login;
上面的程式碼區塊就是這樣做的。
導入模組:導入模組以啟用 Firebase 的 Google 驗證。
GoogleAuthProvider 執行個體:初始化一個新的 GoogleAuthProvider 執行個體以指定 Google 作為驗證提供者。
導航: useNavigate建立重定向應用程式使用者的導航功能。
signUpWithGoogle功能:
使用彈出視窗 ( signInWithPopup ) 非同步處理 Google 登入。
成功後,將使用者重新導向至“/dashboard”頁面。
發生錯誤時,它會顯示警報並記錄錯誤程式碼、訊息、使用者電子郵件和憑證類型等詳細資訊。
登入按鈕:最後,它會傳回一個使用 Tailwind 設計的登入按鈕。
現在讓我們來設計主頁。
主頁將包含一些有關該專案的常見問題和資訊。所以,請隨意跳過這一點。
import Hero from "../components/Hero";
import Benefits from "../components/Benefits";
import FAQ from "../components/FAQ";
import Working from "../components/Working";
import ActionButton from "../components/ActionButton";
const Home = () => {
return <section className="bg-white dark:bg-gray-900 mt-12">
<div className="py-8 px-4 mx-auto max-w-screen-xl text-center lg:py-16 lg:px-12">
<Hero />
<Benefits />
<Working />
<FAQ />
<div className="mt-20">
<ActionButton displayName={"Get started"} link={"#"} />
</div>
</div>
</section>
}
export default Home;
這將建立一個簡單的主頁,如下圖所示。

儀表板包含兩個輸入文字框,它們接受關鍵字,這些關鍵字將用於搜尋要儲存在電子表格中的電子郵件和屬性。
import { useNavigate } from "react-router-dom";
import Fetch from "../components/Fetch";
import { useEffect, useState } from "react";
const Dashboard = ({ user }) => {
const navigate = useNavigate();
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
if (user === null) {
console.log("user: ", user);
navigate('/login');
} else {
setIsLoading(false);
}
}, [user, navigate]);
if (isLoading) {
return <div>Loading...</div>; // Or any loading indicator
}
return (
<section className="bg-white dark:bg-gray-900 mt-12">
<div className="py-8 px-4 mx-auto max-w-screen-xl text-center lg:py-16 lg:px-12">
<span className="font-semibold text-3xl text-gray-900">Enter Keywords (Crisp & Concise)</span>
<Fetch />
</div>
</section>
);
};
export default Dashboard;
這將建立一個漂亮、簡單的儀表板來接受使用者資訊。取得按鈕將觸發後端啟動操作。

在App.jsx檔案中,我們設定了管理使用者身份驗證並控制對特定路由的存取的主要元件。此元件確保只有經過驗證的使用者才能存取特定的應用程式部分,從而提供安全且個人化的使用者體驗。
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import { onAuthStateChanged } from "firebase/auth";
import { auth } from "./config/firebase";
import Navbar from "./components/Navbar";
import Home from "./pages/Home";
import Footer from "./components/Footer";
import Dashboard from "./pages/Dashboard";
import ScrollToTop from "./components/ScrollToTop";
import { useState, useEffect } from "react";
import Login from "./pages/Login";
import Settings from "./pages/Settings";
import Agent from "./pages/Agent";
const ProtectedRoute = ({ user, children }) => {
if (!user) {
return <Navigate to="/login" replace />;
}
return children;
};
const App = () => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
setUser(user);
setLoading(false);
});
return () => unsubscribe();
}, []);
if (loading) {
return <div>Loading...</div>; // Or any loading indicator
}
return (
<BrowserRouter>
<Navbar user={user} />
<ScrollToTop />
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/Agent" element={<Agent />} />
<Route path="/Settings" element={<Settings />} />
<Route
path="/dashboard"
element={
<ProtectedRoute user={user}>
<Dashboard user={user} />
</ProtectedRoute>
}
/>
<Route path="/" element={<Home />} />
</Routes>
<Footer />
</BrowserRouter>
);
}
export default App;
這就是上面函數中發生的事情。
導入:導入 React、Firebase 和路由庫,以及自訂元件,如Navbar 、 Footer和頁面元件( Home 、 Dashboard 、 Login 、 Settings 、 Agents )。
受保護的路由元件:
- Ensures that only authenticated users can access specific routes.
- Redirects unauthenticated users to the login page.
- Uses `useState` to manage the `user` object (authenticated user) and `loading` state.
- `useEffect` sets up a listener (`onAuthStateChanged`) to track user authentication status and update the `user` state accordingly.
- Displays a loading message or indicator while authentication status is being determined.
- Configures routes using `BrowserRouter` and `Routes`.
- `Navbar` and `Footer` are consistently displayed across all pages.
- Routes include:
- `/login` for the login page.
- `/dashboard` for the protected dashboard, only accessible to authenticated users.
- `/` for the home page.
最後,將main.jsx檔案定義為應用程式的入口點。
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.jsx'
import './index.css'
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>,
)
最後,使用以下npm命令執行應用程式。
npm run dev
這將在 localhost:5345 上啟動前端伺服器。
現在您可以存取該應用程式並查看其執行情況。
感謝您的閱讀。
在本文中,您建立了一個完整的 AI 工具,用於管理 Gmail 發票、處理發票並在 Google 表格中更新發票。
如果您喜歡這篇文章,請探索 Composio 儲存庫並為其加註星標,以獲取更多 AI 用例。
{% cta https://dub.composio.dev/gmailgen %}為 Composio 儲存庫加註星標 ⭐{% endcta %}