
這一篇要做的事很單純:在 ERP 裡的每一筆客戶或潛在客戶上按一個鈕,就有一封寫好的草稿,主旨、稱謂、內文、署名都在,人只要看一遍、修改、按送出。
先看成果:
選擇目的:

節慶請安,中秋節

寫好了。

再來一個:目的:初次聯絡:

再一個:目的:產品推薦:選產品(可多選)

信件產出:

帥吧!
請注意一下耗時,最多不超過2秒。
當然,我這個中文系出身的,對這產出的信件是不怎麼滿意。但不修改,給大家看一下,用地端模型,Qwen3.6,跑在兩張 5060-16G 上,可以產出怎樣品質的信件。
為甚麼用地端?因為這樣你的信件內容,才不會被雲端的模型拿去 研究分析!
規則:資料進了系統只是開始,能不能推動下一個動作,才是這套系統有沒有用的判準。
信本來就住在時間軸上
動手前先問一件事:ERPNext 有沒有寄信的功能?答案是它原生就有:每一筆客戶、每一張單底下都有時間軸,寄出的信是一筆 Communication 掛在那裡;表單右上角本來就有「新的電子郵件」的寄信視窗。
所以不另外做一個寄信頁。AI 只負責把主旨和內文填進那個原生視窗,收件人由系統帶,送出那一下由人來操作。這跟先前給 AI 訂的批准矩陣剛好對上:草擬可以自動,對外送出要人核准,對陌生名單群發一律不做。
規則:新功能要落在系統原本的位子上,送出這一步留給人,不要為了方便自己再造一條寄信路。
對象分兩種:Lead 與 Customer
一開始的想法是「選客戶」,但 ERPNext 的語意不是這樣。初次聯絡的對象照定義還不是客戶,是潛在客戶 Lead;名片同步進來的那批就是 Lead,人的 email 直接在那筆資料上。已經開過單(報價單,訂單)的才是客戶 Customer,人的 email 掛在底下的聯絡人 Contact。
所以按鈕要長在兩個地方:Lead 的表單和 Customer 的表單,兩邊行為一樣,差別只在系統去哪裡找收件人。
規則:先弄清楚系統怎麼分對象,再決定按鈕長在哪,不要拿自己的直覺去硬套別人的資料模型。
四種目的,各要什麼 (當然,還可以再增加:請款、催款、出貨通知.....)
信分四種目的:初次聯絡、節慶請安、約拜訪、產品推薦。節慶列了元旦、春節、端午、中秋,台灣做生意就是這幾個節要問候。
每一種都先列清楚「系統給什麼、人補什麼」。初次聯絡,系統給姓名、職稱、公司、名片來源與建檔日期,人補想談的事。節慶請安,系統給公司、聯絡人、最近一筆往來日期,人選節日。約拜訪,人補目的與候選時段。產品推薦,人從品項主檔挑要推的東西,系統把品項說明和對方買過什麼帶進去。
節慶信有一條硬規矩:不推銷。買過什麼可以提一句,金額一律不寫。
規則:每一種信先列出「系統給什麼、人補什麼」,列不出來的欄位就不要讓模型猜。
客戶 email 不提供給 LLM 模型
送給模型的東西逐一列出來:公司名、聯絡人姓名與職稱、最近往來日期、買過的品項、今天日期。客戶的 email 和電話不在清單裡,它們只回給前端填收件人,模型從頭到尾看不到。LLM 寫信本來就不需要知道對方的信箱。
模型可以選:本地的 Qwen,資料不出區網;或雲端的 GPT,走 OpenRouter。這個下拉選單的真正意思是「這批客戶資料准不准出門」。雲端那一層有一份不准送的清單,個人的 email、電話、地址都在上面,是程式在每次呼叫前硬擋,不是靠模型自律。
規則:給模型的欄位要能逐一寫出來,寫不出來的就不送;換模型等於換資料去向,要當成同一個決定。
告訴 claude:
在 ERPNext 的 Lead 與 Customer 表單各加一顆「AI 撰寫客戶信」:
- 目的四種:初次聯絡 / 節慶請安(元旦、春節、端午、中秋)/ 約拜訪 / 產品推薦
- 模型可選:本地 Qwen(資料不出區網)或雲端 GPT(走 OpenRouter)
- 只做到草稿:把主旨與內文填進 ERPNext 原生的寄信視窗,送出由人按;⛔ 不自己寄信、不群發
- 給模型的欄位逐一列出:公司、聯絡人姓名職稱、最近往來、買過品項、今天日期;⛔ email 與電話不進模型
- ERPNext 側只放呼叫與顯示(Server Script + Client Script),不放任何 prompt 與判斷(GPLv3 界線)
- 模型輸出過確定性防線:佔位符、編造數字、簡體字、亂寫聯絡方式、署名重複;過不了不給寄信鈕
- 署名由程式接在信尾;寄件信箱用 mail server 新開的 erpnext 帳號,設成 ERPNext 預設寄件帳號
薄殼與大腦
ERPNext 那一側只放了三樣東西:兩支代呼叫的 Server Script、一顆按鈕的 Client Script。裡面沒有任何一句 prompt、沒有任何判斷,連對話框下拉選單裡的字都是從 AI 服務那邊拿回來的。
原因是授權:ERPNext 這一側的東西要跟著 GPLv3 一起交付原始碼。所以刻意讓它什麼都沒有,大腦全在另一個獨立的 AI 服務裡,兩邊只用 HTTP 講話。
規則:會跟著開源一起交出去的那一層,什麼都不要放;判斷、詞彙、閾值全部住在另一邊。
七道防線
模型寫出來的信,要經過七道確定性的檢查:有沒有留佔位符、有沒有編造數字、有沒有簡體字、有沒有亂寫 email 或電話、署名有沒有重複、有沒有把收件人的公司當成自己的公司、寄件公司名有沒有被改寫。
編造數字那一道最要緊:信裡每一個數字,都必須在資料裡找得到,找不到就是編的。過不了,就顯示紅字的,畫面上就不顯示寄信鈕。
規則:模型輸出交給確定性的程式檢查,不要再問模型一次「你確定嗎」。
八封信,一封一封讀
測試全綠之後,四種目的乘兩個模型,八封信真的打出來,一封一封讀。第一輪兩個模型都犯同一個錯:把客戶的公司當成自己的公司,寫出「感謝您對某某電子的支持」,因為給模型的資料裡只有對方的公司名,沒有一個欄位說「我是誰」。補上寄件人名字與公司、在 prompt 裡明講誰是收件人誰是寄件人,八封全對。
claude 自己做修正:
你是台灣中小企業的商務書信撰寫助理,用繁體中文、台灣商務用語寫一封客戶信。
鐵則:只能用給你的事實,不得自創價格、數字、日期、單號、規格;不寫署名或落款(程式會補);
不留任何佔位符;不要寫對方的 email 或電話。
角色:company_name、contact_name、job_title 是收件人(客戶);sender_name、sender_company 是你(寄件人)。
⛔ 不可把收件人的公司寫成自己的公司:「感謝您對{company_name}的支持」是錯的,
正確是「感謝{company_name}對我們的支持」。sender_company 照原文寫,不翻譯、不改寫。
節慶請安不推銷;有往來紀錄才能提「過去的合作」。輸出只有 JSON:{"subject": "...", "body": "..."}
第二輪本地模型在信尾自己加了一行「敬上」落款,程式再接一次署名就變兩份;還寫出「喜悦」這種簡體變體字。第三輪它把 iron30 自己翻成「鐵30」,prompt 講了照原文寫也壓不住。
壓不住的就不再堆 prompt,改成一道防線標黃字給人看。雲端那個模型八封零警告,但信偏短。
規則:新 prompt 上線前,人眼逐封讀。測試用假資料不會犯模型才會犯的錯,只有真的打過才看得到。
寄件信箱與卡住的佇列
信要寄得出去,先在自家 mail server 開一個 erpnext 的信箱,ERPNext 設成預設寄件帳號,加密連線,只寄不收。測試信寄到 Gmail,到 mail server 的 postfix 紀錄裡對到那一筆,狀態是 sent,Gmail 收下了。
規則:寄出去的信,要到收信端確認。佇列裡的「已送出」不算數。
畫面走一次
(底下的圖片,是叫 claude 自己操作,自己節取出來的圖。)
最後用 headless 瀏覽器照真人的動線走一遍:打開客戶表單,右上角工具列多了一顆按鈕。按下去是對話框:目的、模型、候選時段、補充說明。按產生草稿,底下出現主旨、內文,還有一行黃字警告,寄件公司名被改寫成「鐵30」,請人工確認。
客戶表單右上角多了一顆「AI 撰寫客戶信」,和檢視、建立、操作同一排;下方時間軸已經有一封剛寄出的信

對話框:目的選約拜訪、模型選本地 Qwen,資料不出區網;候選時段與補充說明由人填,其餘欄位系統自己帶

產生草稿後的預覽:模型、耗時、主旨、內文;一行黃字警告寄件公司名被模型改寫成「鐵30」,請人工確認,紅字才會擋住寄信鈕

按開啟寄信視窗,主旨與內文已經在原生寄信視窗裡,填上收件人,送出。回到客戶表單,時間軸多了一筆發出的信。整條路上 AI 只碰了「寫」這一段,其餘都是 ERPNext 原本的東西。
開啟寄信視窗:主旨與內文已經在 ERPNext 原生的寄信視窗裡,寄件帳號是預設的 erpnext 信箱,收件人自己填

送出之後回到客戶表單:時間軸多了一筆發出的信,這就是掛在客戶底下的 Communication,AI 只碰了「寫」那一段

規則:驗收要用真正的表單按過去,看得到時間軸上那一筆才算,API 回 200 不算。
到這裡,系統裡的每一筆潛在客戶和已有交易的客戶,按一個鈕就有一封可以寄的信,資料不出門、是否寄出由人決定。
明天來把語音客服接進這套系統。