← 新闻追踪

每周一早上那张出货表,我手算了三年:花四十分钟写个小程序,让 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)原创内容,转载请注明出处。