← 新聞追蹤

每週一早上那張出貨表,我手算了三年:花四十分鐘寫個小程式,讓 AI 自己伸手去算

來源:業州愚公 | 2026年11月3日

作者:業州愚公

業州愚公是一位需要每週一早上手動計算出貨表的使用者,花了三年時間進行這項工作。為了簡化這個過程,他花了四十分鐘時間寫了一個小程式,利用AI工具自動計算數字。這個程式只需兩個工具,讓AI可以自己從資料夾中提取數字並完成計算。業州愚公的目的是簡化機械動作,減少手動計算的時間和精力,讓自己可以專注於更重要的工作。這個案例展示了如何利用AI工具簡化重複性工作,提高工作效率。

編輯評論 / Editor's Note

這位員工運用簡單的程式設計和AI工具,成功地自動化了每週一早上的出貨表計算工作,節省了大量時間和人力。這個案例展示了如何利用科技和創新思維來提升工作效率和簡化流程。透過這種方式,不僅可以減少人為計算的錯誤,也可以讓員工專注於更高價值的工作任務。

菜鳥之友系列:《AI 工具實操與提示詞》・工具篇作者:業州愚公

星期一早上八點四十,咖啡還沒喝完,三個 CSV 已在螢幕上排好隊。打開第一個,篩日期,拉樞紐,把合計貼進郵件正文,再校對一次小數點。這套動作我做了三年。

上個月把它拆開看,真正需要判斷的只有最後那句「這數字對不對」,前面全是機械動作。機械動作可以交出去——不必等廠商出功能,也不必學會寫完整應用,四十行程式就夠。

上一集講的是把三個現成的工具串成一條線。今天反過來,講自己動手做一個:只會兩件事、大約四十行的 MCP 伺服器。

一、先搞清楚 MCP 伺服器不是「外掛商店」

MCP 全名 Model Context Protocol,是 Anthropic 在 2024 年底公開的開放協議。講白話:它規定「AI 客戶端」和「能做事的小程式」之間怎麼對話——問答格式、出錯怎麼回報。底層是 JSON-RPC 2.0,傳輸主要有兩種:stdio,客戶端把你的程式當子行程啟動,用標準輸入輸出講話;另一種走 HTTP。

一個伺服器提供的東西分三類,新手最常混淆:

能力 誰決定 放什麼 第一版
tools模型自己判斷查檔、算數、寄信先做,兩三個就夠
resources使用者或客戶端指定報表模板、欄位說明第二批再加
prompts使用者主動取用預先寫好的指令模板最後再包

工具排第一,是因為它解決「AI 只會說、不會做」:不必你再複製貼上,它自己伸手去拿。

現成的那麼多,為什麼要自己寫?因為它們不知道你的欄位叫「出貨量(箱)」還是「Qty」,也不知道檔案在 D 槽哪個資料夾。通用工具解通用問題,你家那張表只有你清楚。

二、動手前先回答三個問題

寫程式之前,先讓自己用嘴巴講一遍。講不順,就別寫。

我這次的答案很窄:列出資料夾裡有哪些 CSV、把某一欄加總。就這兩件。看起來笨,但笨工具最不容易出錯。

接下來是最影響成敗的一步:寫工具描述。模型是靠名字和描述,在幾十個工具裡挑一個來用的。描述含糊,它乾脆不用。

寫法 描述 模型的反應
壞處理 CSV不確定何時該用,常跳過
好列出資料夾內所有 .csv,回傳檔名與欄位。不確定檔名或欄位叫什麼時用。知道第一步該先呼叫它探路

第二種多了兩件事:說清楚回傳什麼、什麼時候該用。這兩句比程式更值錢。

三、四十分鐘,四十行程式

環境兩樣:Python 3.10 以上(版本要求以官方文件為準),以及官方 SDK(pip install mcp)。SDK 裡的 FastMCP 封裝,加上裝飾器就把函式變成工具。

程式叫 shipping_server.py,全文如下:

from mcp.server.fastmcp import FastMCP
import csv
from pathlib import Path

mcp = FastMCP("shipping-files")

@mcp.tool()
def list_csv(folder: str) -> str:
    """列出資料夾內所有 CSV,回傳檔名與欄位。"""
    out = []
    for f in sorted(Path(folder).expanduser().glob("*.csv")):
        with f.open(encoding="utf-8-sig", newline="") as fh:
            out.append(f"{f.name}|{', '.join(next(csv.reader(fh), []))}")
    return "\n".join(out) or "沒有 CSV"

@mcp.tool()
def sum_column(file: str, column: str) -> str:
    """把某個 CSV 的某欄加總,回傳筆數與合計。"""
    total, n = 0.0, 0
    with open(file, encoding="utf-8-sig", newline="") as fh:
        for row in csv.DictReader(fh):
            v = (row.get(column) or "").replace(",", "").strip()
            if v:
                total += float(v)
                n += 1
    return f"共 {n} 筆,合計 {total:,.2f}"

if __name__ == "__main__":
    mcp.run()

三個地方值得多看兩眼:

型別註解會變成參數說明。 folder: str 不是給人看的,SDK 拿它生成參數描述,模型才知道該填字串還是數字。省掉型別,模型就容易亂填。

docstring 就是工具描述。 那行中文會送到模型面前,寫「做什麼、什麼時候用」,不是「這是某某函式」。

mcp.run() 預設就是 stdio。 不開連接埠、不跑背景服務,客戶端啟動你、你回話。

四、接到你平常聊天的視窗

支援 MCP 的桌面客戶端(Claude Desktop、Cherry Studio 這類)都讀一份 JSON 設定,結構大同小異,細節以官方最新版本為準:

{
  "mcpServers": {
    "shipping-files": {
      "command": "C:/Users/you/AppData/Local/Programs/Python/Python312/python.exe",
      "args": ["D:/work/mcp/shipping_server.py"]
    }
  }
}

三個新手一定會卡的地方:

一,command 填直譯器完整路徑。 不是 python 三個字。客戶端從圖形介面啟動,不一定繼承你的 PATH。

二,路徑一律絕對路徑。 客戶端啟動子行程時的工作目錄通常不是你以為的那個,相對路徑等於賭運氣。

三,改完要完全退出再開。 關視窗不算,要從選單真正退出,多數客戶端只在啟動時讀一次。

改完重啟,問「D 槽出貨資料夾裡有哪些檔案」,它應該先呼叫 list_csv,列出檔名和欄位,再問你要加總哪一欄。

五、四個一定會踩的坑

坑一:程式裡不要 print。 stdio 模式下,標準輸出就是傳 JSON-RPC 的通道。你印一行「開始處理……」,客戶端收到一段不是 JSON 的東西,連線可能直接斷,而且不會告訴你原因。除錯請寫 stderr 或 logging 到檔案。

坑二:編碼。 Excel 另存的 CSV,在中文 Windows 上常見帶 BOM 的 UTF-8,不處理第一欄會變成 \ufeff出貨量——你以為打錯字,其實是隱形字元。程式裡的 utf-8-sig 就是吃 BOM 用的。

坑三:描述太省。 十個失敗案例八個出在這,工具叫 do_stuff、描述寫「處理資料」,模型只好不用。

坑四:第一版塞太多。 一次給十個工具,模型選擇困難,還吃掉 context window。兩三個清楚的勝過十個模糊的。

驗證小技巧: 先別急著接客戶端,用官方 Inspector(npx @modelcontextprotocol/inspector)連上手動呼叫一次。那裡跑得通,問題就只剩設定檔。

六、安全邊界:它跑在你的權限上

本機 MCP 伺服器不是沙盒裡的訪客。它用你的身分和權限在跑,能讀的檔案就是你能讀的。這點比功能重要。

一個實際教訓: 第一版我把「輸出資料夾」也做成參數,結果模型填了整個磁碟根目錄。雖然只是讀取,但「不確定它下一步要動哪裡」更難受。後來改成固定路徑。

七、這條線往上還能怎麼長

四十行只是起點。同一個骨架再加三樣,就從「能跑」變成「好用」:

加什麼 做法 何時值得
resources把報表模板掛上去,模型照同一格式填每週都交同一種表
prompts把「先列檔、再確認欄位、再加總、照模板輸出」寫成模板流程已重複三次以上
第二個伺服器算數字跟寄信分開,各一支小程式串起來想一次做完一整條活

最快樂的一刻不是它跑通那秒,而是下個星期一,你打開郵件發現數字已經算好,只等你按下寄出。工具的價值不在它多聰明,而在它替你重複了多少次。

工具推薦隨身機器人(新用戶免費試用 7 天)

下一篇預告: 伺服器能跑了,接著要問「它該知道多少」。下一篇講 MCP 的 resources 與 prompts——怎麼把報表模板、欄位定義、內部規矩掛上去,讓 AI 不只會動作,還知道照誰的規矩來。

業州愚公 | SY 視野(sylogs.com)原創內容,轉載請註明出處。