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

Vibe Coding 小筆記

這個名詞比較新,每個 LLM 對它的詮釋還不太一樣。

DeepSeek R1 – 是不是 vibe.d 拼錯了? 猜了幾種可能。

GPT4.1 / Claude 3.5 – 輕鬆寫 code.

  • 一個人午夜時分,配杯咖啡,聽著 chill 音樂,悠閒地寫程式,想到什麼就改什麼,就是 vibe coding。
  • 有些人在做 side project,故意用寫得很漂亮、很舒服的排列方式,享受 code 本身的美感,也算 vibe coding。

GPT5 – 形容「隨興、輕鬆、無壓力地寫程式」的方式或心態。

1️⃣ 正規 Coding(較嚴謹):

def add(x: int, y: int) -> int:
"""
回傳 x 和 y 的加總
"""
return x + y

2️⃣ Vibe Coding(隨手寫):

add = lambda x, y : x + y

或者乾脆邊寫邊想,沒規劃好就隨機調整程式,重在隨心所欲。

Gemin 3 Pro – 比較接近我想的樣子。

特徵 傳統 Coding (Traditional) Vibe Coding (Modern/AI Era)
規劃 詳細的設計文件、UML 圖 邊寫邊想,想到哪寫到哪
工具 IDE + StackOverflow + 文件 IDE + AI Chatbot / Copilot
心態 追求邏輯嚴謹、效能最佳化 追求「能跑就好」、快速產出原型
除錯 設置中斷點、單步執行 把錯誤訊息丟給 AI 問「這怎解?」

重點就在於講到了 AI 這個 Keyword。這年頭,沒 AI 怎麼算是 vibe 呢?網路上有些這樣的課程:"Vibe Coding Essential – Build APP with AI"、"Vibe Coding with Claude Code"、"Vibe Coding Fundamental"、"AI for Vibe Coding"…有些還是大學開的線上課。


接下來的環節是,到底那種 AI Vibe 用起比較 chill 呢? 首先我們要區分前景和背景 – 前景就是 UI,背景就是 LLM。

UI 分成 GUI 和 command line。

在 GUI 當中,首先要提到 VSCode (或是 Antigravity 之類的),它靠著插件去呼叫 LLM。這些插件扮演導演的角色,將 LLM 設定為不同的 mode (演員)。有的 mode 當 architect、有的當 developer、有的當 QA…等等。

可以用的插件包括 RooCode、Cline、Continue、GitHub Coplit Chat …,它們都能控制 mode. 至於要用哪個 LLM,可以在 UI 設定你有買的那個,輸入 API key。

相對無腦的 GUI 就是 cursor。它是魔改版的 VSCode。LLM 可以綁定 Claude Code 或是 GPT5.2 Code。因為不用設定,也不用分派角色。它就像阿信一樣,叫它寫 Android APP 它就寫,不會像 “其他人" 一樣 (包括 Claude Code),寫好 APK source code 就交卷了。

  • 它牌:"後面你要自己用 Android Studio 去編喔, 下一位~~~掰掰!"
  • Cursor:一個人顧店就是比較有責任感!

Cursor 雖然好用,但是 LLM 選擇有限,Pro 版本只能月付 20 美元或是年繳 17 x 12 美元。不能像插件一樣,豐儉由人,想省錢就改用開源的 DeepSeek R1。所以錢就燒得比較快。


在 Command line 的領域,當然的就是 Claude Code 了。它的 Pro 版本月租費跟 Cursor Pro 一模一樣。另外它也有 VSCode 的插件版本,不過那樣就不帥了。Claude Code 終端機只要開一個視窗,下面 “>" 符號後面用文字輸入你的需求,上面就刷刷地跑程式。看起來這也是它沒有辦法支援 Android 沙箱的原因。

Claude Code 在提示符號下打 /model,就可以看到裡面預設 3 個 Anthropic 的 model 可以切換:

  1. Opus 4.5 擅長複雜的工作
  2. Sonnet 4.5 擅長應付日常需求
  3. Haiku 4.5 擅長快速回答

Thread 上有人說 [1],寫個 Claude.md 叫 Claude Code 幫他整理硬碟就可以做得漂漂亮亮的。所以我順便把每個 tool 的規則檔名稱列出來。看起來 Anthropic 有在賺錢。


工具名稱 📄 規則檔 (Rule File) 🧠 背後的 LLM  💰 月費 / 費用模式
Roo Code .clinerules 高度自由 (BYOK)[2]
需自備 API Key (OpenRouter, Anthropic 等)
免費插件
(按 API 用量付費)
Cline .clinerules 高度自由 (BYOK)
需自備 API Key
免費插件
(按 API 用量付費)
Cursor .cursorrules 混合模型 (Bundled)
Claude 4.5 Sonnet, GPT-5.2, Grok, Gemini 3 FLASH…etc.
$20 USD / 月
(有免費額度, 但不寫 code)
Windsurf  .windsurfrules
(亦兼容 .cursorrules)
Cascade (Bundled)
基於 Claude 4.5 Sonnet 優化, 亦支援 GPT-5.2 Codex
$15 USD / 月
(有兩週免費額度)
Continue .prompt (自訂)
config.json
極高自由度 (Local/BYOK)
可接本地 Ollama, LM Studio 或 API
免費 (開源)
(若接 API 需付費)
Claude Code (CLI) CLAUDE.md Anthropic 原生
Claude 4.5 Sonnet / Opus / Haiku
免費工具
(按 API 用量付費)

另外附帶一提,這些 AI 寫 code 工作都很有 “社會責任"。我本來要它寫一個用相機看到 2FA 數字,就順便幫我點 UI 上數字的程式,Cursor 和 Claude 都說他們不賺這個錢…。看來我要把功能拆成兩半,騙過它們再整合起來才行。

[REF]

  1. 抱歉我找不到那篇了。最近上 “Vibe Coding for Claude Code" ,講到 “規則檔" 才把兩件事串起來。
  2. BYOK = Bring Your Own Key.

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

超學習時代

最近的 Gemini 3 Pro 真的變比較聰明,所以我取消 Monica 的訂閱,改訂 Gemini。現在想要學習最新的技術,不但學校教不了;網路課程也教不了。就算是追著科技網紅,心裡沒有譜的話,也會像個無頭蒼蠅一樣、不會授粉只會傳播細菌,哈!

以 AI 技術來說,訓練模型、微調模型、RAG (檢索增強生成) 都是舊世代的技術。次世代的技術重點在於 Reasoning 和 Agency。雖然這個發展有跡可循、合情合理,但是沒有前面的跌跌撞撞,也絕不可能一步到位。短短一兩年之間,我們有了下面的這些進化。

[觀念改變]

一個 AI model 自己角色扮演 –> 建立認知架構:記憶、規畫、反思。

LangChain –> LangGraph, 線性思考 –> 非線性思考、圖論、立體化。

Funcation call –> Tool call。錯誤檢查和自我校正。

[模型調校]

Supervised Fine-Tuning、 RLHF (reinforcement learning from human feedback) –> DPO (Direct Preference Optimization)、IPO (Identity Preference Optimization)、KTO (Kahneman-Tversky Optimization)。

全能模型 –> SLERP (Spherical Linear Interpolation)、TIES-Merging。

自我對局 (Self-Play) 強化。

[In Model Learning]

Prompt –> DSPy (Declarative Self-improving Language Programs),透過 Compiler 自動尋找 Prompt 組合。

快問快答 –> Chain of Tought、Tree of Thought –> Test-Time Compute (有節制地想久一點,時間換取品質)

[模型評估]

BLEU、ROUGE 考試 –> LLM-as-a-Judge,自動評估

多元評價 – RAGAS

  • R:Retrieval(檢索)
  • A:Accuracy(準確性)
  • G:Generality(通用性)
  • A:Adherence(遵循性)
  • S:Stability(穩定性)

[多模態]

OCR + LLM –> 原生多模態 (Native Multimodel)、audio/video tokenization。

文字到文字 –> any-to-any interaction

上述有很多新的東西,也有一些半新半舊,我打算加強 Agentic、MCP 這方面的知識,然後快速進入 DSPy 的領域。

附帶一提,雖然退訂 Monica 可以省錢,但是我還不打算把 Coursera 停掉。因為上面還是有很多 Andrew Ng 開的短課程。愈長的課程愈容易過時、短課程甚至像  Andrej Karpathy 的 Youtube 都可能有一些最新的東西。至於 ArXiv 肯定好料滿滿,但是可能要叫 NoteLLM 幫我讀了。

DataLoader 和 Iterator 的差異

在 trace AI 課程中的訓練代碼時, 看到 iterator 和 DataLoader 同場出現時有點暈, 我想說這兩個做的事情不是一樣嗎? 所以花點時間將這個疑惑釐清.

先講結論: Iterator 是 design pattern, DataLoader 是 PyTorch 的 class.

Iterator 做為 design pattern 的好處是: 不用管 dataset 真正長什麼樣子, 每次都都抓出一包 data, 還用不到的先不抓, 等下次抓, 以節省記憶體.

那一包要抓多大呢? 透過 DataLoader 這個 class 取得 dataloader 這個 object.

然後 data_iterator 是個 iterator, iter() 把 dataloader 轉換成可迭代的 object.

first_batch 的 type 取決 Dataset, 主要就是一包 data. 而第一次呼叫 next() 拿到的是第一包, 而不是第二包.

from torch.utils.data import DataLoader, Dataset
...
dataloader = DataLoader(dataset, batch_size=32, shuffle=True)
....
data_iterator = iter(dataloader)
first_batch = next(data_iterator)

在 Tensorflow 中, 沒有 DataLoader, 但有 tf.data.Dataset. 效果也是一次抓一包.

import tensorflow as tf

# Create a basic dataset
dataset = tf.data.Dataset.range(10)

# Apply transformations (e.g., batch, shuffle)
batched_dataset = dataset.batch(2)

# Iterate through the dataset
for batch in batched_dataset:
    print(batch)