大砲打小鳥之 ChatGPT 翻譯鳳梨酥 LINE Bot
2 |
身為程序員,一直把「增進人類全體之生活」當成使命,沒能力為人類產生貢獻,能「增進家人之生活」也好。
長輩請了外籍看護,國語跟英文只到勉強溝通,最後 LINE 加翻譯軟體竟是目前效果最好的溝通方式。結果我做了件「用大砲打小鳥」的事 - 用 ASP.NET Core 寫 LINE Bot 接 ChatGPT 在 LINE 群組幫忙翻譯,專案代號就命名為「翻譯鳳梨酥 」。
群組回話 LINE Bot、串 ChatGPT API 之前玩過了,這回只是把兩個功能摻在一起,主程式不用一小時寫完,小有快刀斬亂麻的暢快。
(所以說,平日老為賦新辭強說愁,四處找雞毛蒜皮的無聊題目練習,看似沒意義,但說不準能在某個關鍵時刻派上用場,「你練過的功不會背叛你」再添一例。)
使用方式是將「翻譯鳳梨酥」加入群組偷聽對話,看到 # 語系標記它就會跳出來翻譯,貼心設計是翻譯內容一併附上英文供對照,方便核結果正確性。
初始版本有點小問題,像是 "早上七點幫阿公量血壓" 被翻成 "I measured my grandpa's blood pressure at seven in the morning",英文對照發揮作用糾出問題。
加上 "請" 字強調指示是個 Workaround,但時時需要注意措辭是小看了 ChatGPT 的能耐,也讓開發者面子掛不住,這類問題一般可以透過調整 ChatGPT 提示改善。我的原本的初始化提示是:
你是一名多國語言翻譯,請將以下文字內容分別翻譯成英文以及{指定言語},用語需口語化及生活化。
修改成:
你是一名語言翻譯,負責協助僱主與外籍看護溝通,主要工作為將僱主的中文指令翻成外語,將外語翻成中文,用語需口語化及生活化。現在請將以下文字內容分別翻譯成英文以及{指定言語}。
Prompt 調整後,效果明顯改善,目前沒再看到明顯錯誤。
簡單說下程式寫法。ChatGPT 部分我是直接拿 ChatGPT 聊天程式練習 的 ChatGptService 來用,每次當成全新對談,不必帶入先前內容,省 Token (ChatGPT API 以 Token 計費)也加快處理速度。LINE Bot API 需一次送出完整內容,故也用不到 Streaming 技巧。
餘下的全部程式邏輯我寫在 Minimal API Program.cs,整個專案程式就一個 Program.cs 加一個 ChatGptService.cs。Program.cs 如下,LINE API 串接細節由 David 老師的 LineBot SDK 包辦,我的實作邏輯不到一百行,其中三分之一還是資安強迫症發作,我硬是加上 LINE 群組 ID 白名單機制管控權限,防止機器人被加到其他群組盜用。
using System.Security.Cryptography;
using System.Text;
using isRock.LineBot;
NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
var builder = WebApplication.CreateBuilder(args);
Func<string, string> readConfig = (key) => builder.Configuration.GetValue<string>(key) ?? throw new ArgumentNullException(key);
var pwdChk = readConfig("GrantPwd");
var chatSvc = new GptTranslatorService(readConfig("OpenAiUrl"), readConfig("OpenAiKey"), readConfig("OpenAiDepName"));
var lineBotKey = readConfig("LineBotToken");
var lineBot = new Bot(lineBotKey);
var app = builder.Build();
app.MapGet("/", () => "Line GPT Translator Bot");
List<string> groupIds = new List<string>();
var grpIdPath = Path.Combine(app.Environment.ContentRootPath, "data", "groupIds.txt");
LoadGroupIds();
app.MapPost("/Grant", async (context) =>
{
var grpId = context.Request.Form["grpId"].ToString();
var pwd = context.Request.Form["pwd"].ToString();
if (pwd == pwdChk)
{
if (!groupIds.Contains(grpId))
{
logger.Info($"Grant {grpId}");
groupIds.Add(grpId);
SaveGroupIds();
}
await context.Response.WriteAsync("OK");
}
else
{
context.Response.StatusCode = 401;
await context.Response.WriteAsync("Unauthorized");
}
});
app.MapPost("/MsgCallback", async (context) =>
{
var body = context.Request.Body;
using var reader = new StreamReader(body);
var json = await reader.ReadToEndAsync();
var msg = Utility.Parsing(json);
foreach (var evt in msg.events)
{
if (evt.type == "message")
{
if (evt.source.type == "group" && evt.message.type == "text")
{
var grpId = evt.source.groupId;
var text = evt.message.text;
logger.Trace("訊息[{0}]: {1}", grpId, text);
if (text.StartsWith("#REG"))
{
lineBot.ReplyMessage(evt.replyToken, "註冊序號 - " + grpId);
break;
}
if (!groupIds.Contains(grpId))
{
lineBot.ReplyMessage(evt.replyToken, "群組未註冊");
break;
}
var src = text.Substring(3);
if (text.StartsWith("#") && Enum.TryParse<Langs>(text.Substring(1, 2).ToUpper(), out var lang))
{
var transText = await chatSvc.Translate(lang, src);
logger.Trace("翻譯:\n" + transText);
lineBot.ReplyMessage(evt.replyToken, transText);
}
}
}
}
await context.Response.WriteAsync("OK");
});
app.UsePathBase("/line-gpt-trans-bot");
app.Run();
void LoadGroupIds()
{
if (File.Exists(grpIdPath))
{
groupIds = File.ReadAllLines(grpIdPath).Where(o => !string.IsNullOrEmpty(o)).ToList();
}
}
void SaveGroupIds()
{
File.WriteAllLines(grpIdPath!, groupIds.Distinct().ToArray());
}
會寫程式真好!
Example of LINE Bot which using Azure OpenAI ChatGPT API to translate between languages in group conversation.
Comments
# by AMBER
請問可以分這個應用程式給需要的人安裝嗎? 我家中的印尼外看也是語言很不好,照顧媽媽有許多困難,因為語言不通的關係,請問這個翻譯鳳梨酥可以分享讓我使用嗎?
# by Jeffrey
to AMBER,這個服務背後要串接 ChatGPT API,會以使用量計費,而以翻譯這件小事來說,動用 AI 成本過高(所以我說是大砲打小鳥)。 仲介有介紹我們另個免費 LINE 服務 https://www.ligobot.com/ 也可以同時翻譯英文跟印尼文,效果還行,提供你參考。