Show / hide languages:
📋 Course plans · Week 1 / 6

🔐 Week 1 — Foundations

OAuth, MCP, the AI-agent staircase, and your first FunctionTool wired into Hermes.

OAuth, MCP, ขั้นบันได AI agent และ FunctionTool ตัวแรกที่เชื่อมเข้ากับ Hermes

OAuth、MCP、AI 代理阶梯,以及把第一个 FunctionTool 接进 Hermes。

🦙 Ollama ⚡ hermes3:8b 🐍 google-auth-oauthlib ⏱️ 90 min

🎯 Learning objectives

1

Define the staircase

Distinguish a chatbot from a tool-using ReActAgent and place each on the staircase.

แยกแยะระหว่าง chatbot กับ ReActAgent ที่ใช้เครื่องมือ และวางตำแหน่งบนขั้นบันได

区分聊天机器人与会用工具的 ReActAgent,并把它们放到阶梯上对应的位置。

2

Complete OAuth in Python

Run a Google OAuth flow and cache the refresh token locally — without ever exposing your password.

รัน OAuth flow ของ Google และเก็บ refresh token ไว้ในเครื่อง โดยไม่เปิดเผยรหัสผ่าน

在 Python 里跑完 Google OAuth 流程并把 refresh token 缓存到本地 — 不暴露密码。

3

Register a FunctionTool

Add a Python function to agent_pro.py, wrap it with FunctionTool.from_defaults, verify Hermes picks it up.

เพิ่มฟังก์ชัน Python ใน agent_pro.py ห่อด้วย FunctionTool.from_defaults แล้วดู Hermes เลือกใช้

在 agent_pro.py 中加一个 Python 函数,用 FunctionTool.from_defaults 包装,确认 Hermes 能识别并调用。

🔑 Interactive: the OAuth handshake

Click Next step to walk through every actor in the OAuth dance — see who sends what to whom, and which secrets never leave your laptop.

กด Next step เพื่อเดินผ่านทุกตัวละครในการ OAuth — ดูว่าใครส่งอะไรให้ใคร และความลับไหนไม่เคยออกจากเครื่อง

点击 Next step 逐步看完 OAuth 流程 — 谁向谁发了什么、哪些机密从不离开你的电脑。

👤 You
🤖 Hermes / agent_pro.py
🔐 Google OAuth
📧 Gmail API
Step 1 / 6

⚠️ The password never moves

Your Google password is typed into Google's page, not into Python. Hermes only ever holds a short-lived access_token with the gmail.readonly scope.

รหัสผ่าน Google ของคุณพิมพ์ที่หน้า Google เท่านั้น ไม่ได้ส่งเข้า Python Hermes ถือเพียง access_token อายุสั้นที่มีสิทธิ์ gmail.readonly

你的 Google 密码只输入到 Google 自己的页面,不会进入 Python。Hermes 只持有一个短期 access_token,权限范围是 gmail.readonly。

📚 Key concepts

🇬🇧 English

The staircase — chatbot → copilot → tool-using agent → proactive agent (cron-triggered).

OAuth scopes — Google's valet key. readonly lets the agent read; compose lets it draft; send lets it send. Always grant the least.

ReActAgent + FunctionTool — LlamaIndex's ReAct loop makes Hermes pick which Python function to call. Clear docstrings + type hints are the contract.

🇹🇭 ไทย

ขั้นบันได — chatbot → copilot → agent ที่ใช้เครื่องมือ → agent แบบ proactive (ทำงานตาม cron)

OAuth scopes — กุญแจ valet ของ Google: readonly ให้อ่าน compose ให้ร่าง send ให้ส่ง — ให้น้อยที่สุดเสมอ

ReActAgent + FunctionTool — ReAct loop ของ LlamaIndex ทำให้ Hermes เลือกฟังก์ชันมาเรียก docstring กับ type hint คือสัญญา

🇨🇳 中文

阶梯 — 聊天机器人 → 副驾驶 → 工具型代理 → 主动型代理(由 cron 触发)。

OAuth 权限范围 — Google 的"代客钥匙":readonly 只读、compose 可起草、send 可发送 — 永远只授最小权限。

ReActAgent + FunctionTool — LlamaIndex 的 ReAct 循环让 Hermes 选择调用哪个 Python 函数;docstring 和类型注解就是接口约定。

🧪 Hands-on lab

1
2
3
4
1

Install OAuth deps in your existing venv

From the same venv you used for the Mac/Windows stack:

ใน venv เดียวกับที่ใช้ใน Mac/Windows stack:

在你跑 Mac/Windows 栈用过的同一个 venv 里:

pip install google-auth-oauthlib google-api-python-client
2

Add a small gmail_auth.py helper

It runs the browser-based consent flow once, then caches the token to disk so subsequent runs are silent.

รัน consent flow ในเบราว์เซอร์ครั้งแรก แล้วเก็บ token ไว้ในไฟล์ ครั้งต่อ ๆ ไปจะเงียบ

第一次会在浏览器里完成同意流程,之后把 token 缓存到磁盘,后续直接静默使用。

# gmail_auth.py — drop next to agent_pro.py from pathlib import Path from google.oauth2.credentials import Credentials from google_auth_oauthlib.flow import InstalledAppFlow from google.auth.transport.requests import Request SCOPES = ["https://www.googleapis.com/auth/gmail.readonly"] CLIENT = Path.home() / ".config/krueng/google_oauth.json" TOKEN = Path.home() / ".config/krueng/gmail_token.json" def load_user_creds(): if TOKEN.exists(): creds = Credentials.from_authorized_user_file(str(TOKEN), SCOPES) if creds.valid: return creds if creds.expired and creds.refresh_token: creds.refresh(Request()); TOKEN.write_text(creds.to_json()); return creds flow = InstalledAppFlow.from_client_secrets_file(str(CLIENT), SCOPES) creds = flow.run_local_server(port=0) TOKEN.parent.mkdir(parents=True, exist_ok=True) TOKEN.write_text(creds.to_json()) return creds
3

Add the first FunctionTool to agent_pro.py

Append below the existing tools (web_search, generate_image …):

เพิ่มต่อจากเครื่องมือเดิม (web_search, generate_image …):

追加在现有工具(web_search、generate_image …)之后:

from googleapiclient.discovery import build from gmail_auth import load_user_creds def read_unread_gmail(label: str = "INBOX") -> str: """Return up to 10 unread Gmail messages matching a label. Use this whenever the user asks about their inbox, new email, or messages from a specific sender / topic. """ svc = build("gmail", "v1", credentials=load_user_creds()) msgs = svc.users().messages().list( userId="me", q=f"label:{label} is:unread", maxResults=10 ).execute().get("messages", []) return "\n---\n".join(snippet_of(m["id"], svc) for m in msgs) or "(inbox empty)" TOOLS.append(FunctionTool.from_defaults(read_unread_gmail))

💡 docstring = tool description

Hermes reads the docstring to decide when to call this tool. Vague docstring → bad tool selection. Clear examples in the docstring are gold.

Hermes อ่าน docstring เพื่อตัดสินว่าจะเรียกเครื่องมือนี้ เมื่อไหร่ docstring คลุมเครือ = เลือกเครื่องมือผิด ตัวอย่างชัด ๆ ใน docstring มีค่ามาก

Hermes 通过 docstring 决定什么时候调用这个工具。模糊的 docstring 会让它选错工具;写清楚示例非常关键。

4

Run it

$ python agent_pro.py Hermes Pro agent ready. Empty line to quit. you > Check my Gmail for unread emails today. # → Hermes picks read_unread_gmail; browser pops; you approve; Hermes returns summaries

Now revoke the app from your Google permissions page, delete gmail_token.json, ask the same question — and watch the agent fail gracefully. That's the staircase made concrete.

ลองเข้า หน้าจัดการสิทธิ์ Google เพิกถอน แอป แล้วลบ gmail_token.json ถามคำถามเดิม — agent จะ fail แบบ graceful ให้คุณดู

现在到 Google 权限页 撤销这个应用,并删除 gmail_token.json,再问同样的问题 — 看代理如何优雅地失败。阶梯就这么具体地体现出来了。

⚠️ Top gotchas

1. OAuth scope too broad

Start with gmail.readonly only. Don't grant gmail.send in Week 1.

เริ่มที่ gmail.readonly เท่านั้น อย่าเพิ่ง gmail.send ในสัปดาห์ 1

起步只用 gmail.readonly,第一周别给 gmail.send。

2. Hermes doesn't see the tool

Forgot to append to TOOLS or didn't restart. Hermes only sees what's in TOOLS at startup.

ลืม append เข้า TOOLS หรือไม่ได้ restart agent Hermes เห็นเฉพาะที่อยู่ใน TOOLS ตอนเริ่ม

忘了 append 到 TOOLS 或没重启。Hermes 只看启动时 TOOLS 里的工具。

3. Token JSON in git

The cached gmail_token.json is a credential. echo gmail_token.json >> .gitignore right now.

gmail_token.json ที่เก็บไว้ = credential เพิ่มเข้า .gitignore เดี๋ยวนี้เลย

缓存的 gmail_token.json 就是凭证。立刻把它加进 .gitignore。

📝 Assessment & homework

✅ Assessment

Practical: show Hermes returning a summary of your top 3 unread emails. Conceptual: explain (EN/TH/中文) why Hermes never sees your Google password.

ภาคปฏิบัติ: ให้ Hermes สรุปอีเมลที่ยังไม่อ่าน 3 ฉบับแรก ภาคทฤษฎี: อธิบาย (EN/TH/中文) ว่าทำไม Hermes ไม่เห็นรหัสผ่าน Google ของคุณ

实操:让 Hermes 总结你前 3 封未读邮件。概念:用 EN/TH/中文 说明为什么 Hermes 永远看不到你的 Google 密码。

🏠 Homework

Use the agent to paraphrase (don't draft, don't send) replies to 3 complex client emails. Note one place Hermes misunderstood the business context — we'll fix it in Week 2.

ใช้ agent เรียบเรียงใหม่ (ยังไม่ร่าง ยังไม่ส่ง) คำตอบสำหรับอีเมลลูกค้า 3 ฉบับ จดจุดที่ Hermes เข้าใจผิด — สัปดาห์ 2 จะมาแก้กัน

用代理转述(不要起草、不要发送)3 封复杂客户邮件。记下 Hermes 误解业务背景的一处 — 下周修正。