這篇文章的主張很簡單。
試圖以 DX 直接「打掉 Excel」的做法,通常都會失敗。
比較容易成功的,是把 Excel 保留為現場 UI,將背後的計算、資料更新、稽核日誌、報表產生改成 API 化的設計。
附帶的技術文章製作提示中,強調了讀者實務問題的解決、可重現的設計、程式碼/偽程式碼/檢查清單,以及與 Enegaeru API 的自然銜接。
另外,在 Enegaeru API 規格中,也整理了以 OpenAPI 3.0 格式公開的共通 API、登入、補助金搜尋、電費方案取得、電費計算、用電量計算、太陽能發電量計算、設備導入模擬等 API 群。
當你要把 Excel 業務 API 化時,第一件不該做的事是:
不要一開始就說「把 Excel 拿掉吧」。
對現場來說,Excel 不只是試算表軟體。
它是輸入畫面、是筆記本、是報價單草稿、是與客戶對話的紀錄,有時甚至就是業務規則本身。
所以,如果 DX 一上來就要把 Excel 捨棄,現場聽起來會像這樣:
從明天開始,把你工作的工具收走。
改用一套還沒完成的新系統。
那不是 DX,通常只是事故。
這篇文章要說明的是:不要把 Excel 當敵人,而是保留 Excel 作為入口,同時把背後的系統 API 化。
對象不只是工程師。
更希望讓了解 Excel 業務現場的非工程師、DX 負責人、業務企劃、BPO 負責人、流程改善負責人都能讀懂。
本文將涵蓋以下主題:
OpenAPI 是一種標準化的介面描述規格,用來定義 HTTP API,讓人類與電腦都能理解。換言之,它不只讓 API 成為「可使用的元件」,也讓 API 成為「可以被說明的契約」。(OpenAPI Initiative Publications)
Excel 業務 API 化真正重要的,不是最新技術棧。
而是:哪些留在 Excel,哪些交給 API,哪些成為 DB 的正本。
如果這裡弄錯了,不管是 Next.js、Python 還是 AI Agent,最後都只會變成「有漂亮畫面的人工作業」。
先講結論。
Excel 業務的問題,不是因為用了 Excel。
問題在於,Excel 裡把「輸入、計算、判斷、歷程、核准、版本管理」全都混在一起了。
Excel 本身其實很強。
Google Sheets API 以 RESTful 介面提供,可讀寫試算表儲存格值、變更格式、建立試算表等功能。(Google for Developers)
Microsoft 也為 Excel 增益集提供 Excel JavaScript API,可操作工作表、範圍、表格、圖表等 Excel 內部物件。(Microsoft Learn)
也就是說,Excel 在 API 時代不是會消失的工具。
反而可以重新定義成可與 API 連動的業務 UI。
真正的問題在於 Excel 的用法。
例如,是否已經變成下列狀態:
狀態表面上的問題真正的問題報價 Excel 每位承辦人的格式都不同格式不統一沒有正本範本公式壞掉函數錯誤發生計算邏輯無法稽核最新單價不明手動輸入太多資料更新來源分散補助金條件過時查詢花很多時間沒有制度 DB 客戶別檔案散落各處找不到沒有案件管理 DB 想讓 AI 產生報價無法自動化沒有 API 化的正本處理這裡最重要的,是不要責怪 Excel。
Excel 是現場用來補足缺口的「應急處置集合體」。
它不是壞人。
它其實最忠實地記錄了現場正在困擾什麼,是一種化石。
Excel 業務 API 化,不是把 Excel 消滅,而是把埋在 Excel 裡的業務規則抽離出來。
常見的 DX 失敗,通常是這樣發生的:
這個結構,其實根很深。
現場會使用 Excel,不是因為偷懶。
而是因為 Excel 對例外處理太強了。
客戶突然說:
標準系統擅長標準流程。
但現場不是只靠標準流程活著。
所以,在全面廢除 Excel 之前,應該先問這個問題:
是真的該把 Excel 拿掉?
還是應該把 Excel 承擔過多的責任拆開?
大多數情況下,答案是後者。
要在不破壞 Excel 的情況下把它 API 化,必須把業務分成三層。
層級職責具體例子注意事項UI 層人類輸入與確認Excel、Google Sheets、Web 畫面、表單重視現場使用便利性AI 層草稿、摘要、候選建議客戶訪談整理、提案文產生、FAQ 回答不要讓它負責正本計算API/DB 層正本處理、正本資料、稽核電費計算、補助金搜尋、模擬、日誌保存確保可說明性與可重現性這裡最重要的是,不要把 AI 層和 API 層混在一起。
AI 很方便。
但 AI 該做的工作,和 API 該做的工作,不一樣。
領域適合 AI 適合 API/DB客戶備忘摘要◎△Email 文案草稿◎△整理客戶訪談項目◎○偵測輸入錯誤候選○◎電費計算△◎太陽能/儲能經濟效益試算△◎搜尋補助金候選○◎投資回收正本計算△◎報表文字草稿◎○保存稽核日誌△◎AI 擅長文字與模糊資訊。
API 擅長對固定輸入做固定處理並重現結果。
越是讓 AI 思考,就越需要 API 不出錯地計算。
這是 AI 時代業務設計非常重要的重點。
把 Excel 業務 API 化時,直接換成 Web App,成功機率通常不高。
這種狀態下,Excel 承擔了一切:
所以它會壞掉。
改成這樣後,Excel 可以保留為「入口」與「確認畫面」。
但責任要分開。
責任留在 Excel 還是移到 API/DB輸入○△輸入檢查△◎正本計算×◎單價/制度資料×◎結果顯示○○報表輸出△○稽核日誌×◎核准歷程×◎這裡的口訣是:Excel 當畫面、API 當心臟、DB 當記憶。
Excel 業務 API 化,不需要一開始就做成大型系統。
最小架構如下。
元件職責Excel / Google Sheets現場輸入 UIAPI Connector / Add-in串接 Excel 與 APIBackend API認證、輸入轉換、API 呼叫控制Validation輸入值型別、範圍、必填檢查Business API正本計算、搜尋、模擬Result Normalizer將結果整理成適合 Excel 或 PDF 的格式Audit Log DB保存誰在何時、用什麼條件執行Report Generator輸出客戶/簽核用文件Google Sheets API 這類 RESTful 介面,可以用程式進行試算表值的讀寫。(Google for Developers)
在 Microsoft 365 環境中,可透過 Office JavaScript API 讓 Excel 增益集操作 Excel 內部物件。(Microsoft Learn)
所以,一開始不必追求「脫離 Excel」。
更現實的做法反而是:
這個順序比較容易成功。
把 Excel 業務 API 化時,不應該一開始就先寫程式。
第一件要決定的,是資料責任邊界。
輸入資料是指從現場或客戶取得的值。
例如:
輸入資料最重要的是,不要把自由輸入縮得太死。
現場有很多例外。
如果全部都做成下拉選單,會出現無法輸入的案件。
但要用於正本計算的欄位,不可以一直維持自由輸入。
項目輸入方式理由客戶名稱自由輸入可接受表記差異地址自由輸入+地址正規化用來判定地區都道府縣代碼選擇式用於 API 查詢條件電費方案 ID 選擇式用於正本計算每月用量數值輸入用於計算業務備註自由輸入適合 AI 摘要補助金 ID 由 API 取得參照正本資料### 6-2. 主資料
主資料是指業務端應該管理的正本資料。
在 Enegaeru API 的公開規格中,定義了電力事業者取得、電費方案取得、電費方案詳細取得、電費計算等 API。
另外,在補助金資訊列表中,也定義了可組合都道府縣代碼、市區町村、對象設備代碼、補助對象區分等條件進行搜尋的規格。
使用這類業務特化 API 時,就算自家沒有保存所有主檔,也可以把外部 API 當作正本資料來源。
輸出資料必須回到現場可用的形式。
這裡最重要的是,不要直接把 API 回應原封不動交給現場。
API 回應是給機器讀的。
現場或客戶需要的是適合判斷的格式。
API 設計時,粒度很重要。
太細的 API 對工程師方便,但對業務來說可能不好操作。
例如,在把 Excel 業務 API 化時,如果只提供下面這些 API,現場仍然會很困擾:
/customers/plans/rates/subsidies/calculate當然這些是必要的。
但業務人員真正需要的是更貼近業務目的的單位。
所以,API 最好拆成兩層。
API 類型職責例子基礎 API資料取得與計算元件費率方案取得、補助金搜尋、發電量計算業務 API以現場目的為單位報價產生、比較試算、提案書生成在 Enegaeru API 的例子中,補助金資料查詢、電費計算、用電量計算、太陽能發電量計算、設備導入模擬等基礎業務 API,都已整理成公開規格。
Enegaeru 共通公開 API https://www-apidoc.enegaeru.com/sys/
如果要整合進自家業務,再往上包一層「自家用情境 API」會更好用。
這樣 Excel 端就不需要直接呼叫複雜 API。
Excel 只需要:
至於背後要按什麼順序呼叫哪些 API,就交給 Backend 負責。
把 Excel 業務 API 化後,資安問題會立刻浮上檯面。
尤其危險的有三件事:
OWASP API Security Top 10 是為了提升 API 開發、維護相關人員、設計者、管理者與組織對常見 API 資安風險的認知而公開的。(OWASP Foundation)
其中也整理了授權不足、物件屬性層級授權不足等 API 常見風險。(OWASP Foundation)
這是絕對原則。
如果把 API Key 寫進 Excel,檔案一旦被複製,認證資訊也會跟著擴散。
正確做法如下:
Excel 呼叫自家 Backend。
外部 API Key 由 Backend 端保存。
Excel 業務裡,權限設計很容易做得太粗糙。
但如果要 API 化,至少要分成以下角色:
權限可做的事檢視者看結果輸入者輸入條件並進行試算核准者將結果核准為正式提案用資料管理者主檔設定、使用者管理稽核者查看日誌與歷程特別是在報價、補助金、費率計算、投資回收這類業務中,誰核准最終版可供使用非常重要。
稽核日誌看起來像是防守功能。
但在實務上,它也是進攻功能。
有了日誌,就能做到:
至少要保存以下資訊:
{
"request_id": "req_20260501_0001",
"user_id": "user_123",
"customer_id": "customer_456",
"operation": "solar_battery_simulation",
"input_hash": "sha256:xxxx",
"api_version": "v1",
"master_data_version": "2026-05",
"created_at": "2026-05-01T12:00:00+09:00",
"status": "success"
}
重點是,要設計到底是全部保存輸入值,還是只保存雜湊值並另行保管。
若包含個資或機密資訊,日誌粒度也必須一併設計。
API 化常見的失敗,是在錯誤發生時只回傳這樣:
{
"error": "Bad Request"
}
這樣現場就卡住了。
在 Excel 業務 API 化中,錯誤不只是失敗通知,而是告訴現場下一步該修什麼的指引。
{
"message": "Invalid parameter"
}
{
"code": "INVALID_MONTHLY_USAGE",
"message": "每月用量需要 12 個月的資料。",
"field": "monthly_usage",
"how_to_fix": "請以 kWh 為單位輸入 1 月到 12 月的用量。",
"example": [420, 380, 350, 310, 290, 330, 410, 450, 390, 360, 340, 400]
}
在 Enegaeru API 規格中,共通錯誤已整理為 400、403、500、504 等 HTTP 回應。400 代表請求內容造成的錯誤,403 代表認證錯誤,500 代表 API 內部處理錯誤,504 則代表逾時。
在 Excel 業務端,還必須再翻譯成現場可理解的語言。
HTTP 狀態技術意義現場訊息400輸入錯誤輸入條件不足或格式有誤401/403認證・權限錯誤請確認登入狀態或權限500伺服器內部錯誤系統端處理失敗504逾時請拆分條件,或稍後再執行一次把錯誤翻成現場語言。
只要做到這一點,API 導入的壓力就會大幅下降。
以下示範的是,從 Excel 或 Google Sheets 接收到輸入後,先在 Backend 驗證,再交給外部業務 API 的方式。
正式環境中,請加入認證、速率限制、稽核日誌、加密、個資保護、重試控制。
import { z } from "zod";
const SimulationInputSchema = z.object({
customerId: z.string().min(1),
prefectureCode: z.string().regex(/^\d{2}$/),
city: z.string().min(1),
monthlyUsageKwh: z.array(z.number().nonnegative()).length(12),
pvKw: z.number().positive().optional(),
batteryKwh: z.number().nonnegative().optional(),
tariffPlanId: z.string().min(1),
});
type SimulationInput = z.infer<typeof SimulationInputSchema>;
type ApiResult<T> =
| { ok: true; data: T; requestId: string }
| { ok: false; errorCode: string; message: string; field?: string; requestId: string };
async function callBusinessApi(input: SimulationInput): Promise<ApiResult<any>> {
const requestId = crypto.randomUUID();
const parsed = SimulationInputSchema.safeParse(input);
if (!parsed.success) {
const firstError = parsed.error.issues[0];
return {
ok: false,
errorCode: "VALIDATION_ERROR",
message: "輸入值有缺漏或格式錯誤。",
field: firstError.path.join("."),
requestId,
};
}
try {
// 實際的 API Key 與認證資訊請放在 .env 或 Secret Manager。
// 不要放在 Excel 檔或前端。
const response = await fetch(`${process.env.BUSINESS_API_BASE_URL}/simulate`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.BUSINESS_API_TOKEN}`,
},
body: JSON.stringify(parsed.data),
});
if (!response.ok) {
if (response.status === 400) {
return {
ok: false,
errorCode: "BUSINESS_API_BAD_REQUEST",
message: "試算條件有問題,請確認輸入欄位。",
requestId,
};
}
if (response.status === 403) {
return {
ok: false,
errorCode: "BUSINESS_API_FORBIDDEN",
message: "API 認證或權限有問題。",
requestId,
};
}
return {
ok: false,
errorCode: "BUSINESS_API_ERROR",
message: "外部 API 處理失敗。",
requestId,
};
}
const data = await response.json();
// auditLog 為偽程式碼;正式環境請寫入 DB。
await auditLog({
requestId,
customerId: parsed.data.customerId,
operation: "simulate",
inputHash: await hashInput(parsed.data),
status: "success",
});
return {
ok: true,
data,
requestId,
};
} catch (error) {
await auditLog({
requestId,
customerId: input.customerId,
operation: "simulate",
status: "network_error",
});
return {
ok: false,
errorCode: "NETWORK_ERROR",
message: "發生連線錯誤,請稍後再執行一次。",
requestId,
};
}
}
async function hashInput(input: unknown): Promise<string> {
const encoded = new TextEncoder().encode(JSON.stringify(input));
const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
return Array.from(new Uint8Array(hashBuffer))
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
}
async function auditLog(record: {
requestId: string;
customerId: string;
operation: string;
inputHash?: string;
status: string;
}) {
console.log("AUDIT_LOG", record);
}
重點是,不要讓 Excel 直接呼叫外部 API。
Excel
→ 自家 Backend
→ 業務 API
→ 保存日誌
→ 回傳 Excel
只要多這一層,就會更容易管理認證資訊、日誌、錯誤翻譯與資料格式整理。
對非工程師團隊或 BPO 而言,用 Python 處理 CSV/Excel 往往更實際。
from __future__ import annotations
import os
import json
import hashlib
from dataclasses import dataclass
from typing import Any
import pandas as pd
import requests
from pydantic import BaseModel, Field, ValidationError
class SimulationInput(BaseModel):
customer_id: str = Field(min_length=1)
prefecture_code: str = Field(pattern=r"^\d{2}$")
city: str = Field(min_length=1)
monthly_usage_kwh: list[float] = Field(min_length=12, max_length=12)
tariff_plan_id: str = Field(min_length=1)
pv_kw: float | None = None
battery_kwh: float | None = None
@dataclass
class ApiClient:
base_url: str
token: str
def simulate(self, payload: SimulationInput) -> dict[str, Any]:
response = requests.post(
f"{self.base_url}/simulate",
headers={
"Authorization": f"Bearer {self.token}",
"Content-Type": "application/json",
},
json=payload.model_dump(),
timeout=30,
)
if response.status_code == 400:
raise ValueError("輸入條件有問題,請確認 Excel 的必填欄位。")
if response.status_code in (401, 403):
raise PermissionError("API 認證或權限有問題。")
if response.status_code >= 500:
raise RuntimeError("API 端處理失敗,請稍後再試。")
response.raise_for_status()
return response.json()
def input_hash(payload: SimulationInput) -> str:
raw = json.dumps(payload.model_dump(), ensure_ascii=False, sort_keys=True)
return hashlib.sha256(raw.encode("utf-8")).hexdigest()
def load_input_from_excel(path: str) -> SimulationInput:
df = pd.read_excel(path, sheet_name="input")
row = df.iloc[0]
monthly_usage = [
float(row[f"usage_{month:02d}"])
for month in range(1, 13)
]
return SimulationInput(
customer_id=str(row["customer_id"]),
prefecture_code=str(row["prefecture_code"]).zfill(2),
city=str(row["city"]),
tariff_plan_id=str(row["tariff_plan_id"]),
monthly_usage_kwh=monthly_usage,
pv_kw=float(row["pv_kw"]) if not pd.isna(row["pv_kw"]) else None,
battery_kwh=float(row["battery_kwh"]) if not pd.isna(row["battery_kwh"]) else None,
)
def main() -> None:
try:
payload = load_input_from_excel("input.xlsx")
client = ApiClient(
base_url=os.environ["BUSINESS_API_BASE_URL"],
token=os.environ["BUSINESS_API_TOKEN"],
)
result = client.simulate(payload)
print("試算成功")
print("輸入雜湊:", input_hash(payload))
with open("result.json", "w", encoding="utf-8") as f:
json.dump(result, f, ensure_ascii=False, indent=2)
except ValidationError as e:
print("Excel 輸入有問題。")
print(e)
except Exception as e:
print("處理失敗。")
print(str(e))
if __name__ == "__main__":
main()
這樣一來,BPO 人員可以一邊操作 Excel,一邊在背後使用 API 與日誌。
把 Excel 業務 API 化時,常會猶豫:到底要自己全部做,還是使用業務特化 API?
比較如下:
觀點自己實作業務特化 API 活用初期開發自由度高但負擔大較容易快速開始業務 DB 需要自建可把壓力轉給提供方計算邏輯完全可控制但要自己維護依賴 API 規格監管性視設計而定視 API 規格與日誌設計而定維運持續負荷高外部依賴存在但可降低負擔法規・單價更新需自家維運有時可由外部 API 端處理 AI 串接需自行整備若已 API 化就容易串接 BPO 串接需個別設計較容易標準化在再生能源、電費、補助金、太陽能/儲能、EV/V2H、PPA 這些領域,計算邏輯與資料更新都會變得很重。
例如,Enegaeru API 規格包含補助金搜尋、電費計算、太陽能發電量計算、設備導入模擬等功能。2026 年 3 月 2 日的更新紀錄也顯示,新增了設備導入模擬與太陽能發電量計算 API。
在這類領域中,比起自己全部打造,將業務特化 API 組合起來更實際。
特別重要的是,要分清楚哪些是自家必須差異化的領域,哪些可以交給外部 API。
領域應由自家持有嗎適合使用外部 API 嗎客戶接觸點◎△獨特業務流程◎△客戶別提案邏輯○○電費主檔△◎補助金 DB△◎太陽能/儲能試算邏輯△〜○○〜◎報表設計◎○稽核日誌◎○BPO 營運○◎---
這裡以再生能源・太陽能・儲能電池・EV/V2H・PPA 提案業務為例。
這個領域的 Excel 業務很複雜。
若只靠 Excel 來管理,很快就會到極限。
另一方面,如果一口氣換成全新系統,現場又不容易跟上。
因此,較務實的解法是這樣。
在 Enegaeru API 規格中,已整理出登入 API、補助金資訊列表/詳細、電力事業者取得、電費方案取得、電費計算、用電量計算、太陽能發電量計算、設備導入模擬等功能。
Enegaeru 共通公開 API https://www-apidoc.enegaeru.com/sys/
若再搭配 Enegaeru BPO,就更容易處理 API 無法吸收的例外工作。
例如:
API 擅長標準化。
BPO 擅長例外處理。
在 Excel 業務 API 化時,有時不該只想靠 API 完成一切。
「API 負責正本處理、BPO 負責吸收例外」這種設計,在現場很強。
想把 Excel 業務交給 AI Agent 的需求越來越多。
但只讓 AI Agent 直接碰 Excel,風險很高。
因為 AI 可能會做出以下事情:
不是 AI 有問題。
而是把正本計算交給 AI 的設計有問題。
AI Agent 時代正確的分工是這樣:
交給 AI 的工作:
交給 API 的工作:
AI 時代的 SaaS 價值,不只在畫面。
還要看你有沒有讓 AI 能安全呼叫的業務知識 API。
這會成為下一階段的競爭力。
Excel 業務 API 化,應該分階段進行。
第一步不是系統開發,而是 Excel 棚卸。
要確認的項目:
接著,把 Excel 裡的計算邏輯 API 化。
優先順序如下:
凡是和金額有關的項目,優先 API 化。
這是鐵則。
接著,將 Excel 裡的主檔移到外部。
Excel 內的主檔,運作越久越容易腐化。
API 化後,一定要加入日誌。
沒有日誌的 API 化,之後無法重現。
最後才串接 BPO 或 AI Agent。
到了這一步,業務整體才真正接近自動化。
不是。
就算把 Excel 拿掉,如果業務規則沒有整理好,只是把混亂搬到別的地方而已。
只對一半。
API 化是自動化的基礎,但如果沒有輸入品質、例外處理、日誌、權限、運維設計,現場還是跑不起來。
其實相反。
越是把 AI 放進業務流程,API 和 DB 的正本性就越重要。
也不是。
在真實業務裡,常見且有效的做法是:API 負責標準化,BPO 負責吸收例外。
這很危險。
如果現場業務現在是靠 Excel 在跑,最好的起點通常是保留 Excel 當 UI,只先把背後 API 化,這樣可以降低遷移風險。
Excel 業務 API 化的目的,不是把 Excel 消滅。
目的在於,把混在 Excel 裡的責任拆開。
當這個分工成立後,現場就能繼續使用 Excel,而背後則同步進行 API 化、自動化、可稽核化。
在再生能源、太陽能、儲能電池、EV/V2H、PPA、電費、補助金等資料更新與計算邏輯很複雜的業務中,使用業務特化 API 的意義會更大。
像 Enegaeru API 這類業務特化 API,可以把補助金、電費、發電量、設備導入模擬等複雜處理變成 API 串接的一部分,因此能作為「不破壞 Excel、而是標準化背後流程」的選項。
Excel 不是敵人。
Excel 是累積了現場智慧的入口。
真正該打破的,不是 Excel,而是在沒有正本的情況下,讓人每次都得重新拼命的結構。
在太陽能・儲能電池・EV/V2H・PPA 提案這類業務中,電費、補助金、發電量、設備條件、投資回收都會交織在一起,自家若要全部持有所有邏輯與資料更新,負擔往往很重。在這類領域裡,把像 Enegaeru API 這樣的業務特化 API 當作「正本計算層」,讓 Excel 與自家系統專注在輸入、確認與客戶接觸,會是更務實的設計。
Enegaeru 共通公開 API https://www-apidoc.enegaeru.com/sys/
原文出處:https://qiita.com/satoru_higuchi/items/a6aa0300c3a97648c65c