🧭 What you're adding
🇬🇧 English
The Windows Stack already taught your agent four ways to know things: a baked system prompt, RAG over your notes, RAG over a blog, and a Hermes tool loop. Now we teach it to do things — search live, paint, message LINE, and wake itself up on a schedule.
🇹🇭 ไทย
Windows Stack สอน agent ให้ รู้ เรื่องต่าง ๆ แล้ว — system prompt ที่อบไว้ RAG บนโน้ตของคุณ RAG บนบล็อก และ tool loop ของ Hermes ตอนนี้เราจะสอนให้มัน ทำ สิ่งต่าง ๆ — ค้นเว็บ วาดรูป ส่ง LINE และตื่นมาทำงานตามเวลา
🇨🇳 中文
Windows Stack 已经教会代理四种 知道 事情的方式 — 烤进去的系统提示、对自己笔记的 RAG、对博客的 RAG、Hermes 工具循环。现在我们教它 做 事 — 实时搜索、画图、回 LINE、按计划自己醒来。
💡 The big idea — four new powers, one Hermes brain
All four pieces hang off the same Hermes agent loop you already built. Web search and image gen become two new FunctionTools. LINE is a thin Flask server that hands incoming messages to agent.chat(). The scheduler is Windows Task Scheduler running a Python script — which also uses the agent. Nothing is replaced. Everything bolts on.
ทั้งสี่ส่วนต่อเข้ากับ ลูป Hermes agent ตัวเดิม ที่คุณสร้างไปแล้ว ค้นเว็บกับสร้างรูปกลายเป็น FunctionTool สองตัว LINE คือ Flask server บาง ๆ ที่ส่งข้อความเข้า agent.chat() ส่วนตัวจับเวลาคือ Windows Task Scheduler ที่รันสคริปต์ Python ซึ่งก็ใช้ agent เหมือนกัน ไม่ได้ทิ้งของเก่า ทุกอย่างต่อยอด
四块都挂在你已经搭好的 同一个 Hermes 代理循环 上。网页搜索和图像生成是两个新的 FunctionTool。LINE 是个轻量 Flask 服务,把消息交给 agent.chat()。调度器是 Windows 任务计划程序,按计划跑 Python 脚本 — 脚本里照样用代理。什么都没换,全是加法。
✅ Before you start
🇬🇧 English
Work through the Windows Stack page first. You should already have Ollama running, hermes3:8b pulled, llama3.2 pulled, a .venv with LlamaIndex installed, and agent.py working in PowerShell.
🇹🇭 ไทย
ทำตาม หน้า Windows Stack ให้จบก่อน ควรมี Ollama รันอยู่แล้ว hermes3:8b และ llama3.2 ถูก pull แล้ว มี .venv ที่ติดตั้ง LlamaIndex แล้ว และ agent.py ทำงานได้ใน PowerShell
🇨🇳 中文
先把 Windows Stack 那一页过一遍。Ollama 已经在跑、hermes3:8b 和 llama3.2 都已 pull、.venv 里装好了 LlamaIndex、agent.py 能在 PowerShell 里跑通。
1 Install the extras
Open PowerShell in your windows_stack\ folder. If you have an NVIDIA GPU, install the CUDA build of torch first so image gen runs on your GPU:
เปิด PowerShell ในโฟลเดอร์ windows_stack\ ของคุณ ถ้ามีการ์ดจอ NVIDIA ให้ติดตั้ง torch เวอร์ชั่น CUDA ก่อน เพื่อให้ image gen รันบน GPU:
在 windows_stack\ 文件夹里打开 PowerShell。如果有 NVIDIA 显卡,先装 CUDA 版的 torch,让图像生成跑在 GPU 上:
No GPU? Skip the first pip install torch line — the second one will install the CPU build. Image generation will still work, just slower (~60s per image instead of ~5s).
ไม่มี GPU? ข้ามบรรทัด pip install torch แรกได้ บรรทัดที่สองจะติดตั้ง CPU build ให้ image gen ยังทำงานได้ แค่ช้า (~60 วินาที/รูป แทน ~5 วินาที)
没显卡?跳过第一行 pip install torch,第二行会装 CPU 版。图像生成照样能用,就是慢点(每张图 ~60 秒,不是 ~5 秒)。
2 Two new accounts you might need
Web search and image gen are local — no signup. LINE needs a free Messaging API channel from developers.line.biz. ngrok needs a free account at ngrok.com so LINE can reach your PC in development. Install ngrok with winget install ngrok or download the .exe.
ค้นเว็บกับสร้างรูปทำเองในเครื่อง ไม่ต้องสมัคร LINE ต้องเปิด Messaging API channel ฟรีที่ developers.line.biz ngrok ต้องสมัครบัญชีฟรีที่ ngrok.com เพื่อให้ LINE เข้าถึงพีซีของคุณตอนทดสอบ ติดตั้ง ngrok ด้วย winget install ngrok หรือดาวน์โหลด .exe
网页搜索和图像生成都在本地,不用注册。LINE 需要在 developers.line.biz 开一个免费 Messaging API 频道。ngrok 需要在 ngrok.com 注册免费账号,让 LINE 在开发阶段能找到你的电脑。用 winget install ngrok 安装或下载 .exe。
🔎 Tech 1 — Web search as a Hermes tool
LLMs only know what they were trained on. Ask llama3.2 about tonight's weather and it will guess. A search tool lets Hermes look it up just like a person would, then use the snippets to answer.
LLM รู้แค่สิ่งที่เคยถูก train มา ถาม llama3.2 เรื่องอากาศคืนนี้ มันจะเดา เครื่องมือค้นเว็บทำให้ Hermes ค้นดูจริง ๆ เหมือนคน แล้วเอาผลค้นมาตอบ
LLM 只知道训练时见过的东西。问 llama3.2 今晚的天气,它会瞎猜。搜索工具让 Hermes 像人一样 真的去查,然后用搜索片段回答。
web_search, reads the snippets, answers you.The tool
🦴 Why the docstring is a paragraph, not a line
Hermes reads the docstring to decide when to call this tool. A two-sentence docstring — "what it does, when to use it" — cuts wrong-tool errors in half. The "use this whenever…" list turns a vague tool into one the agent reaches for at the right moment.
Hermes อ่าน docstring เพื่อตัดสินใจว่า ควร เรียกเครื่องมือนี้ เมื่อไหร่ docstring สองประโยค — "ทำอะไร เมื่อไหร่ควรใช้" — ลด error เรียกผิดเครื่องมือไปครึ่งหนึ่ง รายการ "ใช้เมื่อ…" เปลี่ยนเครื่องมือคลุมเครือให้ agent หยิบมาใช้ได้ถูกที่
Hermes 用 docstring 决定 什么时候 调这个工具。两句话的 docstring —— "做什么、什么时候用" —— 能把调错工具的错误率减半。"以下情况使用…" 那个列表把模糊工具变成代理会在对的时机伸手去拿的工具。
Try it
🌉 Alternative — Kimi WebBridge instead of DuckDuckGo
duckduckgo-search is the smallest possible web tool — pip install and you have search snippets. But snippets only: no logins, no clicking through, no JS-rendered pages. When you need a real browser, swap in Kimi WebBridge — Moonshot's local browser-automation service that pairs a desktop app with a Chrome/Edge extension and drives your real browser through Chrome DevTools Protocol. Same Hermes agent, much sharper teeth: it can log in, click through paginated results, fill forms. Page content never leaves your machine.
duckduckgo-search เป็นเครื่องมือเว็บที่เล็กที่สุดเท่าที่เป็นไปได้ — pip install เสร็จก็ได้ snippet มาแล้ว แต่ได้แค่ snippet — ล็อกอินไม่ได้ คลิกต่อไม่ได้ อ่านหน้า JS ไม่ได้ ถ้าต้องการเบราว์เซอร์จริง ให้สลับมาใช้ Kimi WebBridge — บริการ browser-automation ในเครื่องของ Moonshot ที่จับคู่แอปเดสก์ท็อปกับส่วนขยาย Chrome/Edge แล้วขับเบราว์เซอร์ จริง ของคุณผ่าน Chrome DevTools Protocol Hermes ตัวเดิม แต่เขี้ยวคมขึ้นเยอะ: ล็อกอินได้ คลิกผ่านหน้าเพจได้ กรอกฟอร์มได้ เนื้อหาเพจไม่เคยออกจากเครื่อง
duckduckgo-search 是最小的网页工具 — pip install 完就有搜索片段了。但只有片段:不能登录、不能点击翻页、读不了 JS 渲染的页面。需要真浏览器时,换成 Kimi WebBridge — Moonshot 的本地浏览器自动化服务,把桌面 App 和 Chrome/Edge 扩展配对,通过 Chrome DevTools Protocol 驱动你的真浏览器。同样的 Hermes 代理,但牙齿锋利多了:能登录、点击翻页、填表单。页面内容永远不离开你的机器。
Install (one-time) — 安装一次即可
- Download the Kimi Desktop App: kimi.com/features/webbridge (Windows installer).
- Install the Kimi WebBridge extension from the Chrome Web Store (search "Kimi WebBridge").
- Open Kimi Desktop → Settings → WebBridge — pair the desktop app with the extension. Both should show as connected.
- Copy the MCP endpoint URL the WebBridge settings show (looks like
http://127.0.0.1:PORT/sse), then run in PowerShell:
The new agent_pro_webbridge.py (in the downloads below) is async because LlamaIndex's MCP client and the workflow-based ReActAgent.run() are both async — that is the only structural difference from agent_pro.py. Run them side by side to compare; pick whichever fits the task at hand.
🎨 Tech 2 — Local image generation
Ollama doesn't generate images — it only reads them. For local pictures we add a second tiny stack: HuggingFace diffusers running Stable Diffusion XL Turbo. On an NVIDIA RTX card it produces a 512×512 image in ~2-3 seconds. On CPU it takes ~60 seconds but still works. Offline after the first model download.
Ollama สร้างรูปไม่ได้ — มันแค่อ่านรูปเป็น สำหรับสร้างรูปในเครื่อง เราเพิ่มสแต็คเล็ก ๆ อันที่สอง: diffusers ของ HuggingFace รัน Stable Diffusion XL Turbo บนการ์ดจอ NVIDIA RTX จะสร้างรูป 512×512 ภายใน ~2-3 วินาที บน CPU ใช้เวลา ~60 วินาทีแต่ทำงานได้ หลังโหลดโมเดลครั้งแรก ใช้แบบออฟไลน์ได้เลย
Ollama 不生成图像 — 它只能读图。要本地画图,我们再加一个小堆栈:HuggingFace 的 diffusers,跑 Stable Diffusion XL Turbo。NVIDIA RTX 卡上每张 512×512 图 ~2-3 秒。CPU 上 ~60 秒,但还是能用。首次下载模型后就能离线用了。
The standalone runner
The Hermes tool wrapper
🐌 First call is slow
The very first time you call generate_image, diffusers downloads ~6 GB of model weights from HuggingFace. After that the pipeline is cached in memory inside the Python process — second image is fast.
ครั้งแรกที่เรียก generate_image diffusers จะดาวน์โหลดโมเดล ~6 GB จาก HuggingFace หลังจากนั้น pipeline ถูก cache ไว้ใน memory ของ process Python — รูปที่สองจะเร็ว
第一次调 generate_image,diffusers 会从 HuggingFace 下载约 6 GB 模型。之后管道会缓存在 Python 进程内存里 — 后续画图很快。
💡 Want to know which device you got?
Add print(_PIPE.device) after the loader. cuda:0 = NVIDIA GPU. cpu = the CUDA wheel wasn't installed; rerun the CUDA pip install command and check nvidia-smi from PowerShell.
เพิ่ม print(_PIPE.device) หลัง loader cuda:0 = GPU ของ NVIDIA cpu = ไม่ได้ติดตั้ง CUDA wheel ให้รัน pip install CUDA ใหม่ และตรวจสอบ nvidia-smi จาก PowerShell
在加载器后加一行 print(_PIPE.device)。cuda:0 = NVIDIA 显卡。cpu = 没装 CUDA wheel;重跑 CUDA 的 pip install 命令,并在 PowerShell 里 nvidia-smi 检查一下。
💬 Tech 3 — Responding to LINE
LINE is the messaging app every Thai student already has. With a free Messaging API channel, you give your agent a phone number — students message your Official Account, the message lands on your PC, Hermes answers, the reply goes back to LINE. Everything in between is one short Flask file.
LINE คือแอปแชทที่นักเรียนไทยทุกคนมีอยู่แล้ว ด้วย Messaging API channel ฟรี คุณกำลังให้ agent มี "เบอร์โทร" — นักเรียนส่งข้อความไปที่ Official Account ของคุณ ข้อความเข้ามาที่พีซี Hermes ตอบ คำตอบกลับไปที่ LINE ทุกอย่างตรงกลางคือไฟล์ Flask สั้น ๆ ไฟล์เดียว
LINE 是每个泰国学生都已经在用的聊天软件。开一个免费 Messaging API 频道,你就给代理装上了 "手机号" — 学生给你的官方账号发消息,消息进到你电脑,Hermes 回复,结果再回到 LINE。中间所有事都在一个短短的 Flask 文件里。
The Flask webhook
- ①Reuse the same agent.
build_agent()is imported fromagent_pro.py— exactly the same 5-tool Hermes loop that works on the command line. The LINE bot is just a different doorway into the same brain. - ②Verify the signature. LINE signs every webhook with HMAC-SHA256 of your channel secret. Without this check, anyone who guesses your URL could push fake messages.
- ③Iterate events. One webhook can carry several events (multiple messages, follow events, etc.). We only handle text messages here.
- ④Hand the text to Hermes. The agent loop is unchanged: it may search the web, generate an image, even fire
send_line_pushto a different user mid-conversation. - ⑤Send the reply. One POST to
/v2/bot/message/reply. ThereplyTokenis consumed once and expires fast (~30s).
Wire LINE to your PC
🛡️ Windows Defender Firewall may prompt you
The first time Flask binds to 0.0.0.0:8000, Windows will pop up a firewall dialog asking whether to allow the connection. Pick "Private networks" only — you don't need this open on public networks.
ครั้งแรกที่ Flask bind ไปที่ 0.0.0.0:8000 Windows จะป๊อปอัพ firewall ถามว่าจะอนุญาตการเชื่อมต่อหรือไม่ เลือกเฉพาะ "Private networks" — ไม่ต้องเปิดกว้างกับ public networks
Flask 第一次绑到 0.0.0.0:8000 时,Windows 防火墙会弹窗问要不要放行。只勾 "专用网络" 就好 — 公共网络上不用开。
🔑 Why ngrok
LINE's servers won't reach your PC directly — your PC has no public IP, your router blocks inbound, and LINE only talks HTTPS. ngrok solves all three by tunnelling a public HTTPS URL through to your localhost:8000. When you ship for real, replace ngrok with a small cloud server (a Lambda, a $5 droplet, or the user's existing lambda/line-chatbot/ pattern).
เซิร์ฟเวอร์ LINE เข้ามาที่พีซีคุณตรง ๆ ไม่ได้ — พีซีคุณไม่มี public IP, router บล็อกขาเข้า, และ LINE คุยแค่ HTTPS ngrok แก้ทั้งสามด้วยการ tunnel public HTTPS URL ไปที่ localhost:8000 ของคุณ ตอน deploy จริงให้แทนที่ ngrok ด้วย cloud server เล็ก ๆ (Lambda, droplet $5, หรือใช้ pattern lambda/line-chatbot/ ที่มีอยู่)
LINE 服务器没办法直接连到你的电脑 — PC 没有公网 IP,路由器挡住入站,而且 LINE 只认 HTTPS。ngrok 一次性解决这三件事,把一个公网 HTTPS 地址通到你的 localhost:8000。真要上线时,把 ngrok 换成一台小云服务器(Lambda、5 美元的 droplet,或者你已有的 lambda/line-chatbot/ 模式)。
⏰ Tech 4 — Scheduled / event-driven tasks
Windows ships Task Scheduler — the built-in scheduler that survives reboots, runs without you being logged in, and can also trigger on events (file changes, system idle, network connect). Tell schtasks.exe to run a small batch wrapper at 7 AM; the wrapper sets a few env vars, then runs Python. The Python script uses the same Hermes agent.
Windows มี Task Scheduler ติดมา — ตัวจัดเวลาในตัวที่รอดจากการรีบูต รันได้แม้คุณไม่ได้ล็อกอิน และยังสั่งให้ทำงานจาก event ต่าง ๆ ได้ (ไฟล์เปลี่ยน เครื่อง idle เครือข่ายเชื่อมต่อ) บอก schtasks.exe ให้รันสคริปต์ batch เล็ก ๆ ตอน 7 โมงเช้า batch ตั้งค่า env var แล้วรัน Python สคริปต์ Python ใช้ Hermes agent ตัวเดียวกัน
Windows 内置 任务计划程序 — 重启后还在、不用登录也能跑、还能用事件触发(文件变化、系统空闲、网络连接)。让 schtasks.exe 在早上 7 点跑一个小 batch 包装脚本:batch 设几个环境变量,再运行 Python。Python 脚本里用同一个 Hermes 代理。
agent_pro.py you can do on a schedule.The Python job
The batch wrapper (env vars + working dir)
- ①Why a batch wrapper. Task Scheduler can pass env vars in the GUI but the editing is awkward and they leak in screenshots. A tiny
.cmdfile keeps secrets editable in one place and makes the task definition portable. - ②Replace both values. Token comes from the LINE Messaging API console. The target ID is a userId (starts with
U), groupId (G), or roomId (R). - ③
cd /d "%~dp0"changes to the folder this.cmdlives in. Task Scheduler launches withC:\Windows\System32as the working directory by default — without this, your.venv\path can't be found. - ④Log everywhere. Background tasks fail silently by default. Piping stdout + stderr to
%TEMP%\turns "why didn't it run?" into a one-line look at the file.
Install & test
🎯 Prefer the Task Scheduler GUI?
Use the bundled KruAI_Daily.xml. Open Task Scheduler → Action → "Import Task…", pick the XML, edit the path to run_daily.cmd, save. Same effect, no command line.
ใช้ KruAI_Daily.xml ที่แถมไป เปิด Task Scheduler → Action → "Import Task…" เลือกไฟล์ XML แก้ path ของ run_daily.cmd แล้ว save ได้ผลเหมือนกัน ไม่ต้องพิมพ์คำสั่ง
用一起提供的 KruAI_Daily.xml。打开 任务计划程序 → 操作 → "导入任务…",选 XML,把 run_daily.cmd 的路径改一下,保存。效果一样,不用命令行。
🎯 The same pattern, ten different jobs
Once you have Task Scheduler → Python → agent, every recurring chore becomes a tiny script: scrape a competitor's website each morning; summarise your inbox at 6 PM into one LINE message; trigger when a folder changes (Task Scheduler supports an "On an event" trigger). The agent loop is the same — only the prompt changes.
เมื่อมี Task Scheduler → Python → agent แล้ว ทุกงานประจำกลายเป็นสคริปต์เล็ก ๆ: ดึงข้อมูลเว็บคู่แข่งทุกเช้า สรุปกล่องอีเมล 6 โมงเย็นเป็นข้อความ LINE หนึ่งข้อความ trigger เมื่อโฟลเดอร์เปลี่ยน (Task Scheduler รองรับ "On an event" trigger) ลูป agent ตัวเดิม เปลี่ยนแค่ prompt
有了 任务计划程序 → Python → 代理 这条链,所有重复杂事都变成一个小脚本:每天早上爬一下对手网站;下午 6 点把收件箱总结成一条 LINE 消息;监视的文件夹一有变化就触发(任务计划程序支持 "事件触发")。代理循环不变,只换 prompt。
🖥️ Tech 5 — A tiny web UI
Ollama exposes a REST API on localhost:11434 but ships no web UI of its own. PowerShell works, LINE works — but sometimes you want to chat in a browser, on your own PC, with markdown, copy-paste, and a textarea bigger than one line. A second tiny Flask file plus one HTML page gets you there. Same build_agent() as before — just a new front door.
Ollama เปิด REST API ที่ localhost:11434 แต่ไม่ได้แถม UI มาเอง PowerShell ใช้ได้ LINE ใช้ได้ — แต่บางทีก็อยากคุยในเบราว์เซอร์ บนเครื่องตัวเอง มี markdown copy-paste และ textarea ใหญ่กว่าหนึ่งบรรทัด ไฟล์ Flask อันที่สองเล็ก ๆ บวกหน้า HTML หนึ่งหน้าก็พอ ใช้ build_agent() ตัวเดิม — แค่เพิ่มประตูหน้าใหม่
Ollama 在 localhost:11434 上暴露 REST API,但它自己不带网页 UI。PowerShell 能用,LINE 能用 — 但有时候你就是想在浏览器里聊,跑在自己 PC 上,能看 markdown、能复制粘贴、textarea 比一行大。再加一个小 Flask 文件加一个 HTML 页面就够了。还是用同一个 build_agent() — 只是多了一扇前门。
The Flask server
- ①Build the agent once. Loading Hermes + registering tools takes a few seconds. Build at import time, not per-request — otherwise every message feels like a fresh boot.
- ②Serve the HTML from the same Flask app. No IIS, no static server. The page and the API are one process, one port — drop the file next to
chat_ui.pyand it just works. - ③One call into the agent.
agent.chat()handles the tool loop internally — web search, image gen, LINE push are all available becausebuild_agent()registered them. The web UI inherits every tool for free. - ④Bind to 127.0.0.1, not 0.0.0.0. No auth on this endpoint — anyone on your Wi-Fi could chat with your agent (and burn your CPU) if you open it up. Keep it local.
threaded=Truelets a second request start while the first is still thinking. Bonus: binding to 127.0.0.1 also means Windows Defender Firewall won't prompt you on first run.
The HTML page
Run it
🚪 The same brain behind four doors
You now have four ways to reach the same build_agent(): the CLI from the original Windows Stack, the LINE webhook from Tech 3, the Task Scheduler job from Tech 4, and this browser UI. None of them know about the others. None of them duplicate logic. Add a new tool to agent_pro.py and all four surfaces inherit it the next time you restart them. That is the payoff of the build_agent() refactor.
ตอนนี้คุณเข้าถึง build_agent() ตัวเดียวกันได้สี่ทาง: CLI จาก Windows Stack เดิม, LINE webhook จาก Tech 3, Task Scheduler job จาก Tech 4 และ UI เบราว์เซอร์นี้ ไม่มีตัวไหนรู้จักตัวอื่น ไม่มีตรรกะซ้ำ เพิ่ม tool ใหม่ใน agent_pro.py แล้วทั้งสี่ทางจะได้รับ tool นั้นเองตอนรีสตาร์ทครั้งต่อไป นี่คือ ผลตอบแทน ของการแยก build_agent() ออกมา
现在你有四种方式接到同一个 build_agent():Windows Stack 里的 CLI、Tech 3 的 LINE webhook、Tech 4 的任务计划程序任务、还有这个浏览器 UI。它们彼此不知道对方存在,也没有重复逻辑。在 agent_pro.py 里加个新工具,下次重启时这四扇门都会自动继承它。这就是把 build_agent() 抽出来的 回报。
If you want polish — Open WebUI
The 70-line UI above is yours to bend. If you'd rather not own the code and just want a ChatGPT-clone — model picker sidebar, multi-user, chat history, file uploads, markdown rendering — install Open WebUI. It's the community-standard front end for Ollama, runs in Docker Desktop for Windows, and talks to the same localhost:11434 Ollama is already serving. Trade-off: heavier (Docker Desktop required) and it talks to Ollama directly, so it doesn't see the agent's tools — only the raw model.
UI 70 บรรทัดข้างบนคือของคุณ ดัดแปลงได้เลย ถ้าไม่อยากเขียนเอง อยากได้ ChatGPT-clone — sidebar เลือกโมเดล หลายผู้ใช้ ประวัติแชท อัพโหลดไฟล์ render markdown — ติดตั้ง Open WebUI เป็น UI มาตรฐานของชุมชน Ollama รันใน Docker Desktop for Windows คุยกับ localhost:11434 ตัวเดียวกับที่ Ollama ฟังอยู่ ข้อแลก: หนักกว่า (ต้องมี Docker Desktop) และคุยกับ Ollama โดยตรง เลยไม่เห็น tool ของ agent — เห็นแค่โมเดลล้วน ๆ
上面 70 行的 UI 是你的,怎么改都行。如果你不想自己写代码,只想要一个 ChatGPT 克隆 — 侧边栏选模型、多用户、聊天历史、文件上传、markdown 渲染 — 装 Open WebUI。它是 Ollama 社区标准前端,跑在 Docker Desktop for Windows 里,连接 Ollama 已经在监听的同一个 localhost:11434。代价:更重(需要 Docker Desktop),而且它 直接 跟 Ollama 对话,所以看不到 agent 的工具 — 只能看到原始模型。
🎯 Which one should you pick?
Start with the Flask UI. It's 70 lines you can read in a coffee break, it inherits every tool you've built, and there's no Docker layer to debug. Move to Open WebUI only if you need conversation history, multi-user accounts, or you're sharing the PC with non-developers who expect a polished interface.
เริ่มที่ Flask UI ก่อน 70 บรรทัด อ่านได้ช่วงพักดื่มกาแฟ ได้ tool ที่คุณสร้างไว้ทั้งหมด และไม่มี Docker ให้มาดีบัก เปลี่ยนไป Open WebUI เมื่อจำเป็นต้องมีประวัติแชท หลายบัญชี หรือมีคนที่ไม่ใช่ developer มาใช้เครื่องนี้ด้วย
先用 Flask UI 起步。70 行,喝杯咖啡的时间就能读完,能继承你建的所有工具,没有 Docker 层要 debug。等到需要聊天历史、多用户账号,或要给非开发者共用这台 PC 时,再换 Open WebUI。
📚 Tech 6 — Curated cloud research via NotebookLM
Hermes is fast but its training is months stale, and DuckDuckGo gives you snippets without context. NotebookLM is Google's "second brain" — you upload sources (PDFs, web pages, YouTube transcripts) and Gemini answers questions with inline citations from only those sources. The notebooklm-py CLI lets a Python script — and therefore a Hermes tool — drive it: create notebooks, add deep-research sources, ask questions, get back grounded answers. The model lives in the cloud; your curated source set is the brain.
Hermes เร็ว แต่ training ของมันค้างหลายเดือน และ DuckDuckGo ให้แค่ snippet ไม่มีบริบท NotebookLM คือ "สมองที่สอง" ของ Google — คุณอัพแหล่งข้อมูล (PDF, หน้าเว็บ, transcript YouTube) แล้ว Gemini ตอบคำถามพร้อม citation จาก เฉพาะ แหล่งเหล่านั้น CLI ชื่อ notebooklm-py ทำให้สคริปต์ Python — และจึงเป็น Hermes tool — ขับมันได้: สร้าง notebook, เพิ่ม deep research sources, ถามคำถาม, ได้คำตอบที่อ้างอิง โมเดลอยู่บนคลาวด์ แต่ ชุดแหล่งข้อมูลที่คุณคัด คือสมอง
Hermes 很快,但它的训练数据已经过期几个月,DuckDuckGo 又只给片段、没有上下文。NotebookLM 是 Google 的"第二大脑" — 你上传资料(PDF、网页、YouTube 字幕),Gemini 仅根据 这些 资料回答问题并标注引用。notebooklm-py CLI 让 Python 脚本 — 也就是 Hermes 工具 — 来开车:建笔记本、添加深度研究素材、提问、拿到带引用的回答。模型在云上跑,你 挑选的资料集 就是它的大脑。
ask_notebook, gets back a cited answer.Install & log in once
Login is one-time per machine — the cookie sits in %USERPROFILE%\.notebooklm\profiles\default\storage_state.json and lasts months. After that, every notebooklm call (from PowerShell or from Python) reuses it. Add sources to the notebook by hand in the web UI, or via notebooklm source add <url> / add-research <topic> --mode deep --import-all.
login ครั้งเดียวต่อเครื่อง — cookie เก็บที่ %USERPROFILE%\.notebooklm\profiles\default\storage_state.json และอยู่ได้หลายเดือน หลังจากนั้นทุก call notebooklm (จาก PowerShell หรือจาก Python) จะใช้ซ้ำ เพิ่มแหล่งข้อมูลผ่าน web UI ด้วยมือ หรือใช้ notebooklm source add <url> / add-research <topic> --mode deep --import-all
每台机器登录一次就够 — cookie 存在 %USERPROFILE%\.notebooklm\profiles\default\storage_state.json,能用好几个月。之后每次 notebooklm 调用(PowerShell 或 Python)都会复用。添加资料可以在网页 UI 手动加,也可以用 notebooklm source add <url> 或 add-research <topic> --mode deep --import-all。
The standalone runner
The Hermes tool wrapper
Add both to agent_pro.py and append them to TOOLS:
เพิ่มทั้งสองลงใน agent_pro.py แล้วต่อท้าย TOOLS:
把两个都加到 agent_pro.py,然后 append 到 TOOLS:
Try it
🔑 Two tools, two jobs
Keep ask_notebook and research_topic as separate tools. Asking is fast (seconds) and used in normal conversation; researching is slow (minutes) and only worth it when you want to permanently grow the notebook. Bundling them into one tool teaches Hermes the wrong cost model and it will keep firing slow research passes when a quick lookup would do.
เก็บ ask_notebook กับ research_topic เป็นเครื่องมือคนละตัว ถามคือเร็ว (วินาที) ใช้ในบทสนทนาปกติ; research คือช้า (นาที) คุ้มต่อเมื่อต้องการเพิ่มของลง notebook ถาวร รวมเป็นตัวเดียวจะสอน Hermes ผิด มันจะยิง research ช้า ๆ ทั้งที่ ask ก็พอ
让 ask_notebook 和 research_topic 保持两个独立工具。问问题是秒级,用于日常对话;做研究是分钟级,只有真要往笔记本里加资料才值得。合成一个工具会让 Hermes 学错"代价模型",明明 ask 就够了,它还会去跑慢慢的 research。
☁️ This one isn't local
NotebookLM runs on Google's servers — the model is Gemini, not Hermes. Your sources, questions, and answers leave your PC. Use it for material you're already happy to share with Google (public papers, your own published blog posts, course PDFs you're allowed to use). Keep anything sensitive in the local Hermes RAG flow you built in the Windows Stack.
NotebookLM รันบน server ของ Google — โมเดลคือ Gemini ไม่ใช่ Hermes แหล่งข้อมูล คำถาม และคำตอบของคุณออกจากเครื่อง ใช้กับเนื้อหาที่คุณยินดีแชร์กับ Google อยู่แล้ว (paper สาธารณะ บล็อกของตัวเอง PDF เรียนที่ใช้ได้) อะไรที่อ่อนไหวเก็บไว้ใน RAG ของ Hermes ในเครื่องที่คุณสร้างใน Windows Stack
NotebookLM 跑在 Google 服务器上 — 模型是 Gemini,不是 Hermes。你的资料、问题、答案都会离开本机。只用在你本来就愿意分享给 Google 的材料上(公开论文、自己发的博客、有授权的课程 PDF)。敏感内容继续走你在 Windows Stack 里搭的本地 Hermes RAG。
🧩 Putting it all together
One file holds the upgraded agent. Two files use it (the LINE bot and the scheduled job). One file is just an image helper. That's the whole bolt-on.
ไฟล์เดียวเก็บ agent ตัวที่อัพเกรดแล้ว สองไฟล์ใช้มัน (LINE bot กับงานตั้งเวลา) อีกหนึ่งไฟล์เป็นแค่ตัวช่วยสร้างรูป เท่านี้คือสิ่งที่เพิ่มทั้งหมด
一个文件装升级后的代理。两个文件用它(LINE 机器人和定时任务)。还有一个文件只是图像助手。这就是全部新增内容。
The whole upgraded agent
🧮 When to use which
| I want the agent to… | Use | Why |
|---|---|---|
| Answer questions about today's facts | web_search tool |
Hermes can't know prices, weather, or news after its training cutoff. |
| Make an image from a prompt | generate_image tool |
SDXL Turbo on CUDA keeps it local — same "no cloud" promise as Ollama. |
| Chat with students in LINE | line_bot.py + ngrok |
Flask is one file. The webhook is the only LINE-specific code; the agent is unchanged. |
| Post a daily message at 7am | Task Scheduler + run_daily.cmd |
Windows-native cron. The job is just a script that uses the same agent. |
| React when a folder changes | Task Scheduler "On an event" | Same task — change the trigger from a daily time to an event log filter. |
| Send a message from the agent mid-conversation | send_line_push tool |
Different from reply — push lets the agent message anyone whose user ID it has. |
| Answer with citations from my own saved sources | ask_notebook tool |
Hermes can't cite; NotebookLM can — and only from sources you chose, so the answer stays grounded. |
| Build up a reading list on a topic | research_topic tool |
NotebookLM's deep research pulls cited web sources into the notebook so future ask_notebook calls have context. |
🔬 Code walkthroughs
Click each panel to expand the file with numbered notes.
คลิกที่แต่ละแผงเพื่อขยายดูไฟล์พร้อมหมายเหตุประกอบ
点击每个面板展开,查看带编号注释的源码。
🛠️ How agent_pro.py wires three new tools
Same skeleton as agent.py from the Windows Stack. The only changes:
- Three new top-level functions:
web_search,generate_image,send_line_push. Each has a 2-sentence docstring. - The
TOOLSlist grows to 5 entries. - The agent-builder is extracted into
build_agent()soline_bot.pyanddaily_word.pycan import and reuse the same configured agent.
Nothing else moves. The ReActAgent loop, the Ollama model choice, the verbose logging — all unchanged.
🎯 Why one Hermes, not three
You could spin up a separate ReActAgent for the LINE bot and another for the cron job. Don't. Building the agent is the slow part (model load, tool registration); reuse is free. The CLI, the LINE bot, and the scheduled job all import the same build_agent() and get the same brain.
💬 How line_bot.py turns LINE into a chat interface
The full source is ~50 lines. The shape is: verify the signature → walk the events → for each text message, run it through the agent → reply with the answer.
Why signature verification matters
LINE webhook URLs are guessable enough that without HMAC verification, an attacker can blast fake events at you. Each fake event:
- Spends one of your daily LINE replies.
- Triggers your agent to run — wasting CPU and potentially money if any tool calls cost.
- Looks like a real user did it in your logs, masking actual abuse.
HMAC-SHA256 of the raw request body with your channel secret, compared in constant time, makes spoofing infeasible.
Why we synchronously call the agent
replyToken expires in ~30 seconds. If Hermes takes 60 seconds because of a slow tool, the reply fails. For the demo we just call agent.chat() synchronously and accept the limit. In production you'd either:
- Send an instant ack reply ("คิดอยู่นะ…") and follow up with
pushwhen done — uses one push message quota per long answer. - Make the agent finish in <30s by skipping image generation on first reply.
🎨 How image_gen.py stays fast and local
Two design choices keep this practical on a laptop or desktop:
- Lazy import.
torchalone takes ~1 second to import. We don't pay that cost until the firstgenerate()call — so importingimage_geninagent_pro.pyis free. - Pipeline caching. The
_PIPEmodule-level variable holds the loaded diffusers pipeline between calls. First image: ~15 seconds (model load + 4 steps). Subsequent images in the same process: ~2-3 seconds on RTX, ~5 seconds on M-series Macs.
Device selection
The loader picks the fastest available backend automatically: CUDA on NVIDIA GPUs, MPS on Apple Silicon, otherwise CPU. Float16 weights for GPU paths; float32 only on CPU where fp16 is slower.
Why SDXL Turbo, not full SDXL
Full SDXL needs 30+ denoising steps and CFG. That's ~30 seconds on an RTX 3060 per image. SDXL Turbo is the distilled version: 4 steps, no guidance, comparable quality for chat-scale prompts. The agent feels responsive instead of like batch art.
⏰ How Task Scheduler survives reboots
Tasks live in the system, not in your shell session. Once registered, they run after every reboot, even before anyone logs in (if you tick "Run whether user is logged on or not" — which requires a password and disables network-drive access).
But Ollama needs to be running too
Your job calls Hermes via Ollama's :11434 server. If Ollama isn't running when 7 AM hits, the call fails. Two fixes:
- Easy: in Settings → Apps → Startup, enable Ollama. It will launch every login.
- Belt-and-braces: add a second Task Scheduler entry with trigger "At log on" that runs
ollama serveand is set to "Do not start a new instance" so duplicate launches no-op.
schtasks cheat sheet
📥 Download the extras
Drop these into the same windows_stack\ folder you built earlier.
วางไฟล์เหล่านี้ในโฟลเดอร์ windows_stack\ เดียวกับที่คุณสร้างไว้ก่อนหน้า
把这些文件放到你之前建好的同一个 windows_stack\ 文件夹里。
🎙 Tech 7 — Voice: listen and speak
Typing is fine. Talking is faster. Whisper turns your microphone into text (runs locally — your audio never leaves the PC). Edge TTS turns the reply into spoken audio (uses Microsoft's free neural voices — sounds great, supports Thai). Together: a hands-free agent.
พิมพ์ก็ดี แต่พูดเร็วกว่า Whisper แปลงเสียงไมค์เป็นข้อความ (รันในเครื่อง — เสียงไม่ออกจาก PC) Edge TTS แปลงคำตอบเป็นเสียงพูด (ใช้เสียงนิวรัลฟรีของ Microsoft เสียงดีมาก รองรับภาษาไทย) รวมกันคือ agent ที่ไม่ต้องใช้มือ
打字也行。说话更快。Whisper 把麦克风转成文字(本地跑 — 音频不离开 PC)。Edge TTS 把回复转成语音(用微软免费的神经语音,音质很好,支持中文)。两者合起来就是一个免提的代理。
🐳 The easy way — Docker Desktop
Don't install these one by one. The local_ai_agent project bundles Ollama + Whisper + Edge TTS + a web UI into one docker compose up. Each piece is a separate container that talks to the others over a private network. Docker Desktop on Windows runs them inside WSL2 — install Docker Desktop, install WSL2 if you haven't, and you're done. See the new Docker page for setup; the rest of this section is the no-Docker version for anyone who wants to wire it by hand.
อย่าติดตั้งทีละตัว โปรเจค local_ai_agent รวม Ollama + Whisper + Edge TTS + Web UI ไว้ใน docker compose up เดียว แต่ละชิ้นเป็น container แยกที่คุยกันผ่านเครือข่ายส่วนตัว Docker Desktop บน Windows รันใน WSL2 — ติดตั้ง Docker Desktop ติดตั้ง WSL2 ถ้ายังไม่มี เท่านี้พอ ดูหน้า Docker ใหม่สำหรับวิธีตั้งค่า ส่วนที่เหลือของหัวข้อนี้คือเวอร์ชันไม่ใช้ Docker สำหรับคนอยากต่อเอง
别一个一个装。local_ai_agent 项目把 Ollama + Whisper + Edge TTS + Web UI 打包到一个 docker compose up 里。每个组件都是独立容器,通过私有网络互相通信。Windows 上的 Docker Desktop 在 WSL2 里跑 — 装 Docker Desktop、没装 WSL2 的话补一下,就完事。新的 Docker 页面有完整步骤;本节余下部分是手动接线的无 Docker 版本。
1 Install Whisper (speech → text)
The faster-whisper backend runs the small model on CPU at near real-time on a modern Windows laptop. No GPU needed — but if you have an NVIDIA card and CUDA installed, swap device="cpu" for device="cuda" below for a big speed-up.
backend faster-whisper รันโมเดล small บน CPU ได้เกือบ real-time บนโน้ตบุ๊ค Windows สมัยใหม่ ไม่ต้องใช้ GPU แต่ถ้ามีการ์ด NVIDIA และติด CUDA แล้ว เปลี่ยน device="cpu" เป็น device="cuda" ด้านล่าง จะเร็วขึ้นเยอะ
faster-whisper 后端在现代 Windows 笔记本的 CPU 上跑 small 模型,几乎实时。不用 GPU — 但如果有 NVIDIA 显卡和 CUDA,把下面的 device="cpu" 换成 device="cuda",速度会快很多。
2 Record from your mic and transcribe
3 Install Edge TTS (text → speech)
No model download, no API key. The edge-tts package talks to Microsoft's free public neural voice endpoint. Online but instant. Bonus: since Edge TTS is a Microsoft service, latency on Windows is often the best of any platform.
ไม่ต้องโหลดโมเดล ไม่ต้อง API key แพ็คเกจ edge-tts คุยกับ endpoint เสียงนิวรัลฟรีของ Microsoft ออนไลน์แต่ทันที โบนัส: เพราะ Edge TTS เป็นบริการของ Microsoft latency บน Windows มักจะดีที่สุดในบรรดา platform
不下模型,不要 API key。edge-tts 直接调微软免费的公共神经语音端点。在线但很快。附赠:Edge TTS 是微软自家服务,Windows 上延迟通常是所有平台里最低的。
4 Speak the reply
5 Glue it into the Hermes loop
Replace the input("you: ") in agent.py with listen(), and pipe the agent's reply into say(). The agent itself doesn't change — only the I/O around it.
เปลี่ยน input("you: ") ใน agent.py เป็น listen() และส่งคำตอบของ agent เข้า say() ตัว agent เหมือนเดิม เปลี่ยนแค่ I/O รอบ ๆ
把 agent.py 里的 input("you: ") 换成 listen(),把代理的回复送进 say()。代理本身不动 — 只换 I/O。
💡 Mixed-language replies — split by script
If your agent answers with "Hello! สวัสดีค่ะ" and you pass it to a single English voice, the Thai sounds robotic. Walk the string by Unicode script (Thai block -, CJK 一-鿿, etc.), synthesize each run with the matching voice, then concatenate with ffmpeg -f concat. The local_ai_agent/tts/server.py has the working function.
ถ้า agent ตอบ "Hello! สวัสดีค่ะ" แล้วส่งเข้าเสียงอังกฤษเดียว ภาษาไทยจะออกหุ่นยนต์ เดิน string แบ่งตาม Unicode script (บล็อกไทย -, CJK 一-鿿, ฯลฯ) สังเคราะห์แต่ละช่วงด้วยเสียงที่ตรง แล้วต่อด้วย ffmpeg -f concat ฟังก์ชันใช้งานได้อยู่ใน local_ai_agent/tts/server.py
如果代理回 "Hello! สวัสดีค่ะ" 然后塞给单一英文嗓音,泰文听起来很机械。按 Unicode 脚本切(泰文块 -、CJK 一-鿿 等等),每段用对的嗓音合成,再用 ffmpeg -f concat 拼。local_ai_agent/tts/server.py 里有现成函数。
⚠️ Mic permission on Windows
First time sounddevice opens the mic, Windows shows a notification asking whether to allow apps to access your microphone. If you missed it, go to Settings → Privacy & security → Microphone and turn on "Let desktop apps access your microphone." Symptoms when blocked: silence with no error, or PortAudioError: Error opening InputStream.
ครั้งแรกที่ sounddevice เปิดไมค์ Windows จะมี notification ถามว่าจะให้แอปใช้ไมค์ไหม ถ้าพลาด ไปที่ Settings → Privacy & security → Microphone แล้วเปิด "Let desktop apps access your microphone" อาการเมื่อโดนบล็อก: เงียบโดยไม่มี error หรือ PortAudioError: Error opening InputStream
第一次 sounddevice 打开麦克风时 Windows 会弹通知问要不要允许应用访问麦克风。错过了的话去 设置 → 隐私和安全性 → 麦克风,打开 "允许桌面应用访问你的麦克风"。被拦的症状:静音无报错,或 PortAudioError: Error opening InputStream。
🤝 Tech 8 — Two agents talking to each other
So far one agent answered you. Now we let two agents answer each other. Same Ollama, same hermes3:8b — two different system prompts, a tiny loop that bounces the reply back and forth. That's the seed of every "teacher + student", "writer + critic", or "planner + executor" pattern you'll read about.
ที่ผ่านมา agent หนึ่งตัวตอบ คุณ ตอนนี้เราให้ agent สองตัวตอบ กันเอง ใช้ Ollama เดิม โมเดล hermes3:8b เดิม — แค่ system prompt คนละแบบ และลูปสั้น ๆ ที่โยนคำตอบกลับไปกลับมา นี่คือเมล็ดของแพทเทิร์น "ครู + นักเรียน" "นักเขียน + นักวิจารณ์" หรือ "ผู้วางแผน + ผู้ลงมือ" ที่เห็นทั่วไป
到目前为止只有一个代理在回答 你。现在我们让两个代理互相回答。还是同一个 Ollama、同一个 hermes3:8b — 只是两套不同的系统提示,外加一个小循环把回复来回扔。这就是 "老师 + 学生"、"作家 + 评论家"、"规划者 + 执行者" 这类模式的最小种子。
DialogueAgent instances. Each one's reply becomes the other's next input. The loop cap is the only thing that stops them.💡 Why two agents beat one prompt
You could fake this with one agent told to "play both sides" — but it collapses into a single voice that always agrees with itself. Two separate DialogueAgents keep separate chat memories, so each one tracks only its own side of the dialogue. That's what makes the disagreement, the follow-up questions, and the role-play feel real.
คุณจะแกล้งทำเป็น "เล่นทั้งสองฝั่ง" ในพรอมต์เดียวก็ได้ — แต่มันยุบรวมเป็นเสียงเดียวที่เห็นด้วยกับตัวเองเสมอ DialogueAgent สองตัวแยกกัน มี memory คนละก้อน แต่ละตัวจำเฉพาะฝั่งของตัวเอง ความขัดแย้ง คำถามตามต่อ และการสวมบทบาทจึงรู้สึกจริง
你也可以让一个代理 "演两个角色" — 但它会塌成一个永远自我同意的声音。两个独立的 DialogueAgent 各自保留聊天记忆,只跟踪自己那一侧的对话。这正是分歧、追问、角色扮演显得真实的原因。
1 Save this as agent_duet.py
In the same windows_stack\ folder. No new pip installs — it reuses everything from agent.py.
ในโฟลเดอร์ windows_stack\ เดียวกัน ไม่ต้อง pip install เพิ่ม ใช้ของจาก agent.py เดิมทั้งหมด
放到同一个 windows_stack\ 文件夹。不用新装 pip — 全部复用 agent.py 已有的东西。
2 Run it
You'll see six alternating turns — May asks, Khun Nan answers with a khao soi recommendation and a Thai phrase, May follows up about songthaews, and so on. Each turn takes a few seconds on a Windows laptop because Ollama runs both agents through the same local model. On a CUDA-enabled card it's noticeably snappier.
จะเห็นบทสนทนาหกรอบสลับกัน — May ถาม Khun Nan ตอบเรื่องข้าวซอยพร้อมคำไทยหนึ่งคำ แล้ว May ถามต่อเรื่องสองแถว เป็นต้น แต่ละรอบใช้เวลาสองสามวินาทีบนโน้ตบุ๊ค Windows เพราะ Ollama รัน agent ทั้งคู่ผ่านโมเดลตัวเดียวกัน บนการ์ด CUDA จะรู้สึกเร็วขึ้นชัดเจน
你会看到六轮一来一回 — May 问、Khun Nan 推荐 khao soi 并配一句泰文,May 接着问 songthaew,依此类推。Windows 笔记本上每轮要几秒,因为 Ollama 是用同一个本地模型轮流跑两个代理。装了 CUDA 的显卡会明显更顺。
💡 Swap the personas, change the pattern
The two-agent skeleton is the same — only the system prompts move. Teacher + student: one explains, the other asks the next question. Writer + critic: one drafts a paragraph, the other returns one improvement. Planner + executor: one writes a step, the other (with tools attached) carries it out and reports back. Same 30 lines, completely different behavior.
โครง agent สองตัวเหมือนเดิม เปลี่ยนแค่ system prompt ครู + นักเรียน: ฝ่ายหนึ่งอธิบาย อีกฝ่ายถามต่อ นักเขียน + นักวิจารณ์: ฝ่ายหนึ่งร่างย่อหน้า อีกฝ่ายเสนอจุดปรับหนึ่งจุด ผู้วางแผน + ผู้ลงมือ: ฝ่ายหนึ่งเขียนขั้นตอน อีกฝ่าย (ที่มี tools) ลงมือทำแล้วรายงานกลับ โค้ด 30 บรรทัดเดิม พฤติกรรมต่างกันสุดขั้ว
两代理骨架不变 — 只动 system prompt。老师 + 学生: 一个讲解,一个提下一个问题。作家 + 评论家: 一个写段落,一个返回一个改进建议。规划者 + 执行者: 一个写步骤,另一个(挂上工具)去执行并回报。同样的 30 行,行为天差地别。
⚠️ Always cap the loop
Without the range(6) cap, two polite agents can chat forever — "Thanks!" / "You're welcome, anything else?" / "Thanks again!" — burning your CPU/GPU and producing no new information. Cap turns, or end the loop when a reply looks like a goodbye (if "bye" in reply.lower(): break). A third "judge" agent that votes done / keep going is the more robust pattern once you outgrow a fixed cap.
ถ้าไม่กำหนด range(6) agent สุภาพสองตัวจะคุยกันไม่มีจบ — "ขอบคุณ!" / "ยินดี มีอะไรเพิ่มไหม?" / "ขอบคุณอีกครั้ง!" — กิน CPU/GPU โดยไม่ได้ข้อมูลใหม่ จำกัดรอบ หรือจบลูปเมื่อคำตอบดูเหมือนลา (if "bye" in reply.lower(): break) เมื่อ cap คงที่ไม่พอ ลองเพิ่ม agent ตัวที่สามเป็น "กรรมการ" โหวต จบ / ไปต่อ จะ robust กว่า
没有 range(6) 这个上限,两个礼貌的代理能聊到天荒地老 —— "谢谢!"/"不客气,还有别的吗?"/"再谢一次!" —— 烧 CPU/GPU 还没新信息。设个轮数上限,或在回复像告别时就退出(if "bye" in reply.lower(): break)。当固定上限不够用时,加一个 "裁判" 代理投票 结束/继续,是更稳的写法。
📱 Bonus — chat from your phone
You already have the Tech 5 chat UI. Bind it to your LAN instead of localhost and any phone on the same Wi-Fi can open it in Chrome/Safari — full Hermes with every tool you registered. No new app, no fork of Google's AI Edge Gallery, no on-device model to maintain. The phone is just a chat window; the PC does the thinking.
Tech 5 chat UI พร้อมอยู่แล้ว แค่ผูกกับ LAN แทน localhost มือถือเครื่องไหนใน Wi-Fi เดียวกันก็เปิดด้วย Chrome/Safari ได้ — ได้ Hermes เต็ม ๆ พร้อมทุก tool ที่ลงทะเบียนไว้ ไม่ต้องลงแอปใหม่ ไม่ต้อง fork AI Edge Gallery ของ Google ไม่ต้องดูแลโมเดลในเครื่อง มือถือเป็นแค่หน้าต่างแชท PC เป็นคนคิด
Tech 5 的聊天 UI 你已经有了。把它绑到 LAN 而不是 localhost,同一个 Wi-Fi 上的任何手机用 Chrome/Safari 都能打开 — 完整的 Hermes,连同你注册过的每个工具。不用装新 App,不用 fork Google 的 AI Edge Gallery,也不用维护设备上的模型。手机只是聊天窗口,PC 做思考。
One line, no file edit
The first time you bind to 0.0.0.0, Windows Defender Firewall will pop up — click Allow access for Private networks only (uncheck Public). Then find your PC's LAN IP and open it on the phone:
ครั้งแรกที่ผูกกับ 0.0.0.0 Windows Defender Firewall จะถามขึ้นมา — กด Allow access เฉพาะ Private networks (ติ๊ก Public ออก) จากนั้นหา IP ของ PC ใน LAN แล้วเปิดบนมือถือ:
第一次绑到 0.0.0.0 时,Windows Defender 防火墙会弹窗 — 只勾 Private networks,点 Allow access(Public 不要勾)。然后查到 PC 在 LAN 上的 IP,在手机上打开:
💡 Why not run AI Edge Gallery on the phone?
Google's AI Edge Gallery runs Gemma on the phone — impressive offline, but it has no public API, so driving it from the PC means forking and rebuilding the app. The easy path flips the direction: the PC (which is already running Hermes + tools) does the inference, and the phone is a thin client. You keep web search, image gen, and LINE push working from the couch.
AI Edge Gallery ของ Google รัน Gemma บนมือถือ — ใช้ออฟไลน์ได้น่าทึ่ง แต่ไม่มี API สาธารณะ จะคุมจาก PC ก็ต้อง fork และ build แอปใหม่ easy path สลับด้าน: PC (ที่รัน Hermes + tools อยู่แล้ว) เป็นคนคิด มือถือเป็นแค่ client บาง ๆ ได้ใช้ web search สร้างรูป และ LINE push จากโซฟา
Google 的 AI Edge Gallery 在手机上跑 Gemma — 离线挺惊艳,但没有公开 API,想从 PC 控制它就得 fork 整个 App 重新打包。简单路径反过来:PC(本来就在跑 Hermes + 工具)做推理,手机做瘦客户端。沙发上也能用网页搜索、生图、LINE 推送。
⚠️ Same Wi-Fi only — there is no auth
The chat UI has no login. On 0.0.0.0:5001, anyone on your LAN can talk to your agent and trigger any tool (including LINE push). Fine at home; don't do this on café or hotel Wi-Fi. To reach it from outside the house, put it behind Tailscale instead of opening the port to the internet.
chat UI ไม่มี login ที่ 0.0.0.0:5001 ใครก็ตามใน LAN คุณคุยกับ agent ได้ และเรียกใช้ tool ได้ทุกตัว (รวมถึง LINE push) อยู่บ้านใช้ได้ อย่าทำที่ Wi-Fi ร้านกาแฟหรือโรงแรม ถ้าจะเข้าจากนอกบ้าน เอาไปอยู่หลัง Tailscale อย่าเปิด port ตรง ๆ ออกอินเทอร์เน็ต
聊天 UI 没登录。0.0.0.0:5001 上,LAN 里任何人都能跟你的代理对话,也能触发任何工具(包括 LINE 推送)。在家没问题,别在咖啡店、酒店 Wi-Fi 上这么干。想从外网访问,把它放到 Tailscale 后面,不要直接对公网开端口。
🩹 When things go wrong
Hermes never calls web_search
The docstring is too vague. Add concrete trigger phrases: "Use this whenever the user asks about current events, prices, weather, sports scores, or anything you wouldn't already know." That's literally a list of cues for Hermes to match against the user's question.
docstring คลุมเครือเกิน เพิ่มประโยคตั้งคำถามชัด ๆ: "Use this whenever the user asks about current events, prices, weather, sports scores, or anything you wouldn't already know." นี่คือลิสต์ของ cue ให้ Hermes จับคู่กับคำถามผู้ใช้
docstring 太模糊。加上具体的触发短语:"Use this whenever the user asks about current events, prices, weather, sports scores, or anything you wouldn't already know." 这就是 Hermes 用来匹配用户问题的关键词列表。
SDXL Turbo runs on CPU even with an RTX card
You installed plain torch instead of the CUDA build. Uninstall and reinstall with the index URL: pip uninstall torch -y; pip install torch --index-url https://download.pytorch.org/whl/cu121. Verify with python -c "import torch; print(torch.cuda.is_available())" — it must print True.
คุณติดตั้ง torch ธรรมดาแทนที่จะเป็น CUDA build ถอน package แล้วติดตั้งใหม่ด้วย index URL: pip uninstall torch -y; pip install torch --index-url https://download.pytorch.org/whl/cu121 ยืนยันด้วย python -c "import torch; print(torch.cuda.is_available())" — ต้องได้ True
你装的是普通 torch,不是 CUDA 版。卸载重装,加上索引 URL:pip uninstall torch -y; pip install torch --index-url https://download.pytorch.org/whl/cu121。用 python -c "import torch; print(torch.cuda.is_available())" 验证 — 必须输出 True。
LINE webhook returns 401
Either your LINE_CHANNEL_SECRET is wrong, or the body got mangled before signature check (e.g., Flask auto-parsed JSON). Use request.get_data() for the raw bytes — that's what the signature was computed over.
อาจเป็นเพราะ LINE_CHANNEL_SECRET ผิด หรือ body ถูกแก้ก่อนเช็ค signature (เช่น Flask parse JSON อัตโนมัติ) ใช้ request.get_data() เพื่อรับ raw bytes — เพราะ signature คำนวณจากข้อมูลนี้
LINE_CHANNEL_SECRET 不对,或者签名校验前 body 被改过(比如 Flask 自动解析了 JSON)。用 request.get_data() 拿原始字节 — 签名就是基于这个算的。
Task Scheduler says "0x1" or "0x80070002"
Both mean Task Scheduler couldn't find a file. Almost always: a path with backslashes that didn't get quoted, or no cd /d "%~dp0" in run_daily.cmd so the script looks in C:\Windows\System32. Check %TEMP%\kruai_daily.err.log for the exact Python error.
ทั้งคู่หมายความว่า Task Scheduler หาไฟล์ไม่เจอ เกือบทุกครั้ง: path มี backslash แต่ไม่ได้ quote หรือไม่มี cd /d "%~dp0" ใน run_daily.cmd เลยทำให้สคริปต์ไปหาที่ C:\Windows\System32 ดู %TEMP%\kruai_daily.err.log เพื่อหา Python error ตัวจริง
两个错都是 Task Scheduler 找不到文件。基本上:带反斜杠的路径没加引号,或者 run_daily.cmd 里少了 cd /d "%~dp0",导致脚本去 C:\Windows\System32 找。看 %TEMP%\kruai_daily.err.log 找真正的 Python 错误。
PowerShell says "running scripts is disabled"
Activating the venv with Activate.ps1 needs script execution enabled. One-time fix in an admin PowerShell: Set-ExecutionPolicy -Scope CurrentUser RemoteSigned. This only allows local scripts to run — downloaded ones still need to be signed or unblocked.
การ activate venv ด้วย Activate.ps1 ต้องเปิดให้รัน script ก่อน แก้ครั้งเดียวใน admin PowerShell: Set-ExecutionPolicy -Scope CurrentUser RemoteSigned นี่อนุญาตเฉพาะ script local เท่านั้น — ของที่ดาวน์โหลดยังต้อง sign หรือ unblock ก่อน
用 Activate.ps1 激活 venv 需要开启脚本执行。在管理员 PowerShell 里一次性修复:Set-ExecutionPolicy -Scope CurrentUser RemoteSigned。只允许本地脚本运行 — 下载的脚本仍需签名或解除阻止。
chat_ui.py answers one message then hangs
Flask's dev server is single-threaded by default — while the agent is thinking, a second request waits. For one user that's fine. For a classroom, keep app.run(host="127.0.0.1", port=5001, threaded=True) or put waitress in front: waitress-serve --listen=127.0.0.1:5001 chat_ui:app. Waitress is the Windows-friendly production server (gunicorn doesn't run on Windows).
dev server ของ Flask เป็น single-thread โดยปริยาย — ระหว่างที่ agent กำลังคิด คำขอที่สองจะรอ ถ้าใช้คนเดียวก็พอ ถ้าทั้งห้องเรียนใช้ ให้คง app.run(host="127.0.0.1", port=5001, threaded=True) หรือเอา waitress มาคุม: waitress-serve --listen=127.0.0.1:5001 chat_ui:app waitress เป็น production server สำหรับ Windows (gunicorn รันบน Windows ไม่ได้)
Flask 开发服务器默认是单线程 — 代理在思考的时候,第二个请求只能等。一个人用没问题。一个班级用,保留 app.run(host="127.0.0.1", port=5001, threaded=True) 或者前面套个 waitress:waitress-serve --listen=127.0.0.1:5001 chat_ui:app。waitress 是 Windows 友好的生产服务器(gunicorn 在 Windows 上跑不起来)。
The agent generates an image but LINE only shows text
LINE image messages need an HTTPS URL — they can't carry a local file. To send images via LINE, expose .\images\ via ngrok or upload to S3 first, then send {"type":"image","originalContentUrl":..., "previewImageUrl":...} instead of plain text.
ข้อความรูปของ LINE ต้องการ URL HTTPS — ส่งไฟล์ local ไปตรง ๆ ไม่ได้ ถ้าจะส่งรูปผ่าน LINE ต้อง expose .\images\ ผ่าน ngrok หรืออัพไป S3 ก่อน แล้วส่ง {"type":"image","originalContentUrl":..., "previewImageUrl":...} แทน text
LINE 的图片消息要 HTTPS 链接 — 不能带本地文件。要走 LINE 发图,先用 ngrok 暴露 .\images\ 或上传到 S3,然后发 {"type":"image","originalContentUrl":..., "previewImageUrl":...} 而不是纯文本。