iT邦幫忙

2025 iThome 鐵人賽

DAY 11
0
生成式 AI

當 .NET 遇見 AI Agents:用 Semantic Kernel × MCP 打造智慧協作應用系列 第 11

Day 11: Semantic Kernel Single Agent 實戰 - 銀行匯款 Agent

  • 分享至 

  • xImage
  •  

這篇我們要來做一個實用又貼近生活的 AI Agent 範例——「銀行匯款 Agent」。

這個助理的任務很單純,就是幫我們完成一筆台幣(TWD)的國內匯款資料蒐集,等資訊都到齊之後,就會自動呼叫工具來建立一筆匯款單。你只需要用聊天的方式輸入資料,它就能幫你搞定一切。

這個想法其實是我家大人無心中給的靈感。有一天她突然說:「我們公司現在有個超強的 AI 系統,客戶只要打字聊天,就可以完成匯款單的 QR Code!」我一聽,直覺就是——咦,這不就是我們當年用 Microsoft LUIS 的時代,就可以靠意圖與實體辨識做到的事嗎?那對現在的生成式 AI 來說,根本就是小菜一碟。

當然,今天我們要做的不是只是辨識幾個欄位那麼簡單,而是讓這個 AI Agent 有能力自己掌握任務流程、決定目前資料是否齊全,如果不齊,就引導使用者補上;等到資訊都 OK 了,它才會主動出手,用工具建立出一張完整的匯款單。

簡單來說,我們要做的這個「匯款助理」Agent,會具備以下幾個能力:

  • 理解匯款需要的資料(例如收款人姓名、銀行代碼、帳號、金額、用途…)
  • 根據聊天互動補齊資料
  • 完成資訊後,自動呼叫工具建立匯款單或做任何事都行(就看你的工具想要做什麼)

接下來就一步步實作這個「匯款助理」Agent吧!

Step 1: 建立 BankRemittancePlugin

首先,我們要建立一個 BankRemittancePlugin,這個插件會提供一些 functions,例如欄位檢核、查銀行名稱、建立匯款草稿等功能,讓我們的 Agent 可以呼叫這些功能來完成任務。

  • 建立BankRemittancePlugin 類別,並加入以下程式碼,用於欄位格式驗證規則:
private static readonly Regex AccountRegex = new(@"^\d{12,16}$", RegexOptions.Compiled);
private static readonly Regex BankCodeRegex = new(@"^\d{3}$", RegexOptions.Compiled);
private static readonly Regex YmdRegex = new(@"^\d{4}-\d{2}-\d{2}$", RegexOptions.Compiled);

private static readonly Dictionary<string, string> BankDirectory = new()
{
    ["004"] = "臺灣銀行",
    ["005"] = "土地銀行",
    ["700"] = "中華郵政",
    ["812"] = "台新銀行",
    ["822"] = "中國信託",
    // …可自行擴充
};

— 建立 function ValidateAccount,用於驗證帳號格式:

[KernelFunction, Description("檢查帳號是否為12~16碼數字")]
    public bool ValidateAccount(
        [Description("要檢查的帳號(僅數字)")] string account)
        => AccountRegex.IsMatch(account ?? string.Empty);

— 建立 function ValidateAccount,用於檢查銀行代碼:

[KernelFunction, Description("檢查銀行代碼是否為3碼數字")]
    public bool ValidateBankCode(
        [Description("銀行代碼(例如 004、812)")] string bankCode)
        => BankCodeRegex.IsMatch(bankCode ?? string.Empty);

— 建立 function ValidateYmdDate,用於檢查日期格式:

[KernelFunction, Description("檢查日期是否為 YYYY-MM-DD 格式")]
    public bool ValidateYmdDate(
        [Description("日期(YYYY-MM-DD)")] string ymd)
        => YmdRegex.IsMatch(ymd ?? string.Empty) && DateOnly.TryParse(ymd, out _);
  • 建立 function GetBankNameByCode,用於查詢銀行名稱:
[KernelFunction, Description("輸入銀行代碼回傳銀行名稱,若無則回傳 Unknown")]
    public string GetBankNameByCode(
        [Description("銀行代碼(3碼數字)")] string bankCode)
        => BankDirectory.TryGetValue(bankCode ?? "", out var name) ? name : "Unknown";
  • 建立 function CreateRemittanceDraft,用於建立匯款草稿:
[KernelFunction, Description("建立匯款草稿(模擬,不會真的送出交易)")]
public string CreateRemittanceDraft(
    [Description("收款銀行代碼(3碼)")] string bank_code,
    [Description("收款帳號(12~16碼數字)")] string payee_account,
    [Description("收款人姓名")] string payee_name,
    [Description("金額(TWD,正整數)")] int amount_TWD,
    [Description("用途(20字以內)")] string purpose,
    [Description("轉帳日期(YYYY-MM-DD),可為今天")] string transfer_date)
{
    // 基本檢核(保護性,再次檢查)
    if (!ValidateBankCode(bank_code)) throw new ArgumentException("bank_code 格式錯誤");
    if (amount_TWD <= 0) throw new ArgumentException("amount_TWD 必須為正整數");
    if (!ValidateYmdDate(transfer_date)) throw new ArgumentException("transfer_date 格式錯誤");
    if (string.IsNullOrWhiteSpace(purpose) || purpose.Length > 20) throw new ArgumentException("purpose 必填且不超過20字");

    var bankName = GetBankNameByCode(bank_code);
    var fee = EstimateFee(bank_code, amount_TWD); // 簡單估算手續費(示意)
    var draft = new RemittanceDraft
    {
        DraftId = $"D{DateTime.UtcNow:yyyyMMddHHmmssfff}",
        CreatedAt = DateTime.UtcNow,
        Currency = "TWD",
        PayeeBankCode = bank_code.Trim(),
        PayeeBankName = bankName,
        PayeeAccount = payee_account.Trim(),
        PayeeName = payee_name.Trim(),
        AmountTwd = amount_TWD,
        TransferDate = transfer_date,
        Purpose = purpose.Trim(),
        EstimatedFee = fee,
        TotalDebit = amount_TWD + fee,
        Status = "DRAFT"
    };

    // 以 JSON 回傳,真實系統可改為回傳物件或其他格式或是直接產生實體表單、QR Code 等
    return JsonSerializer.Serialize(draft, new JsonSerializerOptions { WriteIndented = true });
}
  • 建立function EstimateFeeForPreview ,用於估算跨行/同行手續費:
[KernelFunction, Description("估算跨行/同行手續費(示意用)")]
public int EstimateFeeForPreview(
    [Description("收款銀行代碼(3碼)")] string bank_code,
    [Description("金額(TWD,正整數)")] int amount_TWD)
    => EstimateFee(bank_code, amount_TWD);

private static int EstimateFee(string bankCode, int amount)
{
    // 示意規則:同行 0 元;跨行 15 元;大額(>=500,000)加收 10 元
    var isSameBank = bankCode == "812"; // 假設匯出行是 812 台新
    var fee = isSameBank ? 0 : 15;
    if (amount >= 500_000) fee += 10;
    return fee;
}
  • function 會使用到的資料結構:
public sealed class RemittanceDraft
{
    public string DraftId { get; set; } = default!;
    public DateTime CreatedAt { get; set; }
    public string Status { get; set; } = default!;
    public string Currency { get; set; } = default!;
    public string PayeeBankCode { get; set; } = default!;
    public string PayeeBankName { get; set; } = default!;
    public string PayeeAccount { get; set; } = default!;
    public string PayeeName { get; set; } = default!;
    public int AmountTwd { get; set; }
    public int EstimatedFee { get; set; }
    public int TotalDebit { get; set; }
    public string TransferDate { get; set; } = default!;
    public string Purpose { get; set; } = default!;
}

Step 2: 建立 BankRemittanceAgent

接下來,我們要建立一個 BankRemittanceAgent,這個 Agent 會掛載並使用我們剛剛建立的 BankRemittancePlugin,並且定義它的行為與目標。

  • 建立 Kernel 並掛載 BankRemittancePlugin
var kernel = Kernel.CreateBuilder()
    .AddOpenAIChatCompletion(
        apiKey: Config.OpenAI_ApiKey,
        modelId: Config.ModelId)
    .Build();

// 註冊銀行匯款工具(模擬)
kernel.Plugins.AddFromType<BankRemittancePlugin>();
  • 建立 agent ,並加入以下程式碼:
var agent = new ChatCompletionAgent
{
    Name = "BankRemitAgent",
    Description = "協助使用者以對話方式完成銀行匯款單草稿的 AI 助理",
    Instructions = """
        你是一位「銀行匯款助理」。目標是蒐集完成一筆台幣(TWD)國內轉帳/匯款所需的資訊,並於資訊完整時「只呼叫工具」建立匯款單草稿(模擬),切勿臆測或捏造。

        【必填欄位】
        
        1) 收款銀行代碼 bank_code(3碼)
        2) 收款帳號 payee_account(12-16碼數字)
        3) 收款人姓名 payee_name
        4) 金額 amount_TWD(正整數,單位 TWD)
        5) 用途 purpose(20字以內)
        6) 轉帳日期 transfer_date(YYYY-MM-DD,若未填,預設為今天)

        【若資訊不足怎麼辦】
        - 一次只問一件事,使用簡短追問。
        - 使用使用者原話重述與確認(小結),避免誤會。
        - 對數字與格式進行基本檢核,錯誤時友善提示。
        - 當所有欄位齊全且檢核通過時,呼叫 CreateRemittanceDraft 建立匯款單草稿。

        【安全與規範】
        - 僅處理模擬草稿,不會執行真實交易,不索取或保存 OTP、密碼。
        - 僅使用已註冊的工具;嚴禁自行編造銀行名稱或帳戶資訊。

        【輸出格式】
        - 平時對話使用自然中文。
        - 建立匯款單草稿後,請提供「匯款單草稿」清楚輸出;匯款單草稿內容由工具回傳的 JSON為準。
        """,
    Kernel = kernel,
    Arguments = new(new PromptExecutionSettings
    {
        // 自動函式選擇
        FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
    })
};
  • 建立對話 Thread
var agentThread = new ChatHistoryAgentThread();
  • 與 Agent 互動
Console.WriteLine("💬 請描述你要匯款的資訊(輸入 exit 結束):");
Console.Write("User > ");
string? userInput;
while ((userInput = Console.ReadLine()) is not null)
{
    if (string.IsNullOrWhiteSpace(userInput) ||
        userInput.Equals("exit", StringComparison.OrdinalIgnoreCase))
        break;

    var message = new ChatMessageContent(AuthorRole.User, userInput);

    bool first = true;
    await foreach (StreamingChatMessageContent resp in agent.InvokeStreamingAsync(message, agentThread))
    {
        if (first)
        {
            Console.Write($"{resp.Role} - {resp.AuthorName ?? "*"} > ");
            first = false;
        }
        Console.Write(resp.Content);
    }

    Console.WriteLine();
    Console.WriteLine($"\n# trace chat thread with agent: {agent.Name} - {agent.Description}, threadId: {agentThread.Id}\n");
    Console.Write("User > ");
}

執行 BankRemittanceAgent

=== Semantic Kernel AI Agent 系統 ===
💬 請描述你要匯款的資訊(輸入 exit 結束):
User > 轉 2000 給王小明,用途是午餐費                    
Assistant - BankRemitAgent > 好的,我收到您的需求:

- 金額:2000元
- 收款人姓名:王小明
- 用途:午餐費

請問王小明的「收款銀行代碼」是幾號(三碼數字)呢?

# trace chat thread with agent: BankRemitAgent - 協助使用者以對話方式完成銀行匯款單草稿的 AI 助理, threadId: 01ad8c5ad39e4de6b4d07650214334cc

User > 812
Assistant - BankRemitAgent > 您提供的收款銀行代碼為812(請協助確認),還需要收款帳號(12-16碼數字)。

請提供王小明的帳號,才能繼續。

# trace chat thread with agent: BankRemitAgent - 協助使用者以對話方式完成銀行匯款單草稿的 AI 助理, threadId: 01ad8c5ad39e4de6b4d07650214334cc

User > 886123456789 
Assistant - BankRemitAgent > 您提供的王小明帳號為886123456789(共12碼),格式正確。

請問要設定匯款日期為今天嗎?如果需要指定其它日期,也請提供(格式YYYY-MM-DD)。

# trace chat thread with agent: BankRemitAgent - 協助使用者以對話方式完成銀行匯款單草稿的 AI 助理, threadId: 01ad8c5ad39e4de6b4d07650214334cc

User > 今天
Assistant - BankRemitAgent > 匯款單草稿如下:

- 收款銀行:台新銀行(代碼812)
- 收款帳號:886123456789
- 收款人姓名:王小明
- 金額:2000元
- 用途:午餐費
- 匯款日期:2024-06-13
- 預估手續費:0元
- 扣款總額:2000元

如需後續操作,請告知!

# trace chat thread with agent: BankRemitAgent - 協助使用者以對話方式完成銀行匯款單草稿的 AI 助理, threadId: 01ad8c5ad39e4de6b4d07650214334cc

結語

這樣我們就完成了一個簡單的「銀行匯款助理」Agent,它能夠透過對話的方式,幫助使用者蒐集匯款所需的資訊,並在資訊齊全後,自動呼叫工具建立一張匯款單草稿,當 Agent 能夠正確理解使用者的需求時,並識別出所有必要的資訊,後續要做任何事都能夠輕鬆應對,當然產生QR Code 也不在話下。


上一篇
Day 10:來了!! Single Agent 實戰 - 對帳稽核 Agent
系列文
當 .NET 遇見 AI Agents:用 Semantic Kernel × MCP 打造智慧協作應用11
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言