<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4877487320781767952</id><updated>2026-04-30T11:53:35.954+08:00</updated><category term="生活"/><category term="Python"/><category term="網購"/><category term="好書"/><category term="物聯網"/><category term="好站"/><category term="網頁技術"/><category term="樹莓派"/><category term="電腦"/><category term="還書"/><category term="機器學習"/><category term="ESP8266"/><category term="Raspberry Pi"/><category term="Arduino"/><category term="評論"/><category term="MicroPython"/><category term="主機"/><category term="ESP32"/><category term="理財"/><category term="能源"/><category term="jQuery"/><category term="人工智慧"/><category term="PHP"/><category term="食譜"/><category term="Java"/><category term="Javascript"/><category term="網路爬蟲"/><category term="音樂"/><category term="量化投資"/><category term="旅行"/><category term="小狐狸"/><category term="Linux"/><category term="歷史"/><category term="網路"/><category term="NLP"/><category term="其他"/><category term="LLM"/><category term="EasyUI"/><category term="語言學"/><category term="醫藥"/><category term="Streamlit"/><category term="工作"/><category term="雲端"/><category term="手機"/><category term="索引"/><category term="資料科學"/><category term="HTML5"/><category term="Google"/><category term="R 語言"/><category term="GAE"/><category term="硬體"/><category term="Django"/><category term="自動化"/><category term="OpenAI"/><category term="jQuery UI"/><category term="電子零件"/><category term="App"/><category term="C 語言"/><category term="Mapleboard"/><category term="PyTorch"/><category term="Ubuntu"/><category term="Bootstrap"/><category term="創客"/><category term="飛控"/><category term="Windows"/><category term="jQuery Mobile"/><category term="單晶片"/><category term="tkinter"/><category term="戲劇"/><category term="資料視覺化"/><category term="AIGC"/><category term="Flask"/><category term="ChatGPT"/><category term="數學"/><category term="木工"/><category term="Praat"/><category term="ExtJS"/><category term="農業"/><category term="TensorFlow"/><category term="修行"/><category term="小狐狸生態"/><category term="科學"/><category term="養生"/><category term="Telegram"/><category term="WordPress"/><category term="無人機"/><category term="Line"/><category term="♥"/><category term="♥♥♥"/><category term="專案"/><category term="Excel"/><category term="電子學"/><category term="Fintech"/><category term="p5.js"/><category term="筆電"/><category term="AWS"/><category term="Stable Diffusion"/><category term="gradio"/><category term="教育"/><category term="邏輯設計"/><category term="Gemini"/><category term="SQL"/><category term="架站"/><category term="AutoIt"/><category term="Node.js"/><category term="線上工具"/><category term="電腦技術"/><category term="Android"/><category term="C++"/><category term="Julia"/><category term="Matplotlib"/><category term="vibe coding"/><category term="旅遊"/><category term="英文"/><category term="Azure"/><category term="Gemini CLI"/><category term="Jetson"/><category term="LangChain"/><category term="LoRa"/><category term="保險"/><category term="ASP"/><category term="Banana Pi"/><category term="CSS"/><category term="Git"/><category term="LINE Bot"/><category term="MacOS"/><category term="物理學"/><category term="Chatbot"/><category term="IC 設計"/><category term="♥♥"/><category term="多媒體"/><category term="新軟體"/><category term="統計學"/><category term="計算語言學"/><category term="語音辨識"/><category term="資安"/><category term="韓劇"/><category term="GAS"/><category term="Pandas"/><category term="日文"/><category term="深度學習"/><category term="演算法"/><category term="生活雜記"/><category term="讀書劄記"/><category term="遠端桌面"/><category term="Colab"/><category term="GitHub"/><category term="借書"/><category term="軟體開發"/><category term="Blynk"/><category term="Micro:bit"/><category term="OpenCV"/><category term="Vue"/><category term="大數據"/><category term="好文"/><category term="影像處理"/><category term="機器人"/><category term="自作專案"/><category term="資料庫"/><category term="D3.js"/><category term="GCP"/><category term="Lua"/><category term="Nginx"/><category term="Swift"/><category term="TCP/IP"/><category term="占星術"/><category term="書籤"/><category term="量子計算"/><category term="Altair"/><category term="Bokeh"/><category term="NLTK"/><category term="WSH"/><category term="信號處理"/><category term="語音技術"/><category term="Appfog"/><category term="Claude Code"/><category term="DSP"/><category term="GPU"/><category term="Plotly"/><category term="SDD"/><category term="SciPy"/><category term="Seaborn"/><category term="SpaCy"/><category term="scikit-learn"/><category term="區塊鏈"/><category term="相術"/><category term="系統"/><category term="紫微斗數"/><category term="統計"/><category term="AMB82"/><category term="Corona"/><category term="Firebase"/><category term="Hugging Face"/><category term="Joomla"/><category term="MQTT"/><category term="MongoDB"/><category term="Node-RED"/><category term="OpenSpec"/><category term="SQLite"/><category term="Transformer"/><category term="UAV"/><category term="WebGL"/><category term="tensorflow.js"/><category term="動畫"/><category term="排版"/><category term="繪圖"/><category term="虛擬幣"/><category term="通訊"/><category term="Chart.js"/><category term="Cheatsheet"/><category term="Highcharts"/><category term="JSP"/><category term="Keras"/><category term="Mathematics"/><category term="Midjourney"/><category term="Numpy"/><category term="RAG"/><category term="React"/><category term="STM32"/><category term="Verilog"/><category term="Wireshark"/><category term="ml5.js"/><category term="reflex"/><category term="備忘"/><category term="影像辨識"/><category term="比特幣"/><category term="股市"/><category term="藝術"/><category term="API"/><category term="AngularJS"/><category term="Anime"/><category term="Big Data"/><category term="CKeditor"/><category term="Canva"/><category term="DALL-E"/><category term="Data Mining"/><category term="FPGA"/><category term="Gimini"/><category term="Go"/><category term="Home Assistant"/><category term="JavaFX"/><category term="Kali"/><category term="Kotlin"/><category term="Make"/><category term="Masonite"/><category term="NoSQL"/><category term="NotebookLM"/><category term="Notion"/><category term="OrangePi"/><category term="Ruby"/><category term="Scrapy"/><category term="Three.js"/><category term="TinyMCE"/><category term="TinyML"/><category term="VHDL"/><category term="VoIP"/><category term="YOLO"/><category term="brython"/><category term="bubble.io"/><category term="jqPlot"/><category term="mermaid.js"/><category term="no-code"/><category term="中醫"/><category term="人工"/><category term="保養品"/><category term="儀錶板"/><category term="八字學"/><category term="出版"/><category term="園藝"/><category term="控制系統"/><category term="新產品"/><category term="新知識"/><category term="易經"/><category term="智慧音箱"/><category term="監控"/><category term="線上學習"/><category term="線性代數"/><category term="論文"/><category term="運動"/><category term="電路學"/><category term="韓文"/><title type='text'>小狐狸事務所</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='https://yhhuang1966.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default'/><link rel='alternate' type='text/html' href='https://yhhuang1966.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default?start-index=26&amp;max-results=25'/><author><name>小狐狸事務所</name><uri>http://www.blogger.com/profile/09435160519044041137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>6290</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4877487320781767952.post-4431168071299862016</id><published>2026-04-30T11:53:00.005+08:00</published><updated>2026-04-30T11:53:35.897+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="網購"/><category scheme="http://www.blogger.com/atom/ns#" term="能源"/><title type='text'>momo 購買小米 20000 mAh 行動電源</title><content type='html'>因下周要帶爸與岳父母去沖繩玩, 今天上網買了一顆小米行動電源 :&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://www.momoshop.com.tw/goods/GoodsDetail.jsp?i_code=13951759&quot; target=&quot;_blank&quot;&gt;小米 官方旗艦館 PB2020MI 自帶線行動電源 20000 22.5W 2孔輸出(USB-A/USB-C 可充/自帶線/74Wh)&lt;/a&gt;&amp;nbsp;$599&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;100Wh 以下可帶上飛機&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhW2uQGYod6SIkzO11U2_eshILwgHMknSJBxfuRvhC8rvo-CHzO8AYwQghMEud1EKJLmXdhDbUaybxpyPCOvUIGijlGSwZv6jlDcqMLTI_PeMV3rUl21hLN23hedFzgXoIQxZg4GnMauesveeZHJTA6d6qNPFWmTke1spXPLKHKy-sddQavv8G8s0opyfwh/s1671/momo%20%E8%B3%BC%E8%B2%B7%E5%B0%8F%E7%B1%B3%E8%A1%8C%E5%8B%95%E9%9B%BB%E6%BA%90.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;627&quot; data-original-width=&quot;1671&quot; height=&quot;120&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhW2uQGYod6SIkzO11U2_eshILwgHMknSJBxfuRvhC8rvo-CHzO8AYwQghMEud1EKJLmXdhDbUaybxpyPCOvUIGijlGSwZv6jlDcqMLTI_PeMV3rUl21hLN23hedFzgXoIQxZg4GnMauesveeZHJTA6d6qNPFWmTke1spXPLKHKy-sddQavv8G8s0opyfwh/s320/momo%20%E8%B3%BC%E8%B2%B7%E5%B0%8F%E7%B1%B3%E8%A1%8C%E5%8B%95%E9%9B%BB%E6%BA%90.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;用掉 74 元 momo 幣實付 525 元.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://yhhuang1966.blogspot.com/feeds/4431168071299862016/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment/fullpage/post/4877487320781767952/4431168071299862016' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/4431168071299862016'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/4431168071299862016'/><link rel='alternate' type='text/html' href='https://yhhuang1966.blogspot.com/2026/04/momo-20000-mah.html' title='momo 購買小米 20000 mAh 行動電源'/><author><name>小狐狸事務所</name><uri>http://www.blogger.com/profile/09435160519044041137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhW2uQGYod6SIkzO11U2_eshILwgHMknSJBxfuRvhC8rvo-CHzO8AYwQghMEud1EKJLmXdhDbUaybxpyPCOvUIGijlGSwZv6jlDcqMLTI_PeMV3rUl21hLN23hedFzgXoIQxZg4GnMauesveeZHJTA6d6qNPFWmTke1spXPLKHKy-sddQavv8G8s0opyfwh/s72-c/momo%20%E8%B3%BC%E8%B2%B7%E5%B0%8F%E7%B1%B3%E8%A1%8C%E5%8B%95%E9%9B%BB%E6%BA%90.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4877487320781767952.post-7247231832036012440</id><published>2026-04-30T11:42:00.001+08:00</published><updated>2026-04-30T11:42:07.430+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Claude Code"/><category scheme="http://www.blogger.com/atom/ns#" term="網購"/><title type='text'>(補記) 購買 HiSKIO 課程 : Claude Code 深度應用</title><content type='html'>&lt;div&gt;我可能線上課程買太多買到頭昏了, 最近在臉書看到一門 Claude Code 課程 56 折即將結束, 趁中午截止前上網想先買下 (雖然可能要半年後才會開啟 Claude Code 付費) :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://hiskio.com/courses/2475&quot; target=&quot;_blank&quot;&gt;https://hiskio.com/courses/2475&lt;/a&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;沒想到登入 HiSKIO 赫然看到網站提醒 : 你已購買本課程! 蝦米? 查了訂購記錄, 這門課我今年 2/6 就買了, 當時可能一忙忘了記下來. 這就像我存摺有很多本 (但也不至於到 100 本啦) 藏錢都藏到忘記自己很有錢了. 補記一下免得又忘記了 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmdhaBHYJSIOZxvrpD1ftOKmRClAR6y5vpUPS-oBQECn9dqWH5htVUe5l13E6hpoMtvJm_ngRypMD9O_G6F9lVA0RNdjnS_adg0TyGjhNrQkL44Aqw9wt_Q0RxczOrcBMZvaG8ur8o52kQUIMYBPoV3VUxXEReJ7PyF-IAOFvXRHJxJuHJkN_YQYDMILLH/s1745/HiSKIO%20%E8%AA%B2%E7%A8%8B-Claude%20Code%20%E6%B7%B1%E5%BA%A6%E6%87%89%E7%94%A8.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;770&quot; data-original-width=&quot;1745&quot; height=&quot;141&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmdhaBHYJSIOZxvrpD1ftOKmRClAR6y5vpUPS-oBQECn9dqWH5htVUe5l13E6hpoMtvJm_ngRypMD9O_G6F9lVA0RNdjnS_adg0TyGjhNrQkL44Aqw9wt_Q0RxczOrcBMZvaG8ur8o52kQUIMYBPoV3VUxXEReJ7PyF-IAOFvXRHJxJuHJkN_YQYDMILLH/s320/HiSKIO%20%E8%AA%B2%E7%A8%8B-Claude%20Code%20%E6%B7%B1%E5%BA%A6%E6%87%89%E7%94%A8.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;我在 HiSKIO 目前就只有林彥文老師的 &quot;Vibe Coding 全能實戰課&quot; (已上完, 要複習) 與這堂 Claude Code 課, 等沖繩回來要開始上課了 (還有 TibaMe 與 Hahow 的呵呵).&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://yhhuang1966.blogspot.com/feeds/7247231832036012440/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment/fullpage/post/4877487320781767952/7247231832036012440' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/7247231832036012440'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/7247231832036012440'/><link rel='alternate' type='text/html' href='https://yhhuang1966.blogspot.com/2026/04/hiskio-claude-code.html' title='(補記) 購買 HiSKIO 課程 : Claude Code 深度應用'/><author><name>小狐狸事務所</name><uri>http://www.blogger.com/profile/09435160519044041137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmdhaBHYJSIOZxvrpD1ftOKmRClAR6y5vpUPS-oBQECn9dqWH5htVUe5l13E6hpoMtvJm_ngRypMD9O_G6F9lVA0RNdjnS_adg0TyGjhNrQkL44Aqw9wt_Q0RxczOrcBMZvaG8ur8o52kQUIMYBPoV3VUxXEReJ7PyF-IAOFvXRHJxJuHJkN_YQYDMILLH/s72-c/HiSKIO%20%E8%AA%B2%E7%A8%8B-Claude%20Code%20%E6%B7%B1%E5%BA%A6%E6%87%89%E7%94%A8.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4877487320781767952.post-9134200790282890354</id><published>2026-04-30T10:07:00.005+08:00</published><updated>2026-04-30T10:12:08.841+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Plotly"/><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><category scheme="http://www.blogger.com/atom/ns#" term="網頁技術"/><category scheme="http://www.blogger.com/atom/ns#" term="資料科學"/><category scheme="http://www.blogger.com/atom/ns#" term="資料視覺化"/><title type='text'>Python 學習筆記 : 用 plotly 繪製互動式圖表 (三)</title><content type='html'>&lt;div&gt;本篇旨在測試 Plotly 的圖表匯出功能.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;本系列之前的測試文章參考 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2022/09/python-plotly.html&quot; target=&quot;_blank&quot;&gt;Python 學習筆記 : 用 plotly 繪製互動式圖表 (一)&lt;/a&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2025/06/python-plotly.html&quot; target=&quot;_blank&quot;&gt;Python 學習筆記 : 用 plotly 繪製互動式圖表 (二)&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;更多 Python 資料視覺化學習筆記參考 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2025/06/python.html&quot; target=&quot;_blank&quot;&gt;Python 資料視覺化學習筆記索引&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Plotly 支援多種檔案類型匯出, 可呼叫下表中 Figure 物件的方法匯出 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;

&lt;table border=&quot;1&quot; bordercolor=&quot;#888&quot; cellspacing=&quot;0&quot; style=&quot;background-color: white; border-collapse: collapse; border-color: rgb(136, 136, 136); border-width: 1px; color: #534741; font-family: Arial, Verdana, sans-serif; font-size: 13.3333px; margin: 0px;&quot;&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;th style=&quot;padding: 3px; width: 190px;&quot;&gt;Figure 物件的檔案匯出方法&lt;/th&gt;
      &lt;th style=&quot;padding: 3px; width: 430px;&quot;&gt;說明&lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;padding: 3px;&quot;&gt;fig.write_image(file, **kwargs)&lt;/td&gt;
      &lt;td style=&quot;padding: 3px;&quot;&gt;匯出為靜態圖片（PNG、JPEG、SVG、PDF 等），需安裝 &lt;code&gt;kaleido&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;padding: 3px;&quot;&gt;fig.write_html(file, **kwargs)&lt;/td&gt;
      &lt;td style=&quot;padding: 3px;&quot;&gt;匯出為互動式 HTML 檔，可直接用瀏覽器開啟&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;padding: 3px;&quot;&gt;fig.to_html(full_html=True, include_plotlyjs=&#39;cdn&#39;)&lt;/td&gt;
      &lt;td style=&quot;padding: 3px;&quot;&gt;將圖表轉為 HTML 字串（用於網頁內嵌或 API 回傳）&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;padding: 3px;&quot;&gt;fig.to_json()&lt;/td&gt;
      &lt;td style=&quot;padding: 3px;&quot;&gt;將圖表轉為 JSON 格式（適合儲存、API 傳遞、版本控管）&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;padding: 3px;&quot;&gt;fig.write_json(file)&lt;/td&gt;
      &lt;td style=&quot;padding: 3px;&quot;&gt;將圖表 JSON 結構直接寫入檔案&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;padding: 3px;&quot;&gt;fig.to_dict()&lt;/td&gt;
      &lt;td style=&quot;padding: 3px;&quot;&gt;將圖表轉為 Python 字典格式，可進一步程式操作&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;


&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;注意, fig.write_image() 方法須依賴&amp;nbsp;kaleido 模組, 這是 Plotly 官方推出的匯圖引擎模組, 用來將 Plotly 圖表儲存為靜態圖片.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;1. 匯出圖檔 :&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;呼叫 Figure 物件的 write_image() 方法可將繪製的圖表匯出成圖片檔 (支援 PNG, JPG, SVG, PDF 等檔案類型), 其參數結構如下 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;fig.write_image(&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; file,&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;# 必填，檔案路徑字串或類似檔案的物件&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; format=None,&amp;nbsp; &amp;nbsp; &amp;nbsp; # 圖片格式，如 &#39;png&#39;、&#39;jpeg&#39;、&#39;svg&#39;、&#39;pdf&#39;，若省略會自動從副檔名判斷&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; width=None,&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;# 圖片寬度（像素），預設為圖表原始寬度&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; height=None,&amp;nbsp; &amp;nbsp; &amp;nbsp; # 圖片高度（像素），預設為圖表原始高度&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; scale=1,&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 圖片縮放倍數（例如 2 表示解析度加倍）&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; validate=True,&amp;nbsp; &amp;nbsp; # 是否檢查圖表是否有效（預設 True）&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; engine=&#39;kaleido&#39;&amp;nbsp; # 使用的圖像引擎，預設為 &#39;kaleido&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; )&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;不過使用此方法之前須先安裝 Plotly 的&amp;nbsp;kaleido 模組, 而且 plotly 也要提升至最新版 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;pip install kaleido&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;pip install plotly -U&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;在前一篇測試中使用了 plotly.express 來繪製長條圖, 下列沿用此範例來匯出所繪製的圖檔 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;# plotly_chart_export_1.py&lt;/div&gt;&lt;div&gt;import plotly.express as px&lt;/div&gt;&lt;div&gt;import pandas as pd&lt;/div&gt;&lt;div&gt;import os&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# 1. 資料來源&lt;/div&gt;&lt;div&gt;data={&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &#39;月份&#39;: [&#39;一月&#39;, &#39;二月&#39;, &#39;三月&#39;, &#39;四月&#39;, &#39;五月&#39;],&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &#39;營收&#39;: [120000, 135000, 99000, 150000, 170000]&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# 2. 建立 Figure 圖表物件&lt;/div&gt;&lt;div&gt;# 注意：這裡設定了 width 和 height，匯出圖片時會以此為基準&lt;/div&gt;&lt;div&gt;fig=px.bar(data, x=&#39;月份&#39;, y=&#39;營收&#39;, width=800, height=600, title=&quot;月營收統計圖&quot;)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# 3. 顯示圖表 (選用)&lt;/div&gt;&lt;div&gt;fig.show()&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# 4. 匯出圖檔&amp;nbsp;&lt;/div&gt;&lt;div&gt;# 建立儲存目錄（選用，避免檔案雜亂）&lt;/div&gt;&lt;div&gt;if not os.path.exists(&quot;output&quot;):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; os.mkdir(&quot;output&quot;)&lt;/div&gt;&lt;div&gt;# 匯出為 PNG&lt;/div&gt;&lt;div&gt;fig.write_image(&quot;output/revenue_report.png&quot;, scale=2)&lt;/div&gt;&lt;div&gt;# 匯出為 JPG&lt;/div&gt;&lt;div&gt;fig.write_image(&quot;output/revenue_report.jpg&quot;, scale=2)&lt;/div&gt;&lt;div&gt;print(&quot;圖檔已匯出至 output 資料夾中。&quot;)&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;執行結果除了 fig.show() 會開啟瀏覽器顯示長條圖外, 也會在目前工作目錄下建立 output 子目錄存放匯出的兩個圖檔 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;%Run plotly_chart_export_1.py&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;圖檔已匯出至 output 資料夾中。&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiWhuaXD5-Qkr9bQZFY5l6UIVh2JQOrFVhcFDw_GyXcQDH6IEqLXDHUJq6goPPq5PK4zczAD9DnMi1itazjdsP3SIHQJh7oD8-fg8cNmOGJXKanN9U_rrc1Br6aPLsSnr6E3q3a0fcNkyt8-Y2u1uQgYD8neUVvPd59jDq1fxFVT-Hwy-lin-YYgzvDJd7p/s1131/plotly-export-picture.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;861&quot; data-original-width=&quot;1131&quot; height=&quot;244&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiWhuaXD5-Qkr9bQZFY5l6UIVh2JQOrFVhcFDw_GyXcQDH6IEqLXDHUJq6goPPq5PK4zczAD9DnMi1itazjdsP3SIHQJh7oD8-fg8cNmOGJXKanN9U_rrc1Br6aPLsSnr6E3q3a0fcNkyt8-Y2u1uQgYD8neUVvPd59jDq1fxFVT-Hwy-lin-YYgzvDJd7p/s320/plotly-export-picture.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;2. 匯出網頁 :&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;呼叫 fig.write_html() 可將繪製之圖表匯出為 HTML 檔, 若傳入 include_plotlyjs=&#39;cdn&#39; 參數會使用 CDN 的 plotly 函式庫, 這樣匯出的 HTML 檔較小但須連網才能看到互動圖表; 否則會將 plotly 函式庫一同匯出, 檔案較大些 (約 4MB) 但不須連網, 離線開啟網頁即可看到互動圖表.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;程式碼如下 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;# plotly_chart_export_2.py&lt;/div&gt;&lt;div&gt;import plotly.express as px&lt;/div&gt;&lt;div&gt;import pandas as pd&lt;/div&gt;&lt;div&gt;import os&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# 1. 資料來源&lt;/div&gt;&lt;div&gt;data={&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &#39;月份&#39;: [&#39;一月&#39;, &#39;二月&#39;, &#39;三月&#39;, &#39;四月&#39;, &#39;五月&#39;],&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &#39;營收&#39;: [120000, 135000, 99000, 150000, 170000]&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# 2. 建立 Figure 圖表物件&lt;/div&gt;&lt;div&gt;fig=px.bar(data, x=&#39;月份&#39;, y=&#39;營收&#39;, width=800, height=600, title=&quot;月營收統計圖 (互動式 HTML)&quot;)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# 3. 建立儲存目錄&lt;/div&gt;&lt;div&gt;output_dir=&quot;output&quot;&lt;/div&gt;&lt;div&gt;if not os.path.exists(output_dir):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; os.makedirs(output_dir)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# --- 匯出 HTML&amp;nbsp; ---&lt;/div&gt;&lt;div&gt;# 方式 A：標準匯出 (將 Plotly.js 核心程式碼打包進去，檔案約 4 MB，可離線開啟)&lt;/div&gt;&lt;div&gt;fig.write_html(os.path.join(output_dir, &quot;report_full.html&quot;))&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# 方式 B：輕量化匯出 (使用 CDN 連結，檔案僅約 50KB，開啟時需連網載入 JS)&lt;/div&gt;&lt;div&gt;fig.write_html(&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; os.path.join(output_dir, &quot;report_cdn.html&quot;),&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; include_plotlyjs=&#39;cdn&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; )&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;print(f&quot;✅ HTML 檔案已匯出至 {output_dir} 資料夾。&quot;)&lt;/div&gt;&lt;div&gt;print(&quot;- report_full.html (可離線檢視)&quot;)&lt;/div&gt;&lt;div&gt;print(&quot;- report_cdn.html (體積小，需連網)&quot;)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# 顯示圖表&lt;/div&gt;&lt;div&gt;fig.show()&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;執行結果如下 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;%Run plotly_chart_export_2.py&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;✅ HTML 檔案已匯出至 output 資料夾。&lt;/div&gt;&lt;div&gt;- report_full.html (可離線檢視)&lt;/div&gt;&lt;div&gt;- report_cdn.html (體積小，需連網)&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;開啟 output 資料夾下的網頁檔即可看到長條圖 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiefIMDZ8k9UT2zMlN3nwP9RVhR6frN-W_-PGQkFyvhDruRPAZhpiBOuHlh1Q1Vkn0qb2-D91_L0VYC5QNaxNWLcyQosiV3rQTz8poeq4GZmXb3J6qMblwHvpmBQwcnCTMmxx5uMzFvhgbOs9H8HPfWs9w3vhehZsjh8EZ5Ie8lB2HhcRf1cn3imyaI-H9P/s1635/plotly-export-html-1.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1417&quot; data-original-width=&quot;1635&quot; height=&quot;277&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiefIMDZ8k9UT2zMlN3nwP9RVhR6frN-W_-PGQkFyvhDruRPAZhpiBOuHlh1Q1Vkn0qb2-D91_L0VYC5QNaxNWLcyQosiV3rQTz8poeq4GZmXb3J6qMblwHvpmBQwcnCTMmxx5uMzFvhgbOs9H8HPfWs9w3vhehZsjh8EZ5Ie8lB2HhcRf1cn3imyaI-H9P/s320/plotly-export-html-1.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYF7giOBTHEke9Vi0r3che7LdYLUngE4l63_ANse5HSXiNjLtQXvawHYxv9NnJga2rxx4J1SFZVvVDY__pozO9zhKk9pMymKhsA6lSCf-np0_8lbjxyY1i-DFbLHknM1IEM3p2jJath-F817lc6SNoILKSh9lGuViVaJIn46LTEQVlcog89qe1eamFlOAq/s1638/plotly-export-html-2.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1330&quot; data-original-width=&quot;1638&quot; height=&quot;260&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYF7giOBTHEke9Vi0r3che7LdYLUngE4l63_ANse5HSXiNjLtQXvawHYxv9NnJga2rxx4J1SFZVvVDY__pozO9zhKk9pMymKhsA6lSCf-np0_8lbjxyY1i-DFbLHknM1IEM3p2jJath-F817lc6SNoILKSh9lGuViVaJIn46LTEQVlcog89qe1eamFlOAq/s320/plotly-export-html-2.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://yhhuang1966.blogspot.com/feeds/9134200790282890354/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment/fullpage/post/4877487320781767952/9134200790282890354' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/9134200790282890354'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/9134200790282890354'/><link rel='alternate' type='text/html' href='https://yhhuang1966.blogspot.com/2026/04/python-plotly.html' title='Python 學習筆記 : 用 plotly 繪製互動式圖表 (三)'/><author><name>小狐狸事務所</name><uri>http://www.blogger.com/profile/09435160519044041137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiWhuaXD5-Qkr9bQZFY5l6UIVh2JQOrFVhcFDw_GyXcQDH6IEqLXDHUJq6goPPq5PK4zczAD9DnMi1itazjdsP3SIHQJh7oD8-fg8cNmOGJXKanN9U_rrc1Br6aPLsSnr6E3q3a0fcNkyt8-Y2u1uQgYD8neUVvPd59jDq1fxFVT-Hwy-lin-YYgzvDJd7p/s72-c/plotly-export-picture.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4877487320781767952.post-2060285184414244330</id><published>2026-04-29T23:44:00.012+08:00</published><updated>2026-04-30T00:40:48.478+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><category scheme="http://www.blogger.com/atom/ns#" term="量化投資"/><title type='text'>Python 學習筆記 : 利用語言模型計算技術指標 (一)</title><content type='html'>&lt;div&gt;最近重讀旗標出版的 &quot;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2024/02/ai.html&quot; target=&quot;_blank&quot;&gt;最強 AI 投資分析&lt;/a&gt;&quot; 這本書, 此書於 2023 年底買來看了前幾章便擱下, 也沒時間做測試, 今天重讀第四章後, 決定動手來測試看看, 因為去年 10/7 儲值 5 美元的 OpenAI API Key 目前只用了 0.01 美元, 只剩半年就要被清零了, 得在這之前趕快用掉 (在 Vibe coding 時代親自寫程式已淪落為純興趣了).&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://platform.openai.com/settings/organization/billing/overview&quot; target=&quot;_blank&quot;&gt;https://platform.openai.com/settings/organization/billing/overview&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLzlauJzas3jWj_jiy7u4uazjM_nP9EBusQCOxyxVT7XSOR8XBGQC5S9j4b69njka5XNQZnPQyHds6eWDAbzAuqzsV5sOgO7zbjAijM8uxuUqe6EvVaJuEmciz9FNQUL4JmT5hO1lAyYXaArnaa1_2wuNNsvPWd3nKPymVcKbfq1AVnvt4mtG38TpkJUEK/s1335/openai-api-key-balance.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;617&quot; data-original-width=&quot;1335&quot; height=&quot;148&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLzlauJzas3jWj_jiy7u4uazjM_nP9EBusQCOxyxVT7XSOR8XBGQC5S9j4b69njka5XNQZnPQyHds6eWDAbzAuqzsV5sOgO7zbjAijM8uxuUqe6EvVaJuEmciz9FNQUL4JmT5hO1lAyYXaArnaa1_2wuNNsvPWd3nKPymVcKbfq1AVnvt4mtG38TpkJUEK/s320/openai-api-key-balance.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;書中範例程式碼下載網址 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# &lt;a href=&quot;https://www.flag.com.tw/bk/t/f3933&quot; target=&quot;_blank&quot;&gt;https://www.flag.com.tw/bk/t/f3933&lt;/a&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b style=&quot;background-color: #fce5cd;&quot;&gt;&lt;span style=&quot;color: #990000;&quot;&gt;1. 利用 pandas_ta 計算 SMA 指標 :&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;首先用 pandas_ta 來計算移動平均指標 SMA8 與 SMA13 暖暖身, 畢竟已有近半年沒接觸了, 關於&amp;nbsp; pandas_ta 套件用法參考 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2024/08/python.html&quot; target=&quot;_blank&quot;&gt;Python 量化投資筆記索引&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;下列程式使用 yfinance 取得收盤資料, 然後用 pandas_ta 套件的擴展屬性用法呼叫 df.ta.ma() 計算 SMA 指標, 結果會自動放入 df 的指定欄位, 最後用 kbar 套件繪製 K 線圖, 關於 kbar 套件用法參考 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://github.com/tony1966/kbar&quot; target=&quot;_blank&quot;&gt;https://github.com/tony1966/kbar&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;# ai_stock_test_1.py&lt;/div&gt;&lt;div&gt;import yfinance as yf&lt;/div&gt;&lt;div&gt;import pandas as pd&amp;nbsp;&lt;/div&gt;&lt;div&gt;import pandas_ta as ta&lt;/div&gt;&lt;div&gt;from kbar import KBar&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;if __name__ == &quot;__main__&quot;:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; df=yf.download(&#39;0050.tw&#39;, start=&#39;2024-07-01&#39;, end=&#39;2024-08-21&#39;, auto_adjust=True)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; df.columns=df.columns.map(lambda x: x[0])&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; df[&#39;SMA_8&#39;]=df.ta.sma(length=8)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; df[&#39;SMA_13&#39;]=df.ta.sma(length=13)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; print(df.tail())&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; kb=KBar(df)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; kb.addplot(df[&#39;SMA_8&#39;], panel=2, ylabel=&#39;SMA_8&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; kb.addplot(df[&#39;SMA_13&#39;], panel=2, ylabel=&#39;SMA_13&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; kb.plot(volume=True, mav=[8, 13])&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;此處除了在 panel 2 上繪製 SMA8 與 SMA13 指標外, 同時也在 plot() 方法中指定 mav=[8, 13] 繪製 K 線圖之疊圖 (預設 panel=0), 結果如下 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;%Run ai_stock_test_1.py&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;[*********************100%***********************]&amp;nbsp; 1 of 1 completed&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Close&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;High&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Low&amp;nbsp; ...&amp;nbsp; &amp;nbsp; Volume&amp;nbsp; &amp;nbsp; &amp;nbsp; SMA_8&amp;nbsp; &amp;nbsp; &amp;nbsp;SMA_13&lt;/div&gt;&lt;div&gt;Date&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;...&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;2024-08-14&amp;nbsp; 43.643597&amp;nbsp; 43.909202&amp;nbsp; 43.450429&amp;nbsp; ...&amp;nbsp; 74857276&amp;nbsp; 41.775311&amp;nbsp; 42.438161&lt;/div&gt;&lt;div&gt;2024-08-15&amp;nbsp; 43.305553&amp;nbsp; 43.703958&amp;nbsp; 43.233115&amp;nbsp; ...&amp;nbsp; 45926588&amp;nbsp; 42.397066&amp;nbsp; 42.414943&lt;/div&gt;&lt;div&gt;2024-08-16&amp;nbsp; 44.283455&amp;nbsp; 44.343819&amp;nbsp; 44.029927&amp;nbsp; ...&amp;nbsp; 52823660&amp;nbsp; 42.876964&amp;nbsp; 42.466949&lt;/div&gt;&lt;div&gt;2024-08-19&amp;nbsp; 44.343822&amp;nbsp; 44.597354&amp;nbsp; 44.223093&amp;nbsp; ...&amp;nbsp; 37122372&amp;nbsp; 43.163695&amp;nbsp; 42.518955&lt;/div&gt;&lt;div&gt;2024-08-20&amp;nbsp; 44.367966&amp;nbsp; 44.718080&amp;nbsp; 44.355892&amp;nbsp; ...&amp;nbsp; 43139504&amp;nbsp; 43.562101&amp;nbsp; 42.514312&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;[5 rows x 7 columns]&lt;/div&gt;&lt;div&gt;設定字型為: Microsoft JhengHei&lt;/div&gt;&lt;div&gt;使用指定字型: Microsoft JhengHei&lt;/div&gt;&lt;div&gt;字型候選清單: [&#39;Microsoft JhengHei&#39;, &#39;DejaVu Sans&#39;, &#39;Arial&#39;]&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOW5ON25mXgBGgFfbiyqEfBs3ErQHiGgDfeslN8FMr7uzOmI-A5VJ_2R_UguSwTpQAGSRsoTiEZC4f5-y19SOX-LhsJ8gt8EsUbWTQlEtV-tpTNu9NnDBP0OfZZqr8QtnyHtBQ_7CqJuAz-VRT_c7Ucsg_m2B_mcyQXtfvRhPeZNe9DGhKlbhQhJTF7SD0/s1160/ai-stock-test-1.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;847&quot; data-original-width=&quot;1160&quot; height=&quot;234&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOW5ON25mXgBGgFfbiyqEfBs3ErQHiGgDfeslN8FMr7uzOmI-A5VJ_2R_UguSwTpQAGSRsoTiEZC4f5-y19SOX-LhsJ8gt8EsUbWTQlEtV-tpTNu9NnDBP0OfZZqr8QtnyHtBQ_7CqJuAz-VRT_c7Ucsg_m2B_mcyQXtfvRhPeZNe9DGhKlbhQhJTF7SD0/s320/ai-stock-test-1.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span style=&quot;background-color: #fce5cd;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #990000;&quot;&gt;2. 串接 OpenAI API 計算 SMA 指標 :&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;接下來要串接 OpenAI API, 讓 LLM 模型來生成計算技術指標的程式碼後, 用 exec() 執行該程式碼計算技術指標, 好處是毋須去熟悉例如 pandas_ta, ta, 或 Ta-Lib 套件之函式呼叫介面, 直接用自然語言來指揮 LLM 傳回技術指標計算式, 做法參考書中 ˋ4-1 的範例 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# &lt;a href=&quot;https://colab.research.google.com/drive/19Wg-NToZJ_Io_K6ESOJJFqEYHCR8lQvJ#scrollTo=jkH64Sng6jZh&quot; target=&quot;_blank&quot;&gt;讓 AI 自動生成技術指標的程式碼&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;原程式碼的提示詞使用英文, 作者說經測試使用英文較能得到穩定之回應, 但現在 LLM 日新月異, 對中文的理解能力已非常精準, 因此我將其改寫為中文提示詞, 程式碼如下 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;# ai_stock_test_2.py&lt;/div&gt;&lt;div&gt;from&amp;nbsp; openai import OpenAI, APIError&amp;nbsp;&lt;/div&gt;&lt;div&gt;import yfinance as yf&lt;/div&gt;&lt;div&gt;import pandas as pd&amp;nbsp;&lt;/div&gt;&lt;div&gt;from dotenv import dotenv_values&lt;/div&gt;&lt;div&gt;from kbar import KBar&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;def ask_gpt(&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; messages: list[dict[str, str]],&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; model: str=&#39;gpt-3.5-turbo&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; ) -&amp;gt; str:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; try:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; reply=client.chat.completions.create(&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; model=model,&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; messages=messages&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; )&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return reply.choices[0].message.content or &#39;&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; except APIError as e:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return e.message&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;def ai_helper(df, user_msg):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; role=f&#39;&#39;&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 作為一個專業的程式碼生成機器人，&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 我需要您的協助來根據特定的用戶需求生成 Python 程式碼。&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 為了進行下去，我將提供給您一個遵循格式 {list(df.columns)} 的 DataFrame（df）。&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 您的任務是仔細分析用戶的需求並相應地生成 Python 程式碼。&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 請注意，您的回應須僅包含代碼本身，並且不應包含任何額外的資訊。&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;&#39;&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; # 把 user_msg 加入到 task 的敘述中，讓 AI 知道要算什麼&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; task=f&#39;&#39;&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 您的任務是開發一個名為 &#39;calculate(df)&#39; 的 Python 函式。&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 這個函式應接受一個 DataFrame 作為其參數。確保您僅使用資料集中存在的欄，&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 特別是 {list(df.columns)}。&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 用戶的具體運算需求為：【 {user_msg} 】&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 處理後，該函式應返回處理過的 DataFrame。&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 您的回應應嚴格包含 &#39;calculate(df)&#39; 函式的 Python 程式碼，&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 並排除任何無關的內容。&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;&#39;&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; msg=[{&quot;role&quot;: &quot;system&quot;, &quot;content&quot;: role},&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;{&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: task}]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; reply_data=ask_gpt(msg)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; # 清理 markdown 語法&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; cleaned_code=reply_data.replace(&quot;```&quot;, &quot;&quot;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; cleaned_code=cleaned_code.replace(&quot;python&quot;, &quot;&quot;)&amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; cleaned_code=cleaned_code.strip() # 建議加上 strip() 去除頭尾多餘的空白或換行&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; # 傳回程式碼&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; return cleaned_code&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;if __name__ == &quot;__main__&quot;:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; config=dotenv_values(&#39;.env&#39;)&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; openai_api_key=config.get(&#39;OPENAI_API_KEY&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; client=OpenAI(api_key=openai_api_key)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; df=yf.download(&#39;0050.tw&#39;, start=&#39;2024-07-01&#39;, end=&#39;2024-08-21&#39;, auto_adjust=True)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; df.columns=df.columns.map(lambda x: x[0])&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; code_str=ai_helper(df, &quot;計算 8 日 MA (欄名 SMA_8) 與 13 日 MA (欄名 SMA_13)&quot;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; print(code_str)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; exec(code_str)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; new_df=calculate(df)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; print(new_df.tail())&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; kb=KBar(new_df)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; kb.addplot(new_df[&#39;SMA_8&#39;], panel=2, ylabel=&#39;SMA_8&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; kb.addplot(new_df[&#39;SMA_13&#39;], panel=2, ylabel=&#39;SMA_13&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; kb.plot(volume=True, mav=[8, 13])&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;此程式的 ask_gpt() 函式負責向 GPT 提問並取得回應, 注意, ask_gpt() 的傳入參數都使用了類型提示語法以增加程式碼可讀性. 例如 ask_gpt() 中的 messages: list[dict[str, str]] 意思是 :&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;messages 是一個串列, 裡面的每個元素都是字典.&lt;/li&gt;&lt;li&gt;字典的鍵與值都是字串, 例如 {&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;hello&quot;}&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;參考 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2026/04/openai-api.html&quot; target=&quot;_blank&quot;&gt;AI 應用程式專案 (一) : 新聞稿生成器&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;而 ai_helper() 函式則負責組裝提示詞 (字典串列) 並呼叫 ask_gpt(), 取得回應的指標計算程式碼後進行清理, 傳回純淨之 Python 程式碼給主函式以 exec() 執行, 結果如下 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;%Run ai_stock_test_2.py&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;[*********************100%***********************]&amp;nbsp; 1 of 1 completed&lt;/div&gt;&lt;div&gt;def calculate(df):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; df[&#39;SMA_8&#39;] = df[&#39;Close&#39;].rolling(window=8).mean()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; df[&#39;SMA_13&#39;] = df[&#39;Close&#39;].rolling(window=13).mean()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; return df&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Close&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;High&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Low&amp;nbsp; ...&amp;nbsp; &amp;nbsp; Volume&amp;nbsp; &amp;nbsp; &amp;nbsp; SMA_8&amp;nbsp; &amp;nbsp; &amp;nbsp;SMA_13&lt;/div&gt;&lt;div&gt;Date&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;...&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;2024-08-14&amp;nbsp; 43.643597&amp;nbsp; 43.909202&amp;nbsp; 43.450429&amp;nbsp; ...&amp;nbsp; 74857276&amp;nbsp; 41.775311&amp;nbsp; 42.438161&lt;/div&gt;&lt;div&gt;2024-08-15&amp;nbsp; 43.305553&amp;nbsp; 43.703958&amp;nbsp; 43.233115&amp;nbsp; ...&amp;nbsp; 45926588&amp;nbsp; 42.397066&amp;nbsp; 42.414943&lt;/div&gt;&lt;div&gt;2024-08-16&amp;nbsp; 44.283455&amp;nbsp; 44.343819&amp;nbsp; 44.029927&amp;nbsp; ...&amp;nbsp; 52823660&amp;nbsp; 42.876964&amp;nbsp; 42.466949&lt;/div&gt;&lt;div&gt;2024-08-19&amp;nbsp; 44.343822&amp;nbsp; 44.597354&amp;nbsp; 44.223093&amp;nbsp; ...&amp;nbsp; 37122372&amp;nbsp; 43.163695&amp;nbsp; 42.518955&lt;/div&gt;&lt;div&gt;2024-08-20&amp;nbsp; 44.367966&amp;nbsp; 44.718080&amp;nbsp; 44.355892&amp;nbsp; ...&amp;nbsp; 43139504&amp;nbsp; 43.562101&amp;nbsp; 42.514312&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;[5 rows x 7 columns]&lt;/div&gt;&lt;div&gt;設定字型為: Microsoft JhengHei&lt;/div&gt;&lt;div&gt;使用指定字型: Microsoft JhengHei&lt;/div&gt;&lt;div&gt;字型候選清單: [&#39;Microsoft JhengHei&#39;, &#39;DejaVu Sans&#39;, &#39;Arial&#39;]&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5A_4sOgHQUnJsPWPIvP-wAkRTzRgg4Sqfv76vJWEHprrDTBmXMEL-pVkJ3olAUsvmad0T1-wLpHQKCoDyscwCTKmJR116WNSI3v2QfSEG-9ep70dH8uBycWiFAhC_YBSjE5uNrC83j9sKuItyHN7gG6BGl5EZ6OWaUXEaN8_93_2RpZk7QGpWkKpLPBBW/s1169/ai-stock-test-2.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;843&quot; data-original-width=&quot;1169&quot; height=&quot;231&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5A_4sOgHQUnJsPWPIvP-wAkRTzRgg4Sqfv76vJWEHprrDTBmXMEL-pVkJ3olAUsvmad0T1-wLpHQKCoDyscwCTKmJR116WNSI3v2QfSEG-9ep70dH8uBycWiFAhC_YBSjE5uNrC83j9sKuItyHN7gG6BGl5EZ6OWaUXEaN8_93_2RpZk7QGpWkKpLPBBW/s320/ai-stock-test-2.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;計算出來的 SMA 數值與用 pandas_ta 計算的結果相同, 可見即使沒學過技術指標套件, 也可以利用 LLM 來進行技術指標的量化分析.&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: #fce5cd;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #990000;&quot;&gt;3. 串接 Gemini API 計算 SMA 指標 :&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Gemini 版本的函式要改成 ask_gemini(), 而 ai_helper() 函式基本不變, 只有提示詞類型不同, OpenAI 的提示詞為字典字串, 而 Gemini 則是純字串. 程式碼如下 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;# ai_stock_test_3.py&lt;/div&gt;&lt;div&gt;from google import genai&lt;/div&gt;&lt;div&gt;from google.genai.errors import APIError&lt;/div&gt;&lt;div&gt;import yfinance as yf&lt;/div&gt;&lt;div&gt;import pandas as pd&amp;nbsp;&lt;/div&gt;&lt;div&gt;from dotenv import dotenv_values&lt;/div&gt;&lt;div&gt;from kbar import KBar&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;def ask_gemini(messages: str, model: str=&#39;gemini-2.5-flash&#39;) -&amp;gt; str:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; try:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; reply=client.models.generate_content(&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; model=model,&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; contents=messages&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; )&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return reply.text or &#39;&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; except APIError as e:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return e.message&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;def ai_helper(df, user_msg):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; role=f&#39;&#39;&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 作為一個專業的程式碼生成機器人，&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 我需要您的協助來根據特定的用戶需求生成 Python 程式碼。&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 為了進行下去，我將提供給您一個遵循格式 {list(df.columns)} 的 DataFrame（df）。&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 您的任務是仔細分析用戶的需求並相應地生成 Python 程式碼。&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 請注意，您的回應須僅包含代碼本身，並且不應包含任何額外的資訊。&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;&#39;&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; task=f&#39;&#39;&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 您的任務是開發一個名為 &#39;calculate(df)&#39; 的 Python 函式。&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 這個函式應接受一個 DataFrame 作為其參數。確保您僅使用資料集中存在的欄，&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 特別是 {list(df.columns)}。&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 用戶的具體運算需求為：【 {user_msg} 】&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 處理後，該函式應返回處理過的 DataFrame。&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 您的回應應嚴格包含 &#39;calculate(df)&#39; 函式的 Python 程式碼，&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 並排除任何無關的內容。&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;&#39;&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; # Gemini 的提示詞為字串型態 : 將系統設定與任務直接合併成一段完整的字串&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;background-color: #fcff01;&quot;&gt;msg=f&quot;{role}\n\n{task}&quot;&lt;/span&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; # 呼叫 ask_gemini&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; reply_data=&lt;span style=&quot;background-color: #fcff01;&quot;&gt;ask_gemini&lt;/span&gt;(msg)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; # 清理傳回 markdown 語法&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; cleaned_code=reply_data.replace(&quot;```&quot;, &quot;&quot;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; cleaned_code=cleaned_code.replace(&quot;python&quot;, &quot;&quot;)&amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; cleaned_code=cleaned_code.strip() # 去除頭尾多餘的空白或換行&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; # 傳回程式碼&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; return cleaned_code&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;if __name__ == &quot;__main__&quot;:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; config=dotenv_values(&#39;.env&#39;)&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; gemini_api_key=config.get(&#39;GEMINI_API_KEY&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; client=genai.Client(api_key=gemini_api_key)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; df=yf.download(&#39;0050.tw&#39;, start=&#39;2024-07-01&#39;, end=&#39;2024-08-21&#39;, auto_adjust=True)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; df.columns=df.columns.map(lambda x: x[0])&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; code_str=ai_helper(df, &quot;計算 8 日 MA (欄名 SMA_8) 與 13 日 MA (欄名 SMA_13)&quot;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; print(code_str)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; exec(code_str)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; new_df=calculate(df)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; print(new_df.tail())&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; kb=KBar(new_df)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; kb.addplot(new_df[&#39;SMA_8&#39;], panel=2, ylabel=&#39;SMA_8&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; kb.addplot(new_df[&#39;SMA_13&#39;], panel=2, ylabel=&#39;SMA_13&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; kb.plot(volume=True, mav=[8, 13])&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;結果與上面是一樣的 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;%Run ai_stock_test_3.py&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;[*********************100%***********************]&amp;nbsp; 1 of 1 completed&lt;/div&gt;&lt;div&gt;import pandas as pd&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;def calculate(df):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &quot;&quot;&quot;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; 計算 8 日 MA (欄名 SMA_8) 與 13 日 MA (欄名 SMA_13)。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; Args:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; df (pd.DataFrame): 包含 &#39;Close&#39;, &#39;High&#39;, &#39;Low&#39;, &#39;Open&#39;, &#39;Volume&#39; 欄位的 DataFrame。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; Returns:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; pd.DataFrame: 處理後包含 &#39;SMA_8&#39; 和 &#39;SMA_13&#39; 欄位的 DataFrame。&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &quot;&quot;&quot;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; df[&#39;SMA_8&#39;] = df[&#39;Close&#39;].rolling(window=8).mean()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; df[&#39;SMA_13&#39;] = df[&#39;Close&#39;].rolling(window=13).mean()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; return df&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Close&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;High&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Low&amp;nbsp; ...&amp;nbsp; &amp;nbsp; Volume&amp;nbsp; &amp;nbsp; &amp;nbsp; SMA_8&amp;nbsp; &amp;nbsp; &amp;nbsp;SMA_13&lt;/div&gt;&lt;div&gt;Date&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;...&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;2024-08-14&amp;nbsp; 43.643597&amp;nbsp; 43.909202&amp;nbsp; 43.450429&amp;nbsp; ...&amp;nbsp; 74857276&amp;nbsp; 41.775310&amp;nbsp; 42.438160&lt;/div&gt;&lt;div&gt;2024-08-15&amp;nbsp; 43.305553&amp;nbsp; 43.703958&amp;nbsp; 43.233115&amp;nbsp; ...&amp;nbsp; 45926588&amp;nbsp; 42.397066&amp;nbsp; 42.414943&lt;/div&gt;&lt;div&gt;2024-08-16&amp;nbsp; 44.283459&amp;nbsp; 44.343823&amp;nbsp; 44.029930&amp;nbsp; ...&amp;nbsp; 52823660&amp;nbsp; 42.876964&amp;nbsp; 42.466949&lt;/div&gt;&lt;div&gt;2024-08-19&amp;nbsp; 44.343822&amp;nbsp; 44.597354&amp;nbsp; 44.223093&amp;nbsp; ...&amp;nbsp; 37122372&amp;nbsp; 43.163696&amp;nbsp; 42.518955&lt;/div&gt;&lt;div&gt;2024-08-20&amp;nbsp; 44.367966&amp;nbsp; 44.718080&amp;nbsp; 44.355892&amp;nbsp; ...&amp;nbsp; 43139504&amp;nbsp; 43.562102&amp;nbsp; 42.514312&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;[5 rows x 7 columns]&lt;/div&gt;&lt;div&gt;設定字型為: Microsoft JhengHei&lt;/div&gt;&lt;div&gt;使用指定字型: Microsoft JhengHei&lt;/div&gt;&lt;div&gt;字型候選清單: [&#39;Microsoft JhengHei&#39;, &#39;DejaVu Sans&#39;, &#39;Arial&#39;]&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3ERzV5xHDPEnu2eR7_VpwDVa8LTQV2d4rVAvEVqsTSDXFQI-J7vllyMIU4yA0cYCa81BJAG6HRqW0Fpt2gEl0S6KUyDXzYmqxY4SOlAZaA_xnmfjslFnBzisxTF_lAjnFxnQyIIKmax7xrLeZ1znlyilKmeaJW-VdWGK4qo640v3a0duo-tOKKxgJd-u_/s1139/ai-stock-test-3.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;847&quot; data-original-width=&quot;1139&quot; height=&quot;238&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3ERzV5xHDPEnu2eR7_VpwDVa8LTQV2d4rVAvEVqsTSDXFQI-J7vllyMIU4yA0cYCa81BJAG6HRqW0Fpt2gEl0S6KUyDXzYmqxY4SOlAZaA_xnmfjslFnBzisxTF_lAjnFxnQyIIKmax7xrLeZ1znlyilKmeaJW-VdWGK4qo640v3a0duo-tOKKxgJd-u_/s320/ai-stock-test-3.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://yhhuang1966.blogspot.com/feeds/2060285184414244330/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment/fullpage/post/4877487320781767952/2060285184414244330' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/2060285184414244330'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/2060285184414244330'/><link rel='alternate' type='text/html' href='https://yhhuang1966.blogspot.com/2026/04/python_29.html' title='Python 學習筆記 : 利用語言模型計算技術指標 (一)'/><author><name>小狐狸事務所</name><uri>http://www.blogger.com/profile/09435160519044041137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLzlauJzas3jWj_jiy7u4uazjM_nP9EBusQCOxyxVT7XSOR8XBGQC5S9j4b69njka5XNQZnPQyHds6eWDAbzAuqzsV5sOgO7zbjAijM8uxuUqe6EvVaJuEmciz9FNQUL4JmT5hO1lAyYXaArnaa1_2wuNNsvPWd3nKPymVcKbfq1AVnvt4mtG38TpkJUEK/s72-c/openai-api-key-balance.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4877487320781767952.post-1190065142980599060</id><published>2026-04-28T10:30:00.007+08:00</published><updated>2026-04-28T10:30:53.032+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="React"/><category scheme="http://www.blogger.com/atom/ns#" term="好書"/><title type='text'>市圖還書兩本 (React)</title><content type='html'>&lt;div&gt;前陣子因為在 Vibe Coding 開發中, 發現 AI 經常使用 React 作為前端框架, 於是興起一股學學看的念頭, 從市圖借來幾本 React 的書, 沒有打算深入研究, 只想對核心運作有個基本認識. 不過還沒開卷呢, 下面兩本已被預約須還 :&lt;/div&gt;&lt;div&gt;&lt;ol style=&quot;text-align: left;&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://webpacx.ksml.edu.tw/bookDetail/1644531&quot; target=&quot;_blank&quot;&gt;React全方位基礎入門實戰 : 初心者開始的第一堂必修課&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://webpacx.ksml.edu.tw/bookDetail/1772675&quot; target=&quot;_blank&quot;&gt;流暢的React : 建構快速、高效、直覺的Web應用程式&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div&gt;No.1 作者是 ReacJS 新聞站長, 此書雖較舊了, 但前半部有豐富的 ES6 語法介紹; No.2 書況極新 (2024 年出版), 內容也更豐富, 包含伺服端 React, 與 Next.js 框架等, 下次再回借.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://yhhuang1966.blogspot.com/feeds/1190065142980599060/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment/fullpage/post/4877487320781767952/1190065142980599060' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/1190065142980599060'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/1190065142980599060'/><link rel='alternate' type='text/html' href='https://yhhuang1966.blogspot.com/2026/04/react.html' title='市圖還書兩本 (React)'/><author><name>小狐狸事務所</name><uri>http://www.blogger.com/profile/09435160519044041137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4877487320781767952.post-9131446641142612145</id><published>2026-04-26T22:51:00.009+08:00</published><updated>2026-04-26T22:51:55.398+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="生活"/><title type='text'>2026 年第 16 周記事</title><content type='html'>&lt;div&gt;週五天氣轉陰, 下班時差點淋到雨, 傍晚回鄉下時開始下起小雨, 整個晚上都在下, 甚至連周六也是下整天, 雨天無訪客剛好在家趕 SDD 線上課程的作業, 花了整整一天終於在周六晚上午夜關檔前完成作業上傳, 好險! 45 個學員也只有 9 個趕上截止期限 (我是最後一個哈哈).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;幸好今天出太陽, 趕緊將沙發罩洗好拿去曬, 因下周大帥與仲仔要造訪鄉下家, 得事先整理一下客廳. 下午把馬路邊的三棵芒果樹都套袋完畢, 約莫 60 顆左右, 樹梢還有很多太高無法套袋, 下周要去小漢買網子攤開綁在樹下, 這樣等自然熟掉下來時才不會摔壞.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmrEFu_AVYbmnmj4Iffy4Kjs8F5QsCDGBj6PpGXo83YdrNhFELMWNDplsRf9zpCk77UtX3Adgm93gdj_WMJqz53_EN0LIfQIsLeg_QV0kv6ln-YsgReYvi-X-fYHz09-DSbo8w-uWHTtSTgjLJoy3wwh-18V7ua9-idVlGrkRyFhPafaKIGxl79uhKx-sR/s1477/%E8%B7%AF%E6%97%81%E8%8A%92%E6%9E%9C%E5%A5%97%E8%A2%8B.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1477&quot; data-original-width=&quot;1108&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmrEFu_AVYbmnmj4Iffy4Kjs8F5QsCDGBj6PpGXo83YdrNhFELMWNDplsRf9zpCk77UtX3Adgm93gdj_WMJqz53_EN0LIfQIsLeg_QV0kv6ln-YsgReYvi-X-fYHz09-DSbo8w-uWHTtSTgjLJoy3wwh-18V7ua9-idVlGrkRyFhPafaKIGxl79uhKx-sR/s320/%E8%B7%AF%E6%97%81%E8%8A%92%E6%9E%9C%E5%A5%97%E8%A2%8B.jpg&quot; width=&quot;240&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;由於雨季即將來臨, 蔥價會攀高, 早上跑了一趟種子行買了 20 株青蔥苗+一株九層塔+六株皇宮菜, 年初種了一盆香菜長不好, 傍晚全部拔掉改種青蔥, 九層塔也是盆栽, 皇宮菜則暫時放著澆水, 小舅五月初要叫小耕耘機把菜園的土翻一遍, 說之後保留兩畦給他秋天時種小番茄, 其餘我要種菜或種果樹均好, 因為他家那邊也有一個菜園要顧 (去年他朋友借他使用). 菜園南側因為較遠, 種菜澆水較不便, 打算再種兩棵芭樂樹與木瓜樹.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;毛小妹第二胎 (也是四隻) 小貓現在都在室外了, 這梯都很怕人不親, 我一靠近就跑掉. 上一梯的四隻目前只剩小黑與吉哇哇在家, 小乖與哇哇吉都離家超過一個月不回來了. 毛小妹的妹妹捲尾阿姨我也一周未見她出現, 恐怕也不回來了. 雖說不回來, 其實我認為它們應該都是遇險 (中毒/車禍) 回不來.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQdQcodvrnIlP402ZvBtAewKn5k1boyfA5igMfRgTT8Iw2R0lvpaYpGVaIKtEHZD24rrdtVc0pokfqAF2wQRK4mmjaPiZ_z54L7UwdVtqBhE_xPORcxOzUIAyTezprYW7VzkAlmotrE0IEYUzitS7MrpjVm5YCh8fQGPf_oVJf2Gzf4ipZXS8wI5SpD0ed/s1477/%E6%AF%9B%E5%B0%8F%E5%A6%B9%E7%9A%84%E7%AC%AC%E4%BA%8C%E8%83%8E.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1108&quot; data-original-width=&quot;1477&quot; height=&quot;240&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQdQcodvrnIlP402ZvBtAewKn5k1boyfA5igMfRgTT8Iw2R0lvpaYpGVaIKtEHZD24rrdtVc0pokfqAF2wQRK4mmjaPiZ_z54L7UwdVtqBhE_xPORcxOzUIAyTezprYW7VzkAlmotrE0IEYUzitS7MrpjVm5YCh8fQGPf_oVJf2Gzf4ipZXS8wI5SpD0ed/s320/%E6%AF%9B%E5%B0%8F%E5%A6%B9%E7%9A%84%E7%AC%AC%E4%BA%8C%E8%83%8E.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;車庫雜物實在太多了, 等沖繩回來得來個斷捨離大清理了.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://yhhuang1966.blogspot.com/feeds/9131446641142612145/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment/fullpage/post/4877487320781767952/9131446641142612145' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/9131446641142612145'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/9131446641142612145'/><link rel='alternate' type='text/html' href='https://yhhuang1966.blogspot.com/2026/04/2026-16.html' title='2026 年第 16 周記事'/><author><name>小狐狸事務所</name><uri>http://www.blogger.com/profile/09435160519044041137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmrEFu_AVYbmnmj4Iffy4Kjs8F5QsCDGBj6PpGXo83YdrNhFELMWNDplsRf9zpCk77UtX3Adgm93gdj_WMJqz53_EN0LIfQIsLeg_QV0kv6ln-YsgReYvi-X-fYHz09-DSbo8w-uWHTtSTgjLJoy3wwh-18V7ua9-idVlGrkRyFhPafaKIGxl79uhKx-sR/s72-c/%E8%B7%AF%E6%97%81%E8%8A%92%E6%9E%9C%E5%A5%97%E8%A2%8B.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4877487320781767952.post-5784999000111854772</id><published>2026-04-25T22:17:00.007+08:00</published><updated>2026-04-25T23:32:05.307+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Gemini CLI"/><category scheme="http://www.blogger.com/atom/ns#" term="OpenSpec"/><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><category scheme="http://www.blogger.com/atom/ns#" term="SDD"/><title type='text'>Gemini CLI 學習筆記 : OpenSpec 初體驗 (三)</title><content type='html'>&lt;div&gt;離作業交卷只剩 2.5 小時, 刻不容緩繼續進行第三次迭代.&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;本系列全部測試文章參考 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2026/01/gemini-cli_30.html&quot; target=&quot;_blank&quot;&gt;Gemini CLI 學習筆記索引&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;第三次迭代要在前次基礎上添加對數與三角函數科學計算功能, 同樣使用逐步推進模式, 工作流所需指令如下 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;/opsx:new &amp;lt;iteration_name&amp;gt; (建立迭代之專屬的工作區)&lt;/li&gt;&lt;li&gt;/opsx:propose &amp;lt;requirements&amp;gt; (依需求起草提案書, 規格定義書, 架構設計書, 與任務清單)&lt;/li&gt;&lt;li&gt;/opsx:apply &amp;lt;iteration_name&amp;gt; (依照 tasks.md 中的任務清單逐一實作此迭代功能)&lt;/li&gt;&lt;li&gt;/opsx:archive (迭代完成歸檔)&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;廢話不多說以免誤了軍期, 馬上開工.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;1.&amp;nbsp;建立迭代之專屬工作區 :&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;第三次迭代工作區取名為 calc-scientific :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;gt;&amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;/opsx:new calc-scientific&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCc-m8PVOwRclhl-fPSAYxP5hUkMBhG4QUUEOcoeca7A1CJehptHd7akmMmHBLR5Irlf74cgPTrvo4g32WY5aH_RIGN-BXp3W-JsC_ULCKlT7kz7kXa3q_5Srq31vxhUeo9RF6hk5uZpqPAxwzmcMyGjl9qXzev4yLypmYh5wRPIZkI0orOZH-9bpgvhRa/s2220/gemin-cli-openspec-25.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1073&quot; data-original-width=&quot;2220&quot; height=&quot;155&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCc-m8PVOwRclhl-fPSAYxP5hUkMBhG4QUUEOcoeca7A1CJehptHd7akmMmHBLR5Irlf74cgPTrvo4g32WY5aH_RIGN-BXp3W-JsC_ULCKlT7kz7kXa3q_5Srq31vxhUeo9RF6hk5uZpqPAxwzmcMyGjl9qXzev4yLypmYh5wRPIZkI0orOZH-9bpgvhRa/s320/gemin-cli-openspec-25.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;... (略) ...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgd98hC3mZJYFrlJOVjdqZ2-UaAJojz82eECaEZvwVT2UaNc3E756ddvMsxRf4JFh3C8GADGAL0E9TzTxcEJ_iwRpuK9aBKhiByZE1eFZ_7dKqGr5_8r06PG5zlj5cfRvI2gO6qAfSXa7AwUnjrRcq7eq9VRkvTVYbxA_qudkj1APE2zAUiE-L-JA2mJk18/s2292/gemin-cli-openspec-26.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1139&quot; data-original-width=&quot;2292&quot; height=&quot;159&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgd98hC3mZJYFrlJOVjdqZ2-UaAJojz82eECaEZvwVT2UaNc3E756ddvMsxRf4JFh3C8GADGAL0E9TzTxcEJ_iwRpuK9aBKhiByZE1eFZ_7dKqGr5_8r06PG5zlj5cfRvI2gO6qAfSXa7AwUnjrRcq7eq9VRkvTVYbxA_qudkj1APE2zAUiE-L-JA2mJk18/s320/gemin-cli-openspec-26.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;2. 根據需求填寫提案書 :&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;/opsx:propose &quot;在現有計算器專案上擴充科學計算功能, 包含三角函數 (sin, cos, tan) 與對數 (以 10 為底的 log, 以及自然對數 ln), 請務必處理以下邊界與轉換邏輯：1. 三角函數的輸入值預設為「角度 (Degree)」, 後端需自行轉換為弧度進行計算, 2. 處理 tan(90) 等無效角度的防呆機制, 3. 對數運算需阻擋小於或等於 0 的無效輸入, 並回傳明確的 HTTP 錯誤, 4. 前端介面需優雅地加入這些新按鈕. &quot;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjL23uZfPjSHYTzVRNtaHyzVQCXEDZWR0rtVPSi9Rs8UQYcGF7PyPQul0g7FnH275_dbNDCopqqbdmzQXiMacjLnKeAp8d5p6SxQTNZ76In9gMnRT3u2N_0dwIrsf8q2xDw1r-eVSsxZ5eLkDDouXq7RkjutjtuO7P72HFjgxXU_m0duJks2QayiiBKNrg4/s2282/gemin-cli-openspec-27.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1118&quot; data-original-width=&quot;2282&quot; height=&quot;157&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjL23uZfPjSHYTzVRNtaHyzVQCXEDZWR0rtVPSi9Rs8UQYcGF7PyPQul0g7FnH275_dbNDCopqqbdmzQXiMacjLnKeAp8d5p6SxQTNZ76In9gMnRT3u2N_0dwIrsf8q2xDw1r-eVSsxZ5eLkDDouXq7RkjutjtuO7P72HFjgxXU_m0duJks2QayiiBKNrg4/s320/gemin-cli-openspec-27.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;... (略) ...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjJsONsUegsn0fDtg_Md7CHUpKwTHd63t4I3lZQ79a8eVlpcp7TeirLrnoMWNgk9T1i72qhFOk_Hf9sBhoY_k7urRqCV-Fok29WEinfCdduSowrjulTVaKH1RPWPcusDzAxiuPM4nIdj_M-pCtAsnu3IVThkKQPkN5apDO1Bq18XRlf-fcq1XiKWvYnQvD/s2301/gemin-cli-openspec-28.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1213&quot; data-original-width=&quot;2301&quot; height=&quot;169&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjJsONsUegsn0fDtg_Md7CHUpKwTHd63t4I3lZQ79a8eVlpcp7TeirLrnoMWNgk9T1i72qhFOk_Hf9sBhoY_k7urRqCV-Fok29WEinfCdduSowrjulTVaKH1RPWPcusDzAxiuPM4nIdj_M-pCtAsnu3IVThkKQPkN5apDO1Bq18XRlf-fcq1XiKWvYnQvD/s320/gemin-cli-openspec-28.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;3. 依據任務清單實作程式碼 :&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;/opsx:apply calc-scientific&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6VpQ1E0RYu-xu5ZOtDjFhV2MSJyEP6AwVbmK1mutHZ7SHeXWpqTmU8Lxx2PpbY9sVqAZPS5bboQBgadXUHMRYbtL_BkMQi9wKNQUHDh4O3fj7Jx1x5vtCiMI846M8LQ0YyMkzLaRP5aheasR86QU0W1FfplT8vPSo91RRgI57kH5H_X3uZNxjSbh8_lvr/s2226/gemin-cli-openspec-29.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1157&quot; data-original-width=&quot;2226&quot; height=&quot;166&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6VpQ1E0RYu-xu5ZOtDjFhV2MSJyEP6AwVbmK1mutHZ7SHeXWpqTmU8Lxx2PpbY9sVqAZPS5bboQBgadXUHMRYbtL_BkMQi9wKNQUHDh4O3fj7Jx1x5vtCiMI846M8LQ0YyMkzLaRP5aheasR86QU0W1FfplT8vPSo91RRgI57kH5H_X3uZNxjSbh8_lvr/s320/gemin-cli-openspec-29.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;... (略) ...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjP7lSQOQrg3OeKpJoP6_1crq87rvs51_6mVhPRVAl80aesF0GFCmEIkytygt3r4f2wFyopaj6HFVa2yyCsiR8qo6TumbbKyPr7g2riPSW5IJAIxNjmyKc473I3x93_d2Whu8f9yuU9MYnt22LMJBE9HqrvqkqDVOR300wqPg7aCS99osJuou4Ky5agtoNv/s2294/gemin-cli-openspec-30.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1147&quot; data-original-width=&quot;2294&quot; height=&quot;160&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjP7lSQOQrg3OeKpJoP6_1crq87rvs51_6mVhPRVAl80aesF0GFCmEIkytygt3r4f2wFyopaj6HFVa2yyCsiR8qo6TumbbKyPr7g2riPSW5IJAIxNjmyKc473I3x93_d2Whu8f9yuU9MYnt22LMJBE9HqrvqkqDVOR300wqPg7aCS99osJuou4Ky5agtoNv/s320/gemin-cli-openspec-30.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;完成專案實作馬上作人工測試, 開啟 127.0.0.1:5000 網頁果然多了很多科學計算按鈕 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixtiuBvi0MvidYmauXvb74sFDZjMYQRvbH8tGqnXTeYzJqp3M-BBV5CDKPEb0TkHTK3kZoGNoKskxL3KkC0RnFLLqgERah82iFde7Qvur7BMJdhIL5KpgkWB3tVX0sXqcelB447E1kg371iDj8s3fnmCu6ArhL8awynPmTr7AVU0ouzXzIWl1BUkhqEGvV/s1076/gemin-cli-openspec-31.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1076&quot; data-original-width=&quot;634&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixtiuBvi0MvidYmauXvb74sFDZjMYQRvbH8tGqnXTeYzJqp3M-BBV5CDKPEb0TkHTK3kZoGNoKskxL3KkC0RnFLLqgERah82iFde7Qvur7BMJdhIL5KpgkWB3tVX0sXqcelB447E1kg371iDj8s3fnmCu6ArhL8awynPmTr7AVU0ouzXzIWl1BUkhqEGvV/s320/gemin-cli-openspec-31.jpg&quot; width=&quot;189&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;輸入 30 或 390 按 sin 都會得到正確結果 0.5 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuHEd-Xm0jM2yS4nid0b9P6vIi36qYZZss2p-XI3cPOsJrCm1ONqCUAaX2AIEbmMmqlZPKK5Bl-wHG7E3RKjZRLlwJV_vBUdddAVTUB5p9M6DnQ4VkShqgWvcIZycKeRNvK5-NucX3X33hmrH1Gi0at-GcNZ7xjkcHCG2xNgDkFHvavsp68_lpeT0xWfHW/s1079/gemin-cli-openspec-32.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1079&quot; data-original-width=&quot;626&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuHEd-Xm0jM2yS4nid0b9P6vIi36qYZZss2p-XI3cPOsJrCm1ONqCUAaX2AIEbmMmqlZPKK5Bl-wHG7E3RKjZRLlwJV_vBUdddAVTUB5p9M6DnQ4VkShqgWvcIZycKeRNvK5-NucX3X33hmrH1Gi0at-GcNZ7xjkcHCG2xNgDkFHvavsp68_lpeT0xWfHW/s320/gemin-cli-openspec-32.jpg&quot; width=&quot;186&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;輸入 -30 按 sin 也得到正確結果 -0.5 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdjHrQTDccp8haOZHaYNhAnkVxtrPdzpxBmYDmNpn9M_gx6Cz9lgX9_Qim9pHuqGUca2uQqn2-lub361SGfyh6uEhplHO73AcgY74GdBoGiqUutwnYE3itWHWlurdgYe4fwEANUGmhJbQ5hhEaishKBVnvQEV5rqQDNmsSqzSMdWk_8VCr97e2TrZR-0RR/s1079/gemin-cli-openspec-33.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1079&quot; data-original-width=&quot;635&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdjHrQTDccp8haOZHaYNhAnkVxtrPdzpxBmYDmNpn9M_gx6Cz9lgX9_Qim9pHuqGUca2uQqn2-lub361SGfyh6uEhplHO73AcgY74GdBoGiqUutwnYE3itWHWlurdgYe4fwEANUGmhJbQ5hhEaishKBVnvQEV5rqQDNmsSqzSMdWk_8VCr97e2TrZR-0RR/s320/gemin-cli-openspec-33.jpg&quot; width=&quot;188&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;接下來做 cos 的精度測試, 輸入 90 按 cos 得到一個接近 0 的極小值而非 0, 這是因為我們忘了要 AI 做微小誤差抹零處理之故, 可以在後續迭代中處理掉. 做對數測試, 輸入 1 按 log 得到正確 0, 輸入 0 按 log 則得到 Error (無限大) :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXZqvffXyDoWQXSAR2SW3T0zQlSGgS2lg4FE3n0KWfjRPf4SFxzoYGnZRyU-YyA5d1azGirIij_ScHMNzWdU1PGPiL9_uXop3VWQH6gd12SQqUU2Jfl1FpstwQdMVpBrbRVF8JWkaTJdlX6FdAtCcolBOEwgvMBkrjgG5RqBTCmSmRrXRIkkqtATHUd0Zj/s1066/gemin-cli-openspec-35.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1066&quot; data-original-width=&quot;628&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXZqvffXyDoWQXSAR2SW3T0zQlSGgS2lg4FE3n0KWfjRPf4SFxzoYGnZRyU-YyA5d1azGirIij_ScHMNzWdU1PGPiL9_uXop3VWQH6gd12SQqUU2Jfl1FpstwQdMVpBrbRVF8JWkaTJdlX6FdAtCcolBOEwgvMBkrjgG5RqBTCmSmRrXRIkkqtATHUd0Zj/s320/gemin-cli-openspec-35.jpg&quot; width=&quot;189&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;4. 歸檔結案 :&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&amp;gt;&amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;/opsx: archive&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfiyOtHhn0Am9X6IiM5f1Q87XWMcln5JcVwHHs6n417izXzbX7BBluMCU3LAfRZBY914NdvbZY4B5GJXRAs6lrJP0-13K7xwZ5VcwKgZGBbVeZfEnHtp1r7yNvz-XlGJeheK5Epd-ojELHlVixjPC-OSlcVFO7YM0bjYzp0Pm7QwRwHDJBKuJpOZ9JUcYr/s2213/gemin-cli-openspec-36.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1168&quot; data-original-width=&quot;2213&quot; height=&quot;169&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfiyOtHhn0Am9X6IiM5f1Q87XWMcln5JcVwHHs6n417izXzbX7BBluMCU3LAfRZBY914NdvbZY4B5GJXRAs6lrJP0-13K7xwZ5VcwKgZGBbVeZfEnHtp1r7yNvz-XlGJeheK5Epd-ojELHlVixjPC-OSlcVFO7YM0bjYzp0Pm7QwRwHDJBKuJpOZ9JUcYr/s320/gemin-cli-openspec-36.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;... (略) ...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkLiqR_dnG0E8-GPrheKHWdviyuFeDfTmpkchdHOMOFR3mG_PR6P-LsOF9JfIN5UIOKZ8Uj-IcIi5TgfyWAndjkRq6YEYOdEC2c-ieYH2CcVE5O_17wrA1lw6gfVOryAlbnf6EmmPXZ7cx1_ielR-aFaU4MshfTuR4-IzPk3edSYiK9UQxBU1YnC_qDRXU/s2281/gemin-cli-openspec-37.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1134&quot; data-original-width=&quot;2281&quot; height=&quot;159&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkLiqR_dnG0E8-GPrheKHWdviyuFeDfTmpkchdHOMOFR3mG_PR6P-LsOF9JfIN5UIOKZ8Uj-IcIi5TgfyWAndjkRq6YEYOdEC2c-ieYH2CcVE5O_17wrA1lw6gfVOryAlbnf6EmmPXZ7cx1_ielR-aFaU4MshfTuR4-IzPk3edSYiK9UQxBU1YnC_qDRXU/s320/gemin-cli-openspec-37.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;第三次迭代歸檔完畢, 終於搞定了, 趕緊來去交作業啦!&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;5. 打包專案上傳 GitHub :&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;作業繳交要求將專案上傳 GitHub, 然後將 repo 網址填入 Google 試算表內.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;(1). 將所有變更加入暫存區 (打包) :&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;D:\gemini\calculator-project&amp;gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;git add .&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;warning: in the working copy of &#39;.gemini/commands/opsx/apply.toml&#39;, LF will be replaced by CRLF the next time Git touches it&lt;/div&gt;&lt;div&gt;warning: in the working copy of &#39;.gemini/commands/opsx/archive.toml&#39;, LF will be replaced by CRLF the next time Git touches it&lt;/div&gt;&lt;div&gt;warning: in the working copy of &#39;.gemini/commands/opsx/explore.toml&#39;, LF will be replaced by CRLF the next time Git touches it&lt;/div&gt;&lt;div&gt;warning: in the working copy of &#39;.gemini/commands/opsx/propose.toml&#39;, LF will be replaced by CRLF the next time Git touches it&lt;/div&gt;&lt;div&gt;warning: in the working copy of &#39;.gemini/skills/openspec-apply-change/SKILL.md&#39;, LF will be replaced by CRLF the next time Git touches it&lt;/div&gt;&lt;div&gt;warning: in the working copy of &#39;.gemini/skills/openspec-archive-change/SKILL.md&#39;, LF will be replaced by CRLF the next time Git touches it&lt;/div&gt;&lt;div&gt;warning: in the working copy of &#39;.gemini/skills/openspec-explore/SKILL.md&#39;, LF will be replaced by CRLF the next time Git touches it&lt;/div&gt;&lt;div&gt;warning: in the working copy of &#39;.gemini/skills/openspec-propose/SKILL.md&#39;, LF will be replaced by CRLF the next time Git touches it&lt;/div&gt;&lt;div&gt;warning: in the working copy of &#39;.gitignore&#39;, LF will be replaced by CRLF the next time Git touches it&lt;/div&gt;&lt;div&gt;warning: in the working copy of &#39;.python-version&#39;, LF will be replaced by CRLF the next time Git touches it&lt;/div&gt;&lt;div&gt;warning: in the working copy of &#39;main.py&#39;, LF will be replaced by CRLF the next time Git touches it&lt;/div&gt;&lt;div&gt;warning: in the working copy of &#39;openspec/changes/archive/2026-04-25-calc-basic/.openspec.yaml&#39;, LF will be replaced by CRLF the next time Git touches it&lt;/div&gt;&lt;div&gt;warning: in the working copy of &#39;openspec/changes/archive/2026-04-25-calc-power-root/.openspec.yaml&#39;, LF will be replaced by CRLF the next time Git touches it&lt;/div&gt;&lt;div&gt;warning: in the working copy of &#39;openspec/changes/archive/2026-04-25-calc-scientific/.openspec.yaml&#39;, LF will be replaced by CRLF the next time Git touches it&lt;/div&gt;&lt;div&gt;warning: in the working copy of &#39;openspec/config.yaml&#39;, LF will be replaced by CRLF the next time Git touches it&lt;/div&gt;&lt;div&gt;warning: in the working copy of &#39;pyproject.toml&#39;, LF will be replaced by CRLF the next time Git touches it&lt;/div&gt;&lt;div&gt;warning: in the working copy of &#39;uv.lock&#39;, LF will be replaced by CRLF the next time Git touches it&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;出現的這些 warning 是在 Windows 環境下執行 Git 時常見的警告, 它完全不影響程式碼功能或 GitHub 的上傳結果, 這只是 Git 在提醒換行符號的格式要統一, 因為 OpenSpec 工具或 AI 產生的檔案可能預設使用了 Unix 格式的換行字符 LF, 在 Windows 的命令提示字元下操作時 Git 偵測到這種不一致, 所以主動告知它會自動把這些檔案轉換成 Windows 標準的 CRLF, 因此毋須理會.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;(2). 設定使用者名稱與 Email :&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;D:\gemini\calculator-project&amp;gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;git config --global user.name &quot;Tony&quot;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;D:\gemini\calculator-project&amp;gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;git config --global user.email &quot;blablabla@ms5.hinet.net&quot;&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;(3). 提交變更 (貼標籤/存檔) :&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;D:\gemini\calculator-project&amp;gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;git commit -m &quot;feat: 完成計算機專案 (基礎運算、次方根號、科學計算)&quot;&amp;nbsp;&lt;/span&gt;&lt;/b&gt; &amp;nbsp;&lt;/div&gt;&lt;div&gt;[master (root-commit) 17a5d99] feat: 完成計算機專案 (基礎運算、次方根號、科學計算)&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp;44 files changed, 2523 insertions(+)&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 .gemini/commands/opsx/apply.toml&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 .gemini/commands/opsx/archive.toml&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 .gemini/commands/opsx/explore.toml&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 .gemini/commands/opsx/propose.toml&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 .gemini/skills/openspec-apply-change/SKILL.md&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 .gemini/skills/openspec-archive-change/SKILL.md&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 .gemini/skills/openspec-explore/SKILL.md&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 .gemini/skills/openspec-propose/SKILL.md&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 .gitignore&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 .python-version&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 GEMINI.md&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 README.md&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 calculator/__init__.py&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 calculator/logic.py&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 main.py&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 openspec/changes/archive/2026-04-25-calc-basic/.openspec.yaml&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 openspec/changes/archive/2026-04-25-calc-basic/design.md&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 openspec/changes/archive/2026-04-25-calc-basic/proposal.md&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 openspec/changes/archive/2026-04-25-calc-basic/specs/arithmetic-api/spec.md&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 openspec/changes/archive/2026-04-25-calc-basic/specs/calculator-ui/spec.md&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 openspec/changes/archive/2026-04-25-calc-basic/tasks.md&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 openspec/changes/archive/2026-04-25-calc-power-root/.openspec.yaml&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 openspec/changes/archive/2026-04-25-calc-power-root/design.md&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 openspec/changes/archive/2026-04-25-calc-power-root/proposal.md&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 openspec/changes/archive/2026-04-25-calc-power-root/specs/advanced-arithmetic/spec.md&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 openspec/changes/archive/2026-04-25-calc-power-root/specs/calculator-ui/spec.md&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 openspec/changes/archive/2026-04-25-calc-power-root/tasks.md&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 openspec/changes/archive/2026-04-25-calc-scientific/.openspec.yaml&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 openspec/changes/archive/2026-04-25-calc-scientific/design.md&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 openspec/changes/archive/2026-04-25-calc-scientific/proposal.md&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 openspec/changes/archive/2026-04-25-calc-scientific/specs/calculator-ui/spec.md&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 openspec/changes/archive/2026-04-25-calc-scientific/specs/scientific-functions/spec.md&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 openspec/changes/archive/2026-04-25-calc-scientific/tasks.md&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 openspec/config.yaml&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 openspec/specs/advanced-arithmetic/spec.md&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 openspec/specs/arithmetic-api/spec.md&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 openspec/specs/calculator-ui/spec.md&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 openspec/specs/scientific-functions/spec.md&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 pyproject.toml&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 static/index.html&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 static/script.js&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 static/style.css&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 test_api.py&lt;/div&gt;&lt;div&gt;&amp;nbsp;create mode 100644 uv.lock&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;(4). 在 GitHub 建立空的儲存庫 (Repository) :&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;建立一個空專案 (剛好 calculator-project 可用), 注意,&amp;nbsp;因為在本機已經有 README.md 與 .gitignore 檔案了, 不要勾選 &quot;Add a README&quot; 或 &quot;Add .gitignore&quot; 這兩項, 保持預設的空專案即可, 點擊 Create repository 新增 repo.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://github.com/tony1966/calculator-project&quot; target=&quot;_blank&quot;&gt;https://github.com/tony1966/calculator-project&lt;/a&gt;&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;&lt;b&gt;(5). 綁定並推上雲端&amp;nbsp;:&lt;/b&gt;&lt;/span&gt;&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;告訴本機 Git 這個專案要連線到哪個 GitHub 網址 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;D:\gemini\calculator-project&amp;gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;git remote add origin https://github.com/tony1966/calculator-project.git&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;把專案推上雲端&amp;nbsp; :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;D:\gemini\calculator-project&amp;gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;git push -u origin main&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;div&gt;Enumerating objects: 67, done.&lt;/div&gt;&lt;div&gt;Counting objects: 100% (67/67), done.&lt;/div&gt;&lt;div&gt;Delta compression using up to 16 threads&lt;/div&gt;&lt;div&gt;Compressing objects: 100% (51/51), done.&lt;/div&gt;&lt;div&gt;Writing objects: 100% (67/67), 39.46 KiB | 3.04 MiB/s, done.&lt;/div&gt;&lt;div&gt;Total 67 (delta 3), reused 0 (delta 0), pack-reused 0 (from 0)&lt;/div&gt;&lt;div&gt;remote: Resolving deltas: 100% (3/3), done.&lt;/div&gt;&lt;div&gt;To https://github.com/tony1966/calculator-project.git&lt;/div&gt;&lt;div&gt;&amp;nbsp;* [new branch]&amp;nbsp; &amp;nbsp; &amp;nbsp; main -&amp;gt; main&lt;/div&gt;&lt;div&gt;branch &#39;main&#39; set up to track &#39;origin/main&#39;.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;此指令會出現詢問視窗, 用預設 manager 按 Select 鈕即可, 然後登入 GitHub 帳號即可上傳. 成功後到 Google sheet 登錄專案 repo 的 GitHub 網址即完成作業繳交啦! 我原以為時間太趕只能聽完課程, 沒想到忙了一整天居然搞定作業了, 哈哈.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;心得 : 親自動手做一遍才能真正學會.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000; font-weight: 700;&quot;&gt;6. 跳出 Gemini CLI :&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;專案結束, 輸入 exit 離開專案 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpdkpI0Ul5Sczvn7HsckORNT7ogJE-IvDdGDg8yx8cqUc-U6yKnidoXeek4fyECbGuSFL5W4EaJj0fU6c82wcXKsMUODyuokG2TpGskIDFsNp4Fw0wW1nn9y3cy3aaIbg_uwyZ0D0vfMN1iXvFXGQqKGYdmoPYkORfRBQHmQwD527MItjKaQZFGyl7Ia2T/s2277/gemin-cli-openspec-39.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;887&quot; data-original-width=&quot;2277&quot; height=&quot;125&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpdkpI0Ul5Sczvn7HsckORNT7ogJE-IvDdGDg8yx8cqUc-U6yKnidoXeek4fyECbGuSFL5W4EaJj0fU6c82wcXKsMUODyuokG2TpGskIDFsNp4Fw0wW1nn9y3cy3aaIbg_uwyZ0D0vfMN1iXvFXGQqKGYdmoPYkORfRBQHmQwD527MItjKaQZFGyl7Ia2T/s320/gemin-cli-openspec-39.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;used 從 2% 到 10%, 用掉了 8% 資源. 連續按兩次 Ctrl+C 即可跳出 Gemini CLI 回到 PS 視窗.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://yhhuang1966.blogspot.com/feeds/5784999000111854772/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment/fullpage/post/4877487320781767952/5784999000111854772' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/5784999000111854772'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/5784999000111854772'/><link rel='alternate' type='text/html' href='https://yhhuang1966.blogspot.com/2026/04/gemini-cli-openspec_46.html' title='Gemini CLI 學習筆記 : OpenSpec 初體驗 (三)'/><author><name>小狐狸事務所</name><uri>http://www.blogger.com/profile/09435160519044041137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCc-m8PVOwRclhl-fPSAYxP5hUkMBhG4QUUEOcoeca7A1CJehptHd7akmMmHBLR5Irlf74cgPTrvo4g32WY5aH_RIGN-BXp3W-JsC_ULCKlT7kz7kXa3q_5Srq31vxhUeo9RF6hk5uZpqPAxwzmcMyGjl9qXzev4yLypmYh5wRPIZkI0orOZH-9bpgvhRa/s72-c/gemin-cli-openspec-25.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4877487320781767952.post-7024209365445197089</id><published>2026-04-25T21:24:00.001+08:00</published><updated>2026-04-25T22:18:18.721+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Gemini CLI"/><category scheme="http://www.blogger.com/atom/ns#" term="OpenSpec"/><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><category scheme="http://www.blogger.com/atom/ns#" term="SDD"/><title type='text'>Gemini CLI 學習筆記 : OpenSpec 初體驗 (二)</title><content type='html'>&lt;div&gt;白天完成初次迭代後, 傍晚快馬加鞭進行第二次迭代.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;本系列全部測試文章參考 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# &lt;a href=&quot;https://yhhuang1966.blogspot.com/2026/01/gemini-cli_30.html&quot; target=&quot;_blank&quot;&gt;Gemini CLI 學習筆記索引&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;前一篇的初次迭代由於功能較簡單, 我們使用了 /opsx:ff 快轉指令一鍵生成模式, 快速地完成了從規劃到程式碼生成與測試驗證的工作流. 本篇將在初次碟待的基礎上為基本的四則運算計算器添加次方與根號功能.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;此二次迭代將改用逐步推進模式, 舊版 OpenSec 原本的的工作流需要依序執行下列指令 :&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;/opsx:new &amp;lt;iteration_name&amp;gt; (建立迭代之專屬的工作區)&lt;/li&gt;&lt;li&gt;/opsx:propose &amp;lt;requirements&amp;gt; (依需求起草提案書)&lt;/li&gt;&lt;li&gt;/opsx:continue (完成規格定義書 spec.md)&lt;/li&gt;&lt;li&gt;/opsx:continue (完成架構設計書 design.md)&lt;/li&gt;&lt;li&gt;/opsx:continue (完成任務清單 tasks.md)&lt;/li&gt;&lt;li&gt;/opsx:apply (依照 tasks.md 中的任務清單逐一實作功能)&lt;/li&gt;&lt;li&gt;/opsx:verify (驗證程式碼與規格完整性, 正確性, 一致性)&lt;/li&gt;&lt;li&gt;/opsx:archive (迭代完成歸檔)&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;執行 /opsx:propose 後, AI 只會生成第一份文件 proposal.md (提案書)&amp;nbsp;並把控制權交還給我們來審查功能描述是否與需求符合.&amp;nbsp;審查通過後就可執行三個連續的 /opsx:continue 指令, 一次只產出一份文件, 審查通過後再執行下一個 continue 指令.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;第一個 /opsx:continue 指令, 它會生成規格定義書 spec.md, 注意, 這個檔案可能不只一個, 因為在模組化的軟體架構中, 功能會被解耦分拆在不同資料夾, 例如 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;openspec/changes/calc-power-root/specs/arithmetic-api/spec.md&lt;/div&gt;&lt;div&gt;openspec/changes/calc-power-root/specs/calculator-ui/spec.md&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;每一個功能都會有一個 spec.md 檔, 這些分散的規格定義書共同構成了這次迭代的完整規格.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;第二個 /opsx:continue 指令會產生架構設計書 design.md, 主要是描述內部程式碼要怎麼實作,&amp;nbsp;審查重點在於有沒有過度設計 (Over-engineering), 例如明明 math 套件能做到的功能卻使用 numpy, 這實要將其改為 &quot;使用 Python 內建 math 即可&quot;.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;第三個 (也是最後一個) /opsx:continue 指令會生成任務清單&amp;nbsp;(施工單)&amp;nbsp;tasks.md 檔,&amp;nbsp;這份文件是設計階段與實作階段之間的最後一座橋樑, 也是 AI 執行 /opsx:apply 時唯一的行動指南, 它只關注具體要做哪些動作. 審查時要檢查施工順序是否合理, 若無問題就可以下達 /opsx:apply 指令叫 AI 依照清單逐一實作程式碼, 完成驗證後即可歸檔.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;但新版的 OpenSec 已經把三次 /opsx: continue 指令整合進 /opsx:propose 裡面了, 所以新版的工作流指令序列改為 :&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;/opsx:new &amp;lt;iteration_name&amp;gt; (建立迭代之專屬的工作區)&lt;/li&gt;&lt;li&gt;/opsx:propose &amp;lt;requirements&amp;gt; (依需求起草提案書, 規格定義書, 架構設計書, 與任務清單)&lt;/li&gt;&lt;li&gt;/opsx:apply &amp;lt;iteration_name&amp;gt; (依照 tasks.md 中的任務清單逐一實作此迭代功能)&lt;/li&gt;&lt;li&gt;/opsx:archive (迭代完成歸檔)&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;填寫四大核心工件的步驟其實都整合到 /opsx:propose 指令裡了.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;1.&amp;nbsp;建立迭代之專屬工作區 :&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;第二次迭代的目標是要為四則運算計算器添加次方與開根號功能, 因此迭代名稱可取為&amp;nbsp;calc-power-root :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;/opsx:new calc-power-root&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;同樣地會有一連串的授權詢問, 一律選擇預設的 Allow once 觀察每一步在做甚麼 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2UTWVG2KhUh4BD1pDMtspFOcT5zfySpW7lrUa10pp2CpcAT3BEEOSPEgXkVVj1k7TbkcD7JzBHtGJRYTB_V5zc_y2VXeEz66jrJ7Vb6wEezI2VIKz6ueOR5VDvyyxYTuu8kfeNUDLOxUTir-Pzv4sVIdMLkRgUts7dMC3C2zZbOg9XFvVlfRoT9ii8jOA/s2242/gemin-cli-openspec-15.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;994&quot; data-original-width=&quot;2242&quot; height=&quot;142&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2UTWVG2KhUh4BD1pDMtspFOcT5zfySpW7lrUa10pp2CpcAT3BEEOSPEgXkVVj1k7TbkcD7JzBHtGJRYTB_V5zc_y2VXeEz66jrJ7Vb6wEezI2VIKz6ueOR5VDvyyxYTuu8kfeNUDLOxUTir-Pzv4sVIdMLkRgUts7dMC3C2zZbOg9XFvVlfRoT9ii8jOA/s320/gemin-cli-openspec-15.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;... (略) ...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHKnSjJZVpd6yl4ejk_Yk7EPt6rqHCB0CU57G11gpZ4pfojQ6q3Bdy12rqC1fwc2z_QX7bN3MOzEEk6weDPsguS_Z6mR8fanzXAUQOPZzqDDE0xG0XLMbKkQ56NZEmsSksCaGjCoMcx1l6RLFPKLsh8abJR9hoi0CBTf0FJNEXkaONKcQVpCL0tAG2KZUe/s2292/gemin-cli-openspec-16.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1199&quot; data-original-width=&quot;2292&quot; height=&quot;167&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHKnSjJZVpd6yl4ejk_Yk7EPt6rqHCB0CU57G11gpZ4pfojQ6q3Bdy12rqC1fwc2z_QX7bN3MOzEEk6weDPsguS_Z6mR8fanzXAUQOPZzqDDE0xG0XLMbKkQ56NZEmsSksCaGjCoMcx1l6RLFPKLsh8abJR9hoi0CBTf0FJNEXkaONKcQVpCL0tAG2KZUe/s320/gemin-cli-openspec-16.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;可見 /opsx: new 指令主要是生成四個核心工件 (artifacts) 的待填空模板, 其中的 spec.md 是每個功能會有一個, 放在各自功能的資料夾下面.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;2. 根據需求填寫提案書 :&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;有了核心工件的空模板後, 接下來要用 /opsx: propose 指令注入需求, 讓 AI 協助填寫提案書 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;/opsx:propose &quot;在現有的基礎四則運算計算器專案上, 加上次方與開根號功能. 請確保處理好基礎的邊界條件與防呆機制 (例如對負數開偶數根的錯誤處理) 等. &quot;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;這時 OpenSpec 偵測到這個需求的主題跟上面剛剛用 /opsx:new calc-power-root 建好的 calc-power-root 工作區完全契合, 詢問是否要把這份提案放進剛才那個工作區? 那是當然的, 選 &amp;nbsp;1. 繼續使用 calc-power-root :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjA0Sk9XBtgSCwKcaHJ8_H853dAn1xIr5545ZDhdrvZ6C8S3aer-04dHXV1jYz0drm36iN_-_T9GLithcnqvpsrAyzBmzl12jA5DyAJ-mHwtBCzVCPayKrBWliv9FYyvEwLZWcNRiBZEEokpXoedu0gk1K730_owuSFdbeyxWRPL858cUoHUnHPwp8DNPRT/s2220/gemin-cli-openspec-17.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1021&quot; data-original-width=&quot;2220&quot; height=&quot;147&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjA0Sk9XBtgSCwKcaHJ8_H853dAn1xIr5545ZDhdrvZ6C8S3aer-04dHXV1jYz0drm36iN_-_T9GLithcnqvpsrAyzBmzl12jA5DyAJ-mHwtBCzVCPayKrBWliv9FYyvEwLZWcNRiBZEEokpXoedu0gk1K730_owuSFdbeyxWRPL858cUoHUnHPwp8DNPRT/s320/gemin-cli-openspec-17.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;... (略) ...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLLKno3eK_Yog1NQDReq06JSKsz8vCVbs5dlVZ5EuTorQ9m93kJcg3r8S2zC_-sRvLMgeH_LkljkT6sCFDGTj7_fR_0YfyOmKV4QV643nwKvI1lhmsY4w0btVeijSe2myz7PL7VHB7epjhHO_nEeeTRP-FCReaUlhwod14WqmQiP4p2aTJqwi0TZ7iCMvn/s2312/gemin-cli-openspec-18.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1127&quot; data-original-width=&quot;2312&quot; height=&quot;156&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLLKno3eK_Yog1NQDReq06JSKsz8vCVbs5dlVZ5EuTorQ9m93kJcg3r8S2zC_-sRvLMgeH_LkljkT6sCFDGTj7_fR_0YfyOmKV4QV643nwKvI1lhmsY4w0btVeijSe2myz7PL7VHB7epjhHO_nEeeTRP-FCReaUlhwod14WqmQiP4p2aTJqwi0TZ7iCMvn/s320/gemin-cli-openspec-18.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;可見四大核心工件檔案的填寫都已完成, 開啟這些檔案審查若不需要修改就可以下 /opsx:apply 指令來實作程式碼了.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;3. 依據任務清單實作程式碼 :&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;用 /opsx: apply 指令並指定迭代工作區名稱 (防呆防猜測) :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;/opsx:apply calc-power-root&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;此指令將依照任務清單 tasks.md 內容依序實作程式碼,&amp;nbsp;同樣地會有一連串的授權詢問, 一律選擇預設的 Allow once 觀察每一步在做甚麼 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkjcz5FmKSsiXRsTRispX3qKB-s-hxVDA2W7pWvNraS4Ey7dLzgDwNeRtvMV8lo-To3WYc8-zLjkGtvQJSTopFbSxtzxrEGRJj9G254O-DxoD0Z1BaGXR6LkkxmEHaX3FktX_cCsNxd7qtqjXZzkwz9LVud3eLQ_S4iK8sp0SpJHSgzTSTDf5h5VuLDKEw/s2219/gemin-cli-openspec-19.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1158&quot; data-original-width=&quot;2219&quot; height=&quot;167&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkjcz5FmKSsiXRsTRispX3qKB-s-hxVDA2W7pWvNraS4Ey7dLzgDwNeRtvMV8lo-To3WYc8-zLjkGtvQJSTopFbSxtzxrEGRJj9G254O-DxoD0Z1BaGXR6LkkxmEHaX3FktX_cCsNxd7qtqjXZzkwz9LVud3eLQ_S4iK8sp0SpJHSgzTSTDf5h5VuLDKEw/s320/gemin-cli-openspec-19.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;... (略) ...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj18j-bA1FxGLANzBaZQa8Ia-GSVD9ubWYVnSqhmX7rREhm0OQtGdzjcpYnft_ttKcZyXxYsvY-wkTiVqDJaSmS8drbFtrTzFofcCs5xqb2kzZlDppl_JSQATR3dYHzhwGcg7pqNZNJHb7Gb1NrziUi8uEQfy1xzP6OQeKf7BwxIY7qZYF0nyj5NSsezlUF/s2291/gemin-cli-openspec-20.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1083&quot; data-original-width=&quot;2291&quot; height=&quot;151&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj18j-bA1FxGLANzBaZQa8Ia-GSVD9ubWYVnSqhmX7rREhm0OQtGdzjcpYnft_ttKcZyXxYsvY-wkTiVqDJaSmS8drbFtrTzFofcCs5xqb2kzZlDppl_JSQATR3dYHzhwGcg7pqNZNJHb7Gb1NrziUi8uEQfy1xzP6OQeKf7BwxIY7qZYF0nyj5NSsezlUF/s320/gemin-cli-openspec-20.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;這樣就完成專案實作了 (咦, 這次沒跟我說要開啟 127.0.0.1:5000 來測試?), 我前次迭代執行的 uv rum main.py 沒關掉, 馬上測試 2 的 3 次方得到正確結果 8 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_KmJjW8OhBK5YvtB7wtQTywYN6RzBYyBN7eK9SX0nTqC9lBHTwiSDi0Zf6KLMLgnEdb-v3x7vk_KVtSgdugi84MQjwm_8CUMVH3SQxYwZCq4ImKWQviLAF0DI2tBHJJItkSrzuNwRQgtZrvj7XZRkhOehtI9icY-huvTt3w7AtvSUgS7oXTqZojqRcaVD/s835/gemin-cli-openspec-21.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;835&quot; data-original-width=&quot;635&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_KmJjW8OhBK5YvtB7wtQTywYN6RzBYyBN7eK9SX0nTqC9lBHTwiSDi0Zf6KLMLgnEdb-v3x7vk_KVtSgdugi84MQjwm_8CUMVH3SQxYwZCq4ImKWQviLAF0DI2tBHJJItkSrzuNwRQgtZrvj7XZRkhOehtI9icY-huvTt3w7AtvSUgS7oXTqZojqRcaVD/s320/gemin-cli-openspec-21.jpg&quot; width=&quot;243&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;按 C 鍵清除輸入 49 按開根號也正確得到 7 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5hqZTyr_QhhGn9Ybuz8nuoS1YiTKY5JALbtSvd8vmARkOc7FPDuTWswpyUaSnQCedNMBM5wOccaWlRGGVrvv8jm4qK0q_gua-WfsgnAg9Rh4c6dOIiO9QZyy47VTqeTdtxhyZV2nCYN25QMPmuhXL7dXWIK1cNJnfx0AXFrJGKa9J1ssvhCYSArbgPibz/s830/gemin-cli-openspec-22.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;830&quot; data-original-width=&quot;627&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5hqZTyr_QhhGn9Ybuz8nuoS1YiTKY5JALbtSvd8vmARkOc7FPDuTWswpyUaSnQCedNMBM5wOccaWlRGGVrvv8jm4qK0q_gua-WfsgnAg9Rh4c6dOIiO9QZyy47VTqeTdtxhyZV2nCYN25QMPmuhXL7dXWIK1cNJnfx0AXFrJGKa9J1ssvhCYSArbgPibz/s320/gemin-cli-openspec-22.jpg&quot; width=&quot;242&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;原先的基本四則運算功能維持一樣沒被改壞, 二次迭代終於完成了.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;4. 歸檔結案 :&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;下 /opsx: archive 指令歸檔&amp;nbsp; :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;/opsx: archive&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWnT5YQwoGtaHPcVk6GR2cxnqtInyo2mu-IzxAjs8-H2Uchf5kTpb9oZ2U4SneDZSF0Mit0WOEMQCeOvjDEXqSAxAsGWFDRsycfUTSVNDSJyJhosUyP2sExpjw8b0c1UHHb-LvmBEeTKBbV1Wbbg9u9mmcTk4BCsu044yz03ByIBMnFFI7nGokY9oaggLb/s2066/gemin-cli-openspec-23.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1147&quot; data-original-width=&quot;2066&quot; height=&quot;178&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWnT5YQwoGtaHPcVk6GR2cxnqtInyo2mu-IzxAjs8-H2Uchf5kTpb9oZ2U4SneDZSF0Mit0WOEMQCeOvjDEXqSAxAsGWFDRsycfUTSVNDSJyJhosUyP2sExpjw8b0c1UHHb-LvmBEeTKBbV1Wbbg9u9mmcTk4BCsu044yz03ByIBMnFFI7nGokY9oaggLb/s320/gemin-cli-openspec-23.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;... (略) ...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBemin_ycglNOR0a1TdqMa2DKQEdPAdxNyaqlPXOMhiz2CtLF2H7YCeMzd4EcqO5gO7eMrYy6X0bY0o3lh50Q2ktfV1wxeVxOcrbYW4CGFVOY9nquPXYgFILaqcQFKIK2hLPvBgMuUNGYc0XhvUpkLST3_qlxo_TZETsHGaJfxdf_Lorh-Ghrk4neOYG54/s2285/gemin-cli-openspec-24.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1146&quot; data-original-width=&quot;2285&quot; height=&quot;160&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBemin_ycglNOR0a1TdqMa2DKQEdPAdxNyaqlPXOMhiz2CtLF2H7YCeMzd4EcqO5gO7eMrYy6X0bY0o3lh50Q2ktfV1wxeVxOcrbYW4CGFVOY9nquPXYgFILaqcQFKIK2hLPvBgMuUNGYc0XhvUpkLST3_qlxo_TZETsHGaJfxdf_Lorh-Ghrk4neOYG54/s320/gemin-cli-openspec-24.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;終於完成二次迭代了.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://yhhuang1966.blogspot.com/feeds/7024209365445197089/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment/fullpage/post/4877487320781767952/7024209365445197089' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/7024209365445197089'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/7024209365445197089'/><link rel='alternate' type='text/html' href='https://yhhuang1966.blogspot.com/2026/04/gemini-cli-openspec_25.html' title='Gemini CLI 學習筆記 : OpenSpec 初體驗 (二)'/><author><name>小狐狸事務所</name><uri>http://www.blogger.com/profile/09435160519044041137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2UTWVG2KhUh4BD1pDMtspFOcT5zfySpW7lrUa10pp2CpcAT3BEEOSPEgXkVVj1k7TbkcD7JzBHtGJRYTB_V5zc_y2VXeEz66jrJ7Vb6wEezI2VIKz6ueOR5VDvyyxYTuu8kfeNUDLOxUTir-Pzv4sVIdMLkRgUts7dMC3C2zZbOg9XFvVlfRoT9ii8jOA/s72-c/gemin-cli-openspec-15.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4877487320781767952.post-2574066280757431013</id><published>2026-04-25T17:37:00.011+08:00</published><updated>2026-04-25T19:15:59.895+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Gemini CLI"/><category scheme="http://www.blogger.com/atom/ns#" term="OpenSpec"/><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><category scheme="http://www.blogger.com/atom/ns#" term="SDD"/><title type='text'>Gemini CLI 學習筆記 : OpenSpec 初體驗 (一)</title><content type='html'>&lt;div&gt;最近幾天上完 TibaMe 的規格驅動開發 (SDD) 課程, 準備找一個小專案 (計算器) 用 Gemini CLI 來跑一下 OpenSpec 交作業.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;本系列全部測試文章參考 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2026/01/gemini-cli_30.html&quot; target=&quot;_blank&quot;&gt;Gemini CLI 學習筆記索引&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;關於 SDD 我已看完高見龍老師的部落格文章, 摘要整理在這篇 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2026/04/blog-post_16.html&quot; target=&quot;_blank&quot;&gt;好站 : 高見龍老師的部落格 (SDD 規格驅動開發)&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;1. Gemini CLI 升版 :&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;距離上次測試已經過了快三個月了, Gemini CLI 版本應該也演進了不少, 所以先來升版. 開啟 PS 視窗, 輸入下列指令查詢目前本機版本 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;PS D:\gemini&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;gemini --version&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;div&gt;0.26.0&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;用下列指令升到最新版 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;PS D:\gemini&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;npm install -g @google/gemini-cli@latest&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;added 1 package, removed 609 packages, and changed 6 packages in 3m&lt;/div&gt;&lt;div&gt;npm notice&lt;/div&gt;&lt;div&gt;npm notice New minor version of npm available! 11.6.2 -&amp;gt; 11.13.0&lt;/div&gt;&lt;div&gt;npm notice Changelog: https://github.com/npm/cli/releases/tag/v11.13.0&lt;/div&gt;&lt;div&gt;npm notice To update run: npm install -g npm@11.13.0&lt;/div&gt;&lt;div&gt;npm notice&lt;/div&gt;&lt;div&gt;PS D:\gemini&amp;gt; gemini --version&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: #fcff01;&quot;&gt;0.39.1&lt;/span&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;哇, 一季不見已從 0.26.0 跳到 0.39.1 版. npm 也有新版, 順便也升版 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;PS D:\gemini&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;npm install -g npm@11.13.0&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;removed 60 packages, and changed 90 packages in 9s&lt;/div&gt;&lt;div&gt;PS D:\gemini&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;npm --version&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;11.13.0&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;2. 安裝 OpenSpec :&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;OpenSpec 是一款由 Fission AI 研發的開源輕量級規格驅動開發 (SDD) 工具, 是一套讓 AI 程式助理 (coding agents) 遵循規格的工作流程與 CLI 工具, 目前支援 20 種以上 AI 工具, 它的目標是讓工程師與 AI 在開發軟體之前先對規格達成共識, 減少 vibe coding 常見的偏題與反覆修改弊病, 並且能留下規格紀錄與決策脈絡備查, 提高軟體可維護性.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;安裝 OpenSpec 須有 Node v20.19.0 以上執行環境, 先檢視 Node 版本 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;PS D:\gemini&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;node --version&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;v25.2.1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;我的 Node 是利用 Scoop 安裝 Node 的, 可管理多版本的 Node, 參考 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2025/12/windows-claude-code.html&quot; target=&quot;_blank&quot;&gt;在 Windows 上安裝原生版 Claude Code&lt;/a&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2025/12/windows-gemini-cli.html&quot; target=&quot;_blank&quot;&gt;在 Windows 上安裝 Gemini CLI&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;這樣就可以在 PS 視窗安裝 OpenSpec 了 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;PS D:\gemini&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;npm -g install @fission-ai/openspec@latest&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;added 74 packages in 34s&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;這樣就完成 OpenSpec 安裝了.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;3. 用 uv 建立專案目錄 &amp;amp; 加入版控 :&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;由於作業要求用 OpenSpe 做三次迭代, 我打算用 OpenSpec 來實作一個網頁計算器專案, 第一次迭代要實作基本的四則運算計算機, 第二次迭代添加次方與開根號功能; 第三次迭代則添加對數, 指數, 與三角函數.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;首先用 uv init 指令建立一個專案目錄 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;PS D:\gemini&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;uv init calculator-project&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;Initialized project `calculator-project` at `D:\gemini\calculator-project`&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;然後切換到專案目錄下 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;PS D:\gemini&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;cd calculator-project&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;對此專案進行版本控制, 先檢視 Git 是否已安裝 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;PS D:\gemini\calculator-project&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;git --version&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;git version 2.52.0.windows.1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;關於 Git 安裝與用法參考 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2020/01/git.html&quot; target=&quot;_blank&quot;&gt;Git 學習筆記索引&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;在專案目錄下用 git init 初始化版控 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;PS D:\gemini\calculator-project&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;git init&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;Reinitialized existing Git repository in D:/gemini/calculator-project/.git/&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;版控資訊會儲存在隱藏目錄 .git 底下.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;4. 初始化 OpenSpec :&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;接著用 openspec init 指令為此專案做 OpenSpec 的初始化 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;PS D:\gemini\calculator-project&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;openspec init&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;這時會出現 OpenSpec 歡迎畫面 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFnvV5j2FfTfH_CB6FiUtgwbMs9GM2nrQCaQHKdoZDRXCmW7-pRv9IpHsqpv84DssS99ohEwADWhFQceAwFkltTdULEf0FJ_c0DQbCvzhN-BjYmAe-KoLBmXHvAwd-ns7vuuzMzbE_szUwYvvbftTPdjCSek4TDTPxOY-5o5y2DcLP0sQsKoyfp2Icn2QQ/s1467/openspec-init-1.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;746&quot; data-original-width=&quot;1467&quot; height=&quot;163&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFnvV5j2FfTfH_CB6FiUtgwbMs9GM2nrQCaQHKdoZDRXCmW7-pRv9IpHsqpv84DssS99ohEwADWhFQceAwFkltTdULEf0FJ_c0DQbCvzhN-BjYmAe-KoLBmXHvAwd-ns7vuuzMzbE_szUwYvvbftTPdjCSek4TDTPxOY-5o5y2DcLP0sQsKoyfp2Icn2QQ/s320/openspec-init-1.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;按 Enter 會出現 AI 程式代理工具選單, 可以按上下鍵移動指標來選擇要用的 AI 工具, 按 Space 鍵選擇要使用的代理工具 (可複選, 目前已支援 28 種 AI 程式代理工具), 此處我只選 Gemini CLI :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcTSSHBhCf0Tv9AhE0_SjBoNGL1qjfXulQfy3WD71XOHdPMNQNxTdvgaMk2STmsiYtb1mL5Tk0IeayrF_SBqNWO7NLstXhNgjKXRklJc0JoqfliFFznflM4ibO3FND3uqNpW8lQFOkITDoNISIBfEGM7FMeBnPtCU-8UxcoRmQwu4aywLonGBtp7MU4zui/s1487/openspec-init-2.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;844&quot; data-original-width=&quot;1487&quot; height=&quot;182&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcTSSHBhCf0Tv9AhE0_SjBoNGL1qjfXulQfy3WD71XOHdPMNQNxTdvgaMk2STmsiYtb1mL5Tk0IeayrF_SBqNWO7NLstXhNgjKXRklJc0JoqfliFFznflM4ibO3FND3uqNpW8lQFOkITDoNISIBfEGM7FMeBnPtCU-8UxcoRmQwu4aywLonGBtp7MU4zui/s320/openspec-init-2.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;選定的工具名稱會被列在上方的 Selected : 後面&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIPTd-qHrMJ46zlzwMP7zqHV3OvvL_2k2osFEs3i2KAG3yWN8P8nAMxI8jZdLrXLvpA5T1RCjijCdhsbUJpiHRkGMO4dJWnM6B3S4kj6QONCqPUKBVME0yBj6Ipb1qStlx2O1x85TNegAdSMA026CiVHjfDPhh-BllFdF3SKavLoOQHeB15YxbI5xfiOTF/s1472/openspec-init-3.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;878&quot; data-original-width=&quot;1472&quot; height=&quot;191&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIPTd-qHrMJ46zlzwMP7zqHV3OvvL_2k2osFEs3i2KAG3yWN8P8nAMxI8jZdLrXLvpA5T1RCjijCdhsbUJpiHRkGMO4dJWnM6B3S4kj6QONCqPUKBVME0yBj6Ipb1qStlx2O1x85TNegAdSMA026CiVHjfDPhh-BllFdF3SKavLoOQHeB15YxbI5xfiOTF/s320/openspec-init-3.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;選完後按 Enter 退出 OpenSpec 初始化畫面回到 PS 終端機, 可見已為 Gemini CLI 程式代理建立了 4 個技能 (skills) 與 OpenSpeck 相關之命令 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Note: OpenSpec collects anonymous usage stats. Opt out: OPENSPEC_TELEMETRY=0&lt;/div&gt;&lt;div&gt;√ Select tools to set up (28 available) &lt;span style=&quot;background-color: #fcff01;&quot;&gt;Gemini CLI&lt;/span&gt;&lt;/div&gt;&lt;div&gt;▌ OpenSpec structure created&lt;/div&gt;&lt;div&gt;√ Setup complete for &lt;span style=&quot;background-color: #fcff01;&quot;&gt;Gemini CLI&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;OpenSpec Setup Complete&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Created: &lt;span style=&quot;background-color: #fcff01;&quot;&gt;Gemini CLI&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: #fcff01;&quot;&gt;4 skills and 4 commands&lt;/span&gt; in .gemini/&lt;/div&gt;&lt;div&gt;Config: openspec/config.yaml (schema: spec-driven)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Getting started:&lt;/div&gt;&lt;div&gt;&amp;nbsp; Start your first change: /opsx:propose &quot;your idea&quot;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Learn more: https://github.com/Fission-AI/OpenSpec&lt;/div&gt;&lt;div&gt;Feedback:&amp;nbsp; &amp;nbsp;https://github.com/Fission-AI/OpenSpec/issues&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Restart your IDE for slash commands to take effect.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;做完上面初始化後, 用 tree /f 指令檢視專案目錄 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;PS D:\gemini\calculator-project&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;tree /f&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;列出磁碟區 新增磁碟區 的資料夾 PATH&lt;/div&gt;&lt;div&gt;磁碟區序號為 1258-16B8&lt;/div&gt;&lt;div&gt;D:.&lt;/div&gt;&lt;div&gt;│&amp;nbsp; .gitignore&lt;/div&gt;&lt;div&gt;│&amp;nbsp; .python-version&lt;/div&gt;&lt;div&gt;│&amp;nbsp; main.py&lt;/div&gt;&lt;div&gt;│&amp;nbsp; pyproject.toml&lt;/div&gt;&lt;div&gt;│&amp;nbsp; README.md&lt;/div&gt;&lt;div&gt;│&lt;/div&gt;&lt;div&gt;├─.gemini&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ├─commands&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; └─opsx&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; apply.toml&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; archive.toml&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; explore.toml&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; propose.toml&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; └─skills&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; &amp;nbsp; ├─openspec-apply-change&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; SKILL.md&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; &amp;nbsp; ├─openspec-archive-change&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; SKILL.md&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; &amp;nbsp; ├─openspec-explore&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; SKILL.md&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; &amp;nbsp; └─openspec-propose&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; SKILL.md&lt;/div&gt;&lt;div&gt;│&lt;/div&gt;&lt;div&gt;└─openspec&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; │&amp;nbsp; &lt;span style=&quot;background-color: #fcff01;&quot;&gt;config.yaml&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; ├─changes&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; │&amp;nbsp; └─archive&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; └─&lt;span style=&quot;background-color: #fcff01;&quot;&gt;specs&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;可見 OpenSec 初始化時已在&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;5. 安裝專案所需套件 :&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;雖然對一個計算器專案來說, 只需要純前端 (HTML/CSS/JavaScript) 技術就能完成所有功能, 但為了模擬真實軟體架構常見的前後端搭配組態, 我打算將計算功能邏輯交給後端 Flask 框架來完成, 所以必須先用 uv 工具安裝 Flask, 這樣當 AI 生成程式碼後就可以叫 Gemini CLI&amp;nbsp; 用 uv run 執行專案與驗證結果, 不需要再跳出來處理環境問題. 其次,&amp;nbsp;在使用 /opsx:verify 進行自動化檢查或測試時, 系統會依賴現有的虛擬環境, 如果環境未就緒 (例如缺少 Flask 套件), 驗證步驟可能會出錯.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;PS D:\gemini\calculator-project&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;uv add flask&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;Using CPython 3.12.1 interpreter at: C:\Users\tony1\AppData\Local\Programs\Python\Python312\python.exe&lt;/div&gt;&lt;div&gt;Creating virtual environment at: .venv&lt;/div&gt;&lt;div&gt;Resolved 9 packages in 758ms&lt;/div&gt;&lt;div&gt;Prepared 7 packages in 466ms&lt;/div&gt;&lt;div&gt;░░░░░░░░░░░░░░░░░░░░ [0/8] Installing wheels...&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;warning: Failed to hardlink files; falling back to full copy. This may lead to degraded performance.&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;If the cache and target directories are on different filesystems, hardlinking may not be supported.&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;If this is intentional, set `export UV_LINK_MODE=copy` or use `--link-mode=copy` to suppress this warning.&lt;/div&gt;&lt;div&gt;Installed 8 packages in 98ms&lt;/div&gt;&lt;div&gt;&amp;nbsp;+ blinker==1.9.0&lt;/div&gt;&lt;div&gt;&amp;nbsp;+ click==8.3.3&lt;/div&gt;&lt;div&gt;&amp;nbsp;+ colorama==0.4.6&lt;/div&gt;&lt;div&gt;&amp;nbsp;+ flask==3.1.3&lt;/div&gt;&lt;div&gt;&amp;nbsp;+ itsdangerous==2.2.0&lt;/div&gt;&lt;div&gt;&amp;nbsp;+ jinja2==3.1.6&lt;/div&gt;&lt;div&gt;&amp;nbsp;+ markupsafe==3.0.3&lt;/div&gt;&lt;div&gt;&amp;nbsp;+ werkzeug==3.1.8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;安裝完後用 tree /f 檢視專案目錄, 會多出一個 .venv 隱藏子目錄, 裡面有包含 Flask 與所依賴的 Jinjia 等套件 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;├─&lt;span style=&quot;background-color: #fcff01;&quot;&gt;.venv&lt;/span&gt;&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; .gitignore&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; .lock&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; CACHEDIR.TAG&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; pyvenv.cfg&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ├─Lib&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; └─site-packages&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; _virtualenv.pth&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; _virtualenv.py&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; ├─blinker&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; base.py&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; py.typed&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; _utilities.py&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; __init__.py&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;... (略) ...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; ├─flask&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; │&amp;nbsp; app.py&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; │&amp;nbsp; blueprints.py&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; │&amp;nbsp; cli.py&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; │&amp;nbsp; config.py&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; │&amp;nbsp; ctx.py&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; │&amp;nbsp; debughelpers.py&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; │&amp;nbsp; globals.py&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; │&amp;nbsp; helpers.py&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; │&amp;nbsp; logging.py&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; │&amp;nbsp; py.typed&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; │&amp;nbsp; sessions.py&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; │&amp;nbsp; signals.py&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; │&amp;nbsp; templating.py&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; │&amp;nbsp; testing.py&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; │&amp;nbsp; typing.py&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; │&amp;nbsp; views.py&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; │&amp;nbsp; wrappers.py&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; │&amp;nbsp; __init__.py&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; │&amp;nbsp; │&amp;nbsp; __main__.py&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;... (略) ...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;6. 編輯專案語境檔 GEMINI.md :&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;在之前使用 Vibe coding 的進階方式開發時, 我們透過事先編輯好的專案語境檔 GEMINI.md 一次將專案需求與結構, 程式風格, 任務模板, 限制和偏好等資訊一口氣描述好, 當啟動 Gemini CLI 時它便能了解專案內容, 從而減少來回問答的次數. 這種語境檔因為要交待較多訊息, 所以內容比較冗長, 參考 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2026/01/gemini-cli-geminimd.html&quot; target=&quot;_blank&quot;&gt;Gemini CLI 學習筆記 : 專案語境檔 GEMINI.md&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;如果使用 OpenSpec 做 SDD 開發,&amp;nbsp;GEMINI.md 就會比較簡短, 例如下面的通用模板 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe; font-size: x-small;&quot;&gt;# Project Global Guidelines (GEMINI.md)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe; font-size: x-small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe; font-size: x-small;&quot;&gt;## 1. AI 角色設定 (通用)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe; font-size: x-small;&quot;&gt;你是一位資深的 [Python 後端與全端] 開發專家，精通 [Flask 框架與現代前端技術]，並具備極高的軟體工程素養。&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe; font-size: x-small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe; font-size: x-small;&quot;&gt;## 2. 技術棧與環境配置 (專案特製)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe; font-size: x-small;&quot;&gt;* 核心語言：Python 3.12+ (嚴格使用 `uv` 進行依賴與環境管理)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe; font-size: x-small;&quot;&gt;* 後端框架：[Flask]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe; font-size: x-small;&quot;&gt;* 前端技術：[Vanilla JS, HTML5, CSS3]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe; font-size: x-small;&quot;&gt;* 其他工具：[若無則免，例如 SQLite, 特定硬體 SDK 等]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe; font-size: x-small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe; font-size: x-small;&quot;&gt;## 3. 開發流程與規格遵循 (通用，針對 OpenSpec 用戶)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe; font-size: x-small;&quot;&gt;* 本專案嚴格遵循 Fission AI 的 OpenSpec 工作流 (`/opsx` 指令集)。&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe; font-size: x-small;&quot;&gt;* 你的所有實作必須以 `specs/` 目錄下的文件與 `tasks.md` 為「唯一真相來源 (Single Source of Truth)」。&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe; font-size: x-small;&quot;&gt;* 絕對禁止在未經使用者同意且未更新 Spec 的情況下，自行發明、擴充或竄改 API 規格與業務邏輯。&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe; font-size: x-small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe; font-size: x-small;&quot;&gt;## 4. 程式碼風格守則 (通用，Python 標準)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe; font-size: x-small;&quot;&gt;* 必須包含 Type Hints (型別提示) 與清楚的 Docstrings。&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe; font-size: x-small;&quot;&gt;* 遵守 PEP 8 命名規範 (變數與函式使用 `snake_case`，類別使用 `PascalCase`)。&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe; font-size: x-small;&quot;&gt;* 保持模組化，避免單一檔案過於龐大。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;其中專案特製部分視專案而異, 括號 [] 內容需要手動修改, 其他通用部分則適用於任何用 OpenSpec 開發的專案. GEMINI.md 的角色是專案的憲法, 負責告訴 AI 我們這個專案要用甚麼 approach 來做 (How), 而專案的需求 (what) 會放在 /opsx: propose 指令來交待.&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;在 PS 視窗的專案目錄下輸入 notepad GEMINI.md :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;PS D:\gemini\calculator-project&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;notepad GEMINI.md&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;這時 PS 發現專案目錄下並無 GEMINI.md 檔, 就彈出詢問是否新建此檔, 按是就會開啟記事本 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyUkXZMzRGvhYf2JdGJEXg7Z74I39nYaJuB8LWUJDG-j4wYQgkqkUijIYF6h5Ap2LZ8Uk55kobzvlR3BjVM1KB-2iryOoS5yb__TaR5q8YtYYMiDNK07NvBm-bH2Jw_5hnegjMy30a8Hd5Y6L8FhoCXVgK96XiQ_gZCsSa5o7cxTCnQNBZTQazXtem0xuW/s785/notepad-GEMINI.md-1.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;411&quot; data-original-width=&quot;785&quot; height=&quot;168&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyUkXZMzRGvhYf2JdGJEXg7Z74I39nYaJuB8LWUJDG-j4wYQgkqkUijIYF6h5Ap2LZ8Uk55kobzvlR3BjVM1KB-2iryOoS5yb__TaR5q8YtYYMiDNK07NvBm-bH2Jw_5hnegjMy30a8Hd5Y6L8FhoCXVgK96XiQ_gZCsSa5o7cxTCnQNBZTQazXtem0xuW/s320/notepad-GEMINI.md-1.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;複製上面的語境檔通用範本貼到 GEMINI.md 後存檔 (此處我將 ## 2 的其他工具內容改為 [無]) :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjc_e2SpxsyLpw7f1AoBbrw4fcaUoS6uklK2YSsT1n3YINgw6iYj2h23q0Wuq8namrHZaoCGrIAr3MBZoZ4kunRzgX9p4EpFwPmLS2bAmCQ3QmXhFhdDNNlrSPckX_YrmilyTvTbNjvKgy4T6INJOzVhRb85OWpxH1k6AGP0_s5FmY-flO-0kLtB28KQNQ/s1984/notepad-GEMINI.md-2.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1056&quot; data-original-width=&quot;1984&quot; height=&quot;170&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjc_e2SpxsyLpw7f1AoBbrw4fcaUoS6uklK2YSsT1n3YINgw6iYj2h23q0Wuq8namrHZaoCGrIAr3MBZoZ4kunRzgX9p4EpFwPmLS2bAmCQ3QmXhFhdDNNlrSPckX_YrmilyTvTbNjvKgy4T6INJOzVhRb85OWpxH1k6AGP0_s5FmY-flO-0kLtB28KQNQ/s320/notepad-GEMINI.md-2.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;再次用 tree /s 檢視專案目錄下已有此 GEMINI.md 檔了 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;PS D:\gemini\calculator-project&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;tree /f&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;列出磁碟區 新增磁碟區 的資料夾 PATH&lt;/div&gt;&lt;div&gt;磁碟區序號為 1258-16B8&lt;/div&gt;&lt;div&gt;D:.&lt;/div&gt;&lt;div&gt;│&amp;nbsp; .gitignore&lt;/div&gt;&lt;div&gt;│&amp;nbsp; .python-version&lt;/div&gt;&lt;div&gt;│&amp;nbsp; &lt;span style=&quot;background-color: #fcff01;&quot;&gt;GEMINI.md&lt;/span&gt;&lt;/div&gt;&lt;div&gt;│&amp;nbsp; main.py&lt;/div&gt;&lt;div&gt;│&amp;nbsp; pyproject.toml&lt;/div&gt;&lt;div&gt;│&amp;nbsp; README.md&lt;/div&gt;&lt;div&gt;│&amp;nbsp; uv.lock&lt;/div&gt;&lt;div&gt;│&lt;/div&gt;&lt;div&gt;├─.gemini&lt;/div&gt;&lt;div&gt;│&amp;nbsp; ├─commands&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; └─opsx&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; apply.toml&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; archive.toml&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; explore.toml&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; propose.toml&lt;/div&gt;&lt;div&gt;│&amp;nbsp; │&lt;/div&gt;&lt;div&gt;│&amp;nbsp; └─skills&lt;/div&gt;&lt;/div&gt;&lt;div&gt;... (略) ...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;7. 啟動 Gemini CLI 進行初次迭代&amp;nbsp; :&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;完成上面準備工作後, 終於要開啟 Gemini CLI 開始用 OpenSpec 幹活了. 在專案目錄下輸入 gemini 指令 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;PS D:\gemini\calculator-project&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;gemini&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;詢問是否信任此目錄, 當然要選 1. Trust folder (calculator-project) 才會進入 Gemini CLI 介面 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhakJCQEpxa5sDDy1xKhWjOhQKh7XbBHtf8lWpt1TjT0Jh5XU6Ml16VWuiVc-6Jv0AxDV4fZdhPu4WKEjrkJWzHjN1SaUYnl3c7xP9hQQZ3eRqoZHqYO9aw0hYmVfMvQkYyQUCRYg1ioHoRM7YlUdGukcVkxQvU15AahT4WN4rVy8YYBeiJ4MvVYkoFDSBa/s2279/gemin-cli-openspec-1.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1085&quot; data-original-width=&quot;2279&quot; height=&quot;152&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhakJCQEpxa5sDDy1xKhWjOhQKh7XbBHtf8lWpt1TjT0Jh5XU6Ml16VWuiVc-6Jv0AxDV4fZdhPu4WKEjrkJWzHjN1SaUYnl3c7xP9hQQZ3eRqoZHqYO9aw0hYmVfMvQkYyQUCRYg1ioHoRM7YlUdGukcVkxQvU15AahT4WN4rVy8YYBeiJ4MvVYkoFDSBa/s320/gemin-cli-openspec-1.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIdkDF-siZX8TgaNP-ifJ_voheuDefFvMDLJT41R8tgqi7pr7xAFb9Fupjujy2pStj1YKv7tqWs4MfAZ-kTRErxaDUhEIcIkVvpZFjIm5nq08NFxmJ4lMb83pNYlgVrKUplkKFVjYdz2YU_AoLSkpVAToTX08UE5yj3r79tPAHnqfH831_qz2OggAPjXlF/s2300/gemin-cli-openspec-2.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1070&quot; data-original-width=&quot;2300&quot; height=&quot;149&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIdkDF-siZX8TgaNP-ifJ_voheuDefFvMDLJT41R8tgqi7pr7xAFb9Fupjujy2pStj1YKv7tqWs4MfAZ-kTRErxaDUhEIcIkVvpZFjIm5nq08NFxmJ4lMb83pNYlgVrKUplkKFVjYdz2YU_AoLSkpVAToTX08UE5yj3r79tPAHnqfH831_qz2OggAPjXlF/s320/gemin-cli-openspec-2.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;出現 &amp;gt; 提示號表示 AI 已經讀取了 GEMINI.md 內容知道自己是誰, 也知道這個資料夾裡有 OpenSpec 環境, 這樣就可以開始用 OpenSpec 的 斜線指令集 /opsx 進行 SDD 開發了.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;如上所述, 這個專案作業要求至少進行三次開發迭代, 第一次迭代是要做出一個基本的四則運算計算器, 由於較簡單, 此處會使用快轉模式, 直接用 /opsx: ff 指令一鍵生成完成專案規劃與程式碼生成與測試, 整個工作流只需要的指令如下 (依序) :&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;/opsx:new &amp;lt;iteration_name&amp;gt; (建立迭代之專屬的工作區)&lt;/li&gt;&lt;li&gt;/opsx:ff &amp;lt;requirements&amp;gt; (依需求快轉生成設計文件與程式碼並完成測試)&lt;/li&gt;&lt;li&gt;/opsx:archive (迭代完成歸檔)&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;其中 iteration_name 是自訂的, 為了專案的易讀性以及讓 AI 能隱約猜到任務方向, 通常會遵循以下命名最佳實踐來取名 :&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;使用 Kebab-case (連字號命名法) : &lt;br /&gt;全小寫英文, 單字之間用連字號 - 隔開, 避免使用空格, 大寫或特殊符號.&amp;nbsp;&lt;/li&gt;&lt;li&gt;具備語意 (Semantic) : 名稱要能直接反映這次迭代的核心目的.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;初次迭代我選用 cals-basic 作為工作區名稱.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;(1).&amp;nbsp;建立迭代之專屬工作區 :&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;這會在專案目錄下建立此次迭代之專屬工作區, 例如迭代名稱是 calc-basic 的話就建立對應的資料夾 specs/changes/calc-basic/&amp;nbsp;來收納這次迭代的所有相關討論與設計文件.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;系統會自動生成標準 SDD 流程所需的空文件或帶有基礎標題的模板, 例如空的 proposal.md, spec.md, design.md 以及 tasks.md, 此階段相當於是買了一本有分類索引標籤 (提案, 規格, 設計, 任務) 的空白筆記本, 以便在後續工作流中依需求來填空或修改.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;輸入下列指令並於一連串授權詢問時一律選擇預設的 Allow once :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;gt;&amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;/opsx:new calc-basic&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoRR189R-dfKeHdue_YNNraR2PxAUOZS9XrghUfdCML6ECaAzSon_z0w8I8jJtp4K1sdLRaRHiPQXTADJRsoOsSrfzLIu8OclNqkXmUS6KNRvY5OuF1Xx3ZrBWpYobSlxRu7VXsK2Vjc_Y9YQinnBFEIFZEWEa9iITIgYRGc05jHE0X6dhHkPZMPf0rIp9/s2301/gemin-cli-openspec-4.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1062&quot; data-original-width=&quot;2301&quot; height=&quot;148&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoRR189R-dfKeHdue_YNNraR2PxAUOZS9XrghUfdCML6ECaAzSon_z0w8I8jJtp4K1sdLRaRHiPQXTADJRsoOsSrfzLIu8OclNqkXmUS6KNRvY5OuF1Xx3ZrBWpYobSlxRu7VXsK2Vjc_Y9YQinnBFEIFZEWEa9iITIgYRGc05jHE0X6dhHkPZMPf0rIp9/s320/gemin-cli-openspec-4.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;... (略) ...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhK9iwjS6IP6uQYATBVMvT_v9ZSCFx_dC_QPqtGaH5FCxTX2YVtHeyFyCZVKp-VeoTYi6r2upngo3y7fRv6FZhyWWW_3ErncR72bpriAWXjqD71DZqInLIR-GqcCm86iLqCRV5h9RUfCETlys4zFk18hZPRg38zkgEsjUMl1kSlj5M-iDgdIfdVtKmzuNhC/s2294/gemin-cli-openspec-6.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1130&quot; data-original-width=&quot;2294&quot; height=&quot;158&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhK9iwjS6IP6uQYATBVMvT_v9ZSCFx_dC_QPqtGaH5FCxTX2YVtHeyFyCZVKp-VeoTYi6r2upngo3y7fRv6FZhyWWW_3ErncR72bpriAWXjqD71DZqInLIR-GqcCm86iLqCRV5h9RUfCETlys4zFk18hZPRg38zkgEsjUMl1kSlj5M-iDgdIfdVtKmzuNhC/s320/gemin-cli-openspec-6.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;工作區建完後, 專案所需的 OpenSpec 模板文件也都已建好, 但它們目前只是空洞的骨架而已, 接下來的工作流 (/opsx:ff 或 /opsx:continue) 就會根據需求來填寫這些標準 SSD 流程的四份核心文件模板 (proposal.md, spec.md, design.md 以及 tasks.md), 如果需求較複雜, 可能也會生成額外的 spec-xxx.md 文件, 相當於是在骨架中進行靈魂注入.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;(2). 依需求快轉 (ff) 生成設計文件與程式碼 :&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;此階段 OpenSpec 會根據我們提供的專案需求, 透過 AI 去修改或填空上一個指令 (/opsx: new) 生出的四份核心文件 proposal.md (提案), spec.md (介面規格), design.md (架構設計) 與 tasks.md (任務清單), 並且據此生成程式碼同時完成測試, 可說是集成了 /opsx:propose, /opsx:continue, /opsx: apply, 以及 /opsx: verify 這四個指令的功能於一身, 一氣呵成完成專案實作.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;輸入如下指令來進行快轉 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;/opsx:ff &quot;請幫我規劃一個基礎四則運算計算機軟體, 後端使用 Flask 提供 POST API, 前端使用純 HTML/JS, 並提供基本的使用者介面.&quot;&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjml_ZP61fHhTCDUogtqwg3nh_JKiuJJQxqDJNjDejHmtNZOH9S2hXeVzLaI__fpwaWO9i7t6ypkxGKr0K19PPaVzzUa__yjHZKwVPgokto7TAEEmG-TZtITmz-Yz4AJNlJajTkjz0LgTK6KaZ5oijJA0omSmg9tLF2qDW1DwALtSzdzwswWWtFiP3A007Y/s2295/gemin-cli-openspec-8.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1016&quot; data-original-width=&quot;2295&quot; height=&quot;142&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjml_ZP61fHhTCDUogtqwg3nh_JKiuJJQxqDJNjDejHmtNZOH9S2hXeVzLaI__fpwaWO9i7t6ypkxGKr0K19PPaVzzUa__yjHZKwVPgokto7TAEEmG-TZtITmz-Yz4AJNlJajTkjz0LgTK6KaZ5oijJA0omSmg9tLF2qDW1DwALtSzdzwswWWtFiP3A007Y/s320/gemin-cli-openspec-8.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;... (略) ...&lt;/div&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNcrd87nu-zjjcxFrT__r6PEF1Hm8kBG55GdyRwf18TMck_uGKIQ8yfJwX4Hi22NH54X9q_TcheEpB-A2vOBRBeOCP335SstRvwxchmxQRVLBEK6ADdfGv9ilcO1pPcgGNz7nwuc8ylV9ugl2BldHuSVRXuibsQoCQETsntG6fimcEdAFGp6aTsBYYVw2I/s1922/gemin-cli-openspec-9.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1120&quot; data-original-width=&quot;1922&quot; height=&quot;186&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNcrd87nu-zjjcxFrT__r6PEF1Hm8kBG55GdyRwf18TMck_uGKIQ8yfJwX4Hi22NH54X9q_TcheEpB-A2vOBRBeOCP335SstRvwxchmxQRVLBEK6ADdfGv9ilcO1pPcgGNz7nwuc8ylV9ugl2BldHuSVRXuibsQoCQETsntG6fimcEdAFGp6aTsBYYVw2I/s320/gemin-cli-openspec-9.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;同樣地, 在它修改生成檔案與依此生成整個專案程式碼過程中會不斷要求授權, 我都選預設的 Allow once, 這樣可以一步步觀察 OpenSpec 做了哪些事, 過程中會顯示生成的程式碼與單元測試檔, 完成後結果如下 :&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqLa16H54dlHjO9ldHm4US-LDSXhoOiQX3v0G1hW5pxPGQE5NjLTSMcDK4nI49WDXHueRy4qEwpb8JgtcZW5qthencT6fBUMYKR3a-dENIgDap8-nhyphenhyphenRpYIZ2ybW7GtyJIn-aikOHpZSgsoNjXd0y91YBfQPRtHIDOqwrgZpgNSrHkVgukboal8Kw1ORfw/s2316/gemin-cli-openspec-10.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1043&quot; data-original-width=&quot;2316&quot; height=&quot;144&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqLa16H54dlHjO9ldHm4US-LDSXhoOiQX3v0G1hW5pxPGQE5NjLTSMcDK4nI49WDXHueRy4qEwpb8JgtcZW5qthencT6fBUMYKR3a-dENIgDap8-nhyphenhyphenRpYIZ2ybW7GtyJIn-aikOHpZSgsoNjXd0y91YBfQPRtHIDOqwrgZpgNSrHkVgukboal8Kw1ORfw/s320/gemin-cli-openspec-10.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;這時可以另開一個命令提示字元或 PS 視窗, 切到專案目錄下, 用 python main.py 或 uv run main.py 執行此專案, 然後開啟瀏覽器訪問&amp;nbsp;http://127.0.0.1:5000 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;PS D:\gemini&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;cd calculator-project&amp;nbsp;&lt;/span&gt;&lt;/b&gt; &amp;nbsp;&lt;/div&gt;&lt;div&gt;PS D:\gemini\calculator-project&amp;gt; uv run main.py&lt;/div&gt;&lt;div&gt;&amp;nbsp;* Serving Flask app &#39;main&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp;* Debug mode: on&lt;/div&gt;&lt;div&gt;WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.&lt;/div&gt;&lt;div&gt;&amp;nbsp;* Running on &lt;span style=&quot;background-color: #fcff01;&quot;&gt;http://127.0.0.1:5000&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Press CTRL+C to quit&lt;/div&gt;&lt;div&gt;&amp;nbsp;* Restarting with stat&lt;/div&gt;&lt;div&gt;&amp;nbsp;* Debugger is active!&lt;/div&gt;&lt;div&gt;&amp;nbsp;* Debugger PIN: 125-928-819&lt;/div&gt;&lt;div&gt;127.0.0.1 - - [25/Apr/2026 16:44:54] &quot;GET / HTTP/1.1&quot; 200 -&lt;/div&gt;&lt;div&gt;127.0.0.1 - - [25/Apr/2026 16:44:54] &quot;GET /static/style.css HTTP/1.1&quot; 200 -&lt;/div&gt;&lt;div&gt;127.0.0.1 - - [25/Apr/2026 16:44:54] &quot;GET /static/script.js HTTP/1.1&quot; 200 -&lt;/div&gt;&lt;div&gt;127.0.0.1 - - [25/Apr/2026 16:44:54] &quot;GET /favicon.ico HTTP/1.1&quot; 404 -&lt;/div&gt;&lt;div&gt;127.0.0.1 - - [25/Apr/2026 16:45:11] &quot;POST /api/multiply HTTP/1.1&quot; 200 -&lt;/div&gt;&lt;div&gt;127.0.0.1 - - [25/Apr/2026 16:45:23] &quot;POST /api/divide HTTP/1.1&quot; 400 -&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;果然顯示了四則運算計算器頁面, 測試計算功能正常 (7 * 8=56, 1 除以 0 顯示 Error ) :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpOosZvw7gFFXkx3HzGu8JB1XL3tM-7AbegU4IbGsOIet0XrcUhyO8zkr5RrxJ66bwWc1YDJnZ4oNYZYiDNQxOtFsloVJaKLVxfyuLRKVTuM-j-6Zqn1WZCi388QjSo85FdgkHfu3gnDvuuHRDvsrIUOvp9oF0lZuVku_D0sNvOL9W4R3cCZF6kshfSQgE/s841/gemin-cli-openspec-11.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;841&quot; data-original-width=&quot;638&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpOosZvw7gFFXkx3HzGu8JB1XL3tM-7AbegU4IbGsOIet0XrcUhyO8zkr5RrxJ66bwWc1YDJnZ4oNYZYiDNQxOtFsloVJaKLVxfyuLRKVTuM-j-6Zqn1WZCi388QjSo85FdgkHfu3gnDvuuHRDvsrIUOvp9oF0lZuVku_D0sNvOL9W4R3cCZF6kshfSQgE/s320/gemin-cli-openspec-11.jpg&quot; width=&quot;243&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlRTP-OXGPlsR3RA9zVRPz1-R5iW8gHC3dB3GK4DTyntrg90pke3TBPZesGnKIwybCQSVtbcEoALPRtHcBkDRdgteuYEyfxHd8aVq6_KlA-RbYwHGX77hVVxSXR2mS1xnZAG7-CxCVjuP_cnyT-QVkuRosj0jVVX-luE9Hh5PTemijeAoiDH-8Fd_0t-jT/s838/gemin-cli-openspec-12.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;838&quot; data-original-width=&quot;638&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlRTP-OXGPlsR3RA9zVRPz1-R5iW8gHC3dB3GK4DTyntrg90pke3TBPZesGnKIwybCQSVtbcEoALPRtHcBkDRdgteuYEyfxHd8aVq6_KlA-RbYwHGX77hVVxSXR2mS1xnZAG7-CxCVjuP_cnyT-QVkuRosj0jVVX-luE9Hh5PTemijeAoiDH-8Fd_0t-jT/s320/gemin-cli-openspec-12.jpg&quot; width=&quot;244&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;(3). 歸檔結案 :&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;完成以上功能驗證後, 就可以用 /opsx: archive 指令檢查所有的 Artifacts 是否齊全, 是的話將此次迭代的所有紀錄, 文件, 與過程收整, 並移動到 archive 資料夾收存 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;/opsx:archive&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;同樣地會有一連串的授權請求, 一律選擇預設的 Allow once :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgY-Cw_27A3cZKcPy1c0RFZ_I45cvezbcejMNZ-UkS8JVftlZEPWx-6BmZfeJC_U29Ysx8S_T2AygHJFYzb6LDohjCwVp1K8wMMZhWfoS88Moq41bkiFyT_HKmzrnTITSgPkzRXQBKL0EY4yeHJhonmD08-VNMcZTqx5XF9ToCw0Fv7RXbHZFp8XOnHcoa0/s2240/gemin-cli-openspec-13.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1090&quot; data-original-width=&quot;2240&quot; height=&quot;156&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgY-Cw_27A3cZKcPy1c0RFZ_I45cvezbcejMNZ-UkS8JVftlZEPWx-6BmZfeJC_U29Ysx8S_T2AygHJFYzb6LDohjCwVp1K8wMMZhWfoS88Moq41bkiFyT_HKmzrnTITSgPkzRXQBKL0EY4yeHJhonmD08-VNMcZTqx5XF9ToCw0Fv7RXbHZFp8XOnHcoa0/s320/gemin-cli-openspec-13.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;... (略) ...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgViAl7Ou6zEWtcBlMoepdFsYftigUppEOX6o_MARMsXTtcxKLA8Xg9M05oD2qi5K711BTaT5W-PWXxwlS18uOyCyMVpBU6hJE1x_MXEzbrdApUIvktKrcaVjh_mLkOUILuZa-fCkY1X6kCDRXsxFa_wY7hr9hDUQ3xgnNcFXwDfXL_emaaOUBc17SubR7w/s2288/gemin-cli-openspec-14.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1135&quot; data-original-width=&quot;2288&quot; height=&quot;159&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgViAl7Ou6zEWtcBlMoepdFsYftigUppEOX6o_MARMsXTtcxKLA8Xg9M05oD2qi5K711BTaT5W-PWXxwlS18uOyCyMVpBU6hJE1x_MXEzbrdApUIvktKrcaVjh_mLkOUILuZa-fCkY1X6kCDRXsxFa_wY7hr9hDUQ3xgnNcFXwDfXL_emaaOUBc17SubR7w/s320/gemin-cli-openspec-14.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;可見所有此次迭代的紀錄文件都已歸檔於&amp;nbsp;openspec/changes/archive/2026-04-25-calc-basic/ 資料夾底下, 這些都是未來維護軟體的重要資料. 好啦! 終於完成初次迭代.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://yhhuang1966.blogspot.com/feeds/2574066280757431013/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment/fullpage/post/4877487320781767952/2574066280757431013' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/2574066280757431013'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/2574066280757431013'/><link rel='alternate' type='text/html' href='https://yhhuang1966.blogspot.com/2026/04/gemini-cli-openspec.html' title='Gemini CLI 學習筆記 : OpenSpec 初體驗 (一)'/><author><name>小狐狸事務所</name><uri>http://www.blogger.com/profile/09435160519044041137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFnvV5j2FfTfH_CB6FiUtgwbMs9GM2nrQCaQHKdoZDRXCmW7-pRv9IpHsqpv84DssS99ohEwADWhFQceAwFkltTdULEf0FJ_c0DQbCvzhN-BjYmAe-KoLBmXHvAwd-ns7vuuzMzbE_szUwYvvbftTPdjCSek4TDTPxOY-5o5y2DcLP0sQsKoyfp2Icn2QQ/s72-c/openspec-init-1.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4877487320781767952.post-505762303419409700</id><published>2026-04-23T09:55:00.010+08:00</published><updated>2026-04-23T13:57:34.874+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><category scheme="http://www.blogger.com/atom/ns#" term="網路爬蟲"/><title type='text'>Python 學習筆記 : 市圖借書與預約爬蟲程式架構</title><content type='html'>&lt;div&gt;這兩天完成市圖爬蟲改版 (v13), 由於此爬蟲機制較複雜, 由地端與雲端程式共同運作來維護佈署於 Render 的 Serverless 平台上的資料庫 (kmsl_books 資料表), 這與 v10 版之前的系統完全由地端爬蟲程式一手包辦爬取與傳訊完全不同. 爬蟲任務主要由放在高雄家的樹莓派主機負責, 目前主要是市圖借書與預約狀況與母校圖書館自動續借兩個任務而已, 今天抽空畫了一張架構圖以資備忘.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Pi 3B 對接 render.com 上的 5e6i 這個端點 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiErtb4Mj_Q_at4ite3rqt2_gdsD4griLbxmfAYizjpopUbOs5m3YxCQ1FZtVK4So95QhmhQIIBITNxavK5GpETGsknHJiG00SLNq9Rj-J-ChijmI5x3Y_MhhBj7Ae4z5-0HGbLDeKcQ_jVBW78_cTxlqn2nN6MiGSqkZdzPbKUWAayv44xbCsYCzH7wf5W/s1221/kaopi3-ksml-lib.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1154&quot; data-original-width=&quot;1221&quot; height=&quot;302&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiErtb4Mj_Q_at4ite3rqt2_gdsD4griLbxmfAYizjpopUbOs5m3YxCQ1FZtVK4So95QhmhQIIBITNxavK5GpETGsknHJiG00SLNq9Rj-J-ChijmI5x3Y_MhhBj7Ae4z5-0HGbLDeKcQ_jVBW78_cTxlqn2nN6MiGSqkZdzPbKUWAayv44xbCsYCzH7wf5W/s320/kaopi3-ksml-lib.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;Pi 3A+&amp;nbsp; 則對接 render.com 上的 fdof 這個端點 :&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi599cT41wH65obfHF9pQvlbOyMS6u2hU6BaJdLCVJIAQ1qJXxBQyy4i1UR0sRT7uVyrbVy6BIek4a-sRHY1CRvrmz0Ckl65h5HSDuLlpaKRFU76cr-XbUNXuPWOFVWivUH341Vvbr2FZTNzUZZQvjk6d4cSdCQqTG_Ym8Yem4Vgau0aDGCjHVm8hs-X_AV/s1240/pi3aplus-ksml-lib.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1168&quot; data-original-width=&quot;1240&quot; height=&quot;301&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi599cT41wH65obfHF9pQvlbOyMS6u2hU6BaJdLCVJIAQ1qJXxBQyy4i1UR0sRT7uVyrbVy6BIek4a-sRHY1CRvrmz0Ckl65h5HSDuLlpaKRFU76cr-XbUNXuPWOFVWivUH341Vvbr2FZTNzUZZQvjk6d4cSdCQqTG_Ym8Yem4Vgau0aDGCjHVm8hs-X_AV/s320/pi3aplus-ksml-lib.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;佈署於樹莓派上的市圖爬蟲程式 ksml_lib_13.py 負責每小時爬取網頁擷取借書與預約狀態後, 呼叫佈署於 render.com 的 serverless 平台函式 update_ksml_books.py, 將資訊儲存在 kmsl_books 資料表裡, 結果如下 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTcyxwQOwlySsSity5g2IpZN05vGeBqPVuLOTSwXY3nTDsow1hvW28iJLH29hZVHGnc724RRy5TrytWz52uBVK7HWNQ9cN0I1Usm14WAm4WLdqlFKm8gZxYkm14YWLGLKYgPbPj1ElGYXRUok0shUu7yFx63URmXrXkfwvLk1-h37ZrHBJ6-THeWE76xJu/s2537/render.com-ksml-books-1.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1222&quot; data-original-width=&quot;2537&quot; height=&quot;154&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTcyxwQOwlySsSity5g2IpZN05vGeBqPVuLOTSwXY3nTDsow1hvW28iJLH29hZVHGnc724RRy5TrytWz52uBVK7HWNQ9cN0I1Usm14WAm4WLdqlFKm8gZxYkm14YWLGLKYgPbPj1ElGYXRUok0shUu7yFx63URmXrXkfwvLk1-h37ZrHBJ6-THeWE76xJu/s320/render.com-ksml-books-1.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;另外一支本地程式 get_books_messages.py 透過 crontab 驅動定時呼叫 serverless 平台函式 send_books_messages.py 讀取 kmsl_books 資料表, 然後由發送到 Telegram, 參考最近三版異動 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2025/10/python-v10_20.html&quot; target=&quot;_blank&quot;&gt;Python 學習筆記 : 市圖借書與預約爬蟲程式改版 v11&lt;/a&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2025/12/python-v12.html&quot; target=&quot;_blank&quot;&gt;Python 學習筆記 : 市圖借書與預約爬蟲程式改版 v12 (於 Pi 400)&lt;/a&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2026/04/python-v13.html&quot; target=&quot;_blank&quot;&gt;Python 學習筆記 : 市圖借書與預約爬蟲程式改版 v13&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;母校圖書館爬蟲 nkust_lib_11.py 的任務相對簡單, 只是登入網站後按全部續借鈕, 然後瀏覽借書頁面, 擷取已被預約的書目後用 Telegram 傳送通知訊息而已, 參考 :&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2026/04/python-v11.html&quot; target=&quot;_blank&quot;&gt;Python 學習筆記 : 母校圖書館借書與預約爬蟲程式改版 v11&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;至於 Pi 400 上佈署的爬蟲與 Pi 3B 的相同 (對接 5e6i), 但正常情況會關掉 crontab 內的 ksml_lib_13.py (不執行), 僅執行 get_books_messages.py 與 nkust_lib_11.py, 若 Pi 3 主機出問題再開啟 Pi 400 的市圖爬蟲.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://yhhuang1966.blogspot.com/feeds/505762303419409700/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment/fullpage/post/4877487320781767952/505762303419409700' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/505762303419409700'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/505762303419409700'/><link rel='alternate' type='text/html' href='https://yhhuang1966.blogspot.com/2026/04/python.html' title='Python 學習筆記 : 市圖借書與預約爬蟲程式架構'/><author><name>小狐狸事務所</name><uri>http://www.blogger.com/profile/09435160519044041137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiErtb4Mj_Q_at4ite3rqt2_gdsD4griLbxmfAYizjpopUbOs5m3YxCQ1FZtVK4So95QhmhQIIBITNxavK5GpETGsknHJiG00SLNq9Rj-J-ChijmI5x3Y_MhhBj7Ae4z5-0HGbLDeKcQ_jVBW78_cTxlqn2nN6MiGSqkZdzPbKUWAayv44xbCsYCzH7wf5W/s72-c/kaopi3-ksml-lib.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4877487320781767952.post-505050466771746572</id><published>2026-04-21T11:06:00.012+08:00</published><updated>2026-04-29T11:21:14.251+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="旅行"/><title type='text'>沖繩之旅行程規劃 : 5/5~5/8</title><content type='html'>&lt;div&gt;年初時原本打算入春後帶爸坐郵輪遊沖繩, 但想到上回搭麗星郵輪去香港 (原本是石垣島+沖繩, 因颱風改走香港) 的經驗, 在海上時無法上網形同斷訊, 在船上日吃五餐會變胖, 於是打消了遊輪計畫, 改為搭飛機自助遊, 原本想說嘗試在沖繩自駕, 但又對右駕系統沒自信能駕馭, 那就包車吧! 我跟水某與菁菁加上爸四個人包一台車即可, 不過後來加入岳父母, 小姨子, 與剛高中畢業的外甥, 團員來到 8 人, 所以必須包 10 人座的小巴啦 (Toyota Hiace)!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;今天用 Gemini 規劃這四天三夜親子沖繩旅遊行程如下 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Day 1 :&amp;nbsp;5/5 (週一) 抵達那霸入住國際通嘉新酒店&lt;/div&gt;&lt;div&gt;&lt;br /&gt;


&lt;table border=&quot;1&quot; bordercolor=&quot;#888&quot; cellspacing=&quot;0&quot; style=&quot;background-color: white; border-collapse: collapse; border-color: rgb(136, 136, 136); border-width: 1px; color: #534741; font-family: Arial, Verdana, sans-serif; font-size: 13.3333px; margin: 0px;&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th style=&quot;background-color: #f2f2f2; padding: 10px; text-align: left; width: 99.011px;&quot;&gt;&amp;nbsp;時間軸&lt;/th&gt;
&lt;th style=&quot;background-color: #f2f2f2; padding: 10px; text-align: left; width: 502.011px;&quot;&gt;&amp;nbsp;行程說明&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;15:55 - 18:45&lt;/td&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;搭乘華航 CI132 抵達那霸機場，可預約 10人座單程接機直達飯店。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;20:30 -&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;Check-in國際通嘉新酒店。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;21:00 -&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;晚餐 + 散步： 踩點國際通。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;


&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;PS :&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;晚餐請包車業者沖繩行腳代訂島唄與地料理 Tubaraama 餐廳 21:00 用餐.&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;Day 2 :&amp;nbsp;5/6 (週二) 北部遠征（水族館＋古宇利島）&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;table border=&quot;1&quot; bordercolor=&quot;#888&quot; cellspacing=&quot;0&quot; style=&quot;background-color: white; border-collapse: collapse; border-color: rgb(136, 136, 136); border-width: 1px; color: #534741; font-family: Arial, Verdana, sans-serif; font-size: 13.3333px; margin: 0px;&quot;&gt;
  &lt;tbody&gt;
    &lt;tr&gt;&lt;th style=&quot;background-color: #f2f2f2; padding: 10px; text-align: left; width: 99.011px;&quot;&gt;&amp;nbsp;時間軸&lt;/th&gt;&lt;th style=&quot;background-color: #f2f2f2; padding: 10px; text-align: left; width: 502.011px;&quot;&gt;&amp;nbsp;行程說明&lt;/th&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;09:00 -&amp;nbsp;&lt;/td&gt;&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;出發 (建議包車 10 小時)： 往返北部車程長，包車可在車上補眠。&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;11:00 -&amp;nbsp;&lt;/td&gt;&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;古宇利島： 欣賞跨海大橋美景，推薦搭乘古宇利海洋塔的電動導覽車。&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;13:00 -&amp;nbsp;&lt;/td&gt;&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;午餐： 北部景觀餐廳（如：百年古家大家）。&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;15:00 -&amp;nbsp;&lt;/td&gt;&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;沖繩美麗海水族館： 觀看黑潮之海大水槽與鯨鯊。&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;19:00 -&amp;nbsp;&lt;/td&gt;&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;返回那霸晚餐。&lt;/td&gt;&lt;/tr&gt;    
  &lt;/tbody&gt;
&lt;/table&gt;
  &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;Day 3 :&amp;nbsp;5/7 (週三)&amp;nbsp;景觀與購物（瀨長島＋永旺來客夢）&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;

&lt;table border=&quot;1&quot; bordercolor=&quot;#888&quot; cellspacing=&quot;0&quot; style=&quot;background-color: white; border-collapse: collapse; border-color: rgb(136, 136, 136); border-width: 1px; color: #534741; font-family: Arial, Verdana, sans-serif; font-size: 13.3333px; margin: 0px;&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th style=&quot;background-color: #f2f2f2; padding: 10px; text-align: left; width: 99.011px;&quot;&gt;&amp;nbsp;時間軸&lt;/th&gt;
&lt;th style=&quot;background-color: #f2f2f2; padding: 10px; text-align: left; width: 502.011px;&quot;&gt;&amp;nbsp;行程說明&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;09:00 -&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;出發 (包車 10 小時，今天景點較多且分散，包車最省體力。)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;09:40 -&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;知念岬公園： 欣賞 270 度無敵海景，這裡步道平緩，長輩看海非常舒心。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;11:30 -&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;瀨長島 Umikaji Terrace：&amp;nbsp;午餐推薦幸福鬆餅/天使鬆餅 Happy Pancake。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;14:30 -&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;美國村： 異國風情街區散步，拍拍彩色房子。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;16:30 -&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;永旺來客夢 (AEON Mall Rycom)：直衝寶可夢中心，晚餐： 永旺美食街&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;20:00 -&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;返回國際通： 回飯店後可進行最後採買。&lt;/td&gt;
&lt;/tr&gt;  
&lt;/tbody&gt;
&lt;/table&gt;



&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Day 4 :&amp;nbsp;5/8 (週四)&amp;nbsp;神社祈福・悠閒歸途&lt;/div&gt;&lt;div&gt;&lt;br /&gt;

&lt;table border=&quot;1&quot; bordercolor=&quot;#888&quot; cellspacing=&quot;0&quot; style=&quot;background-color: white; border-collapse: collapse; border-color: rgb(136, 136, 136); border-width: 1px; color: #534741; font-family: Arial, Verdana, sans-serif; font-size: 13.3333px; margin: 0px;&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th style=&quot;background-color: #f2f2f2; padding: 10px; text-align: left; width: 99.011px;&quot;&gt;&amp;nbsp;時間軸&lt;/th&gt;
&lt;th style=&quot;background-color: #f2f2f2; padding: 10px; text-align: left; width: 502.011px;&quot;&gt;&amp;nbsp;行程說明&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;09:00 -&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;波上宮： 到琉球第一神社祈福。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;12:00 -&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;午餐： 國際通周邊（如：第一牧志公設市場吃海鮮或道地阿古豬）。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;15:30 -&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;啟程前往機場： 長輩搭計程車，其他往縣廳站坐單軌 (13-16 分鐘)。&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;18:30 - 19:15&lt;/td&gt;
&lt;td style=&quot;padding: 10px;&quot;&gt;&amp;nbsp;搭乘華航 CI2133 返回高雄小港。&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;


&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;💡 行程建議：&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;知念岬 vs 美國村： &lt;br /&gt;知念岬在東南角，美國村與永旺在中部。這天行程景點多，需要 10 人座包車接送。&lt;/li&gt;&lt;li&gt;關於「天使鬆餅」： &lt;br /&gt;瀨長島的幸福鬆餅（Happy Pancake）極熱門，建議事先網路預約，否則現場排隊會很久。&lt;/li&gt;&lt;li&gt;寶可夢中心： &lt;br /&gt;永旺來客夢的寶可夢中心很大可以慢慢逛，長輩可以帶到商場中間的大型水族箱區坐著休息。&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;💡 關於美麗海水族館 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;黑潮之海 (The Kuroshio Sea)：&lt;br /&gt;這是水族館的靈魂。巨大的壓克力水槽中，數隻巨大的鯨鯊與鬼蝠魟在藍色海水中緩緩游動。&lt;/li&gt;&lt;li&gt;海豚秀 (Okichan Theater)：&lt;br /&gt;位於水族館主體建築外的海邊。看表演時可以吹海風、看海景。提醒：海豚表演場地離水族館出口有一小段路，建議推輪椅前往較輕鬆。&lt;/li&gt;&lt;li&gt;觸摸池：&lt;br /&gt;可以嘗試觸摸海星和海參，是很有趣的互動。&lt;/li&gt;&lt;li&gt;水族館的「鯨鯊餵食秀」：&lt;br /&gt;通常在下午 15:00 和 17:00 各有一場。鯨鯊會直立起來進食，非常震撼，建議將行程調整在下午抵達，看完餵食秀再離開。&lt;br /&gt;&lt;/li&gt;&lt;li&gt;輪椅借用：&lt;br /&gt;美麗海水族館的入口處提供免費輪椅借用。即便長輩平時行走自如，但水族館園區很大，強烈建議借一台預備，當長輩逛到一半疲累時可以推著他們走，確保行程愉快不掃興。&lt;/li&gt;&lt;li&gt;避開正午陽光：&lt;br /&gt;5 月的沖繩陽光已很強。建議中午在古宇利島附近的景觀餐廳用餐（吹冷氣看海），下午最熱的時候留在水族館室內，這對長輩的體力保持最有幫助。&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirklcvH_qcii8vDhRMAabCqq_cPc3rVZOsF7xNEh1p1Sfm7b_8xAjwqoUSBU-7Z9LNBl2xuL8HbSCH2AJOdKYmp-uN5Qy-eMchForOYs9vEi1o8R-LnzqPAWHz_MdcOrlZt37Zb60At-MlL3qNStYw5GkBOTzN_EngDiqpnPyod6P4l8SOZL3f-e7bLY46/s1035/%E6%B2%96%E7%B9%A9%E7%BE%8E%E9%BA%97%E6%B5%B7%E6%B0%B4%E6%97%8F%E9%A4%A8-1jpg.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1035&quot; data-original-width=&quot;838&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirklcvH_qcii8vDhRMAabCqq_cPc3rVZOsF7xNEh1p1Sfm7b_8xAjwqoUSBU-7Z9LNBl2xuL8HbSCH2AJOdKYmp-uN5Qy-eMchForOYs9vEi1o8R-LnzqPAWHz_MdcOrlZt37Zb60At-MlL3qNStYw5GkBOTzN_EngDiqpnPyod6P4l8SOZL3f-e7bLY46/s320/%E6%B2%96%E7%B9%A9%E7%BE%8E%E9%BA%97%E6%B5%B7%E6%B0%B4%E6%97%8F%E9%A4%A8-1jpg.jpg&quot; width=&quot;259&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;💡 古宇利島 (Kouri Island) :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;古宇利島被稱為「神之島」或「戀之島」，從本島開車經過古宇利大橋 時，兩側的翡翠綠海色被譽為「古宇利藍」，是沖繩最美的公路景觀之一。&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;古宇利大橋：&lt;br /&gt;長約 2 公里。建議請包車司機在過橋時放慢車速，這段路不用走路，坐在車內就能欣賞絕佳海景。&lt;/li&gt;&lt;li&gt;古宇利海洋塔 (Kouri Ocean Tower)：&lt;br /&gt;這裡有自動駕駛的無人導覽小車，會載著全家人緩緩繞行山坡抵達塔頂。沿途會播放語音介紹，並能從高處俯瞰整座大橋，風景極其壯觀。&lt;/li&gt;&lt;li&gt;心形岩 (Heart Rock)：&lt;br /&gt;這位於島的另一端海灘。注意：前往沙灘的路徑較不平坦且多沙，長輩可在車上休息或在岸邊咖啡廳喝茶看海。&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEik9CajEscV1dbLKWCtbkmrruGekEMt3DGnQHiF1khHnvUjX3_thI1zqbltRrzqyrXA9nQB5N_jX2bLLrkhZ42yUhDacMq7alsvBuaiDSBu2L1Akb4xY5WjR6g1o4iPX_a-y80uea97Q9HFuj8JkOhrCy3H5C-gZiIJ0souztoDVPpz6r5_3wbUyDDN9Mqz/s1329/%E6%B2%96%E7%B9%A9%E5%8F%A4%E5%AE%87%E5%88%A9%E5%B3%B6-1.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;794&quot; data-original-width=&quot;1329&quot; height=&quot;191&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEik9CajEscV1dbLKWCtbkmrruGekEMt3DGnQHiF1khHnvUjX3_thI1zqbltRrzqyrXA9nQB5N_jX2bLLrkhZ42yUhDacMq7alsvBuaiDSBu2L1Akb4xY5WjR6g1o4iPX_a-y80uea97Q9HFuj8JkOhrCy3H5C-gZiIJ0souztoDVPpz6r5_3wbUyDDN9Mqz/s320/%E6%B2%96%E7%B9%A9%E5%8F%A4%E5%AE%87%E5%88%A9%E5%B3%B6-1.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;💡知念岬公園 (Cape Chinen Park) :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;位於沖繩本島東南部的南城市，是一個非常純粹、能讓人感受到大自然震撼的美麗景點。沿著公園的紅色扶手步道往下走一點點，可以拍到步道延伸進大海的感覺。建議停留 30 到 45 分鐘。這裡適合安靜地看海，當作早上的第一個景點，呼吸一下新鮮空氣。知念岬的風有時會比較大，即便 5 月天氣轉熱，建議帶件輕薄的防風外套。&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;270 度的無敵海景 :&lt;br /&gt;知念岬三面環海，視野極其開闊。從展望台望出去，可以看到湛藍的太平洋（天氣好時呈現漸層的藍色），還能遠眺被琉球人視為聖島的「久高島」。這裡與其他懸崖景點不同，從停車場到主要的景觀台和草地步道都非常平緩，推輪椅或讓長輩慢慢散步都非常輕鬆。即使長輩不想走太遠，只要下車站在入口附近的涼亭，就能看到極佳的海景，不需要長途跋涉。&lt;/li&gt;&lt;li&gt;滑翔傘愛好者的天堂 :&lt;br /&gt;這裡因為地形關係，常能看到彩色滑翔傘在空中飛翔，看著滑翔傘配上背景的海景，拍照也非常漂亮。&lt;/li&gt;&lt;li&gt;緊鄰「齋場御嶽」:&lt;br /&gt;知念岬公園就在琉球王國第一聖地「齋場御嶽」附近（走路約 5-10 分鐘）。對文化感興趣，可以順便去走走；但要注意「齋場御嶽」內有很多崎嶇的石頭路，對長輩來說比較吃力，建議留在知念岬公園看海、吹風即可。&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiywbmJkfq2t_7EjuRW-841no4VemvNlUsj9KCwOs_uxwHvyVrtNQgMeRh1gFrJi5b-mbME6kYSb-gRzOGGAhar02FYl9sRnwRhOgExNgCgvlpwNJrVfPSFjhcS17wPn7CurrUhTunpF7Dn2bghFJY1ZtDsnp0tlFqqpTS89JQ9sln7Csa-PRHDbkrRde2a/s2033/%E7%9F%A5%E5%BF%B5%E5%B2%AC%E5%85%AC%E5%9C%92-1.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1003&quot; data-original-width=&quot;2033&quot; height=&quot;158&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiywbmJkfq2t_7EjuRW-841no4VemvNlUsj9KCwOs_uxwHvyVrtNQgMeRh1gFrJi5b-mbME6kYSb-gRzOGGAhar02FYl9sRnwRhOgExNgCgvlpwNJrVfPSFjhcS17wPn7CurrUhTunpF7Dn2bghFJY1ZtDsnp0tlFqqpTS89JQ9sln7Csa-PRHDbkrRde2a/s320/%E7%9F%A5%E5%BF%B5%E5%B2%AC%E5%85%AC%E5%9C%92-1.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;💡瀨長島 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;瀨長島位於那霸機場南端，從那霸市區（嘉新酒店）往南開到瀨長島只要約 20 分鐘，接著再往北去美國村和永旺來客夢百貨，動線非常順。雖然它是一座島，但開車過橋只需要 1 分鐘，完全沒有「搭船」的負擔。因為離機場極近，這裡是看飛機起降的絕佳地點。瀨長島的白色建築是依山坡而建，樓梯較多。建議包車司機直接載到最上層放人，由上往下逛會輕鬆很多。&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Umikaji Terrace (瀨長島陽台) :&lt;br /&gt;這是像希臘聖托里尼一樣的純白色建築群，有非常多靠海的露天咖啡廳，可以坐在遮陽傘下吹著海風、看著藍色海洋和飛機。不需要爬高爬低，非常放鬆。也有很多文創小店和網美下午茶店（例如幸福鬆餅），非常適合拍照打卡。&lt;/li&gt;&lt;li&gt;足湯體驗： &lt;br /&gt;島上有免費的足湯（靠近琉球溫泉瀨長島飯店），如果走累了，在那裡泡腳看飛機也是一種享受。&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;💡美國村（American Village）:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;位於沖繩本島中部的北谷町，是一個將美式西海岸風情與沖繩海島氛圍完美融合的綜合型景區。這裡曾經是美軍基地，後來改建為商圈，是沖繩最具異國風情的地方，非常適合下午短暫停留。美國村範圍很大，建議請包車司機載到最靠近海邊的 「Depot Island Seaside」 區下車。那裡是最精華、色彩最鮮艷的區域 (寶可夢迷會發現建築的牆面上隱藏了很多「皮卡丘」彩繪)，也是步道起點，從那裡沿著海岸步道往南逛，走到落日海灘 (Sunset Beach) 大約只需 10-15 分鐘，那裡有一間 Aeon 北谷店（超市），可以買點當地零食或飲料，司機可以約在 Aeon 門口接。美國村離永旺來客夢百貨（AEON Mall Rycom）車程只需約 15-20 分鐘，建議下午在美國村拍照散步，傍晚再前往永旺吃晚餐與購物。&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;獨特的建築與視覺（拍照聖地）:&lt;br /&gt;這裡的建築色彩鮮艷，牆面充滿了各種美式風格的塗鴉與裝飾，隨處都能拍出極具風格的照片。&lt;/li&gt;&lt;li&gt;海濱步道： &lt;br /&gt;美國村緊鄰著北谷公園的落日海灘。這裡有一條非常平緩的海濱步道，可以一邊吹著海風，一邊散步看海，完全沒有階梯負擔。&lt;/li&gt;&lt;li&gt;美式潮流文創 :&lt;br /&gt;這裡有很多美式古著店、潮流服飾以及好拍的文創商店。此外，這裡也常能看到大型的模型或獨特的車輛裝飾。也有非常多舒適的戶外咖啡廳（如著名的 Pokébowls 或 ZHYVAGO COFFEE），走累了可以坐在靠海的位子看著來往的各國遊客與美軍家庭，也是一種很不一樣的觀察體驗。&lt;/li&gt;&lt;li&gt;落日夕陽： &lt;br /&gt;美國村的夕陽是全沖繩前三名的美景。在傍晚時分坐在岸邊看夕陽是非常浪漫的。傍晚在 Depot Island 的紅磚區有時會有街頭魔術表演。&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqs7X9fuqjj_xAUxsIbWAc3FjDmW_mv176rL8Jxb_nzB822vrNLZbiKaO9WyE0wF8gl4-XxkLCPb74v9jqh_2MxHHQnCQf_uX9ESCY7G-WJwaFsL4kjZrVCI-siLdphRPseXCKQ2pC_JkcFa5_mXWiXcW5Hi6rBm_V-JvFgIYaXF0s2HUZVnSKdPw6j4UA/s1827/%E6%B2%96%E7%B9%A9%E7%BE%8E%E5%9C%8B%E6%9D%91-1.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1131&quot; data-original-width=&quot;1827&quot; height=&quot;198&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqs7X9fuqjj_xAUxsIbWAc3FjDmW_mv176rL8Jxb_nzB822vrNLZbiKaO9WyE0wF8gl4-XxkLCPb74v9jqh_2MxHHQnCQf_uX9ESCY7G-WJwaFsL4kjZrVCI-siLdphRPseXCKQ2pC_JkcFa5_mXWiXcW5Hi6rBm_V-JvFgIYaXF0s2HUZVnSKdPw6j4UA/s320/%E6%B2%96%E7%B9%A9%E7%BE%8E%E5%9C%8B%E6%9D%91-1.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjA30zNEGnXmRhRIBj4E3aIUrGcamR_f6inxItGjNzw18fRUDLuXy2oweGI9Knevp-ooOSwXTp68V2Q-_ggQkLRanT2hPwTmGOSVC2yqS39_VDL5ccgtNcV3VcY14KiFniHjDlqJecVE3XHcSfdnnZSh17SHs4iC7hGutzgMtXH-txQScEPa97_TY1npvY4/s1927/%E6%B2%96%E7%B9%A9%E7%BE%8E%E5%9C%8B%E6%9D%91-2.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1157&quot; data-original-width=&quot;1927&quot; height=&quot;192&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjA30zNEGnXmRhRIBj4E3aIUrGcamR_f6inxItGjNzw18fRUDLuXy2oweGI9Knevp-ooOSwXTp68V2Q-_ggQkLRanT2hPwTmGOSVC2yqS39_VDL5ccgtNcV3VcY14KiFniHjDlqJecVE3XHcSfdnnZSh17SHs4iC7hGutzgMtXH-txQScEPa97_TY1npvY4/s320/%E6%B2%96%E7%B9%A9%E7%BE%8E%E5%9C%8B%E6%9D%91-2.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;💡永旺來客夢 (AEON Mall Rycom) :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;永旺夢樂城來客夢（AEON Mall Okinawa Rycom）是沖繩規模最大、設施最完善的購物中心，對您的家庭成員來說，這裡不僅是購物點，更是一個全齡化的休閒空間。從美國村到永旺來客夢約 20 分鐘車程.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;震撼的入口大水族箱（Rycom Aquarium）:&lt;br /&gt;一進大門，一樓大廳就有一個巨大的水族箱。雖然比不上美麗海水族館，但裡面有近千條色彩繽紛的熱帶魚和小型鯊魚。&lt;/li&gt;&lt;li&gt;寶可夢中心 (Pokémon Center)： &lt;br /&gt;位於四樓，是沖繩唯一的官方專賣店，有許多沖繩限定（如穿著花襯衫的皮卡丘）的商品。&lt;/li&gt;&lt;li&gt;潮流品牌： &lt;br /&gt;這裡聚集了 Uniqlo, GU, H&amp;amp;M, Old Navy 等各大品牌，還有日本當地的文創小物，非常適合年輕人。&lt;/li&gt;&lt;li&gt;AEON Style： &lt;br /&gt;二樓有大型超市和藥妝區，可以在這裡一次買齊要帶回台灣的沖繩零食、泡麵或是藥妝，不用在國際通辛苦提貨。&lt;/li&gt;&lt;li&gt;Rycom Sky Diner :&lt;br /&gt;五樓有非常多美食餐廳，從傳統琉球料理、日式豬排、壽司到美式牛排都有。五樓的戶外露台可以俯瞰中部的海景。如果天氣好，在這裡吃午餐或晚餐視野極佳。&lt;/li&gt;&lt;li&gt;美食街： &lt;br /&gt;三樓、四樓的美食廣場設有寬敞的無障礙座位區。&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgey9np5mPZobzRUaSZrsrxVvnGPZtD-mzGs9doVLKPtkO8WmPn5qQ40HvJ4z8DYgaq0zdutudTJBoisL8f296-qh5JpjRI67E-HBKQP21pTaueQXnpXzcN6luiq3lQyjItRvrJuOLJdVrnxw7hsHm4cZwh_lE5R66BgX2WvHZOPG10vnsl211L4KFCsYyq/s2182/%E6%B2%96%E7%B9%A9%E6%B0%B8%E6%97%BA%E4%BE%86%E5%AE%A2%E5%A4%A2-1.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;981&quot; data-original-width=&quot;2182&quot; height=&quot;144&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgey9np5mPZobzRUaSZrsrxVvnGPZtD-mzGs9doVLKPtkO8WmPn5qQ40HvJ4z8DYgaq0zdutudTJBoisL8f296-qh5JpjRI67E-HBKQP21pTaueQXnpXzcN6luiq3lQyjItRvrJuOLJdVrnxw7hsHm4cZwh_lE5R66BgX2WvHZOPG10vnsl211L4KFCsYyq/s320/%E6%B2%96%E7%B9%A9%E6%B0%B8%E6%97%BA%E4%BE%86%E5%AE%A2%E5%A4%A2-1.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;💡包車資訊 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;5/6 與 5/7 日兩天需包車, 8 個人需 10 人座的車.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;# &lt;a href=&quot;https://footprint-rentacar.com/&quot; target=&quot;_blank&quot;&gt;沖繩行腳&lt;/a&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;先掃 LINE 官方帳號 QR 碼 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdGtBdWY6oNhRpUF8WMuPRrXTF4wKOPN39WaDg50akxMVb26tESMUvH2G3RWfYIg8iDSQzHoq-yc6_Uk_kG6gNPSgGZHh_Ib-aEQtXmayFeS6Zel-bad4IKVO4AqMzlBDB63A0H5OtX0cst21w7cuj-O2nvhVE6b_kQNs-BjnIou4VwNqWNQJdMQaGaUxz/s497/%E6%B2%96%E7%B9%A9%E8%A1%8C%E8%85%B3-1.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;482&quot; data-original-width=&quot;497&quot; height=&quot;310&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdGtBdWY6oNhRpUF8WMuPRrXTF4wKOPN39WaDg50akxMVb26tESMUvH2G3RWfYIg8iDSQzHoq-yc6_Uk_kG6gNPSgGZHh_Ib-aEQtXmayFeS6Zel-bad4IKVO4AqMzlBDB63A0H5OtX0cst21w7cuj-O2nvhVE6b_kQNs-BjnIou4VwNqWNQJdMQaGaUxz/s320/%E6%B2%96%E7%B9%A9%E8%A1%8C%E8%85%B3-1.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;然後簡述包車日期與行程概要, 請其報價 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;&lt;i&gt;您好，我打算與親友去沖繩旅行，需要中文司機包車兩天，每天10小時，麻煩您報價，謝謝您。行程大致如下：&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;&lt;i&gt;沖繩&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;&lt;i&gt;1.乘客人數：8&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;&lt;i&gt;2.行李件數：4&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;&lt;i&gt;3.包車日期：5/6~5/7&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;&lt;i&gt;4.行程規劃：5/6 古宇利島，水族館；5/7 知念岬公園，瀬長島，美國村，永旺來客夢&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;&lt;i&gt;5.出發地點：國際通嘉新酒店&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;&lt;i&gt;6.結束地點：國際通嘉新酒店&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://yhhuang1966.blogspot.com/feeds/505050466771746572/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment/fullpage/post/4877487320781767952/505050466771746572' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/505050466771746572'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/505050466771746572'/><link rel='alternate' type='text/html' href='https://yhhuang1966.blogspot.com/2026/04/5558.html' title='沖繩之旅行程規劃 : 5/5~5/8'/><author><name>小狐狸事務所</name><uri>http://www.blogger.com/profile/09435160519044041137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirklcvH_qcii8vDhRMAabCqq_cPc3rVZOsF7xNEh1p1Sfm7b_8xAjwqoUSBU-7Z9LNBl2xuL8HbSCH2AJOdKYmp-uN5Qy-eMchForOYs9vEi1o8R-LnzqPAWHz_MdcOrlZt37Zb60At-MlL3qNStYw5GkBOTzN_EngDiqpnPyod6P4l8SOZL3f-e7bLY46/s72-c/%E6%B2%96%E7%B9%A9%E7%BE%8E%E9%BA%97%E6%B5%B7%E6%B0%B4%E6%97%8F%E9%A4%A8-1jpg.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4877487320781767952.post-7859785069258808687</id><published>2026-04-20T16:26:00.003+08:00</published><updated>2026-04-20T19:18:21.398+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="電腦"/><title type='text'>HDD/SSD 健康狀態檢測軟體 CrystalDiskInfo</title><content type='html'>&lt;div&gt;昨天成功地用 chdisk 救回 WD 5TB 硬碟後, 用&amp;nbsp;CrystalDiskInfo (CDI) 檢查了硬碟健康狀況為優良, 詢問 Gemini 得知它不僅能檢測硬碟, 還能檢測 SDD, 目前幾乎所有電腦都改用 SDD, 這種儲存跟汽車電瓶一樣, 接近壽命終點時說掛就掛, 而且內容就不回來, 所以要經常查看 SDD 健康狀態, 低於 50% 壽命時就要考慮更換或提高備份頻率, 以免後悔不及.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;以下是 Gemini 對 CDI 的說明與建議 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;===========================================================&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;p data-path-to-node=&quot;1&quot; style=&quot;font-family: &amp;quot;Google Sans Text&amp;quot;, sans-serif; line-height: 1.15; margin-top: 0px;&quot;&gt;對現代電腦來說，&lt;span style=&quot;background-color: #fcff01;&quot;&gt;用 CDI 監測 SSD 的健康狀況甚至比 HDD 更重要&lt;/span&gt;，因為 SSD 有明確的**「寫入壽命限制」**。&lt;/p&gt;&lt;p data-path-to-node=&quot;2&quot; style=&quot;font-family: &amp;quot;Google Sans Text&amp;quot;, sans-serif; line-height: 1.15; margin-top: 0px;&quot;&gt;不過，看 SSD 的報告時，重點指標與你剛才看那顆 5TB HDD 的邏輯不太一樣：&lt;/p&gt;&lt;h3 data-path-to-node=&quot;3&quot; style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, sans-serif; line-height: 1.15; margin-top: 0px;&quot;&gt;1. SSD 檢測的黃金指標：剩餘壽命&lt;/h3&gt;&lt;p data-path-to-node=&quot;4&quot; style=&quot;font-family: &amp;quot;Google Sans Text&amp;quot;, sans-serif; line-height: 1.15; margin-top: 0px;&quot;&gt;在 SSD 的報告中，最上方大大的「健康狀態」通常會直接顯示一個百分比，例如 &lt;b data-index-in-node=&quot;39&quot; data-path-to-node=&quot;4&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;「良好 98%」&lt;/b&gt;。&lt;/p&gt;&lt;ul data-path-to-node=&quot;5&quot; style=&quot;font-family: &amp;quot;Google Sans Text&amp;quot;, sans-serif; line-height: 1.15; margin-top: 0px; padding-inline-start: 32px;&quot;&gt;&lt;li style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;p data-path-to-node=&quot;5,0,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;這是什麼：&lt;/b&gt; 這是根據 SSD 廠商定義的 &lt;b data-index-in-node=&quot;21&quot; data-path-to-node=&quot;5,0,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;TBW (Total Bytes Written，總寫入量)&lt;/b&gt; 估算出來的。&lt;/p&gt;&lt;/li&gt;&lt;li style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;p data-path-to-node=&quot;5,1,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;怎麼看：&lt;/b&gt; * &lt;b data-index-in-node=&quot;7&quot; data-path-to-node=&quot;5,1,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;100% - 90%：&lt;/b&gt; 像新的一樣。&lt;/p&gt;&lt;ul data-path-to-node=&quot;5,1,1&quot; style=&quot;line-height: 1.15; margin-top: 0px; padding-inline-start: 32px;&quot;&gt;&lt;li style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;p data-path-to-node=&quot;5,1,1,0,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,1,0,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;低於 50%：&lt;/b&gt; 雖然還能用，但建議開始尋找下一顆硬碟。&lt;/p&gt;&lt;/li&gt;&lt;li style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;p data-path-to-node=&quot;5,1,1,1,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,1,1,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;低於 10% 或 0%：&lt;/b&gt; 隨時可能進入「唯讀模式」或直接掛掉，必須立刻更換。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 data-path-to-node=&quot;6&quot; style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, sans-serif; line-height: 1.15; margin-top: 0px;&quot;&gt;2. 重點關注項目（對比 HDD）&lt;/h3&gt;&lt;p data-path-to-node=&quot;7&quot; style=&quot;font-family: &amp;quot;Google Sans Text&amp;quot;, sans-serif; line-height: 1.15; margin-top: 0px;&quot;&gt;打開 CDI 看你的 SSD 時，請盯住這兩項：&lt;/p&gt;&lt;ul data-path-to-node=&quot;8&quot; style=&quot;font-family: &amp;quot;Google Sans Text&amp;quot;, sans-serif; line-height: 1.15; margin-top: 0px; padding-inline-start: 32px;&quot;&gt;&lt;li style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;p data-path-to-node=&quot;8,0,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;03 剩餘壽命 (Percentage Used)：&lt;/b&gt; 顯示已消耗的設計壽命。&lt;/p&gt;&lt;/li&gt;&lt;li style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;p data-path-to-node=&quot;8,1,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;F1 總計主機寫入量 (Total Host Writes)：&lt;/b&gt; 這告訴你這顆 SSD 自出廠以來到底寫入了多少資料（通常以 GB 或 TB 為單位）。對於開發者來說，如果你頻繁編譯大型專案或跑大量 Log，這個數字會跳很快。&lt;/p&gt;&lt;/li&gt;&lt;li style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;p data-path-to-node=&quot;8,2,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,2,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;01 介面 CRC 錯誤計數：&lt;/b&gt; 如果這個數值不是 0，通常代表你的 &lt;b data-index-in-node=&quot;34&quot; data-path-to-node=&quot;8,2,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;SATA 線&lt;/b&gt;或是 &lt;b data-index-in-node=&quot;43&quot; data-path-to-node=&quot;8,2,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;M.2 插槽&lt;/b&gt;接觸不良，而不是 SSD 本身壞了。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr data-path-to-node=&quot;9&quot; style=&quot;font-family: &amp;quot;Google Sans Text&amp;quot;, sans-serif; line-height: 1.15; margin-top: 0px;&quot; /&gt;&lt;h3 data-path-to-node=&quot;10&quot; style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, sans-serif; line-height: 1.15; margin-top: 0px;&quot;&gt;3. SSD 與 HDD 在 CDI 中的差異&lt;/h3&gt;&lt;table data-path-to-node=&quot;11&quot; style=&quot;font-family: &amp;quot;Google Sans Text&amp;quot;, sans-serif; line-height: 1.15; margin-bottom: 32px; margin-top: 0px;&quot;&gt;&lt;thead style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;tr style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;td style=&quot;border: 1px solid; line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;strong style=&quot;line-height: 1.15; margin-bottom: 0px; margin-top: 0px;&quot;&gt;項目&lt;/strong&gt;&lt;/td&gt;&lt;td style=&quot;border: 1px solid; line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;strong style=&quot;line-height: 1.15; margin-bottom: 0px; margin-top: 0px;&quot;&gt;HDD (傳統硬碟)&lt;/strong&gt;&lt;/td&gt;&lt;td style=&quot;border: 1px solid; line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;strong style=&quot;line-height: 1.15; margin-bottom: 0px; margin-top: 0px;&quot;&gt;SSD (固態硬碟)&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;tr style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;td style=&quot;border: 1px solid; line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;span data-path-to-node=&quot;11,1,0,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,1,0,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;主要風險&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;border: 1px solid; line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;span data-path-to-node=&quot;11,1,1,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;物理壞軌、機械磨損&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;border: 1px solid; line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;span data-path-to-node=&quot;11,1,2,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;閃存顆粒寫入次數耗盡&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;td style=&quot;border: 1px solid; line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;span data-path-to-node=&quot;11,2,0,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,2,0,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;關鍵指標&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;border: 1px solid; line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;span data-path-to-node=&quot;11,2,1,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;05 (重定位磁區)、C5 (等候重定)&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;border: 1px solid; line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;span data-path-to-node=&quot;11,2,2,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;03 (剩餘壽命百分比)&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;td style=&quot;border: 1px solid; line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;span data-path-to-node=&quot;11,3,0,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,3,0,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;溫度&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;border: 1px solid; line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;span data-path-to-node=&quot;11,3,1,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;建議 50°C 以下&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;border: 1px solid; line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;span data-path-to-node=&quot;11,3,2,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;較高，NVMe 飆到 60-70°C 算常見&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;td style=&quot;border: 1px solid; line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;span data-path-to-node=&quot;11,4,0,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,4,0,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;異常徵兆&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;border: 1px solid; line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;span data-path-to-node=&quot;11,4,1,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;異音、讀取極慢、chkdsk 噴錯誤&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;border: 1px solid; line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;span data-path-to-node=&quot;11,4,2,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;突然偵測不到、檔案消失、藍屏&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;hr data-path-to-node=&quot;12&quot; style=&quot;font-family: &amp;quot;Google Sans Text&amp;quot;, sans-serif; line-height: 1.15; margin-top: 0px;&quot; /&gt;&lt;h3 data-path-to-node=&quot;13&quot; style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, sans-serif; line-height: 1.15; margin-top: 0px;&quot;&gt;4. 檢查建議&lt;/h3&gt;&lt;p data-path-to-node=&quot;14&quot; style=&quot;font-family: &amp;quot;Google Sans Text&amp;quot;, sans-serif; line-height: 1.15; margin-top: 0px;&quot;&gt;切換到你的 &lt;b data-index-in-node=&quot;22&quot; data-path-to-node=&quot;14&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;C 槽（通常是 SSD）&lt;/b&gt; 看一下：&lt;/p&gt;&lt;ol data-path-to-node=&quot;15&quot; start=&quot;1&quot; style=&quot;font-family: &amp;quot;Google Sans Text&amp;quot;, sans-serif; line-height: 1.15; margin-top: 0px; padding-inline-start: 32px;&quot;&gt;&lt;li style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;p data-path-to-node=&quot;15,0,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;15,0,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;檢查百分比：&lt;/b&gt; 確認你的系統碟還有多少壽命。&lt;/p&gt;&lt;/li&gt;&lt;li style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;p data-path-to-node=&quot;15,1,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;15,1,0&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;檢查傳輸模式：&lt;/b&gt; 確認它是否跑在 &lt;code data-index-in-node=&quot;16&quot; data-path-to-node=&quot;15,1,0&quot; style=&quot;font-family: &amp;quot;Google Sans Text&amp;quot;, sans-serif; line-height: 1.15; margin-top: 0px;&quot;&gt;PCIe 4.0 x4&lt;/code&gt; 或是你主機板支援的最高速度（如果你是用高效能 NVMe SSD）。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p data-path-to-node=&quot;16&quot; style=&quot;font-family: &amp;quot;Google Sans Text&amp;quot;, sans-serif; line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16&quot; style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;特別提醒：&lt;/b&gt;&lt;/p&gt;&lt;p data-path-to-node=&quot;16&quot; style=&quot;font-family: &amp;quot;Google Sans Text&amp;quot;, sans-serif; line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;span style=&quot;background-color: #fcff01;&quot;&gt;SSD 如果顯示「黃燈（警告）」，通常不像 HDD 那樣還能透過 &lt;code data-index-in-node=&quot;39&quot; data-path-to-node=&quot;16&quot; style=&quot;font-family: &amp;quot;Google Sans Text&amp;quot;, sans-serif; line-height: 1.15; margin-top: 0px;&quot;&gt;chkdsk&lt;/code&gt; 救回「邏輯損壞」。SSD 一旦顆粒老化出現錯誤，通常是不可逆的，**「備份」**是唯一的救贖&lt;/span&gt;。&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;下面是我的 LG Gram 筆電兩顆 SSD 檢測結果 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;一. C 碟 : 三星 1TB Samsung PM9A1 (MZVL21T0HCLR)&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiU6uYl5lm_UP5oir8rvvFBgmQv41XJFpMYd8RQ-5keh0TGu65GPmrafJSDMoIc7-mY3jLoOQy6x7-rmss8xbAQmO7cCeGebaXv5hMSiQjQTw4CqPKGaLz9LNQrswuuLTWvdNd7DbxXPZQKHKlRN_0swibeXbtFHCf1WxNrHVfgzEXT3wrDkAfjDGqpTRV9/s1207/LG_GRAM_SSD_C%E7%A2%9F%E6%AA%A2%E6%B8%AC%E7%B5%90%E6%9E%9C.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1207&quot; data-original-width=&quot;1002&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiU6uYl5lm_UP5oir8rvvFBgmQv41XJFpMYd8RQ-5keh0TGu65GPmrafJSDMoIc7-mY3jLoOQy6x7-rmss8xbAQmO7cCeGebaXv5hMSiQjQTw4CqPKGaLz9LNQrswuuLTWvdNd7DbxXPZQKHKlRN_0swibeXbtFHCf1WxNrHVfgzEXT3wrDkAfjDGqpTRV9/s320/LG_GRAM_SSD_C%E7%A2%9F%E6%AA%A2%E6%B8%AC%E7%B5%90%E6%9E%9C.jpg&quot; width=&quot;266&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Gemini 的 SSD 健康檢查報告分析：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;h3 data-path-to-node=&quot;2&quot;&gt;1. 核心健康指標&lt;/h3&gt;&lt;ul data-path-to-node=&quot;3&quot;&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,0,0&quot;&gt;健康狀態：良好 98%&lt;/b&gt;。&lt;/p&gt;&lt;ul data-path-to-node=&quot;3,0,1&quot;&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;3,0,1,0,0&quot;&gt;對於已經使用 &lt;b data-index-in-node=&quot;7&quot; data-path-to-node=&quot;3,0,1,0,0&quot;&gt;5323 小時&lt;/b&gt;（約 7 個多月不間斷運作）的系統碟來說，只消耗了 2% 的壽命，這代表它的耐用度非常高。依照這個速度，這顆 SSD 要達到壽命臨界值可能還需要十年以上。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;3,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,1,0&quot;&gt;SSD 總寫入量：29,768 GB (約 29 TB)&lt;/b&gt;。&lt;/p&gt;&lt;ul data-path-to-node=&quot;3,1,1&quot;&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;3,1,1,0,0&quot;&gt;這反映了你作為軟體工程師的工作量。頻繁的編譯、下載與檔案操作都會累積寫入量。29 TB 雖然聽起來很多，但這顆 SSD 的設計寫入壽命通常在 600 TBW 以上，目前僅用了不到 5%。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 data-path-to-node=&quot;4&quot;&gt;2. 值得注意的細節&lt;/h3&gt;&lt;ul data-path-to-node=&quot;5&quot;&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;5,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot;&gt;傳輸模式：PCIe 4.0 x4 | PCIe 4.0 x4&lt;/b&gt;。&lt;/p&gt;&lt;ul data-path-to-node=&quot;5,0,1&quot;&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;5,0,1,0,0&quot;&gt;這代表你的硬碟正確地運行在最高頻寬下，沒有因為插槽或設定問題被降速。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;5,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;溫度：39°C&lt;/b&gt;。&lt;/p&gt;&lt;ul data-path-to-node=&quot;5,1,1&quot;&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;5,1,1,0,0&quot;&gt;對於 NVMe SSD 來說，這個溫度非常低且理想。這說明 LG Gram 的內部散熱設計（或這顆 Samsung SSD 的發熱控制）做得很好，在高負載運作時也不容易發生因過熱而降速（Thermal Throttling）的情況。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;5,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,2,0&quot;&gt;不安全關機次數：33 次 (0x21)&lt;/b&gt;。&lt;/p&gt;&lt;ul data-path-to-node=&quot;5,2,1&quot;&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;5,2,1,0,0&quot;&gt;這通常發生在電腦當機、強制長按電源鍵關機，或是電池完全沒電自動關機時。雖然次數不多，但 SSD 對斷電比 HDD 敏感，建議盡量避免在檔案寫入時強制關機。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr data-path-to-node=&quot;6&quot; /&gt;&lt;h3 data-path-to-node=&quot;7&quot;&gt;3. 對於這顆 SSD 的維護建議&lt;/h3&gt;&lt;p data-path-to-node=&quot;8&quot;&gt;注意, &lt;b data-index-in-node=&quot;20&quot; data-path-to-node=&quot;8&quot;&gt;SSD 維護上&lt;/b&gt;做法與 HDD 完全相反：&lt;/p&gt;&lt;ol data-path-to-node=&quot;9&quot; start=&quot;1&quot;&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;9,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,0&quot;&gt;絕對不要執行「磁碟重組」：&lt;/b&gt;
SSD 內部有自己的「磨損均衡 (Wear Leveling)」機制，會自動安排資料存放位置以延長壽命。手動重組只會白白浪費寫入次數，對速度毫無幫助。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;9,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,1,0&quot;&gt;確保開啟 TRIM 指令：&lt;/b&gt;
Windows 10/11 預設會開啟。它會告訴 SSD 哪些區塊的資料已刪除可以回收。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;9,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,2,0&quot;&gt;保留 15% 以上空間：&lt;/b&gt;
雖然 1TB 很大，但 SSD 如果塞得太滿（超過 90%），寫入放大效應會增加，導致壽命消耗變快且速度變慢。建議 C 碟隨時保留約 150GB 左右的空閒空間。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;9,3,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,3,0&quot;&gt;定期用 CDI 檢查「03 可用備用 (Available Spare)」：&lt;/b&gt;
如果這個數值（目前是 100%）開始下降，才代表 SSD 真的開始出現損壞的記憶體顆粒。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p data-path-to-node=&quot;10&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10&quot;&gt;總結：&lt;/b&gt;
這是一顆頂級的系統碟，目前正處於巔峰狀態，無需任何特殊整理。你那顆 WD 5TB HDD 比較需要關注「整理」，而這顆 Samsung SSD 只要維持正常使用、不要塞太滿即可。&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;二. D 碟 WD Blue SN570 (1TB) :&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZ8c57F05dZHeFEqSzLFGt6ICFRYN1yvAaHBiL_iSZM4_oYHg3fMbJU1yPKbEZqSQduuvKTX9Uj6eocQw-PuLSgCliVOvPoMwz8RiCoZp5IpY9sq7RfJBXTWLL_7evwpNlnT-sBGAWrimGXwqFT7VS3suKNc1UM07H4Q9wkcJeLGlpqEt6mvcCmXum9yEF/s1206/LG_GRAM_SSD_D%E7%A2%9F%E6%AA%A2%E6%B8%AC%E7%B5%90%E6%9E%9C.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1206&quot; data-original-width=&quot;1012&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZ8c57F05dZHeFEqSzLFGt6ICFRYN1yvAaHBiL_iSZM4_oYHg3fMbJU1yPKbEZqSQduuvKTX9Uj6eocQw-PuLSgCliVOvPoMwz8RiCoZp5IpY9sq7RfJBXTWLL_7evwpNlnT-sBGAWrimGXwqFT7VS3suKNc1UM07H4Q9wkcJeLGlpqEt6mvcCmXum9yEF/s320/LG_GRAM_SSD_D%E7%A2%9F%E6%AA%A2%E6%B8%AC%E7%B5%90%E6%9E%9C.jpg&quot; width=&quot;269&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Gemini 的 SSD 健康檢查報告分析：&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;p data-path-to-node=&quot;0&quot; style=&quot;font-family: &amp;quot;Google Sans Text&amp;quot;, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;這顆 &lt;b data-index-in-node=&quot;7&quot; data-path-to-node=&quot;0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;WD Blue SN570&lt;/b&gt; 簡直就是「新車剛過磨合期」的狀態！雖然你是後來擴充的，但從數據上看，這顆硬碟幾乎沒怎麼被「操」到。&lt;/p&gt;&lt;p data-path-to-node=&quot;1&quot; style=&quot;font-family: &amp;quot;Google Sans Text&amp;quot;, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;以下是針對這顆擴充碟的分析：&lt;/p&gt;&lt;h3 data-path-to-node=&quot;2&quot; style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;1. 核心健康指標&lt;/h3&gt;&lt;ul data-path-to-node=&quot;3&quot; style=&quot;font-family: &amp;quot;Google Sans Text&amp;quot;, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important; padding-inline-start: 32px;&quot;&gt;&lt;li style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;3,0,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,0,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;健康狀態：良好 100%&lt;/b&gt;。&lt;/p&gt;&lt;ul data-path-to-node=&quot;3,0,1&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important; padding-inline-start: 32px;&quot;&gt;&lt;li style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;3,0,1,0,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;這非常罕見，代表這顆 SSD 的設計壽命完全還沒被消耗。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;3,1,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,1,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;已用時間總計：82 小時&lt;/b&gt;。&lt;/p&gt;&lt;ul data-path-to-node=&quot;3,1,1&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important; padding-inline-start: 32px;&quot;&gt;&lt;li style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;3,1,1,0,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;這真的非常新！對比你 C 碟的 5323 小時，這顆 D 碟基本上才剛開始運作不久。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;3,2,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,2,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;SSD 總寫入量：137 GB&lt;/b&gt;。&lt;/p&gt;&lt;ul data-path-to-node=&quot;3,2,1&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important; padding-inline-start: 32px;&quot;&gt;&lt;li style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;3,2,1,0,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;這個寫入量極低。以 SN570 1TB 版本高達 &lt;b data-index-in-node=&quot;25&quot; data-path-to-node=&quot;3,2,1,0,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;600 TBW&lt;/b&gt; 的耐用度來看，你目前只用了 &lt;span class=&quot;math-inline&quot; data-index-in-node=&quot;47&quot; data-math=&quot;0.02\%&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;$0.02\%$&lt;/span&gt; 的壽命。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 data-path-to-node=&quot;4&quot; style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;2. 性能與規格分析&lt;/h3&gt;&lt;ul data-path-to-node=&quot;5&quot; style=&quot;font-family: &amp;quot;Google Sans Text&amp;quot;, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important; padding-inline-start: 32px;&quot;&gt;&lt;li style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;5,0,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;傳輸模式：PCIe 3.0 x4&lt;/b&gt;。&lt;/p&gt;&lt;ul data-path-to-node=&quot;5,0,1&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important; padding-inline-start: 32px;&quot;&gt;&lt;li style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;5,0,1,0,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;這是正確的。SN570 本身就是 PCIe 3.0 的規格，所以它跑在 3.0 x4 是發揮全速。雖然你的筆電插槽可能支援到 4.0（如 C 碟那顆），但這顆 D 碟會穩定運行在 3.0 的最高速。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;5,1,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;溫度：33°C&lt;/b&gt;。&lt;/p&gt;&lt;ul data-path-to-node=&quot;5,1,1&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important; padding-inline-start: 32px;&quot;&gt;&lt;li style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;5,1,1,0,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;比 C 碟還要低，這是一個非常涼爽的操作溫度，對電子零件的長壽非常有幫助。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr data-path-to-node=&quot;6&quot; style=&quot;font-family: &amp;quot;Google Sans Text&amp;quot;, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot; /&gt;&lt;h3 data-path-to-node=&quot;7&quot; style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;3. 為什麼這顆硬碟特別適合當「資料存放區」？&lt;/h3&gt;&lt;p data-path-to-node=&quot;8&quot; style=&quot;font-family: &amp;quot;Google Sans Text&amp;quot;, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;SN570 是屬於「無快取 (DRAM-less)」但性能平衡的 SSD。這類硬碟的特性是：&lt;/p&gt;&lt;ul data-path-to-node=&quot;9&quot; style=&quot;font-family: &amp;quot;Google Sans Text&amp;quot;, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important; padding-inline-start: 32px;&quot;&gt;&lt;li style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;9,0,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;省電：&lt;/b&gt; 對於 LG Gram 這種強調續航的筆電非常友善。&lt;/p&gt;&lt;/li&gt;&lt;li style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;9,1,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,1,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;發熱低：&lt;/b&gt; 很適合放在筆電的第二個擴充槽。&lt;/p&gt;&lt;/li&gt;&lt;li style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;9,2,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,2,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;穩定：&lt;/b&gt; 非常適合作為存放專案代碼、文件或照片的副碟。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 data-path-to-node=&quot;10&quot; style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;4. 給 Tony 的維護建議&lt;/h3&gt;&lt;p data-path-to-node=&quot;11&quot; style=&quot;font-family: &amp;quot;Google Sans Text&amp;quot;, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;這顆硬碟目前完全不需要任何維護。但既然它是 D 碟，我建議你可以利用它來&lt;b data-index-in-node=&quot;36&quot; data-path-to-node=&quot;11&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;分擔 C 碟的壓力&lt;/b&gt;：&lt;/p&gt;&lt;ul data-path-to-node=&quot;12&quot; style=&quot;font-family: &amp;quot;Google Sans Text&amp;quot;, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important; padding-inline-start: 32px;&quot;&gt;&lt;li style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;12,0,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,0,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;存放開發專案：&lt;/b&gt; 既然你的工作涉及 Python 和金融資料分析（kbar 庫等），可以把這些專案放在 D 碟。&lt;/p&gt;&lt;/li&gt;&lt;li style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;12,1,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;虛擬機或 Docker：&lt;/b&gt; 如果有使用這些工具，將它們的映像檔放在 D 碟，可以避免 C 碟過快消耗壽命，且這顆 SN570 的速度對於這類應用綽綽有餘。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 data-path-to-node=&quot;13&quot; style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;總結&lt;/h3&gt;&lt;p data-path-to-node=&quot;14&quot; style=&quot;font-family: &amp;quot;Google Sans Text&amp;quot;, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;你這台 LG Gram 的硬碟配置非常健康：&lt;/p&gt;&lt;ol data-path-to-node=&quot;15&quot; start=&quot;1&quot; style=&quot;font-family: &amp;quot;Google Sans Text&amp;quot;, sans-serif !important; line-height: 1.15 !important; margin-top: 0px !important; padding-inline-start: 32px;&quot;&gt;&lt;li style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;15,0,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;15,0,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;C 碟 (Samsung)：&lt;/b&gt; 強大的系統碟，負責作業系統與軟體運行。&lt;/p&gt;&lt;/li&gt;&lt;li style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;p data-path-to-node=&quot;15,1,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;15,1,0&quot; style=&quot;line-height: 1.15 !important; margin-top: 0px !important;&quot;&gt;D 碟 (WD Blue)：&lt;/b&gt; 極新的擴充碟，適合存放長期資料與專案。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://yhhuang1966.blogspot.com/feeds/7859785069258808687/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment/fullpage/post/4877487320781767952/7859785069258808687' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/7859785069258808687'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/7859785069258808687'/><link rel='alternate' type='text/html' href='https://yhhuang1966.blogspot.com/2026/04/hddssd-crystaldiskinfo.html' title='HDD/SSD 健康狀態檢測軟體 CrystalDiskInfo'/><author><name>小狐狸事務所</name><uri>http://www.blogger.com/profile/09435160519044041137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiU6uYl5lm_UP5oir8rvvFBgmQv41XJFpMYd8RQ-5keh0TGu65GPmrafJSDMoIc7-mY3jLoOQy6x7-rmss8xbAQmO7cCeGebaXv5hMSiQjQTw4CqPKGaLz9LNQrswuuLTWvdNd7DbxXPZQKHKlRN_0swibeXbtFHCf1WxNrHVfgzEXT3wrDkAfjDGqpTRV9/s72-c/LG_GRAM_SSD_C%E7%A2%9F%E6%AA%A2%E6%B8%AC%E7%B5%90%E6%9E%9C.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4877487320781767952.post-2958842096396053002</id><published>2026-04-20T10:57:00.000+08:00</published><updated>2026-04-20T10:57:05.075+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="電腦"/><title type='text'>WD 5TB 隨身硬碟無法讀取問題</title><content type='html'>&lt;div&gt;週六下午在上緯育的 SSD (OpenSec) 線上課程時, 插在 USB 的 WD 5TB 隨身硬碟突然擱登兩聲離線, 我以為是碰到插頭接觸不良而已. 週日晚上要複製檔案時接上 USB, 有讀到驅動 G 碟, 但無法讀取檔案目錄結構 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3uC5yfCLyoR8aPBSyKZX8wnk7ifN6_aMk2YD0iFesLCtRdH49nk9fRqxuzRtBhcafPl7ajWQ_lMkacA2eqjjoSv-KW_8DX2pD3Ebqf_qaraYjQZqC-125ZmNd2T9LU7ixxf4aNfzH5TgwewrbyGfv1PuIJXIe_fzrQalHYnGxFCw9sfF2eCzzjgYsSW-k/s612/WD-5TB-%E7%84%A1%E6%B3%95%E8%AE%80%E5%8F%96-1.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;313&quot; data-original-width=&quot;612&quot; height=&quot;164&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3uC5yfCLyoR8aPBSyKZX8wnk7ifN6_aMk2YD0iFesLCtRdH49nk9fRqxuzRtBhcafPl7ajWQ_lMkacA2eqjjoSv-KW_8DX2pD3Ebqf_qaraYjQZqC-125ZmNd2T9LU7ixxf4aNfzH5TgwewrbyGfv1PuIJXIe_fzrQalHYnGxFCw9sfF2eCzzjgYsSW-k/s320/WD-5TB-%E7%84%A1%E6%B3%95%E8%AE%80%E5%8F%96-1.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;週日晚上回到高雄後, 拿另外一條 USB 線連接筆電還是一樣, 於是問 Gemini 該怎麼辦, 它建議我先以管理員身分開啟命令提示字元視窗, 然後用 chdisk 檢查修復檔案目錄連結 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;C:\Users\tony1&amp;gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;chkdsk G: /f&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;檔案系統的類型是 NTFS。&lt;/div&gt;&lt;div&gt;磁碟區標籤為 Elements。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;階段 1: 正在檢查基本檔案系統結構...&lt;/div&gt;&lt;div&gt;&amp;nbsp; 已經處理 249088 個檔案記錄。&lt;/div&gt;&lt;div&gt;檔案驗證完成。&lt;/div&gt;&lt;div&gt;階段期間 (檔案記錄驗證): 22.14 秒。&lt;/div&gt;&lt;div&gt;&amp;nbsp; 已經處理 47 個大型檔案記錄。&lt;/div&gt;&lt;div&gt;階段期間 (孤立檔案記錄修復): 0.52 毫秒。&lt;/div&gt;&lt;div&gt;&amp;nbsp; 已經處理 0 個損毀的檔案記錄。&lt;/div&gt;&lt;div&gt;階段期間 (不正確檔案記錄檢查): 0.98 毫秒。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;階段 2: 正在檢查檔案名稱連結...&lt;/div&gt;&lt;div&gt;正在修正檔案 5 中索引 $I30 的錯誤。&lt;/div&gt;&lt;div&gt;CHKDSK 在檔案 5 之索引 $I30 的點陣圖中發現可用空間被標示為已配置。&lt;/div&gt;&lt;div&gt;正在為檔案 5 中的索引 $I30 進行排序。&lt;/div&gt;&lt;div&gt;&amp;nbsp; 已經處理 2386 個重新分析記錄。&lt;/div&gt;&lt;div&gt;&amp;nbsp; 已經處理 276974 個索引項目。&lt;/div&gt;&lt;div&gt;索引驗證完成。&lt;/div&gt;&lt;div&gt;階段期間 (索引驗證): 3.86 分鐘。&lt;/div&gt;&lt;div&gt;CHKDSK 正在掃描未編製索引的檔案以重新連線到其原始目錄。&lt;/div&gt;&lt;div&gt;正在將孤立的檔案 $MFT (0) 復原成目錄檔案 5。&lt;/div&gt;&lt;div&gt;正在將孤立的檔案 $MFTMirr (1) 復原成目錄檔案 5。&lt;/div&gt;&lt;div&gt;正在將孤立的檔案 $LogFile (2) 復原成目錄檔案 5。&lt;/div&gt;&lt;div&gt;正在將孤立的檔案 $Volume (3) 復原成目錄檔案 5。&lt;/div&gt;&lt;div&gt;正在將孤立的檔案 $AttrDef (4) 復原成目錄檔案 5。&lt;/div&gt;&lt;div&gt;正在將孤立的檔案 . (5) 復原成目錄檔案 5。&lt;/div&gt;&lt;div&gt;正在將孤立的檔案 $Bitmap (6) 復原成目錄檔案 5。&lt;/div&gt;&lt;div&gt;正在將孤立的檔案 $Boot (7) 復原成目錄檔案 5。&lt;/div&gt;&lt;div&gt;正在將孤立的檔案 $BadClus (8) 復原成目錄檔案 5。&lt;/div&gt;&lt;div&gt;正在將孤立的檔案 $Secure (9) 復原成目錄檔案 5。&lt;/div&gt;&lt;div&gt;正在略過關於復原孤立項的後續訊息。&lt;/div&gt;&lt;div&gt;&amp;nbsp; 已經掃描 51 個未編製索引的檔案。&lt;/div&gt;&lt;div&gt;&amp;nbsp; 51 個未編製索引的檔案已復原到原始目錄。&lt;/div&gt;&lt;div&gt;階段期間 (孤立重新連接): 2.44 秒。&lt;/div&gt;&lt;div&gt;&amp;nbsp; 0 個未編製索引的檔案已復原到 [Lost and Found]。&lt;/div&gt;&lt;div&gt;階段期間 (孤兒復原丟失與發現): 36.93 毫秒。&lt;/div&gt;&lt;div&gt;&amp;nbsp; 已經處理 2386 個重新分析記錄。&lt;/div&gt;&lt;div&gt;階段期間 (重新剖析點與物件識別碼驗證): 14.32 毫秒。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;階段 3: 正在檢查安全性描述元...&lt;/div&gt;&lt;div&gt;安全性描述元驗證完成。&lt;/div&gt;&lt;div&gt;階段期間 (安全性描述項驗證): 50.83 毫秒。&lt;/div&gt;&lt;div&gt;&amp;nbsp; 已經處理 13943 個資料檔案。&lt;/div&gt;&lt;div&gt;階段期間 (資料屬性驗證): 1.80 毫秒。&lt;/div&gt;&lt;div&gt;正在修正主檔案表格 (MFT) 鏡像的錯誤。&lt;/div&gt;&lt;div&gt;正在修正磁碟區點陣圖的錯誤。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Windows 已修正檔案系統。&lt;/div&gt;&lt;div&gt;不需要進一步的動作。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;磁碟空間總計&amp;nbsp; &amp;nbsp; 4769272 MB。&lt;/div&gt;&lt;div&gt;234806 個檔案共&amp;nbsp; &amp;nbsp; 4371444 MB。&lt;/div&gt;&lt;div&gt;13945 個索引共&amp;nbsp; &amp;nbsp; &amp;nbsp; 71928 KB。&lt;/div&gt;&lt;div&gt;損壞的磁區共&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 0 KB。&lt;/div&gt;&lt;div&gt;系統使用空間&amp;nbsp; &amp;nbsp; &amp;nbsp;464123 KB。&lt;/div&gt;&lt;div&gt;記錄檔共佔用&amp;nbsp; &amp;nbsp; &amp;nbsp; 65536 KB。&lt;/div&gt;&lt;div&gt;磁碟可用空間&amp;nbsp; 406840116 KB。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;每個配置單位有&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;4096 個位元組。&lt;/div&gt;&lt;div&gt;磁碟上共有 1220933887 個配置單位。&lt;/div&gt;&lt;div&gt;磁碟上有&amp;nbsp; 101710029 個可用的配置單位。&lt;/div&gt;&lt;div&gt;總持續時間: 4.28 分鐘 (256882 毫秒)。&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;----------------------------------------&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;我把以上結果貼給 Gemini 評估, 結果如下 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;-----------------------------------------------------------&lt;/div&gt;&lt;div&gt;這代表你的 5TB 硬碟硬體暫時看起來是健康的，這次純粹是軟體層面的「索引混亂」。&lt;/div&gt;&lt;div&gt;&lt;p data-path-to-node=&quot;1&quot;&gt;從你的 Log 來看，這次修復了幾個關鍵問題：&lt;/p&gt;&lt;h3 data-path-to-node=&quot;2&quot;&gt;1. 修復了什麼？&lt;/h3&gt;&lt;ul data-path-to-node=&quot;3&quot;&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,0,0&quot;&gt;索引點陣圖 (Bitmap) 錯誤&lt;/b&gt;：這就像是圖書館的目錄說某個位置是空的，但實際上那裡放了書。&lt;code data-index-in-node=&quot;47&quot; data-path-to-node=&quot;3,0,0&quot;&gt;chkdsk&lt;/code&gt; 重新校正了這些空間分配。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;3,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,1,0&quot;&gt;孤立檔案 (Orphaned Files) 復原&lt;/b&gt;：這部分最關鍵。你的 &lt;code data-index-in-node=&quot;35&quot; data-path-to-node=&quot;3,1,0&quot;&gt;$MFT&lt;/code&gt;（主檔案表格）等系統檔案一度失去了連結，導致 Windows 看不到目錄。&lt;code data-index-in-node=&quot;76&quot; data-path-to-node=&quot;3,1,0&quot;&gt;chkdsk&lt;/code&gt; 把這些「走丟」的系統檔案重新接回了目錄樹（檔案 5）。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;3,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,2,0&quot;&gt;MFT 鏡像修正&lt;/b&gt;：主檔案表格的備份也被同步修正了，確保下次讀取穩定。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr data-path-to-node=&quot;4&quot; /&gt;&lt;h3 data-path-to-node=&quot;5&quot;&gt;2. 後續建議 (SOP)&lt;/h3&gt;&lt;p data-path-to-node=&quot;6&quot;&gt;雖然現在讀得到了，但身為開發者，我們都知道「事出必有因」。為了確保這顆 5TB 隨身硬碟之後不再搞事，建議你做以下幾件事：&lt;/p&gt;&lt;ul data-path-to-node=&quot;7&quot;&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;7,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,0,0&quot;&gt;立刻檢查 S.M.A.R.T. 資訊：&lt;/b&gt;
雖然 &lt;code data-index-in-node=&quot;23&quot; data-path-to-node=&quot;7,0,0&quot;&gt;chkdsk&lt;/code&gt; 顯示 0 壞軌，但那是「邏輯層面」。建議用 &lt;b data-index-in-node=&quot;52&quot; data-path-to-node=&quot;7,0,0&quot;&gt;CrystalDiskInfo&lt;/b&gt; 看一下 &lt;code data-index-in-node=&quot;72&quot; data-path-to-node=&quot;7,0,0&quot;&gt;05 (重新分配磁區數)&lt;/code&gt; 和 &lt;code data-index-in-node=&quot;87&quot; data-path-to-node=&quot;7,0,0&quot;&gt;C5 (等候重映射的磁區數)&lt;/code&gt; 是否為 0。如果這兩個數值不是 0，代表硬碟物理壽命快到頭了，趕快趁現在備份。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;7,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,1,0&quot;&gt;養成「安全移除」的習慣：&lt;/b&gt;
這次的錯誤（索引與點陣圖不符）最常發生在&lt;b data-index-in-node=&quot;33&quot; data-path-to-node=&quot;7,1,0&quot;&gt;資料寫入中途拔掉 USB&lt;/b&gt;，或是 &lt;b data-index-in-node=&quot;49&quot; data-path-to-node=&quot;7,1,0&quot;&gt;USB 供電不穩&lt;/b&gt;（例如插在前端 USB 孔或 Hub 上）。下次拔除前，務必點選右下角的「安全移除硬體」。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;7,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,2,0&quot;&gt;停用「快速啟動」或檢查寫入快取：&lt;/b&gt;
如果你常用這顆硬碟，可以在「裝置管理員」&amp;gt;「磁碟機」&amp;gt;「WD Elements」內容中，確認「原則」設定。如果追求安全，可以選擇「快速移除」（停用寫入快取）。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr data-path-to-node=&quot;8&quot; /&gt;&lt;h3 data-path-to-node=&quot;9&quot;&gt;3. 關於資料安全&lt;/h3&gt;&lt;p data-path-to-node=&quot;10&quot;&gt;這顆硬碟已經 4.3TB 滿載（剩下約 400GB），這對 5TB 硬碟來說負擔不小，且檔案系統毀損過一次後，穩定性會略微下降。&lt;/p&gt;&lt;p data-path-to-node=&quot;11&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11&quot;&gt;建議下一步：&lt;/b&gt;
既然現在恢復了，趁這幾天趕快把裡面&lt;b data-index-in-node=&quot;24&quot; data-path-to-node=&quot;11&quot;&gt;最不可取代&lt;/b&gt;的資料（例如你的 e-book 草稿、kbar 原始碼等）同步到雲端或另一顆實體硬碟上。&lt;/p&gt;&lt;/div&gt;&lt;div&gt;-----------------------------------------------------&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;做完 chdisk 果然就恢復正常了, 還好能修復連結, 否則裡面去年以來的上課資料毀壞那就虧大了. 接下來我按照其建議去下載安裝&amp;nbsp;CrystalDiskInfo 來檢測硬碟狀況, 此軟體是日本人開發的, 體積很小直接到微軟 Store 下載安裝 (網路上的免安裝版我用 VirusTotal 掃描幾乎都有夾帶木馬) :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://apps.microsoft.com/detail/xp8k4rgx25g3gm?hl=zh-TW&amp;amp;gl=TW&quot; target=&quot;_blank&quot;&gt;https://apps.microsoft.com/detail/xp8k4rgx25g3gm?hl=zh-TW&amp;amp;gl=TW&lt;/a&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;檢測結果如下 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgry1FAnVrkrYwnpbo4FOot8B6A2NzhGszF6JcF_KflJiQ7T7xo_78QBJvX8v-a1JHEbez4NBiI-Vv1DYthIz2CY27NtwH984aWRUdnD2Xs_P1w3qqVCbB30P3DMqc4WF4Zj9Azyu24NWuDpymfOzHp8xrdyl-HTG1kbCQf3iiIZ1i7TJLYEEzbOjxdoEhs/s1050/WD-5TB-%E7%84%A1%E6%B3%95%E8%AE%80%E5%8F%96-2.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1050&quot; data-original-width=&quot;1012&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgry1FAnVrkrYwnpbo4FOot8B6A2NzhGszF6JcF_KflJiQ7T7xo_78QBJvX8v-a1JHEbez4NBiI-Vv1DYthIz2CY27NtwH984aWRUdnD2Xs_P1w3qqVCbB30P3DMqc4WF4Zj9Azyu24NWuDpymfOzHp8xrdyl-HTG1kbCQf3iiIZ1i7TJLYEEzbOjxdoEhs/s320/WD-5TB-%E7%84%A1%E6%B3%95%E8%AE%80%E5%8F%96-2.jpg&quot; width=&quot;308&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;貼給 Gemini 分析結果如下 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;p data-path-to-node=&quot;0&quot;&gt;這份健康報告非常漂亮，這顆 WD 5TB 隨身硬碟&lt;b data-index-in-node=&quot;46&quot; data-path-to-node=&quot;0&quot;&gt;目前的硬體狀態非常健康&lt;/b&gt;。&lt;/p&gt;&lt;p data-path-to-node=&quot;1&quot;&gt;以下是針對這張截圖的幾個核心分析：&lt;/p&gt;&lt;h3 data-path-to-node=&quot;2&quot;&gt;1. 關鍵健康指標 (核心數據)&lt;/h3&gt;&lt;ul data-path-to-node=&quot;3&quot;&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,0,0&quot;&gt;健康狀態：良好&lt;/b&gt;。這是最直觀的結果。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;3,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,1,0&quot;&gt;05 (重定位磁區計數)：&lt;/b&gt; 原始值為 &lt;code data-index-in-node=&quot;19&quot; data-path-to-node=&quot;3,1,0&quot;&gt;0&lt;/code&gt;。這代表硬碟沒有任何物理壞軌被替換掉，碟盤表面狀況良好。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;3,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,2,0&quot;&gt;C5 (等候重定的磁區計數)：&lt;/b&gt; 原始值為 &lt;code data-index-in-node=&quot;21&quot; data-path-to-node=&quot;3,2,0&quot;&gt;0&lt;/code&gt;。這代表沒有任何疑似壞軌在排隊。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;3,3,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;3,3,0&quot;&gt;C6 (無法校正的磁區計數)：&lt;/b&gt; 原始值為 &lt;code data-index-in-node=&quot;21&quot; data-path-to-node=&quot;3,3,0&quot;&gt;0&lt;/code&gt;。再次確認磁區資料讀寫穩定。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 data-path-to-node=&quot;4&quot;&gt;2. 使用記錄&lt;/h3&gt;&lt;ul data-path-to-node=&quot;5&quot;&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;5,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,0,0&quot;&gt;已用時間總計：3931 小時&lt;/b&gt;。這顆硬碟大約已經服役了 5 個多月（如果是 24 小時不關機計算），對於一顆隨身硬碟來說，正處於「壯年期」。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;5,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0&quot;&gt;已用次數總計：578 次&lt;/b&gt;。平均每次開機使用約 6.8 小時。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;5,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,2,0&quot;&gt;轉速：4800 RPM&lt;/b&gt;。這是典型的 2.5 吋節能/高容量硬碟規格，發熱量會比較低，目前的 &lt;b data-index-in-node=&quot;46&quot; data-path-to-node=&quot;5,2,0&quot;&gt;37°C&lt;/b&gt; 非常理想。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr data-path-to-node=&quot;6&quot; /&gt;&lt;h3 data-path-to-node=&quot;7&quot;&gt;3. 為什麼會發生「檔案目錄毀損」？&lt;/h3&gt;&lt;p data-path-to-node=&quot;8&quot;&gt;既然硬體沒問題，那為什麼會噴錯誤？最可能的兇手有三個：&lt;/p&gt;&lt;ol data-path-to-node=&quot;9&quot; start=&quot;1&quot;&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;9,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,0,0&quot;&gt;異常拔除：&lt;/b&gt; 在 Windows 還在寫入快取資料時，就直接拔掉 USB 線，導致 NTFS 的 &lt;b data-index-in-node=&quot;48&quot; data-path-to-node=&quot;9,0,0&quot;&gt;MFT (Master File Table)&lt;/b&gt; 沒寫完。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;9,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,1,0&quot;&gt;供電瞬間不穩：&lt;/b&gt; 5TB 的碟盤密度很高，對電流波動敏感。如果插在不穩的 USB 插座，讀寫中途掉電也會導致邏輯錯誤。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;9,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;9,2,0&quot;&gt;磁碟已滿的副作用：&lt;/b&gt; 你的硬碟已經用了 4.3TB 以上，當空間極度剩餘不足時，NTFS 的碎片整理或元數據更新會變得很頻繁，這時候若發生中斷，毀損機率會變高。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h3 data-path-to-node=&quot;10&quot;&gt;4. 維護建議&lt;/h3&gt;&lt;ul data-path-to-node=&quot;11&quot;&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;11,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,0,0&quot;&gt;繼續使用：&lt;/b&gt; 沒問題，它不是快掛了，只是「軟體感冒」剛修好。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;11,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,1,0&quot;&gt;安全退出：&lt;/b&gt; 之後請務必點選 Windows 右下角的「安全移除硬體」。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;11,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,2,0&quot;&gt;避免前端插槽：&lt;/b&gt; 建議優先插在電腦機殼後方的 USB 孔（或供電足夠的電競筆電插孔），穩定電壓是 5TB 硬碟的長壽關鍵。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;------------------------------------------------&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;這顆硬碟就回來了真是好佳在啊! 趕緊開啟 MIS 桌機, 把內容複製到 18TB 大硬碟裡備份. 這顆也快裝滿了, 也準備要換新的一顆來用了.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://yhhuang1966.blogspot.com/feeds/2958842096396053002/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment/fullpage/post/4877487320781767952/2958842096396053002' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/2958842096396053002'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/2958842096396053002'/><link rel='alternate' type='text/html' href='https://yhhuang1966.blogspot.com/2026/04/wd-5tb.html' title='WD 5TB 隨身硬碟無法讀取問題'/><author><name>小狐狸事務所</name><uri>http://www.blogger.com/profile/09435160519044041137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3uC5yfCLyoR8aPBSyKZX8wnk7ifN6_aMk2YD0iFesLCtRdH49nk9fRqxuzRtBhcafPl7ajWQ_lMkacA2eqjjoSv-KW_8DX2pD3Ebqf_qaraYjQZqC-125ZmNd2T9LU7ixxf4aNfzH5TgwewrbyGfv1PuIJXIe_fzrQalHYnGxFCw9sfF2eCzzjgYsSW-k/s72-c/WD-5TB-%E7%84%A1%E6%B3%95%E8%AE%80%E5%8F%96-1.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4877487320781767952.post-1874631718687413114</id><published>2026-04-19T17:44:00.001+08:00</published><updated>2026-04-19T17:50:22.101+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="生活"/><title type='text'>2026 年第 15 周記事</title><content type='html'>&lt;div&gt;又過了一周了, 四月下旬天氣變得熱起來, 今年的芒果樹結果不多, 菜園的只有兩棵樹有結果, 另外路邊的只有零星結一些, 我今天下午拿去年剩餘的袋子去套袋, 只完成菜園的那兩棵樹, 路邊的等下周再套. 套袋時在儲水池旁的芒果樹上發現一個鳥巢, 裡面有孵化的雛鳥兩隻 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcJtZiwoU24VZ2emCMvplF2W0FzxdMlgq_DKE-eEfYPpNLDSURSpt3l7DLmrRpJ9D_jsQiv0bf5gA-PBAnSMnH_rNapc_S_OE9vAtbBkreE2ZfKm5C-xi_IEKhtKXMajV8SQXsEUi4GO_GmRstoxqqItp-w_PbObm_LfYXUMZFn73RNn8d8vYCeVBBgh-E/s1477/%E8%8A%92%E6%9E%9C%E6%A8%B9%E4%B8%8A%E7%9A%84%E9%B3%A5%E5%B7%A2.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1108&quot; data-original-width=&quot;1477&quot; height=&quot;240&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcJtZiwoU24VZ2emCMvplF2W0FzxdMlgq_DKE-eEfYPpNLDSURSpt3l7DLmrRpJ9D_jsQiv0bf5gA-PBAnSMnH_rNapc_S_OE9vAtbBkreE2ZfKm5C-xi_IEKhtKXMajV8SQXsEUi4GO_GmRstoxqqItp-w_PbObm_LfYXUMZFn73RNn8d8vYCeVBBgh-E/s320/%E8%8A%92%E6%9E%9C%E6%A8%B9%E4%B8%8A%E7%9A%84%E9%B3%A5%E5%B7%A2.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;我傳給 Gemini 辨認, 說是白頭翁的雛鳥 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6XzKWU7N1iacz2KdhzNsixVB5OO4HIqQHAVoXv_UHF4NLn9kPEK1O3en2Oc6hHhyMNKuZNemr4soY-Z3zsqSquNKkJgddqw0pivlp9WHzXe6QL2lHi-vTfEQXStQuyQOOUUaAL-ugB_Ex7rkzEToKlTZCYi7Q9Os9CiL65o6qoZ1nJ-8yLHKx91ixWcWH/s912/%E7%99%BD%E9%A0%AD%E7%BF%81.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;683&quot; data-original-width=&quot;912&quot; height=&quot;240&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6XzKWU7N1iacz2KdhzNsixVB5OO4HIqQHAVoXv_UHF4NLn9kPEK1O3en2Oc6hHhyMNKuZNemr4soY-Z3zsqSquNKkJgddqw0pivlp9WHzXe6QL2lHi-vTfEQXStQuyQOOUUaAL-ugB_Ex7rkzEToKlTZCYi7Q9Os9CiL65o6qoZ1nJ-8yLHKx91ixWcWH/s320/%E7%99%BD%E9%A0%AD%E7%BF%81.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;這幾天回頭整理我的 5 台 Linux 主機, 其中三台樹莓派是我的主要爬蟲主機, 前陣子不明原因當機, 直到前幾天才有時間重灌, 順便把爬蟲程式升版. 接下來要做的有下列三件 :&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Serverless 平台加入 API Key&amp;nbsp;&lt;/li&gt;&lt;li&gt;高雄光世代固定 IP &amp;amp; 全屋通路由器設定&lt;/li&gt;&lt;li&gt;Mapleboard 外接 USB 儲存設定&lt;/li&gt;&lt;li&gt;鄉下的 Pi 3 主機升版為 Bulleye&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;鄉下的 Pi 3 主機目前跑的是 Stretch, 這根本是老古董了, 今天已備份 SD 卡內資料, 準備買一張 32 GB 卡來升版為 Bulleye.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://yhhuang1966.blogspot.com/feeds/1874631718687413114/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment/fullpage/post/4877487320781767952/1874631718687413114' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/1874631718687413114'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/1874631718687413114'/><link rel='alternate' type='text/html' href='https://yhhuang1966.blogspot.com/2026/04/2026-15.html' title='2026 年第 15 周記事'/><author><name>小狐狸事務所</name><uri>http://www.blogger.com/profile/09435160519044041137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcJtZiwoU24VZ2emCMvplF2W0FzxdMlgq_DKE-eEfYPpNLDSURSpt3l7DLmrRpJ9D_jsQiv0bf5gA-PBAnSMnH_rNapc_S_OE9vAtbBkreE2ZfKm5C-xi_IEKhtKXMajV8SQXsEUi4GO_GmRstoxqqItp-w_PbObm_LfYXUMZFn73RNn8d8vYCeVBBgh-E/s72-c/%E8%8A%92%E6%9E%9C%E6%A8%B9%E4%B8%8A%E7%9A%84%E9%B3%A5%E5%B7%A2.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4877487320781767952.post-1290185826844348559</id><published>2026-04-18T23:32:00.007+08:00</published><updated>2026-04-19T07:53:57.995+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="網路爬蟲"/><title type='text'>Python 學習筆記 : 市圖借書與預約爬蟲程式改版 v13</title><content type='html'>&lt;div&gt;將母校圖書館爬蟲升版後打鐵趁熱, 今天順便將此次優化 Selenium 爬蟲的技巧也套用在市圖爬蟲程式上, 可同時於 Pi 400, Pi 3B, 與 Pi 3A+ 上執行.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;本系列全部測試文章索引參考 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2025/03/blog-post_10.html&quot; target=&quot;_blank&quot;&gt;Python 網頁爬蟲筆記索引&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;新版 v13 程式碼如下 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;# ksml_lib_13.py&lt;/div&gt;&lt;div&gt;from selenium import webdriver&lt;/div&gt;&lt;div&gt;from selenium.webdriver.common.by import By&lt;/div&gt;&lt;div&gt;from selenium.webdriver.chrome.options import Options&lt;/div&gt;&lt;div&gt;from selenium.webdriver.chrome.service import Service&lt;/div&gt;&lt;div&gt;import re&lt;/div&gt;&lt;div&gt;from datetime import datetime&lt;/div&gt;&lt;div&gt;import time&lt;/div&gt;&lt;div&gt;import requests&lt;/div&gt;&lt;div&gt;import sys&lt;/div&gt;&lt;div&gt;from dotenv import dotenv_values&lt;/div&gt;&lt;div&gt;import os&lt;/div&gt;&lt;div&gt;import socket&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;async def telegram_send_text(text):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; bot=Bot(token=TELEGRAM_TOKEN)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; try:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; await bot.send_message(&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; chat_id=TELEGRAM_ID,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; text=text&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; )&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return True&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; except Exception as e:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(f&#39;Error sending text: {e}&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return False&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;def get_books(account, password):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; browser=None&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;background-color: #fcff01;&quot;&gt;result=(None, None)&lt;/span&gt;&amp;nbsp; # 預設回傳值&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; try:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 登入我的書房&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 設定一個在 SD 卡上的暫存目錄 (for Trixie)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; chrome_tmp_path=os.path.expanduser(&#39;~/chrome_tmp&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if not os.path.exists(chrome_tmp_path):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; os.makedirs(chrome_tmp_path)&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options=Options()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options.add_argument(&quot;--headless=new&quot;) # 新版無頭擬真瀏覽器&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options.add_argument(&quot;--no-sandbox&quot;) # Trixie 必加&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options.add_argument(&quot;--disable-dev-shm-usage&quot;) # 避免擠爆 /dev/shm&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options.add_argument(&#39;--disable-gpu&#39;) # 避免 GPU 驅動崩潰&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 強迫使用 SD 卡空間 (特別是 Trixie 必須)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options.add_argument(f&#39;--user-data-dir={chrome_tmp_path}&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 限制快取大小為 100MB (防止 chrome_tmp 資料夾隨著時間變得巨大)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options.add_argument(&#39;--disk-cache-size=104857600&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options.binary_location=&#39;/usr/bin/chromium&#39;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; service=Service(&#39;/usr/bin/chromedriver&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; browser=webdriver.Chrome(service=service, options=options)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; browser.implicitly_wait(60)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; browser.set_window_size(1920, 1080)&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 載入網頁&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; browser.get(&#39;https://webpacx.ksml.edu.tw/personal/&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; loginid=browser.find_element(By.ID, &#39;logxinid&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; loginid.send_keys(account)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; pincode=browser.find_element(By.ID, &#39;pincode&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; pincode.send_keys(password)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; div_btn_grp=browser.find_element(By.CLASS_NAME, &#39;btn_grp&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; login_btn=div_btn_grp.find_element(By.TAG_NAME, &#39;input&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; login_btn.click()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 擷取借閱紀錄&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; div_redblock=browser.find_element(By.CLASS_NAME, &#39;redblock&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; div_redblock.click()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; books=browser.find_elements(By.CLASS_NAME, &#39;bookdata&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; borrow_books=[]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; for book in books:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; item=dict()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; book_name=book.find_element(By.XPATH, &#39;./h2/a&#39;).text&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; item[&#39;book_name&#39;]=book_name.replace(&#39;/&#39;, &#39;&#39;).strip()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; book_site=book.find_element(By.XPATH, &#39;./ul[3]/li[1]&#39;).text&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; reg=r&#39;典藏地：(\S+)&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; item[&#39;book_site&#39;]=re.findall(reg, book_site)[0]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; reg=r&#39;\d{4}-\d{2}-\d{2}&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; due_date=book.find_element(By.XPATH, &#39;./ul[4]/li[2]&#39;).text&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; item[&#39;due_date&#39;]=re.findall(reg, due_date)[0]&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; due_times=book.find_element(By.XPATH, &#39;./ul[5]/li[1]&#39;).text&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; item[&#39;due_times&#39;]=re.findall(r&#39;\d{1}&#39;, due_times)[0]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; try:&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; state=book.find_element(By.XPATH, &#39;./ul[6]/li[1]&#39;).text&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; except:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; state=&#39;&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; finally:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if &#39;有人預約&#39; in state:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; item[&#39;state&#39;]=&#39;, 有人預約&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; item[&#39;state&#39;]=&#39;&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; borrow_books.append(item)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(&#39;擷取借閱紀錄 ... OK&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; browser.back() # 回上一頁&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 擷取預約紀錄&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; div_blueblock=browser.find_element(By.CLASS_NAME, &#39;blueblock&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; div_blueblock.click()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; books=browser.find_elements(By.CLASS_NAME, &#39;bookdata&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; reserve_books=[]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; for book in books:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; item=dict()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; book_name=book.find_element(By.XPATH, &#39;./h2/a&#39;).text&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; item[&#39;book_name&#39;]=book_name.replace(&#39;/&#39;, &#39;&#39;).strip()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; sequence=book.find_element(By.XPATH, &#39;./ul[7]/li[1]&#39;).text&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if &#39;預約待取&#39; in sequence:&amp;nbsp; # 已到館&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; item[&#39;ready_for_pickup&#39;]=True&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; reg=r&#39;\d{4}-\d{2}-\d{2}&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; item[&#39;expiration&#39;]=re.findall(reg, sequence)[0]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; item[&#39;sequence&#39;]=&#39;0&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else: # 預約中&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; item[&#39;ready_for_pickup&#39;]=False&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; item[&#39;expiration&#39;]=&#39;&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; item[&#39;sequence&#39;]=re.findall(r&#39;\d+&#39;, sequence)[0]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; reserve_books.append(item)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(&#39;擷取預約紀錄 ... OK&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;background-color: #fcff01;&quot;&gt;result=(borrow_books, reserve_books)&lt;/span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; except Exception as e:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(f&#39;發生錯誤 : {e}&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; finally:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if browser:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; try:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;background-color: #fcff01;&quot;&gt;browser.quit()&lt;/span&gt;&amp;nbsp; # 釋放記憶體&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(&#39;資源已釋放&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; except:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; pass&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;background-color: #fcff01;&quot;&gt;return result&lt;/span&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;if __name__ == &#39;__main__&#39;:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; start=time.time()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; config=dotenv_values(&#39;.env&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; TELEGRAM_TOKEN=config.get(&#39;TELEGRAM_TOKEN&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; TELEGRAM_ID=config.get(&#39;TELEGRAM_ID&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; #print(TELEGRAM_TOKEN)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; #print(TELEGRAM_ID)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; host_name=socket.gethostname()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; print(f&#39;主機 : {host_name}&#39;)&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; if len(sys.argv) != 3:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(f&#39;用法: {sys.argv[0]} 帳號 密碼&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; sys.exit(1)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; # 取得傳入的帳密參數&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; account=sys.argv[1]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; password=sys.argv[2]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; # 呼叫 get_books() 取得借書與預約書&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; borrow_books, reserve_books=get_books(account, password)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; b_msg=&#39;&#39;&amp;nbsp; # 借書資訊字串初始值&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; r_msg=&#39;&#39;&amp;nbsp; # 預約資訊字串初始值&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; # 處理借書&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; if borrow_books:&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; borrow=[]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; for book in borrow_books:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; book_name=book[&#39;book_name&#39;]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; book_site=book[&#39;book_site&#39;]&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; due_times=book[&#39;due_times&#39;]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; due_date=book[&#39;due_date&#39;]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; state=book[&#39;state&#39;]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; due_date=datetime.strptime(due_date, &#39;%Y-%m-%d&#39;) # 到期日&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; today_str=datetime.today().strftime(&#39;%Y-%m-%d&#39;)&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; today=datetime.strptime(today_str, &quot;%Y-%m-%d&quot;)&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; delta=(due_date-today).days&amp;nbsp; # 計算離到期日還有幾天&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if delta &amp;lt; 0:&amp;nbsp; # 負數=已逾期&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; msg=f&#39;🅧 {book_name} (逾期 {abs(delta)} 天{state}, {book_site})&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; borrow.append(msg)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; elif delta == 0:&amp;nbsp; # 0=今天到期&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; msg=f&#39;⓿ {book_name} (今日到期, 續借次數 {due_times}{state}, {book_site})&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; borrow.append(msg)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; elif delta == 1:&amp;nbsp; # 1=明天到期&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; msg=f&#39;❶ {book_name} (明日到期, 續借次數 {due_times}{state}, {book_site})&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; borrow.append(msg)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; elif delta == 2:&amp;nbsp; # 2=後天到期&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; msg=f&#39;❷ {book_name} (後天到期, 續借次數 {due_times}{state}, {book_site})&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; borrow.append(msg)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; elif 2 &amp;lt; delta &amp;lt; 8:&amp;nbsp; # 3 天以上一周內到期&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; msg=f&#39;✦ {book_name} ({book[&quot;due_date&quot;]} 到期, &#39;\&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; f&#39;續借次數 {due_times}{state}, {book_site})&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; borrow.append(msg)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 製作借書到期摘要字串&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if len(borrow) != 0:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; borrow.insert(0, f&#39;\n❖ {account} 的借閱 :&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; b_msg=&#39;\n&#39;.join(borrow)&amp;nbsp; # 更新借書資訊字串&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(&#39;產生借書到期摘要 ... OK&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; # 處理預約書&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; if reserve_books:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; reserve=[]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; i=0&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; j=[&#39;①&#39;, &#39;②&#39;, &#39;③&#39;, &#39;④&#39;, &#39;⑤&#39;]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; k=[&#39;❶&#39;, &#39;❷&#39;, &#39;❸&#39;, &#39;❹&#39;, &#39;❺&#39;]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 預約狀態&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; for book in reserve_books:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; book_name=book[&#39;book_name&#39;]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; sequence=book[&#39;sequence&#39;]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ready_for_pickup=book[&#39;ready_for_pickup&#39;] # 已到館&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; expiration=book[&#39;expiration&#39;]&amp;nbsp; # 取書截止日&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if ready_for_pickup:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; msg=f&#39;{k[i]} {book_name} (已到館, 保留期限 {expiration})&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; msg=f&#39;{j[i]} {book_name} (順位 {sequence})&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; reserve.append(msg)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; i += 1&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 製作預約書摘要字串&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if len(reserve) != 0:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; reserve.insert(0, f&#39;\n❖ {account} 的預約 :&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; r_msg=&#39;\n&#39;.join(reserve)&amp;nbsp; # 更新資訊字串&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; print(&#39;產生預約書摘要 ... OK&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; if b_msg or r_msg:&amp;nbsp; # 任一不為空字串就更新資料表&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; url=&quot;&lt;span style=&quot;background-color: #fcff01;&quot;&gt;https://serverless-5e6i.onrender.com/function/update_ksml_books&lt;/span&gt;&quot;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; payload={&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;account&quot;: account,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;borrow_books&quot;: b_msg,&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;reserve_books&quot;: r_msg&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; res=requests.post(url, json=payload)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(res.json())&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; end=time.time()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; print(f&#39;執行時間:{end-start}&#39;)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;此次改版也修正了 try-except-finally 結構, 把 browser.close() 改成 browser.quit(), 前者雖然會把視窗關掉, 但背景的 chromedriver 可能還在跑繼續吃 RAM,&amp;nbsp;導致背景殘留了一堆 chromedriver 的殭屍進程. 此優化版程式在 Pi 3B, 3A+ 與 Pi 400 均可順利執行.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;執行結果如下 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;pi@kaopi3:~ $ &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;python ksml_lib_12.py faxxxxxx 123456&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;擷取借閱紀錄 ... OK&lt;/div&gt;&lt;div&gt;擷取預約紀錄 ... OK&lt;/div&gt;&lt;div&gt;產生借書到期摘要 ... OK&lt;/div&gt;&lt;div&gt;產生預約書摘要 ... OK&lt;/div&gt;&lt;div&gt;{&#39;message&#39;: &#39;faxxxxxx 的資料已更新&#39;, &#39;status&#39;: &#39;success&#39;}&lt;/div&gt;&lt;div&gt;執行時間:667.9564106464386&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;pi@pi3aplus:~ $&amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;python ksml_lib_12.py faxxxxxx 123456&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;主機 : pi3aplus&lt;/div&gt;&lt;div&gt;擷取借閱紀錄 ... OK&lt;/div&gt;&lt;div&gt;擷取預約紀錄 ... OK&lt;/div&gt;&lt;div&gt;資源已釋放&lt;/div&gt;&lt;div&gt;產生借書到期摘要 ... OK&lt;/div&gt;&lt;div&gt;產生預約書摘要 ... OK&lt;/div&gt;&lt;div&gt;{&#39;message&#39;: &#39;faxxxxxx 的資料已更新&#39;, &#39;status&#39;: &#39;success&#39;}&lt;/div&gt;&lt;div&gt;執行時間:675.6979095935822&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;(myenv313) pi@pi400:~ $&amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;python ksml_lib_12.py faxxxxxx 123456&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;主機 : pi400&lt;/div&gt;&lt;div&gt;擷取借閱紀錄 ... OK&lt;/div&gt;&lt;div&gt;擷取預約紀錄 ... OK&lt;/div&gt;&lt;div&gt;資源已釋放&lt;/div&gt;&lt;div&gt;產生借書到期摘要 ... OK&lt;/div&gt;&lt;div&gt;產生預約書摘要 ... OK&lt;/div&gt;&lt;div&gt;{&#39;message&#39;: &#39;faxxxxxx 的資料已更新&#39;, &#39;status&#39;: &#39;success&#39;}&lt;/div&gt;&lt;div&gt;執行時間:573.2406423091888&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;可見 Pi 3B 與 3A+ 速度差不多, 但 Pi 400 就快了 100 秒.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;注意, 此處呼叫的後端端點有兩個, 都是建置在 Render 平台上的 serverless 服務, 目前我有兩個端點, 分配如下 :&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;kaopi3 : https://serverless-5e6i.onrender.com/function/send_books_messages&lt;/li&gt;&lt;li&gt;pi3aplus :&amp;nbsp;https://serverless-fdof.onrender.com/function/send_books_messages&lt;/li&gt;&lt;li&gt;pi400 :&amp;nbsp;https://serverless-fdof.onrender.com/function/send_books_messages (備用)&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;擷取並傳送借書資訊的程式 get_ksml_books_messages.py 也是要設定對應端點, 例如 kaopi3 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;# get_ksml_books_messages.py&lt;/div&gt;&lt;div&gt;import requests&lt;/div&gt;&lt;div&gt;import socket&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;host_name=socket.gethostname()&lt;/div&gt;&lt;div&gt;print(f&#39;主機 : {host_name}&#39;)&lt;/div&gt;&lt;div&gt;params={&#39;crawler&#39;: f&#39;{host_name}&#39;}&lt;/div&gt;&lt;div&gt;url=&#39;https://serverless-5e6i.onrender.com/function/send_books_messages&#39;&lt;/div&gt;&lt;div&gt;res=requests.get(url, params=params)&lt;/div&gt;&lt;div&gt;print(res)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;此爬蟲程式會呼叫 serverless 平台上的 send_books_messages.py 函式讀取 serverless.db 上記錄的借書與預約資訊, 並送出 Telegram 訊息. send_books_messages.py 函式內容如下 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;# send_ksml_books_messages.py&lt;/div&gt;&lt;div&gt;import asyncio&lt;/div&gt;&lt;div&gt;import sqlite3&lt;/div&gt;&lt;div&gt;from telegram import Bot&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;async def telegram_send_text(token, chat_id, text):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &quot;&quot;&quot;非同步傳送 Telegram 訊息&quot;&quot;&quot;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; try:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; bot=Bot(token=token)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; await bot.send_message(chat_id=chat_id, text=text)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return True&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; except Exception as e:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(f&quot;傳送失敗: {e}&quot;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return False&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;def main(request, **kwargs):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; DB_PATH=&#39;./serverless.db&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; config=kwargs.get(&#39;config&#39;, {})&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; telegram_token=config.get(&#39;TELEGRAM_TOKEN&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; telegram_id=config.get(&#39;TELEGRAM_ID&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; if not telegram_token or not telegram_id:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return &#39;未設定 TELEGRAM_TOKEN 或 TELEGRAM_ID&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; try:&amp;nbsp; # 連線資料庫&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; conn=sqlite3.connect(DB_PATH)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; cur=conn.cursor()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; cur.execute(&quot;SELECT borrow_books, reserve_books FROM ksml_books;&quot;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; rows=cur.fetchall()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; conn.close()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; except Exception as e:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return f&#39;資料庫讀取失敗: {e}&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; if not rows:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return &#39;沒有任何資料可傳送&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; # 傳送訊息&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; success_count=0&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; fail_count=0&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; for borrow_books, reserve_books in rows:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; for msg in [borrow_books, reserve_books]:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if msg and msg.strip():&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ok=asyncio.run(telegram_send_text(telegram_token, telegram_id, msg))&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if ok:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; success_count += 1&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; fail_count += 1&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; return f&#39;傳送完成：成功 {success_count} 筆，失敗 {fail_count} 筆&#39;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;市圖爬蟲架構較複雜, 得畫一張圖來備忘才行.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://yhhuang1966.blogspot.com/feeds/1290185826844348559/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment/fullpage/post/4877487320781767952/1290185826844348559' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/1290185826844348559'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/1290185826844348559'/><link rel='alternate' type='text/html' href='https://yhhuang1966.blogspot.com/2026/04/python-v13.html' title='Python 學習筆記 : 市圖借書與預約爬蟲程式改版 v13'/><author><name>小狐狸事務所</name><uri>http://www.blogger.com/profile/09435160519044041137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4877487320781767952.post-2141063547542946216</id><published>2026-04-18T00:51:00.006+08:00</published><updated>2026-04-18T11:32:20.106+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="樹莓派"/><category scheme="http://www.blogger.com/atom/ns#" term="網路爬蟲"/><title type='text'>Python 學習筆記 : 母校圖書館借書與預約爬蟲程式改版 v11</title><content type='html'>&lt;div&gt;之前版本的爬蟲我都把帳號密碼金鑰等等都寫在程式碼裡, 這實在不是好的做法, 今天將爬蟲進行改版, 將帳密金鑰都存放在隱藏檔 .env 中, 於程式裡利用 dotenv 從 .env 檔讀取出來. 關於 dotenv 用法參考 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2024/01/api.html&quot; target=&quot;_blank&quot;&gt;Python 學習筆記 : 如何隱藏 API 金鑰或權杖&lt;/a&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2025/05/python-dotenv.html&quot; target=&quot;_blank&quot;&gt;Python 學習筆記 : dotenv 取值的另類用法&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;本系列全部文章索引參考 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2025/03/blog-post_10.html&quot; target=&quot;_blank&quot;&gt;Python 網頁爬蟲筆記索引&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;先將目前的第 10 版爬蟲複製一份到第 11 版 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;pi@kaopi3:~ $ &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;cp nkust_lib_10.py nkust_lib_11.py&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;用 nano 編輯程式碼 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;pi@kaopi3:~ $ &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;nano nkust_lib_11.py&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;修改為如下 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;# nkust_lib_11.py&lt;/div&gt;&lt;div&gt;from selenium import webdriver&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;from selenium.webdriver.common.by import By&lt;/div&gt;&lt;div&gt;from selenium.webdriver.common.action_chains import ActionChains&lt;/div&gt;&lt;div&gt;from selenium.webdriver.chrome.options import Options&lt;/div&gt;&lt;div&gt;from selenium.webdriver.chrome.service import Service&lt;/div&gt;&lt;div&gt;import time&lt;/div&gt;&lt;div&gt;import requests&lt;/div&gt;&lt;div&gt;from datetime import datetime&lt;/div&gt;&lt;div&gt;import asyncio&lt;/div&gt;&lt;div&gt;from telegram import Bot&lt;/div&gt;&lt;div&gt;import re&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: #fcff01;&quot;&gt;from dotenv import dotenv_values&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;async def telegram_send_text(text):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; bot=Bot(token=&lt;span style=&quot;background-color: #fcff01;&quot;&gt;TELEGRAM_TOKEN&lt;/span&gt;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; try:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; await bot.send_message(&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; chat_id=&lt;span style=&quot;background-color: #fcff01;&quot;&gt;TELEGRAM_ID&lt;/span&gt;,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; text=text&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; )&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return True&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; except Exception as e:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(f&#39;Error sending text: {e}&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return False&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;def get_nkust_lib():&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; browser=None&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; msg=&quot;無法取得資料&quot;&amp;nbsp; # ✅ 防止未賦值&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; try:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options=Options()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options.add_argument(&quot;--headless=new&quot;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options.add_argument(&quot;--no-sandbox&quot;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options.add_argument(&quot;--disable-dev-shm-usage&quot;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options.binary_location=&quot;/usr/bin/chromium&quot;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; service=Service(&quot;/usr/bin/chromedriver&quot;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; browser=webdriver.Chrome(service=service, options=options)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; browser.implicitly_wait(60)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; browser.set_window_size(1920, 1080)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; url=(&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;https://nkust.primo.exlibrisgroup.com/discovery/login?&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;vid=886NKUST_INST:86NKUST&amp;amp;lang=zh-tw&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; )&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; browser.get(url)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 按其他讀者&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; md_list=browser.find_elements(By.TAG_NAME, &#39;md-list-item&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if len(md_list) &amp;gt; 1:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; md_list[1].click()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(&#39;按其他讀者 ... OK&#39;)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 登入系統&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; login_user_name=browser.find_element(By.ID, &#39;LoginUserName&#39;)&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; login_user_name.send_keys(&lt;span style=&quot;background-color: #fcff01;&quot;&gt;NKUST_LIB_ID&lt;/span&gt;)&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; login_password=browser.find_element(By.ID, &#39;LoginPassword&#39;)&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; login_password.send_keys(&lt;span style=&quot;background-color: #fcff01;&quot;&gt;NKUST_LIB_PWD&lt;/span&gt;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; login_btn=browser.find_element(By.CLASS_NAME, &#39;button-large&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; login_btn.click()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(&#39;登入系統 ... OK&#39;)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 按名字顯現選單&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; user_btn=browser.find_element(By.CLASS_NAME, &#39;user-button&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; actions=ActionChains(browser)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; actions.move_to_element(user_btn)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; actions.click(user_btn)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; actions.perform()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(&#39;按名字顯現選單 ... OK&#39;)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 按我的借閱鈕&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; xpath=&#39;/html/body/div[3]/md-menu-content/md-menu-item[3]/button&#39;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; my_borrow=browser.find_element(By.XPATH, xpath)&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; actions.move_to_element(my_borrow)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; actions.click(my_borrow)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; actions.perform()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(&#39;按我的借閱 ... OK&#39;)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 按全部續借&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; xpath=(&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;/html/body/primo-explore/div/prm-account/md-content&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;/div[2]/prm-account-overview/md-content/md-tabs/&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;md-tabs-content-wrapper/md-tab-content[2]/div/&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;div/prm-loans/div[1]/div[2]/div[2]/button&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; )&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; all_borrow=browser.find_element(By.XPATH, xpath)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; actions.move_to_element(all_borrow)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; actions.click(all_borrow)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; actions.perform()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(&#39;按全部續借 ... OK&#39;)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 檢查續借結果&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; xpath=(&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;/html/body/primo-explore/div/prm-account/md-content&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;/div[2]/prm-account-overview/md-content/md-tabs&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;/md-tabs-content-wrapper/md-tab-content[2]/div/div&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;/prm-loans/div[2]/prm-alert-bar/div/div/span&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; )&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; alert_span=browser.find_element(By.XPATH, xpath)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if &#39;所有借閱資料已成功續借&#39; in alert_span.text:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; msg=&#39;❖ 所有借閱資料已成功續借&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; msg=&#39;❖ 只有部分借閱資料已成功續借&#39;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 檢查是否有 &quot;載入更多結果&quot; 按鈕&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; for i in range(3):&amp;nbsp; # 最多 3 頁&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; load_more=browser.find_elements(By.CLASS_NAME, &#39;button-confirm&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if not load_more:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; break&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; load_more[0].click()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; time.sleep(2)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 抓取所有借閱書目&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; loan_items=browser.find_elements(By.TAG_NAME, &#39;md-list-item&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; unrenew_books=[]&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; for item in loan_items:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; try:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # ✅ 1. 必須有書名才是書&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; title_elem=item.find_elements(By.CSS_SELECTOR, &quot;h3 a&quot;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if not title_elem:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; continue&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; title=title_elem[0].text.strip()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; item_text=item.text.strip()&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # ✅ 2. 取得到期日&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; due_text=&#39;&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; due_elem=item.find_elements(By.XPATH, &#39;.//p[@data-qa=&quot;automation_mlc_record_date&quot;]&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if due_elem:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; m=re.search(r&#39;\d{2}/\d{2}/\d{4}&#39;, due_elem[0].text)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if m:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; due_text=m.group(0)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # ✅ 3. 只收真正「被預約 / 無法續借」&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if any(k in item_text for k in [&quot;被預約&quot;, &quot;無法續借&quot;, &quot;recall&quot;]):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; unrenew_books.append((title, due_text))&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; except Exception:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; continue&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if unrenew_books:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; msg=&quot;被預約的書：\n&quot; + &quot;\n&quot;.join(&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; [f&quot;{i+1:&amp;gt;2}. {t[0]}　到期日: {t[1]}&quot; for i, t in enumerate(unrenew_books)]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; )&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; msg=&quot;全部書籍皆已續借&quot;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(&#39;搜尋被預約書籍 ... OK&#39;)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; except Exception as e:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(e)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; finally:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if browser:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; browser.quit()&amp;nbsp; # ✅ 防呆&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return msg&amp;nbsp; # ✅ msg 一定有值&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;if __name__ == &#39;__main__&#39;:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; start=time.time()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;background-color: #fcff01;&quot;&gt;config=dotenv_values(&#39;.env&#39;)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;background-color: #fcff01;&quot;&gt;NKUST_LIB_ID=config.get(&#39;NKUST_LIB_ID&#39;)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;background-color: #fcff01;&quot;&gt;NKUST_LIB_PWD=config.get(&#39;NKUST_LIB_PWD&#39;)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;background-color: #fcff01;&quot;&gt;TELEGRAM_TOKEN=config.get(&#39;TELEGRAM_TOKEN&#39;)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;background-color: #fcff01;&quot;&gt;TELEGRAM_ID=config.get(&#39;TELEGRAM_ID&#39;)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; #print(NKUST_LIB_ID)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; #print(NKUST_LIB_PWD)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; #print(TELEGRAM_TOKEN)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; #print(TELEGRAM_ID)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; msg=get_nkust_lib()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; if msg:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; now=datetime.now().strftime(&#39;%Y-%m-%d %H:%M:%S&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; msg=&#39;\n&#39; + now + &#39;\n&#39; + msg&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if asyncio.run(telegram_send_text(msg)):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(&#39;訊息傳送成功!&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(&#39;訊息傳送失敗!&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; print(msg)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; end=time.time()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; print(f&#39;執行時間:{end-start}&#39;)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;黃底高亮者為修改或添加的部分, 執行結果如下 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;pi@kaopi3:~ $ &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;python nkust_lib_11.py&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;按其他讀者 ... OK&lt;/div&gt;&lt;div&gt;登入系統 ... OK&lt;/div&gt;&lt;div&gt;按名字顯現選單 ... OK&lt;/div&gt;&lt;div&gt;按我的借閱 ... OK&lt;/div&gt;&lt;div&gt;按全部續借 ... OK&lt;/div&gt;&lt;div&gt;訊息傳送成功!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;2026-04-18 00:37:52&lt;/div&gt;&lt;div&gt;❖ 所有借閱資料已成功續借&lt;/div&gt;&lt;div&gt;執行時間:81.74577593803406&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;2026-04-18 補充 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;早上又對程式碼進行局部微調, 版本號仍維持 v11, 主要修改如下 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;1. 添加主機名稱訊息 :&amp;nbsp;&lt;br /&gt;利用&amp;nbsp;socket.gethostname()&amp;nbsp;即可得到主機名稱. 這樣收到 Telegram 才知道是哪台主機的爬蟲完成的.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;2. 使用 SD 卡儲存 Chrome/Chromium 暫存資料 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;由於 Pi 400 的 Trixie 仍然開發演進中, 最新版本改用 Chrome 而非 Chromium, 跑 Selenium 爬蟲時使用 DRAM 來暫存資料, 可能導致 DRAM 塞滿而使瀏覽器閃退, 於是改為強制將暫存資料放在 chrome_tmp 資料夾下, 瀏覽器設定修改為 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 設定一個在 SD 卡上的暫存目錄 (for Trixie)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;background-color: #fcff01;&quot;&gt;chrome_tmp_path=os.path.expanduser(&#39;~/chrome_tmp&#39;)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;background-color: #fcff01;&quot;&gt;if not os.path.exists(chrome_tmp_path):&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;background-color: #fcff01;&quot;&gt;os.makedirs(chrome_tmp_path)&lt;/span&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options=Options()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options.add_argument(&quot;--headless=new&quot;) # 新版無頭擬真瀏覽器&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options.add_argument(&quot;--no-sandbox&quot;) # Trixie 必加&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options.add_argument(&quot;--disable-dev-shm-usage&quot;) # 避免擠爆 /dev/shm&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;background-color: #fcff01;&quot;&gt;options.add_argument(&#39;--disable-gpu&#39;)&lt;/span&gt; # 避免 GPU 驅動崩潰&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 強迫使用 SD 卡空間 (特別是 Trixie 必須)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;background-color: #fcff01;&quot;&gt;options.add_argument(f&#39;--user-data-dir={chrome_tmp_path}&#39;)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 限制快取大小為 100MB (防止 chrome_tmp 資料夾隨著時間變得巨大)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;span style=&quot;background-color: #fcff01;&quot;&gt;options.add_argument(&#39;--disk-cache-size=104857600&#39;)&lt;/span&gt;&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;以上設定雖然是針對 Trixie, 但在 Bulleye (Pi 3 &amp;amp; Pi 3A+) 上跑也是可以的. 完整程式碼如下 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;# nkust_lib_11.py&lt;/div&gt;&lt;div&gt;from selenium import webdriver&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;from selenium.webdriver.common.by import By&lt;/div&gt;&lt;div&gt;from selenium.webdriver.common.action_chains import ActionChains&lt;/div&gt;&lt;div&gt;from selenium.webdriver.chrome.options import Options&lt;/div&gt;&lt;div&gt;from selenium.webdriver.chrome.service import Service&lt;/div&gt;&lt;div&gt;import time&lt;/div&gt;&lt;div&gt;import requests&lt;/div&gt;&lt;div&gt;from datetime import datetime&lt;/div&gt;&lt;div&gt;import asyncio&lt;/div&gt;&lt;div&gt;from telegram import Bot&lt;/div&gt;&lt;div&gt;import re&lt;/div&gt;&lt;div&gt;from dotenv import dotenv_values&lt;/div&gt;&lt;div&gt;import os&lt;/div&gt;&lt;div&gt;import socket&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;async def telegram_send_text(text):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; bot=Bot(token=TELEGRAM_TOKEN)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; try:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; await bot.send_message(&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; chat_id=TELEGRAM_ID,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; text=text&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; )&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return True&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; except Exception as e:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(f&#39;Error sending text: {e}&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return False&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;def get_nkust_lib():&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; browser=None&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; msg=&quot;無法取得資料&quot;&amp;nbsp; # ✅ 防止未賦值&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; try:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 設定一個在 SD 卡上的暫存目錄 (for Trixie)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; chrome_tmp_path=os.path.expanduser(&#39;~/chrome_tmp&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if not os.path.exists(chrome_tmp_path):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; os.makedirs(chrome_tmp_path)&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options=Options()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options.add_argument(&quot;--headless=new&quot;) # 新版無頭擬真瀏覽器&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options.add_argument(&quot;--no-sandbox&quot;) # Trixie 必加&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options.add_argument(&quot;--disable-dev-shm-usage&quot;) # 避免擠爆 /dev/shm&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options.add_argument(&#39;--disable-gpu&#39;) # 避免 GPU 驅動崩潰&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 強迫使用 SD 卡空間 (特別是 Trixie 必須)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options.add_argument(f&#39;--user-data-dir={chrome_tmp_path}&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 限制快取大小為 100MB (防止 chrome_tmp 資料夾隨著時間變得巨大)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options.add_argument(&#39;--disk-cache-size=104857600&#39;)&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; options.binary_location=&quot;/usr/bin/chromium&quot;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; service=Service(&quot;/usr/bin/chromedriver&quot;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; browser=webdriver.Chrome(service=service, options=options)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; browser.implicitly_wait(60)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; browser.set_window_size(1920, 1080)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; url=(&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;https://nkust.primo.exlibrisgroup.com/discovery/login?&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;vid=886NKUST_INST:86NKUST&amp;amp;lang=zh-tw&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; )&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; browser.get(url)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 按其他讀者&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; md_list=browser.find_elements(By.TAG_NAME, &#39;md-list-item&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if len(md_list) &amp;gt; 1:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; md_list[1].click()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(&#39;按其他讀者 ... OK&#39;)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 登入系統&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; login_user_name=browser.find_element(By.ID, &#39;LoginUserName&#39;)&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; login_user_name.send_keys(NKUST_LIB_ID)&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; login_password=browser.find_element(By.ID, &#39;LoginPassword&#39;)&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; login_password.send_keys(NKUST_LIB_PWD)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; login_btn=browser.find_element(By.CLASS_NAME, &#39;button-large&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; login_btn.click()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(&#39;登入系統 ... OK&#39;)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 按名字顯現選單&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; user_btn=browser.find_element(By.CLASS_NAME, &#39;user-button&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; actions=ActionChains(browser)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; actions.move_to_element(user_btn)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; actions.click(user_btn)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; actions.perform()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(&#39;按名字顯現選單 ... OK&#39;)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 按我的借閱鈕&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; xpath=&#39;/html/body/div[3]/md-menu-content/md-menu-item[3]/button&#39;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; my_borrow=browser.find_element(By.XPATH, xpath)&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; actions.move_to_element(my_borrow)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; actions.click(my_borrow)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; actions.perform()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(&#39;按我的借閱 ... OK&#39;)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 按全部續借&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; xpath=(&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;/html/body/primo-explore/div/prm-account/md-content&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;/div[2]/prm-account-overview/md-content/md-tabs/&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;md-tabs-content-wrapper/md-tab-content[2]/div/&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;div/prm-loans/div[1]/div[2]/div[2]/button&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; )&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; all_borrow=browser.find_element(By.XPATH, xpath)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; actions.move_to_element(all_borrow)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; actions.click(all_borrow)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; actions.perform()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(&#39;按全部續借 ... OK&#39;)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 檢查續借結果&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; xpath=(&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;/html/body/primo-explore/div/prm-account/md-content&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;/div[2]/prm-account-overview/md-content/md-tabs&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;/md-tabs-content-wrapper/md-tab-content[2]/div/div&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &#39;/prm-loans/div[2]/prm-alert-bar/div/div/span&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; )&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; alert_span=browser.find_element(By.XPATH, xpath)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if &#39;所有借閱資料已成功續借&#39; in alert_span.text:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; msg=&#39;❖ 所有借閱資料已成功續借&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; msg=&#39;❖ 只有部分借閱資料已成功續借&#39;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 檢查是否有 &quot;載入更多結果&quot; 按鈕&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; for i in range(3):&amp;nbsp; # 最多 3 頁&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; load_more=browser.find_elements(By.CLASS_NAME, &#39;button-confirm&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if not load_more:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; break&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; load_more[0].click()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; time.sleep(2)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # 抓取所有借閱書目&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; loan_items=browser.find_elements(By.TAG_NAME, &#39;md-list-item&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; unrenew_books=[]&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; for item in loan_items:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; try:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # ✅ 1. 必須有書名才是書&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; title_elem=item.find_elements(By.CSS_SELECTOR, &quot;h3 a&quot;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if not title_elem:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; continue&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; title=title_elem[0].text.strip()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; item_text=item.text.strip()&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # ✅ 2. 取得到期日&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; due_text=&#39;&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; due_elem=item.find_elements(By.XPATH, &#39;.//p[@data-qa=&quot;automation_mlc_record_date&quot;]&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if due_elem:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; m=re.search(r&#39;\d{2}/\d{2}/\d{4}&#39;, due_elem[0].text)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if m:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; due_text=m.group(0)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; # ✅ 3. 只收真正「被預約 / 無法續借」&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if any(k in item_text for k in [&quot;被預約&quot;, &quot;無法續借&quot;, &quot;recall&quot;]):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; unrenew_books.append((title, due_text))&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; except Exception:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; continue&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if unrenew_books:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; msg=&quot;被預約的書：\n&quot; + &quot;\n&quot;.join(&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; [f&quot;{i+1:&amp;gt;2}. {t[0]}　到期日: {t[1]}&quot; for i, t in enumerate(unrenew_books)]&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; )&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; msg=&quot;全部書籍皆已續借&quot;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(&#39;搜尋被預約書籍 ... OK&#39;)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; except Exception as e:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(e)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; finally:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if browser:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; browser.quit()&amp;nbsp; # ✅ 防呆&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return msg&amp;nbsp; # ✅ msg 一定有值&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;if __name__ == &#39;__main__&#39;:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; start=time.time()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; config=dotenv_values(&#39;.env&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; NKUST_LIB_ID=config.get(&#39;NKUST_LIB_ID&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; NKUST_LIB_PWD=config.get(&#39;NKUST_LIB_PWD&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; TELEGRAM_TOKEN=config.get(&#39;TELEGRAM_TOKEN&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; TELEGRAM_ID=config.get(&#39;TELEGRAM_ID&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; #print(NKUST_LIB_ID)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; #print(NKUST_LIB_PWD)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; #print(TELEGRAM_TOKEN)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; #print(TELEGRAM_ID)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; host_name=socket.gethostname()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; print(f&#39;主機 : {host_name}&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; msg=get_nkust_lib()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; if msg:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; now=datetime.now().strftime(&#39;%Y-%m-%d %H:%M:%S&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; msg=&#39;\n&#39; + now + &#39;\n&#39; + msg + &#39;\n&#39; + f&#39;(host_name)&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if asyncio.run(telegram_send_text(msg)):&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(&#39;訊息傳送成功!&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; else:&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; print(&#39;訊息傳送失敗!&#39;)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; print(msg)&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; end=time.time()&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; print(f&#39;執行時間:{end-start}&#39;)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;執行結果 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;pi@kaopi3:~ $ python nkust_lib_11.py&lt;/div&gt;&lt;div&gt;主機 : kaopi3&lt;/div&gt;&lt;div&gt;按其他讀者 ... OK&lt;/div&gt;&lt;div&gt;登入系統 ... OK&lt;/div&gt;&lt;div&gt;按名字顯現選單 ... OK&lt;/div&gt;&lt;div&gt;按我的借閱 ... OK&lt;/div&gt;&lt;div&gt;按全部續借 ... OK&lt;/div&gt;&lt;div&gt;搜尋被預約書籍 ... OK&lt;/div&gt;&lt;div&gt;訊息傳送成功!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;2026-04-18 11:14:19&lt;/div&gt;&lt;div&gt;全部書籍皆已續借&lt;/div&gt;&lt;div&gt;(kaopi3)&lt;/div&gt;&lt;div&gt;執行時間:358.6611738204956&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://yhhuang1966.blogspot.com/feeds/2141063547542946216/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment/fullpage/post/4877487320781767952/2141063547542946216' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/2141063547542946216'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/2141063547542946216'/><link rel='alternate' type='text/html' href='https://yhhuang1966.blogspot.com/2026/04/python-v11.html' title='Python 學習筆記 : 母校圖書館借書與預約爬蟲程式改版 v11'/><author><name>小狐狸事務所</name><uri>http://www.blogger.com/profile/09435160519044041137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4877487320781767952.post-2451598088654875866</id><published>2026-04-17T13:59:00.001+08:00</published><updated>2026-04-17T14:00:28.766+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Gemini"/><category scheme="http://www.blogger.com/atom/ns#" term="♥♥♥"/><category scheme="http://www.blogger.com/atom/ns#" term="索引"/><title type='text'>Google Gemini API 學習索引</title><content type='html'>&lt;div&gt;過去兩年來串接大語言模型 API 我最常用的是 OpenAI, 這必須要先綁信用卡購買隨用即付額度才能使用; 而 Google Gemini 則不需要, 免費仔目前 gemini-2.5-flash 享有的請求限制為 10 次/分, 25 萬 token/分, 250 次/日, 對於新手非常慷慨. 最近打算對 Gemini API 做較全面的測試, 先把過去的測試文章整理程如下索引以利查考 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2024/09/google-gemini-api-key.html&quot; target=&quot;_blank&quot;&gt;申請 Google Gemini API Key&lt;/a&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2024/09/google-gemini-api.html&quot; target=&quot;_blank&quot;&gt;Google Gemini API 學習筆記 (一) : 文字聊天測試&lt;/a&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2025/10/google-gemini-api.html&quot; target=&quot;_blank&quot;&gt;Google Gemini API 學習筆記 (二) : 有記憶的聊天機器人&lt;/a&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2026/04/openai-api.html&quot; target=&quot;_blank&quot;&gt;AI 應用程式專案 (一) : 新聞稿生成器&lt;/a&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2026/04/openai-api-youtube.html&quot; target=&quot;_blank&quot;&gt;AI 應用程式專案 (二) : Youtube 字幕摘要生成器&lt;/a&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2026/04/gemini-api.html&quot; target=&quot;_blank&quot;&gt;Google Gemini API 學習筆記 : 新舊 API 呼叫方法整理&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;~ 進行中 ~&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://yhhuang1966.blogspot.com/feeds/2451598088654875866/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment/fullpage/post/4877487320781767952/2451598088654875866' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/2451598088654875866'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/2451598088654875866'/><link rel='alternate' type='text/html' href='https://yhhuang1966.blogspot.com/2026/04/google-gemini-api.html' title='Google Gemini API 學習索引'/><author><name>小狐狸事務所</name><uri>http://www.blogger.com/profile/09435160519044041137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4877487320781767952.post-5942891524268212810</id><published>2026-04-17T12:46:00.006+08:00</published><updated>2026-04-17T13:45:32.052+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Gemini"/><title type='text'>Google Gemini API 學習筆記 : 新舊 API 呼叫方法整理</title><content type='html'>&lt;div&gt;Google 在 2024 年底推出新版的 SDK 套件 google-genai 來取代舊版的 google-generativeai, 目前我的 LG Gram 筆電的虛擬環境安裝的最新 langchain-core 就是依賴於&amp;nbsp;google-genai, 但昨天在 Pi 3A+ 的 Bulleye 上測試發現無法安裝新版的 google-genai, 只能用舊版的&amp;nbsp;google-generativeai, 所以我將呼叫原生 Gemini API 時新舊兩種用法整理如下備查. 參考 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://yhhuang1966.blogspot.com/2024/09/google-gemini-api.html&quot; target=&quot;_blank&quot;&gt;Google Gemini API 學習筆記 (一) : 文字聊天測試&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;1. 舊版 Gemin API (google-generativeai) 用法 :&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;安裝 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: #fcff01;&quot;&gt;pip install google-generativeai&lt;/span&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;匯入 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: #fcff01;&quot;&gt;import google.generativeai as genai&lt;/span&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;設定金鑰 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;genai.configure(api_key=api_key)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;建立模型 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;model=genai.GenerativeModel(&#39;gemini-2.5-flash&#39;)&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;提問 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;reply=model.generate_content(&#39;你是誰?&#39;)&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;取得回覆 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;print(reply.text)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;2. 新版 Gemin API (google-genai) 用法 :&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;安裝 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: #fcff01;&quot;&gt;pip install google-genai&lt;/span&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;匯入 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;f&lt;span style=&quot;background-color: #fcff01;&quot;&gt;rom google import genai&lt;/span&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;建立 Client 物件 (設定金鑰) :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;client=genai.Client(api_key=api_key)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;建立模型 &amp;amp; 提問 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;reply=client.models.generate_content(&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; model=&#39;gemini-2.5-flash&#39;,&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; contents=&#39;你是誰?&#39;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; )&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;取得回覆 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;print(reply.text)&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://yhhuang1966.blogspot.com/feeds/5942891524268212810/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment/fullpage/post/4877487320781767952/5942891524268212810' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/5942891524268212810'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/5942891524268212810'/><link rel='alternate' type='text/html' href='https://yhhuang1966.blogspot.com/2026/04/gemini-api.html' title='Google Gemini API 學習筆記 : 新舊 API 呼叫方法整理'/><author><name>小狐狸事務所</name><uri>http://www.blogger.com/profile/09435160519044041137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4877487320781767952.post-1251468959427824810</id><published>2026-04-17T10:07:00.004+08:00</published><updated>2026-04-17T10:53:15.891+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Gemini"/><category scheme="http://www.blogger.com/atom/ns#" term="OpenAI"/><category scheme="http://www.blogger.com/atom/ns#" term="樹莓派"/><title type='text'>樹莓派學習筆記 : 在 Bulleye 上安裝 google-generativeai 套件</title><content type='html'>&lt;div&gt;昨天成功重灌 Pi 3A+ 的 Bulleye 後嘗試安裝 langchain-core, 結果因為版本衝突不順利, 且就算安裝成功, 由於 Pi 3A+ 只有 512MB DRAM, 跑 langchain 太沉重了只好放棄, 改為安裝原生 SDK. 安裝 openai 套件成功且可順利匯入 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;pi@pi3aplus:~ $ &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;pip install openai&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple&lt;/div&gt;&lt;div&gt;Collecting openai&lt;/div&gt;&lt;/div&gt;&lt;div&gt;...(略)...&lt;/div&gt;&lt;div&gt;&lt;div&gt;Installing collected packages: openai&lt;/div&gt;&lt;div&gt;Successfully installed openai-2.32.0&lt;/div&gt;&lt;div&gt;pi@pi3aplus:~ $ &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;python&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;Python 3.9.2 (default, Jan 24 2026, 09:41:14)&amp;nbsp;&lt;/div&gt;&lt;div&gt;[GCC 10.2.1 20210110] on linux&lt;/div&gt;&lt;div&gt;Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.&lt;/div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;from openai import OpenAI&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;exit()&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;安裝 Gemini 舊版 API 的 google-generativeai 套件 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;pi@pi3aplus:~ $ &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;pip install google-generativeai&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple&lt;/div&gt;&lt;div&gt;Collecting google-generativeai&lt;/div&gt;&lt;div&gt;&amp;nbsp; Downloading google_generativeai-0.8.6-py3-none-any.whl (155 kB)&lt;/div&gt;&lt;/div&gt;&lt;div&gt;...(略)...&lt;/div&gt;&lt;div&gt;&lt;div&gt;Successfully installed google-ai-generativelanguage-0.6.15 google-api-python-client-2.194.0 google-auth-httplib2-0.3.1 google-generativeai-0.8.6 grpcio-1.80.0 grpcio-status-1.71.2 httplib2-0.31.2 protobuf-5.29.6 uritemplate-4.2.0&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;但用 import google.generativeai as genai 匯入時會出現 grpcio 相關錯誤, 原因也是版本衝突問題, AI 建議改用下列安裝指令鎖住版本 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;pi@pi3aplus:~ $ &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;pip install &quot;google-generativeai==0.3.1&quot; \&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;google-ai-generativelanguage==0.4.0&quot; \&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;grpcio==1.54.2&quot; \&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;grpcio-status==1.54.2&quot; \&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;protobuf==4.25.3&quot; \&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; --force-reinstall --no-cache-dir&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple&lt;/div&gt;&lt;div&gt;Collecting google-generativeai==0.3.1&lt;/div&gt;&lt;div&gt;&amp;nbsp; Downloading google_generativeai-0.3.1-py3-none-any.whl (146 kB)&lt;/div&gt;&lt;/div&gt;&lt;div&gt;...(略)...&lt;/div&gt;&lt;div&gt;&lt;div&gt;Successfully installed certifi-2026.2.25 cffi-2.0.0 charset-normalizer-3.4.7 cryptography-46.0.7 google-ai-generativelanguage-0.4.0 google-api-core-2.29.0 google-auth-2.49.2 google-generativeai-0.3.1 googleapis-common-protos-1.73.0 grpcio-1.54.2 grpcio-status-1.54.2 idna-3.11 proto-plus-1.27.1 protobuf-4.25.3 pyasn1-0.6.3 pyasn1-modules-0.4.2 pycparser-2.23 requests-2.32.5 tqdm-4.67.3 typing-extensions-4.15.0 urllib3-2.6.3&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;這樣匯入時就只報出無關緊要的 Warning 了 (這些警告只是提醒 : Python 3.9 太舊了, 以後可能不支援而已) :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;pi@pi3aplus:~ $ &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;python&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;Python 3.9.2 (default, Jan 24 2026, 09:41:14)&amp;nbsp;&lt;/div&gt;&lt;div&gt;[GCC 10.2.1 20210110] on linux&lt;/div&gt;&lt;div&gt;Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.&lt;/div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;import google.generativeai as genai&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;/home/pi/.local/lib/python3.9/site-packages/google/api_core/_python_version_support.py:246: FutureWarning: You are using a non-supported Python version (3.9.2). Google will not post any further updates to google.api_core supporting this Python version. Please upgrade to the latest Python version, or at least Python 3.10, and then update google.api_core.&lt;/div&gt;&lt;div&gt;&amp;nbsp; warnings.warn(message, FutureWarning)&lt;/div&gt;&lt;div&gt;/home/pi/.local/lib/python3.9/site-packages/google/auth/__init__.py:54: FutureWarning: You are using a Python version 3.9 past its end of life. Google will update google-auth with critical bug fixes on a best-effort basis, but not with any other fixes or features. Please upgrade your Python version, and then update google-auth.&lt;/div&gt;&lt;div&gt;&amp;nbsp; warnings.warn(eol_message.format(&quot;3.9&quot;), FutureWarning)&lt;/div&gt;&lt;div&gt;/home/pi/.local/lib/python3.9/site-packages/google/oauth2/__init__.py:40: FutureWarning: You are using a Python version 3.9 past its end of life. Google will update google-auth with critical bug fixes on a best-effort basis, but not with any other fixes or features. Please upgrade your Python version, and then update google-auth.&lt;/div&gt;&lt;div&gt;&amp;nbsp; warnings.warn(eol_message.format(&quot;3.9&quot;), FutureWarning)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;如果不想看到這些警告, 可以在程式開頭用下列程式碼隱藏 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;import warnings&lt;/div&gt;&lt;div&gt;warnings.filterwarnings(&quot;ignore&quot;, category=FutureWarning)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;如果想永久隱藏, 就要用 nano ~/.bashrc 去修改設定檔, 在最底下加上 :&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;export PYTHONWARNINGS=&quot;ignore::FutureWarning&quot;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;用下列程式測試可正確載入 API :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;import grpc&lt;/div&gt;&lt;div&gt;print(f&quot;GRPC 版本: {grpc.__version__}&quot;) # 應該要是 1.54.2&lt;/div&gt;&lt;div&gt;import google.generativeai as genai&lt;/div&gt;&lt;div&gt;print(&quot;Gemini SDK 成功載入！&quot;)&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;import google.generativeai as genai&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;print(genai.__version__)&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;0.3.1&lt;/div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;try:&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;...&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;print(&quot;SDK 載入成功，準備測試屬性...&quot;)&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;...&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;model = genai.GenerativeModel(&#39;gemini-pro&#39;)&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;...&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;print(&quot;模型物件建立成功！&quot;)&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;... &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;except Exception as e:&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;...&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;print(f&quot;執行出錯: {e}&quot;)&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;...&amp;nbsp;&lt;/div&gt;&lt;div&gt;SDK 載入成功，準備測試屬性...&lt;/div&gt;&lt;div&gt;模型物件建立成功！&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;我把 Gemini API 金鑰放在環境變數檔 .env 裡的 GEMINI_API_KEY 中利用 dotenv 套件讀取後實際呼叫 Gemini API 測試 OK :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;import google.generativeai as genai&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;from dotenv import dotenv_values&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;config=dotenv_values(&#39;.env&#39;)&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;gemini_api_key=config.get(&#39;GEMINI_API_KEY&#39;)&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;genai.configure(api_key=gemini_api_key)&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;model=genai.GenerativeModel(&#39;gemini-2.5-flash&#39;)&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;reply=model.generate_content(&#39;你是誰?&#39;)&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;print(reply.text)&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;我是一個大型語言模型，由 Google 訓練。&lt;/div&gt;&lt;div&gt;我沒有名字、沒有身體，也沒有個人情感或意識。我的目的是回答你的問題、提供資訊、進行對話，並在各種任務上提供幫助。&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;下面是 OpenAI API 的測試 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;from openai import OpenAI&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt;&amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;from dotenv import dotenv_values&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt;&amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;config=dotenv_values(&#39;.env&#39;)&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;openai_api_key=config.get(&#39;OPENAI_API_KEY&#39;)&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;client=OpenAI(api_key=openai_api_key)&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;reply=client.chat.completions.create(&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;...&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;messages=[&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;...&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;{&quot;role&quot;: &quot;user&quot;,&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;...&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;&quot;content&quot;: &quot;你是誰?&quot;,&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;...&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;}],&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;...&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;model=&quot;gpt-3.5-turbo&quot;&lt;/span&gt;&lt;/b&gt;,&lt;/div&gt;&lt;div&gt;...&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;)&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&amp;gt;&amp;gt;&amp;gt; &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;print(reply.choices[0].message.content)&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;我是一個AI人工智能助手，可以與你進行對話、回答問題和提供信息。有什麼我可以幫助你的嗎？&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://yhhuang1966.blogspot.com/feeds/1251468959427824810/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment/fullpage/post/4877487320781767952/1251468959427824810' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/1251468959427824810'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/1251468959427824810'/><link rel='alternate' type='text/html' href='https://yhhuang1966.blogspot.com/2026/04/bulleye-google-generativeai.html' title='樹莓派學習筆記 : 在 Bulleye 上安裝 google-generativeai 套件'/><author><name>小狐狸事務所</name><uri>http://www.blogger.com/profile/09435160519044041137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4877487320781767952.post-4205584842394415909</id><published>2026-04-17T07:06:00.006+08:00</published><updated>2026-04-17T07:08:18.787+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Python"/><category scheme="http://www.blogger.com/atom/ns#" term="樹莓派"/><title type='text'>樹莓派學習筆記 : 消除 PATH 環境變數警告的方法</title><content type='html'>&lt;div&gt;高雄家的 Pi 3 A+ 主機在三月下旬我去日本時不明原因當機 (應該是 8GB TF 卡掛了), 它負責的爬蟲停擺, 回來後一直沒時間修復, 昨晚找出一片 32GB TF 卡重新燒錄 Bulleye 後已重新上線. 但在更新套件時出現一個警告 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;pi@pi3aplus:~ $ &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;pip install --upgrade requests urllib3&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple&lt;/div&gt;&lt;div&gt;Requirement already satisfied: requests in /usr/lib/python3/dist-packages (2.25.1)&lt;/div&gt;&lt;div&gt;Collecting requests&lt;/div&gt;&lt;/div&gt;&lt;div&gt;... (略)...&lt;/div&gt;&lt;div&gt;&lt;div&gt;Installing collected packages: urllib3, charset-normalizer, requests&lt;/div&gt;&lt;div&gt;&amp;nbsp; &lt;span style=&quot;background-color: #fcff01;&quot;&gt;WARNING: The script normalizer is installed in &#39;/home/pi/.local/bin&#39; which is not on PATH.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;background-color: #fcff01;&quot;&gt;&amp;nbsp; Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location&lt;/span&gt;.&lt;/div&gt;&lt;div&gt;Successfully installed charset-normalizer-3.4.7 requests-2.32.5 urllib3-2.6.3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;此警告意思是套件有安裝可執行腳本 (此處為 normalizer), 但該腳本所在目錄 /home/pi/.local/bin 沒有在 PATH 裡, 所以若在終端機直接打指令 (normalizer) 時會找不到, 出現 command not found 錯誤. 如果根本不用 CLI 工具執行此套件可忽略此警告, 但以後 pip install 時仍可能出現, 為了不礙眼, 乾脆將 /home/pi/.local/bin 加入 PATH 裡一勞永逸, 做法如下 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;編輯 .bashrc :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;pi@pi3aplus:~ $ &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;nano ~/.bashrc&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;在最底下加入此目錄 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;export PATH=&quot;$HOME/.local/bin:$PATH&quot;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaJV-yGpSYDe3yA3ma3iYAF18wOpMTeETvlP5-hzNdgbmvOVlVjszpN1OPKONknycz79RNZ2ABrIAW6t3t3boNXmX02YBDtD_X3tPnYdgdlzvBDFyXEDZ4s2-skiZCFurix7VwuQUP7HShNBh_ni2HV4qYzlfYPFGsJ1do4e88dMHJipN3cRVu_okGTLQE/s1607/pi3aplus-pip-warning-add-path.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1092&quot; data-original-width=&quot;1607&quot; height=&quot;217&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaJV-yGpSYDe3yA3ma3iYAF18wOpMTeETvlP5-hzNdgbmvOVlVjszpN1OPKONknycz79RNZ2ABrIAW6t3t3boNXmX02YBDtD_X3tPnYdgdlzvBDFyXEDZ4s2-skiZCFurix7VwuQUP7HShNBh_ni2HV4qYzlfYPFGsJ1do4e88dMHJipN3cRVu_okGTLQE/s320/pi3aplus-pip-warning-add-path.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;重新執行&amp;nbsp;.bashrc 即可 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;pi@pi3aplus:~ $ &lt;b&gt;&lt;span style=&quot;color: #2b00fe;&quot;&gt;source ~/.bashrc&lt;/span&gt;&lt;/b&gt;&amp;nbsp; &amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://yhhuang1966.blogspot.com/feeds/4205584842394415909/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment/fullpage/post/4877487320781767952/4205584842394415909' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/4205584842394415909'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/4205584842394415909'/><link rel='alternate' type='text/html' href='https://yhhuang1966.blogspot.com/2026/04/path.html' title='樹莓派學習筆記 : 消除 PATH 環境變數警告的方法'/><author><name>小狐狸事務所</name><uri>http://www.blogger.com/profile/09435160519044041137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaJV-yGpSYDe3yA3ma3iYAF18wOpMTeETvlP5-hzNdgbmvOVlVjszpN1OPKONknycz79RNZ2ABrIAW6t3t3boNXmX02YBDtD_X3tPnYdgdlzvBDFyXEDZ4s2-skiZCFurix7VwuQUP7HShNBh_ni2HV4qYzlfYPFGsJ1do4e88dMHJipN3cRVu_okGTLQE/s72-c/pi3aplus-pip-warning-add-path.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4877487320781767952.post-5897696635403186449</id><published>2026-04-16T11:18:00.015+08:00</published><updated>2026-04-24T11:31:17.563+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="SDD"/><category scheme="http://www.blogger.com/atom/ns#" term="vibe coding"/><category scheme="http://www.blogger.com/atom/ns#" term="好站"/><title type='text'>好站 : 高見龍老師的部落格 (SDD 規格驅動開發)</title><content type='html'>&lt;div&gt;三月時內訓課程請到六角學院的高見龍老師來講 SDD 規格驅動開發, 讓我覺得獲益匪淺, 其實課程內容的精髓大部份都可以在其部落格找到 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://kaochenlong.com/&quot; target=&quot;_blank&quot;&gt;https://kaochenlong.com/&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;部落格中與&amp;nbsp;SDD 規格驅動開發有關的是這幾篇 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://kaochenlong.com/sdd-spec-driven-development&quot; target=&quot;_blank&quot;&gt;SDD 規格驅動開發&lt;/a&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://kaochenlong.com/sdd-spec-as-source&quot; target=&quot;_blank&quot;&gt;Spec-as-source 的理想與現實&lt;/a&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://kaochenlong.com/openspec&quot; target=&quot;_blank&quot;&gt;OpenSpec 讓 SDD 變簡單的三個指令&lt;/a&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://kaochenlong.com/spectra-with-openspec&quot; target=&quot;_blank&quot;&gt;Spectra：給 OpenSpec 的圖形介面&lt;/a&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://kaochenlong.com/spectra-app-2&quot; target=&quot;_blank&quot;&gt;Spectra 2.0&lt;/a&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;其中 Spectra 是高老師為 OpenSpec 開發的 GUI 工具, 可以讓 OpenSpec 用起來更簡單更直覺, 剛好我最近正在學 OpenSpec 用得上 (雖然我只開發小專案, 但江湖在走, SDD 還是要有). 參考 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://github.com/kaochenlong/spectra-app&quot; target=&quot;_blank&quot;&gt;https://github.com/kaochenlong/spectra-app&lt;/a&gt;&amp;nbsp;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;我去年參加公司的 Hahow 企業版線上課程時就上了不少高老師的課 (Python, Javascript, 網頁前端), 不管是口條或內容都非常棒, 雖說 AI 時代學習這些技能似乎不那麼重要了, 但有基礎的人與菜菜小白終究是不同的.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;2026-04-23 補充 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;這幾天上完 TibaMe 的 SDD 課程後, 回頭來複習高老師的文章, 摘要整理如下 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;一. Vibe Coding 的問題 :&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;AI 產出的程式碼風格不一致 :&lt;br /&gt;開啟不同的對話詢問相同需求, AI 可能用完全不同的方法跟架構來生成程式碼.&amp;nbsp;&lt;/li&gt;&lt;li&gt;AI 不會主動審查需求的完整性 :&lt;br /&gt;AI 通常是針對提問中的需求焦點實作, 不會補全其他需要搭配的功能, 導致東補西補或過度猜測實作了不需要的功能.&lt;/li&gt;&lt;li&gt;可維護性與可擴展性較差 :&amp;nbsp;&lt;br /&gt;AI 生成的程式碼主要以能跑就好為目標, 忽略長期維護所需的架構設計, 對需要交接的大型專案維護不利.&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;二. SDD 的核心概念 :&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;先寫規格再寫程式 :&lt;br /&gt;在寫程式之前先把規格寫清楚,&amp;nbsp;把規格變成 AI 和人類之間的共同語言.&amp;nbsp;&lt;/li&gt;&lt;li&gt;規格是跟實作分開的, 可以用同一份規格去嘗試不同的技術組合, 看哪個最適合.&lt;/li&gt;&lt;li&gt;SDD 讓 AI 的產出更可控, 更符合我們的期待.&lt;/li&gt;&lt;li&gt;SDD 最重要的價值是把規格留下來備查.&amp;nbsp;&lt;br /&gt;如果開發的時候有留下 spec, 它記錄的不只是要做什麼, 還有為什麼要這樣做. 它是開發當下的決策脈絡, 是需求和實作之間的橋樑.&amp;nbsp;重點是這個 spec 不只是給人看的, 還能給 AI 看, 這才是 SDD 真正的亮點. SDD 不只是指揮 AI 做事, 而且還能讓專案的知識不會流失.&amp;nbsp;&lt;/li&gt;&lt;li&gt;Vibe coding 是「邊做邊想」, SDD 是「想清楚再做&lt;span color=&quot;oklch(0.371 0 0)&quot; face=&quot;ui-sans-serif, system-ui, sans-serif, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Noto Color Emoji&amp;quot;&quot; style=&quot;background-color: white; font-size: 20px;&quot;&gt;」.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;三. SDD 工作流程的三個階段 (與所對應的 md 文件) :&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;需求階段 (對應文件 requirements.md) :&lt;br /&gt;此階段目標是將模糊的想法變成清晰可被驗證的需求, 需求要按照 User Story (使用者故事) 格式描述, 並附上具體的驗收標準. 使用者故事格式 :&amp;nbsp;&lt;br /&gt;作為 (角色), 我希望 (功能), 這樣我就能 (好處)&lt;br /&gt;例如 :&amp;nbsp;&lt;br /&gt;作為一個使用者, 我希望能夠用 Email 和密碼登入, 這樣我就能存取我的個人資料.&lt;br /&gt;驗收標準要用 EARS 格式撰寫, 例如 :&lt;br /&gt;當使用者輸入正確的 Email 和密碼的時候系統應該將使用者導向首頁.&lt;/li&gt;&lt;li&gt;設計階段 (對應文件 design.md) :&amp;nbsp;&lt;br /&gt;由 AI 幫忙產出一份技術設計文件, 包含系統架構圖, 資料流程, 資料模型, 錯誤處理策略, 測試策略等. 此階段的重點是讓我們有機會在寫程式之前就發現問題以降低修改成本, 不要等到等到程式都寫完了才發現.&amp;nbsp;可要求 AI 加入 wireframe mock 來確認介面的設計是否符合要求.&amp;nbsp;&lt;/li&gt;&lt;li&gt;任務階段 (對應文件 tasks.md) :&lt;br /&gt;根據需求和設計, 把工作拆成一個一個可追蹤的小任務,&amp;nbsp;每個任務同樣都有明確的目標和驗收標準且會追溯到最初的需求編號. 任務的顆粒度 (granularity, 切多大或多小) 很重要, 但這沒有一定要的標準答案, 只要小到可以在一個合理的時間內完成並驗證即可.&amp;nbsp;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div&gt;這三份文件會隨著專案演進而同步更新, 亦即, 規格會跟程式碼保持同步.&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;四. EARS (Easy Approach to Requirements Syntax) 格式 : &lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;此格式源自航空業, 使用固定順序的 When…shall… (then) 語法來撰寫需求, 它清楚地說明了觸發條件 (輸入正確的帳密) 和預期行為 (跳轉到首頁), 此種格式具有足夠的可讀性, 也具有機器讀取所需的結構性, 使需求更容易被測試,&amp;nbsp;減少了 AI 的猜測空間,&amp;nbsp;生成的結果更可預測.&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;五.&amp;nbsp;SDD 的三個層級 :&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;ol style=&quot;text-align: left;&quot;&gt;&lt;li&gt;規格優先 (Spec-first) :&lt;br /&gt;在寫程式之前先寫好規格, 然後用規格來引導 AI 生成程式碼. 此作法比 Vibe Coding 好的地方是至少有一份文件記錄當初想要什麼.&lt;/li&gt;&lt;li&gt;規格錨定 (Spec-anchored) :&lt;br /&gt;把規格保留下來且加進版本控制裡, 使其隨著專案演進而更新.&amp;nbsp;當要修改功能的時候不是直接去改程式碼, 而是先改規格, 讓程式碼跟著規格走. 這樣做好處是永遠有一份文件知道系統應該做什麼, 對團隊協作尤其有利.&amp;nbsp;&lt;/li&gt;&lt;li&gt;規格即原始碼 (Spec-as-source) :&lt;br /&gt;此為最激進層級, 只編輯規格不直接編輯程式碼, 程式碼完全由規格自動生成 (規格是真相的來源), 這樣就能確保兩者永遠一致. 但要把規格寫得非常非常精確, 才能確保生成的程式碼是正確的.&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;六. OpenSpec :&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;開源的輕量級工具 :&amp;nbsp;&lt;a href=&quot;https://github.com/Fission-AI/OpenSpec&quot; target=&quot;_blank&quot;&gt;https://github.com/Fission-AI/OpenSpec&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Brownfield-first (棕地優先) :&amp;nbsp;&lt;br /&gt;不只適合從零開始的新專案, 更適合在現有系統上做修改.&lt;/li&gt;&lt;li&gt;工作流程簡單 : proposal -&amp;gt; apply -&amp;gt; archive&lt;/li&gt;&lt;li&gt;支援主流的 AI coding 工具 : Gemini CLI, Claude Code, Copilot, ...&lt;/li&gt;&lt;li&gt;強調 &quot;fluid not rigid, iterative not waterfall&quot;, 核心理念 &quot;Agree before you build&quot;.&amp;nbsp;&lt;/li&gt;&lt;li&gt;常用指令 :&lt;/li&gt;&lt;ul&gt;&lt;li&gt;/opsx:new 開一個新的變更&lt;/li&gt;&lt;li&gt;/opsx:ff 一口氣產生 proposal, specs, design, tasks&lt;/li&gt;&lt;li&gt;/opsx:apply 實作&lt;/li&gt;&lt;li&gt;/opsx:archive 歸檔 (留下完整的決策紀錄)&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;七. SDD 的缺點 :&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;SDD 會讓開發速度變慢 :&amp;nbsp;&lt;br /&gt;SDD 適合有一定複雜度的專案,&amp;nbsp;小工具直接用 Vibe Coding 即可.&lt;/li&gt;&lt;li&gt;SDD 會改變工程師的角色 :&lt;br /&gt;程式員 -&amp;gt; PM 的掙扎 (&lt;span style=&quot;background-color: #fcff01;&quot;&gt;how 變成 what&lt;/span&gt;)&amp;nbsp;&lt;br /&gt;從寫程式碼轉變成設計架構和審查結果.&lt;/li&gt;&lt;li&gt;做好 SDD 需要學習新的技能 :&lt;br /&gt;寫好規格不容易,&amp;nbsp;需要時間訓練, 要學會如何把模糊的需求變成清晰的驗收標準, 如何設計可測試的系統.&lt;/li&gt;&lt;li&gt;若既有系統本來就沒有規格文件, 導入 SDD 得先花時間把現有行為記錄下來, 這是導入成本.&amp;nbsp;&lt;/li&gt;&lt;li&gt;同樣的 spec, 同樣的 prompt, 可能有不同的產出.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span style=&quot;background-color: #fce5cd; color: #990000;&quot;&gt;八. 結語 :&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;SDD 適合大專案或團隊協作, Vibe coding 較適合小專案, 小工具, side projects, 或快速原型展示等應用, 而大型專案應該以 SDD 規格驅動開發為準繩.&lt;/li&gt;&lt;li&gt;&amp;nbsp;Vibe Coding 對兩種人很有幫助, 一是很有經驗的開發者, 他們懂得怎麼除錯和解決問題, 透過 AI 幫忙加速開發; 另一種是程式小白, 透過 AI 就可以把想法變成可以運作的軟體, 即使不懂程式設計也做得到.&lt;/li&gt;&lt;li&gt;全自動的 Spec-as-source 可能還要再等等, 也許目前 Spec-anchored 會是比較好的解法.&amp;nbsp;&lt;/li&gt;&lt;li&gt;SDD 的價值不只是指揮 AI, 更是留下脈絡讓未來可以追溯.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://yhhuang1966.blogspot.com/feeds/5897696635403186449/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment/fullpage/post/4877487320781767952/5897696635403186449' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/5897696635403186449'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/5897696635403186449'/><link rel='alternate' type='text/html' href='https://yhhuang1966.blogspot.com/2026/04/blog-post_16.html' title='好站 : 高見龍老師的部落格 (SDD 規格驅動開發)'/><author><name>小狐狸事務所</name><uri>http://www.blogger.com/profile/09435160519044041137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4877487320781767952.post-6559526734754673723</id><published>2026-04-15T15:19:00.002+08:00</published><updated>2026-04-15T15:20:39.515+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="vibe coding"/><category scheme="http://www.blogger.com/atom/ns#" term="人工智慧"/><title type='text'>好站 : 工程師下班有約</title><content type='html'>&lt;div&gt;最近上了幾堂林鼎淵老師的 Vibe coding 內訓課程, 覺得他是實力厚實備課非常認真的老師, 這從等候程式執行的幾分鐘, 他都有準備小知識與開發經驗談來墊檔與分享可見一斑. 他經營的 YT 頻道有非常多實用與最新的 AI 技術分享影片 (尤其是 Vibe coding), 值得記下來有空時學習 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#&amp;nbsp;&lt;a href=&quot;https://www.youtube.com/@dlcorner&quot;&gt;(1626) 工程師下班有約 - YouTube&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://yhhuang1966.blogspot.com/feeds/6559526734754673723/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment/fullpage/post/4877487320781767952/6559526734754673723' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/6559526734754673723'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/6559526734754673723'/><link rel='alternate' type='text/html' href='https://yhhuang1966.blogspot.com/2026/04/blog-post_15.html' title='好站 : 工程師下班有約'/><author><name>小狐狸事務所</name><uri>http://www.blogger.com/profile/09435160519044041137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4877487320781767952.post-5950611286702959856</id><published>2026-04-14T19:21:00.003+08:00</published><updated>2026-04-14T19:23:39.364+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="vibe coding"/><category scheme="http://www.blogger.com/atom/ns#" term="網購"/><title type='text'>(補記) 購買 Accupass AI 實戰工作坊</title><content type='html'>月初 (大約 4/6) 在 Accupass 買了一堂 Vibe coding 線上課程忘了記錄下來, 今天整理截圖才發現, 4/18 (六) 13:30~16:30 上課, 馬上列入行事曆以免錯過 :&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQWYHP1ce7hj0Z1-ISwRxUFtrEw9z7roXPr4SHBQweus1KLS47JCP46qJup32H5IvIDqcLxvC2y9RA-7TXkMh9JXsrQNA6hxrJU7OSSIz42QOE1KdkJ_i7gL9R47JBrP6wTcwIKMkxigy53XyFPvTQjf_-3f_6OKK2fQZXsW5-vl3DXl9wYRES41w4uZ6U/s2307/Accupass%20AI%20%E5%AF%A6%E6%88%B0%E5%B7%A5%E4%BD%9C%E5%9D%8A%E8%AA%B2%E7%A8%8B.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;946&quot; data-original-width=&quot;2307&quot; height=&quot;131&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQWYHP1ce7hj0Z1-ISwRxUFtrEw9z7roXPr4SHBQweus1KLS47JCP46qJup32H5IvIDqcLxvC2y9RA-7TXkMh9JXsrQNA6hxrJU7OSSIz42QOE1KdkJ_i7gL9R47JBrP6wTcwIKMkxigy53XyFPvTQjf_-3f_6OKK2fQZXsW5-vl3DXl9wYRES41w4uZ6U/s320/Accupass%20AI%20%E5%AF%A6%E6%88%B0%E5%B7%A5%E4%BD%9C%E5%9D%8A%E8%AA%B2%E7%A8%8B.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;我在 Hahow 也還有四門課買了尚未啟用學習, 第二季要來完成進度了.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://yhhuang1966.blogspot.com/feeds/5950611286702959856/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment/fullpage/post/4877487320781767952/5950611286702959856' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/5950611286702959856'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/5950611286702959856'/><link rel='alternate' type='text/html' href='https://yhhuang1966.blogspot.com/2026/04/accupass-ai.html' title='(補記) 購買 Accupass AI 實戰工作坊'/><author><name>小狐狸事務所</name><uri>http://www.blogger.com/profile/09435160519044041137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQWYHP1ce7hj0Z1-ISwRxUFtrEw9z7roXPr4SHBQweus1KLS47JCP46qJup32H5IvIDqcLxvC2y9RA-7TXkMh9JXsrQNA6hxrJU7OSSIz42QOE1KdkJ_i7gL9R47JBrP6wTcwIKMkxigy53XyFPvTQjf_-3f_6OKK2fQZXsW5-vl3DXl9wYRES41w4uZ6U/s72-c/Accupass%20AI%20%E5%AF%A6%E6%88%B0%E5%B7%A5%E4%BD%9C%E5%9D%8A%E8%AA%B2%E7%A8%8B.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4877487320781767952.post-2752210402017360639</id><published>2026-04-14T16:38:00.003+08:00</published><updated>2026-04-14T16:38:23.414+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="手機"/><title type='text'>如何尋找靜音中的手機</title><content type='html'>&lt;div&gt;前天 (週日) 爸坐阿泉伯的車去愛心會開會, 手機掉在車上未發覺, 回到家才發現手機不見了, 我透過定位發現手機在阿泉伯家, 但去他家找了三遍都沒找到, 撥電話都沒聽到響鈴, 我懷疑爸是否誤將手機關靜音, 於是問 Gemini 如何在手機靜音狀態下讓它發出聲音? 方法如下 :&lt;/div&gt;&lt;div&gt;&lt;ol style=&quot;text-align: left;&quot;&gt;&lt;li&gt;在電腦或其他行動裝置的瀏覽器網址 google.com/android/find&amp;nbsp;&lt;/li&gt;&lt;li&gt;登入要尋找的手機的 Google 帳號&lt;/li&gt;&lt;li&gt;在左側面板或選單中點擊 「播放音效」(Play Sound)&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div&gt;我開啟自己的手機瀏覽器, 連線 google.com/android/find, 登入爸的 Google 帳戶後, 點選 &quot;安全性與登入&quot; :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVRmnhel5YQ6BNf1sKgVFmbjdSh9H-UjmfVe-Gtg-ObjGrdiUPjK71r3XzAXjkz9-cSgM4fqQ9W6hXABSF2CZFU7Y4CFCqY1Y6z04KAdMLOh0NBm94VVizyerw3MelR2YntaZYuWv0Edbp95lJ5h-HRUx1GLHgXVr2xVsjI144JLZeTufyuhQsjwZACp_Q/s1649/android-find-mobile-phone-1.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1649&quot; data-original-width=&quot;993&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVRmnhel5YQ6BNf1sKgVFmbjdSh9H-UjmfVe-Gtg-ObjGrdiUPjK71r3XzAXjkz9-cSgM4fqQ9W6hXABSF2CZFU7Y4CFCqY1Y6z04KAdMLOh0NBm94VVizyerw3MelR2YntaZYuWv0Edbp95lJ5h-HRUx1GLHgXVr2xVsjI144JLZeTufyuhQsjwZACp_Q/s320/android-find-mobile-phone-1.jpg&quot; width=&quot;193&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;點選尋找遺失的裝置 :&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3HY6x1BQBNyAM0lP5wC1kGYvDxOxO7wVtdCJAJZRz4MgxgBaKSdcDoElDUG9SY5YCjJRgbEXlSlCay6IX1Ni-Joxn_U92DZP0Rux7k88kCyS0gLr8G8ADeP4JFcnv356KOjE5zRNjs6KaqC51YL-k22aotuOQc_JojmwTJDBBaDuS5GYd3GXLS2fr9cZk/s1537/android-find-mobile-phone-2.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1537&quot; data-original-width=&quot;1065&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3HY6x1BQBNyAM0lP5wC1kGYvDxOxO7wVtdCJAJZRz4MgxgBaKSdcDoElDUG9SY5YCjJRgbEXlSlCay6IX1Ni-Joxn_U92DZP0Rux7k88kCyS0gLr8G8ADeP4JFcnv356KOjE5zRNjs6KaqC51YL-k22aotuOQc_JojmwTJDBBaDuS5GYd3GXLS2fr9cZk/s320/android-find-mobile-phone-2.jpg&quot; width=&quot;222&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;點選要找尋的手機 OPPO A38 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglqzCzzeU9KVs7sKQAnpkiH5mBWLeZJUbiDMjbEfVO87upcpWP0iN_-p_6p7c5Z_3w4x4uXQKHzsDUmeaHiHN21Te-KwdiSkYG-2_TMsCJQPfEgrgS4vmE_pkj_041oJSDT1HX-C89s_yAlJPatUfv9I6KpBZcfWVVtLqDTUsH7N-m82f3x_gfTx91x667/s1655/android-find-mobile-phone-3.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1655&quot; data-original-width=&quot;989&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglqzCzzeU9KVs7sKQAnpkiH5mBWLeZJUbiDMjbEfVO87upcpWP0iN_-p_6p7c5Z_3w4x4uXQKHzsDUmeaHiHN21Te-KwdiSkYG-2_TMsCJQPfEgrgS4vmE_pkj_041oJSDT1HX-C89s_yAlJPatUfv9I6KpBZcfWVVtLqDTUsH7N-m82f3x_gfTx91x667/s320/android-find-mobile-phone-3.jpg&quot; width=&quot;191&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;按 &quot;播放音效&quot; 就會持續播放 5 分鐘的響鈴了 :&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2aez9GSj0z2cz5qOxailAwMJZqvVnKRh151ViocRC2eDJ3xoR37ekjDghoHZVXAUY-uaHDvnr2uywyVtiKUS681Xz1-5DGM4JfeyKv8vldNaZjTKu_nnUZ530HyrK-W7I5bbCrKZ2CJouk7Ocm95NeG_7A_Xt39rT4rRyQDPWpyDr9gJ5rXwn-tqmwuep/s1777/android-find-mobile-phone-4.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1777&quot; data-original-width=&quot;921&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2aez9GSj0z2cz5qOxailAwMJZqvVnKRh151ViocRC2eDJ3xoR37ekjDghoHZVXAUY-uaHDvnr2uywyVtiKUS681Xz1-5DGM4JfeyKv8vldNaZjTKu_nnUZ530HyrK-W7I5bbCrKZ2CJouk7Ocm95NeG_7A_Xt39rT4rRyQDPWpyDr9gJ5rXwn-tqmwuep/s320/android-find-mobile-phone-4.jpg&quot; width=&quot;166&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjagdHsebJfXwCKBq_6qX9BhpVCHMk41dFr7HQYPOW14YXjShtVRGxlhhnrLUl67oHx_4tnxeXieCZF8HwBRGfA2wia7hQJjtX_w7MCQ1zBlNv19QeqsqKmJseLuzZkGFJ4WoneIwmH3rNcGXmxys_o7lGm-3a7irdAsC3ixX3nieZDd0gN8gqxEgRA5zDn/s1827/android-find-mobile-phone-5.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1827&quot; data-original-width=&quot;895&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjagdHsebJfXwCKBq_6qX9BhpVCHMk41dFr7HQYPOW14YXjShtVRGxlhhnrLUl67oHx_4tnxeXieCZF8HwBRGfA2wia7hQJjtX_w7MCQ1zBlNv19QeqsqKmJseLuzZkGFJ4WoneIwmH3rNcGXmxys_o7lGm-3a7irdAsC3ixX3nieZDd0gN8gqxEgRA5zDn/s320/android-find-mobile-phone-5.jpg&quot; width=&quot;157&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;最後是在阿泉伯媳婦提醒下在車子副駕椅子下找到手機, 雖然這個方法並未建功 (在車內就算響鈴除非走近也聽不到), 但如果要在家裡某個角落找尋亂放的手機就能派上用場了.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://yhhuang1966.blogspot.com/feeds/2752210402017360639/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment/fullpage/post/4877487320781767952/2752210402017360639' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/2752210402017360639'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/2752210402017360639'/><link rel='alternate' type='text/html' href='https://yhhuang1966.blogspot.com/2026/04/blog-post.html' title='如何尋找靜音中的手機'/><author><name>小狐狸事務所</name><uri>http://www.blogger.com/profile/09435160519044041137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVRmnhel5YQ6BNf1sKgVFmbjdSh9H-UjmfVe-Gtg-ObjGrdiUPjK71r3XzAXjkz9-cSgM4fqQ9W6hXABSF2CZFU7Y4CFCqY1Y6z04KAdMLOh0NBm94VVizyerw3MelR2YntaZYuWv0Edbp95lJ5h-HRUx1GLHgXVr2xVsjI144JLZeTufyuhQsjwZACp_Q/s72-c/android-find-mobile-phone-1.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4877487320781767952.post-2824965805147393719</id><published>2026-04-12T23:57:00.007+08:00</published><updated>2026-04-13T00:10:41.062+08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="生活"/><title type='text'>2026 年第 14 周記事</title><content type='html'>時光飛逝, 思緒還駐留在大阪城門口初綻的櫻花, 怎麼一轉眼已來到四月中旬矣, 三周前還要穿長袖冬衣, 現在已換季來到夏天了, 三月真是一個天氣多變的月份.&amp;nbsp;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;本周回頭抓了一下 LangChain 與 LLM 串接的進度, 其實只是想對借來的書有所交代而已, 目前重點還是 Vibe coding, 以及較專業些的 SDD, 這周末都在補課, 之前購買的線上課程在 3/28 就開課了, 但那時在忙掃墓, 所以沒跟上. 上完課覺得, 我其實都只開發小型專案而已, SDD 對我而言似乎大砲打小鳥. 不過或許以後用得著, 就學起來吧.&amp;nbsp;&amp;nbsp;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;今天早上爸去參加愛心會理監事開會, 中午回來吃過飯後發現手機不見了, 我用 Google 地圖定位發現手機在阿泉伯家, 但我騎車下去找, 現場撥電話也沒聽到響鈴, 猜測可能掉水裡, 但周邊水溝來回也沒找著. 傍晚忙完芭樂套袋, 騎機車再次去阿泉伯家, 剛好遇見他媳婦阿勤, 我說好奇怪, 明明定位在你家, 為何找不到? 她說會不會掉在車裡, 因為早上爸是搭阿泉伯車去開會, 打開車門果然掉在椅子下, 難怪撥電話都聽不到響鈴. 幸好找到了, 不然得買新手機, 且還要跑中華電信換 SIM 卡.&amp;nbsp;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://yhhuang1966.blogspot.com/feeds/2824965805147393719/comments/default' title='張貼留言'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment/fullpage/post/4877487320781767952/2824965805147393719' title='0 個意見'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/2824965805147393719'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/4877487320781767952/posts/default/2824965805147393719'/><link rel='alternate' type='text/html' href='https://yhhuang1966.blogspot.com/2026/04/2026-14.html' title='2026 年第 14 周記事'/><author><name>小狐狸事務所</name><uri>http://www.blogger.com/profile/09435160519044041137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>