古風 Manifold 小筆記

文天祥的正氣歌 [1] 說:"天地有正氣,雜然賦流形。" 這個 “流形" 就是 Manifold 的中文翻譯。後兩句 “下則為河嶽" 就是說一般人站在地球上,還以為地球是平的,"上則為日星" 就是抬頭一看,感到世界大到甚至不只是 3D。

正經地說,流形是一個局部看起來像歐幾里得空間(平坦的空間),但整體結構可能非常複雜且彎曲的拓樸空間。一張 1024×1024 的彩色照片,在數學上是一個高達 300 萬維度的空間,但「有意義的貓狗照片」只佔據這個 300 萬維空間裡一個極小的低維流形,不是每個空間中的值都有特殊意義。這種群聚就像是太陽系裡面的星球彼此靠得很"近",若是出了太陽系,宇宙就顯得很稀疏,直到遇到其他星系。

對 AI 來說,世界模型充滿雜訊且計算量太大。模型會透過編碼器(Encoder),將高維的觀測數據「壓縮」並映射到一個低維度的潛在空間(Latent Space) 中。這個潛在空間本質上就是在捕捉數據所在的流形。一旦 AI 掌握了這個低維流形的潛在空間,它就能在這個平滑、連續的流形上做計算。這也可以解釋 transformer – encoder、decoder 為何會有用。

當初 DeepSeek 出來的時候,也用上了 latent space 壓縮的技術。我當時想,做完這個 lossy 的壓縮,雖然節省計算量,但是資訊丟掉就回不來了啊!? 因為那個 monent 我還沒有流形的觀念;如果有,我就不用緊張了。

在數學上,流形不一定可以微分。但是要訓練 AI ,就要計算梯度。因此我們只能假設它可以被微分,也就是要假裝它更平坦一點。因此就算真實的流形不可微,我們也得製造出一個可微分的流形。所謂 “人心惟危,道心惟微" [2],人類明明知道會有危險的奇點 (Singularity),但是演算法只能讓它可微分。以目前 AI 強大的戰力來看,這個有意的忽略,似乎影響不大。

[REF]

  1. 正氣歌
  2. 十六字心傳

養龍蝦的小筆記

龍蝦 OpenClaw 最近很紅,安裝龍蝦後, 有幾個特別的小心得可以分享。像是龍蝦會燒 token 這種事,大家都知道就跳過。我裝的是 Intel X86 和 ARM 的 Ubuntu。

  1. 賜名 – 我把它取名為螃蟹, 不管是 AICrab、MyCrab,它都會把圖示改成螃蟹。也就是說它在乎自己的名字,而且理解其意義。

2. 重新安裝

如果出了大錯, 想要重新安裝的話:光是砍掉 ~.openclaw 目錄還不夠,因為有些東西放在 /usr/bin/.nvm、 /usr/bin/nodejs 下面。如果沒有斬草除根。就算重新安裝還是會記住一些錯誤的設定。此時就算叫出 doctor 也修不好。

openclaw doctor --fix // 看醫生也治不好

3. Telegram 設定好,龍蝦和 Telegram 串不起來。網路文章通常會少寫一步 pairing. XXX 就是 telegram token。

openclaw pairing approve telegram XXXXXX

4. 搞爛掉之後,會遇到 gateway 連不上。

主要是 gateway token 出錯。就算用 openclaw config 重新設定成只看 password,它還是說 gateway API 錯,要去 UI 改。但是 UI 點進去啥都沒有,這條路子我修不好,最後選擇重灌。

openclaw config // 看起來都對,跑起來不對

5. Clawhub 安裝了還是找不到。這是因為 gloabl 安裝也不會自動加到路徑。

npm install -g clawhub // 以為必成功
npm list -g --depth=0 | grep clawhub // 什麼都撈不到
npm list -g clawhub // 看看它裝到哪裡去了?
echo $PATH // 果然沒有
NPM_GLOBAL_BIN=$(npm bin -g) // 找出來
echo "export PATH=\$NPM_GLOBAL_BIN:\$PATH\" >> ~/.bashrc // 加進去
source ~/.bashrc // 重新執行

6. 一把 key 不宜養太多隻龍蝦

本來在 X86 還用得好好的,後來想試試看 ARM 的版本時,因為接到 LLM Oauth 的 toekn 都用同一個吧?! 結果兩隻龍蝦都掛了,靜置一晚後復活,不敢再同時養。

API rate limit reached. Plase try again later.

整體來說,養龍蝦還是滿好玩的。最後用一點篇幅歪樓到 skill。龍蝦其中一個厲害的地方是 skill [1]。Skill 主要是 prompt、又帶 metadata (簡短自述)、和可選用的 code,跟 MCP 的 prompt (可選)、metadata (資料)、加 tools 算是有異曲同工之妙。

以我的認知,原本 MCP 的特色是好好描述目標、但在執行手段上保留彈性,好處是使用者可以對背後的操作一無所知。而 Claude Code / 龍蝦正面表列 skill,等於是把手段也大致講了,此時歪樓的機會更低。相對地、這個場景對使用者的知識水準要求更高,但 openClaw 的使用者正是龍蝦本人,這個門檻問題自然就不存在了 – 只需要擔心它拆家…就好?!

[後記]

我發現寄養家庭 (伺服器代管) 好像不能讓我免費嵌入 Youtube 影片,所以我做了一點調整。並補充一段內容。

Skill 與 Tools

我覺得現在人勝過 AI 的地方,主要是知識更新的速度。我問 AI Tools 和 Skills 有什麼差別的話,有的 AI 會說這是類似的東西,只是各家命名不同 (Claude 4.6 Sonnet 2026/3/5)。但如果你信了,就沒有機會聽到另外一個解釋 (文皓 blog 2026-02-05)[2]。

Tools 可以想像為食材,Skill 可以想像為烹飪技巧、食譜。所以我們有時候會聽到 Agent Skill 這種說法,因為 Agent 確實可以實現 Skill,但是沒有人說 Agent tools。

[REF]

  1. 程序員老王 – 10分钟弄懂 什么是大模型Skill
  2. https://yu-wenhao.com/zh-TW/blog/openclaw-tools-skills-tutorial/

馬年快閃電磁學

馬年到了! 和馬有關的成語、吉祥話、成句都紛紛出爐,連馬克斯威爾 (Maxwell) 方程式都出來湊一腳。其它都先不管,講一下和量子力學有關的部分。

第四定律 – “安培-馬克士威定律" 原本長這樣子:

右邊第一項安培定律告訴我們電流 J 流動產生磁場。μ0 是真空磁導率。

右邊第二項是馬克斯威爾後來加上去的修正項。意思是:「變化的電場」(E 對時間微分) 也能產生磁場,係數是"真空磁導率" μ0 再乘上 “真空電容率" ε0

為什麼不用一個 μ1 搞定,而要放任 μ0 ε0 存在,增加電信系學生背公式的困難度呢?原因有三個。

  1. 增加這兩項的相似性。一看就都是電流家族。
  2. 隱藏彩蛋。 μ0 ε0 = 1 / C2 ,Maxwell 把這兩個係數乘出 μ1 的時候,發現他和光速的平方的倒數幾乎一樣。因此他預測了光也是電磁波。However,是說他的計算機有平方鍵和倒數鍵嗎? 為什麼這樣也能感應到?

總之,與其用路人般的 μ1 ,不如寫成光速的平方的倒數更酷。

第三個一點 (教官式中文上身,主要是我不會打清單標題 3)。上述的表示法是國際單位制 (Système International d’Unités,簡稱SI),便於使用安培、伏特這些單位。假如使用高斯單位制 (Gaussian units) 或 自然單位制,公式就可以直接吸收掉係數 (早說嘛,我大二就可以輕鬆了),表示為

    在量子力學中,甚至把光速視為常數 1,那公式就更簡潔了。

    在量子力學中,原本的公式都成立;只是要在量子場論/量子電動力學 (Quantum Electrodynamics, QED) 中重新詮釋。簡單地說,電磁場都不再連續,而是量子態。電與磁對稱,遵守規範對稱性 (Gauge Symmetry) [5]。

    跳過不直覺不好記的部分,我們可以簡單記得。馬克斯威爾觀察到 μ0 ε0 參數相乘近似光速的平方的倒數,在忽略量子效應時是對的,但也真的是個近似值。想要精準地推算,就不能忽略量子效應,例如光子的離散性 [6]。

    古典世界看起來是連續、對稱的類比,走近一看原來是數位的。我們理解世界的解析度愈低就愈簡單,解析度愈高就會愈覺得 “是這樣沒錯,但不是這樣"。有沒有發現?從蛇年到馬年就是不連續的;所以這又給了我們一次重新立大志做大事的機會。

    祝大家蛇年量子跳躍到馬年,別擔心測不準,因為幸福的位置已經塌縮在你身邊!

    [REF]

    1. 國際單位制
    2. 高斯單位制
    3. 自然單位制
    4. 諧振子
    5. 規範場論
    6. 光電效應

    MCP 小整理

    學習 MCP (Model Context Protocol) 的時候, 我看了兩個版本的網路資料,一個是 Antropic 版 [1],一個是 Scrimba 版 [2],兩者的教法天差地別,容後再敘。如果只要用一個關鍵詞來理解,那還真得是 USB type C。所以我先說明一下 MCP 它 “USB-C" 在哪裡?

    USB-C 主要的特徵是 protocol 無需使用者設定,接頭即插即用,正反都可以插,不會在只有兩個選擇中竟然插錯兩三次…^^。對於使用大語言模型的人 (user) 來說,同樣不需要知道 MCP Server 怎麼運作?MCP Server 們只是在哪邊等 MCP client 呼叫。至於 MCP Client 怎麼知道要呼叫哪個 server?當然,要先問啊!

    在初始化階段,MCP Client 會根據 local host 的 configuration – 通常是 JSON 檔,先問問每個 MCP server 有哪些本領 (tools)?接下來就等著 LLM 自己評估需要那些 tools?例如要查新竹的陣風幾級?MCP Client 早已將收集到的工具清單注入給 LLM,讓它知道 MCP Server 1 有報氣象的 weather tool.,那 LLM 就會呼叫這個 tool,MCP Client 自動會找到對應 MCP server 1,而不需要再次輪詢所有 MCP server 們。

    如果兩個 MCP server 都有一樣的 tool,目前的 MCP Client 實作通常會處理這件事(例如在工具名稱前加上 Server 前綴 serverA_get_status),或者後連線的會覆蓋前面的。這取決於 Client 的具體實作方式。

    最後描述一下上課心得。

    Antropic 的課程主打 AI 本人改作業,每一題 5 分滿分的英文申論題。這個我寫得好挫折。按照自己的意思寫,只有 2 ~ 3 分不等。AI 說你要用課程中的關鍵字啊!所以我要添上 toolslistrequest (MCP client 問 MCP server 會啥子時用的) 等 function call。抄得太多,AI 又說你要用自己的話回答。基本上 AI 也會提示,"你剛剛沒講到 security 喔",真是要被 “蕊死",好不容易才抓到訣竅。這是我第一次上網課上到有恐懼心理,還好最後 pass 了。

    Scrimba 的課程偏向引導,code 示範完就停下來,"now is your turn"。但依樣畫葫蘆還是比較容易的。而且它不改作業,只要 submit 後,就回到講師寫正確答案。像是 MCP 比喻成 USB,強調 MCP 三元素: tools、resources (database)、和 prompt (主要是 prompt template, 這個是 optional),都只出現在本課程。Antropic 是 MCP 發明人,當然它怎麼說怎麼對。對於學習者,聽聽 second opinion 也是不錯的。

    [REF]

    1. https://www.coursera.org/learn/introduction-to-model-context-protocol/home/welcome
    2. https://www.coursera.org/learn/intro-to-model-context-protocol-mcp/home/welcome

    LCEL 與 Agent

    LCEL 全名 LangChain Expression Language, 是一種描述 LangChain 架構的語言。最大的特徵就是看到 A = B | C | D 這種表示法 – 說明了 LCEL 串接多個可執行單元的特性,是 LangChain 的進階實現。而 ‘|’ 的理解和 Linux 的 pipe 很像,它是非同步的串接。

    舉例來說,一個 LLM 的 LangChain 可以表示為:

    chain = prompt | LLM model | Output Parser

    依序做好這三件事:有方向性的提示、強大的大語言模型、友善的輸出格式,就可以提升使用者體驗。

    但是顯然這樣還不太夠,比方說,LLM 需要查一筆它沒被訓練過的資料,在上面的 pipe 就無法做到。換個例子,我想幫 AI 助理取個名字,它也會說好。但是一轉眼什麼山盟海誓都忘光了!

    顯然,我們要有個負責的 agent,把 function call 的能力加進來;而且 call 完之後,還要餵給 LLM 做 “人性化" 的自然語言潤飾。這是一個循環的路徑,直到 AI 判斷它不再需要任何外部工具,就可以結束迴圈,跟使用者報告最終結果了。

    那麼這個 code 長什麼樣子? 首先把基本元件準備好:

    # 初始化模型
    llm = ChatOpenAI(model="gpt-4o", temperature=0)
    
    # 將工具打包成列表
    tools = [get_current_weather] # 以問天氣的函式為例
    
    # 給出配合 function 的 prompt
    prompt = ChatPromptTemplate.from_messages(
        [
            ("system", "你是天氣助理,請根據工具的結果來回答問題。"),
            ("placeholder", "{chat_history}"),  # 預留給歷史訊息
            ("human", "{input}"), # 真正的輸入
            ("placeholder", "{agent_scratchpad}"), # 於思考和記錄中間步驟
        ]
    )

    創建 agent

    # 創建 Tool Calling Agent
    # 將 LLM、Prompt 和 Tools 組合起來,處理 Function Calling 的所有複雜流程
    agent = create_tool_calling_agent(llm, tools, prompt)

    執行 agent

    # 創建執行器, 跑 模型思考 -> 呼叫工具 -> 再次思考 -> 輸出答案的循環
    agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

    設計人機介面

    def run_langchain_example(user_prompt: str):
        print(f"👤 用戶提問: {user_prompt}")
        
        # 使用 invoke 運行 Agent,LangChain 會自動管理多輪 API 呼叫
        result = agent_executor.invoke({"input": user_prompt})
        
        print(f"🌟 最終答案: {result['output']}")

    測試案例

    # 示範一:Agent 判斷需要呼叫工具
    run_langchain_example("請問新竹現在的天氣怎麼樣?請告訴我攝氏溫度。")
    
    # 示範二:Agent 判斷不需要呼叫工具,直接回答
    run_langchain_example("什麼是 LLM?請用中文簡短回答。")

    這邊要留意的是,第一個問天氣的問題,LLM 顯然要用到 tool 去外面問,第二個問題不需要即時的資料,所以它自己回答就好。

    意猶未盡的讀者可能好奇 function 長怎樣? 其實就很一般,主要的特色是用 @tool decorator。這樣可以獲得很多好處,最重要的一點是 agent 都認得它的 JSON 輸出,方便資料異步流動。

    # LangChain 會自動將這個 Python 函數轉換成模型可理解的 JSON Schema
    from langchain_core.tools import tool
    
    @tool
    def get_current_weather(location: str, unit: str = "celsius") -> str:
        if "新竹" in location or "hsinchu" in location.lower():
            return "風大啦! 新竹就是風大啦!"
        else:
            return f"我不知道 {location} 的天氣啦!"

    另外,追根究柢的讀者可能想問,code 裡面怎麼沒看到 ‘|’? 它跑那裡去了? 沒錯,上面講的都是 agent,它比較進階,可以動態跑流程。反而 LCEL 只是 LangChain 的實行方式,它就是一個線性的 chain。

    我們由奢返儉,回過頭來看 LCEL,它不能跟 agent 比,只能打敗傳統的 LangChain。標為紅色的是 LCEL 特色的 coding style。

    def demonstrate_legacy_chain():
     
        # 定義模型
        llm = ChatOpenAI(temperature=0)
        
        # 定義 Prompt Template
        template = "Translate English text to Chinese: {text}"
        prompt = PromptTemplate(template=template, input_variables=["text"])
        
        # 建立 Chain (透過類別組合)
        # 缺點:語法較冗長,看不到資料流向,且 output 通常包含原始 meta data
        chain = LLMChain(llm=llm, prompt=prompt)
        
        # 執行
        input_text = "Make American Great Again!"
        result = chain.run(input_text)

    VS

    def demonstrate_lcel():
        
        # 定義模型
        model = ChatOpenAI(temperature=0)
        
        # 定義 Prompt Template, 使用更現代的 ChatPromptTemplate
        prompt = ChatPromptTemplate.from_template("Translate English text to Chinese: {text}")
        
        # 定義 Output Parser (將 AI Message 轉為純字串)
        output_parser = StrOutputParser()
        
        # 建立 Chain (使用 Pipe '|' 運算符)
        # 優點:Unix 風格管道,由左至右邏輯清晰,易於修改和擴展
        chain = prompt | model | output_parser
        
        # 執行
        input_text = "Make American Great Again!"
        result = chain.invoke({"text": input_text})

    這兩個都是一次性的 Q&A。

    1. Functions, Tools and Agents with LangChain