2026年10月29日 星期四

[AI 分享] 傳統軟體公司如何找到 AI 轉型方向

 [AI 分享] 傳統軟體公司如何找到 AI 轉型方向

摘要 : AI 轉型不必等產品成熟;讓客戶看見、連結夥伴、走進現場,才能在真實場景中找到方向。



內容:


最近,我特意到寧波拜訪一位認識多年的朋友。他從程式開發起家,創業十多年,至今仍保有技術人的務實與實幹。多年來,他累積了不少客戶,也有自己的產品。談到 AI,他說了一句很真切的話:「AI 真的是讓人又愛又恨。」愛的是它帶來巨大機會,恨的是既有產品、技術和商業模式都可能被顛覆。


和他的團隊交流後,我一直在想:傳統軟體公司該如何轉型 AI?怎麼找到合適的產品方向,又該從哪裡起步?我認為,首先要打破過去的產品思維。以往做軟體,常是先關起門研發產品,再找客戶銷售、交付;但 AI 應用仍在快速探索,沒有必要等產品成熟才行動。比起先做出一套完整產品,更重要的是找到真實場景與可行的解決方案。


第一步,讓客戶知道你正在做 AI。 可以透過短影片持續分享 AI 技術、產業觀點、探索過程和初步成果。這不只是為了獲取流量,更是為了讓老客戶、老朋友更新對你的印象。否則,即使他們有 AI 需求,想到的可能仍是你過去提供的 ERP、SRM 等傳統軟體,而不會第一時間找你。起步時,不必急著把產品打磨到完美,先讓客戶看見你、想起你。


第二步,連結夥伴,不必事事自行研發。 如果目前沒有自己的 AI 產品,就多認識做 AI 的朋友,連結產品與技術資源。只要別人的產品能解決客戶問題,就可以協助引進與推廣。這既幫助客戶,也支持合作夥伴;更重要的是,你能參與 AI 專案從需求辨識、方案設計到落地實施的過程。這些經驗會反過來幫助你改進產品,甚至發現新的方向。因此,不必因為想守住客戶資源,就錯過合作機會。


第三步,走進客戶現場,先理解問題。 拜訪合作多年的客戶,不一定要帶著產品,也不一定每次都要談成生意。可以多問問:哪些工作每天都在重複?哪些經驗會隨著員工離職而流失?有價值的 AI 場景,往往藏在業務人員日常的困擾裡,而不是坐在辦公室就能想出來。一次現場交流未必立刻帶來訂單,卻可能讓你找到真正值得解決的問題。


第四步,找到場景後,願意投入並與客戶共創。 如果某個場景確實有業務價值,客戶也願意配合,就值得投入資源。由客戶提供真實場景、業務人員與資料,軟體公司提供技術能力,雙方一起把場景跑通。早期不必只盯著眼前能賺多少錢;即使需要投入一些成本,也可以在服務客戶的同時,累積技術、經驗與夥伴資源。


傳統軟體公司的 AI 轉型,首先不是技術問題,而是思維問題。方向不是在辦公室裡反覆研究出來的,而是在客戶現場、在實際專案中跑出來的。與其原地研究一百次,不如走訪客戶一次,找到一個真實場景。不要等一切都準備好了才出發;很多時候,只有出發後,才知道還需要準備什麼。

[AI 分享] 讓 Codex 更順手的十個實用技巧

 [AI 分享] 讓 Codex 更順手的十個實用技巧

摘要:整理 Codex 的十種用法,涵蓋不中斷工作時提問、規劃任務、管理對話與複用經驗。



內容:


Codex 正在執行任務時,如果突然想補充問題,卻又不想打斷它,可以使用側邊聊天。例如,詢問它為什麼採取某個做法、報錯代表什麼,或為何執行某段指令碼。輸入 side 或點選入口開啟臨時聊天,就能一邊了解情況,一邊讓原本的工作繼續進行。


如果只是看不懂回答中的術語,或想深入了解某一句話,可以框選那段文字,再點選**「更多詳情」**。Codex 會針對選取的內容進一步解釋。


開始動手前,也有三個技巧值得使用。計劃模式適合需求尚未釐清、步驟較多,或需要比較不同做法的任務。輸入 /plan,先與 Codex 討論方案,確認方向後再切回執行模式。目標模式則適合結果明確、但中途需要反覆查資料和調整做法的任務;輸入 go,說清楚最終目標與完成標準,讓它持續朝目標推進。完成程式碼修改後,還可以輸入 /review,選擇要檢查的改動,請 Codex 找出錯誤、遺漏,以及可能對原有功能造成的影響。


對話進行一段時間後,可以用三個技巧管理會話。輸入 status,查看目前對話的狀態與用量;如果任務還沒完成、對話卻已經很長,可以輸入 /compact 壓縮上下文,再接著做原本的事。若想保留先前討論,同時沿用既有背景探索另一個方向,則可輸入 fork,分出新的對話分別推進。


最後兩個技巧適合長期使用。對於檔案位置、交付格式等每次都要重複說明的固定要求,可以整理進專案的 agents.md,方便 Codex 日後在該專案工作時參考。若已經磨合出一套好用的流程,例如整理訪談的方法,也可以把流程製作成 skill;下次選用對應的 skill,再提供新材料,就不必從頭交代一次。


這十個技巧涵蓋了工作中提問、動手前規劃、完成後檢查、對話管理,以及保存長期經驗。原分享者也將它們整理成速查卡,放在影片最後一個畫面,供需要的人截圖保存。

MFA010 - 函式工具呼叫(Function Calling)

MFA010 - 函式工具呼叫(Function Calling)

在上一篇關於 Microsoft Agent Framework 系列教學文章 "MFAMFA009 - 結構化輸出(JSON)" 中,我們學會了如何讓 AI 代理生成結構化的 JSON 購物清單,並確保每個食材的分類與份量正確。這樣我們就不再需要去關注生成出來的那些非結構的文字內容,可以直接面對結構化的 Json 物件。

在完成這篇文章的練習,

接下來看看如何做到這樣的效果。

建立 Console 專案

  • 開啟 Visual Studio 2026
  • 選擇「建立新專案」
  • 在 [建立新的專案] 視窗中,在右方清單內,找到並選擇「主控台應用程式」 項目
  • 然後點擊右下方「下一步」按鈕
  • 此時將會看到 [設定新的專案] 對話窗
  • 在該對話窗的 [專案名稱] 欄位中,輸入專案名稱,例如 "csFunctionCallingAgent"
  • 然後點擊右下方「下一步」按鈕
  • 接著會看到 [其他資訊] 對話窗
  • 在這個對話窗內,確認使用底下的選項
    • 架構:.NET 10.0 (或更新版本)
    • 勾選 不要使用最上層陳述式 (這是我的個人習慣)
  • 然後點擊右下方「建立」按鈕
  • 現在,已經完成了這個 Console 主控台 專案的建立

安裝需要用到的 NuGet 套件

要完成這個第一次接觸 Microsoft Agent Framework 練習專案,需要安裝以下幾個 NuGet 套件:

Microsoft.Extensions.AI

這個套件將會為生成式 AI 定義的一層「統一抽象介面 + Middleware 管線」,讓開發者可以更方便地整合不同的生成式 AI 模型,並且在應用程式中使用一致的方式進行呼叫與管理。

  • 滑鼠右擊 [csFunctionCallingAgent] 專案節點
  • 點選彈出功能表的 [管理 NuGet 套件] 項
  • 在 [瀏覽] 索引標籤中,搜尋並且安裝底下的 NuGet 套件
    • Microsoft.Extensions.AI

Microsoft.Extensions.AI.OpenAI

這個套件是微軟官方為 .NET 整合 AI 服務所提供的擴充套件。它實現了 IChatClient 與 IEmbeddingGenerator 核心抽象介面,讓開發人員能以一致的程式碼,輕鬆串接 OpenAI 或相容的 API 端點(例如 Azure OpenAI、GitHub Models 等)。

  • 滑鼠右擊 [csFunctionCallingAgent] 專案節點
  • 點選彈出功能表的 [管理 NuGet 套件] 項
  • 在 [瀏覽] 索引標籤中,搜尋並且安裝底下的 NuGet 套件
    • Microsoft.Extensions.AI.OpenAI

Microsoft.Agents.AI

這個套件是微軟官方推出 Microsoft Agent Framework(微軟智慧代理框架)的核心 .NET 套件。它主要用於在 .NET 環境中建構、協調與部署 AI 代理(AI Agents) 及多代理工作流(Multi-agent workflows),是用來取代舊版 AutoGen 的企業級解決方案。

  • 滑鼠右擊 [csFunctionCallingAgent] 專案節點
  • 點選彈出功能表的 [管理 NuGet 套件] 項
  • 在 [瀏覽] 索引標籤中,搜尋並且安裝底下的 NuGet 套件
    • Microsoft.Agents.AI

OpenAI

這個套件為官方的 OpenAI .NET 客戶端類別庫,是 OpenAI NuGet 套件。此套件由 OpenAI 與 Microsoft 合作開發,提供直接存取 OpenAI 官方 REST API 的便利管道。

  • 滑鼠右擊 [csFunctionCallingAgent] 專案節點
  • 點選彈出功能表的 [管理 NuGet 套件] 項
  • 在 [瀏覽] 索引標籤中,搜尋並且安裝底下的 NuGet 套件
    • OpenAI

宣告 API 端點與 Key 環境變數

關於這個小節的內容,將會在這個系列每篇文章中都會用到,因此,可以參考 MFA001 Microsoft Agent Framework 初次體驗開發出 AI 代理 文章中的 [宣告 API 端點與 Key 環境變數] 說明內容,若這裡提到的環境變數已經建立好了,則可以跳過這個小節內容。

修改 Program.cs 程式碼

為了要完成這個需求 : 如何用 Azure OpenAI 的 gpt-5.6-luna 聊天模型建立一個簡單 AI 代理,並產生一篇與鵝有關的詩。 因此,我們需要修改 Program.cs 程式碼,這時需要完成底下的操作。

  • 在專案內,找到並且開啟 [Program.cs] 檔案
  • 使用底下程式碼內容來取代 [Program.cs] 檔案的內容
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI;
using OpenAI.Chat;
using System.ClientModel;
using System.ComponentModel;
using System.Text;
using System.Text.Json.Nodes;

namespace csFunctionCallingAgent;

internal class Program
{
    private static readonly HttpClient Http = new();
    private static int _toolCallCount;

    static async Task Main(string[] args)
    {
        Console.OutputEncoding = Encoding.UTF8;

        var apiKey = Environment.GetEnvironmentVariable("AzureOpenAI_Key");
        var endpoint = Environment.GetEnvironmentVariable("AzureOpenAI_Endpoint");
        var model = "gpt-5.6-luna";

        if (string.IsNullOrWhiteSpace(apiKey) || string.IsNullOrWhiteSpace(endpoint))
        {
            Console.WriteLine("請先設定環境變數 AzureOpenAI_Key 與 AzureOpenAI_Endpoint 後再執行。");
            return;
        }

        IChatClient chatClient =
            new ChatClient(
                    model,
                    new ApiKeyCredential(apiKey),
                    new OpenAIClientOptions { Endpoint = new Uri(endpoint) })
                .AsIChatClient();

        AIAgent agent = new ChatClientAgent(
            chatClient,
            "你是穿搭顧問,用繁體中文精簡回答。需要天氣資訊時請使用工具查詢,不要自己猜測天氣。", // 系統提示詞
            "穿搭顧問", // 代理名稱
            null, // 代理描述
            [AIFunctionFactory.Create(GetWeatherForecast)]); // 交給代理的工具,要不要呼叫由模型決定

        string[] questions =
        [
            "我明天要去台北上班,該穿什麼?",
            "白色襯衫搭什麼顏色的褲子好看?",
            "我明天早上在台北、晚上到高雄,要帶哪些衣服?",
        ];

        Console.WriteLine($"使用的模型:{model}");

        foreach (var question in questions)
        {
            _toolCallCount = 0;
            Console.WriteLine();
            Console.WriteLine($"❓ {question}");

            var response = await agent.RunAsync(question);

            Console.WriteLine(response.Text);
            Console.WriteLine($"(本題呼叫工具 {_toolCallCount} 次)");
        }
    }

    [Description("查詢指定城市在今天起第幾天的天氣預報")]
    private static async Task<string> GetWeatherForecast(
        [Description("城市的英文名稱,例如 Taipei、Kaohsiung")] string city,
        [Description("從今天起算第幾天:0=今天,1=明天,最多 6")] int daysFromToday)
    {
        _toolCallCount++;
        Console.WriteLine($"🔧 呼叫 GetWeatherForecast(city: {city}, daysFromToday: {daysFromToday})");

        daysFromToday = Math.Clamp(daysFromToday, 0, 6);

        // 1. 城市名稱 → 經緯度
        var geo = JsonNode.Parse(await Http.GetStringAsync(
            $"https://geocoding-api.open-meteo.com/v1/search?name={Uri.EscapeDataString(city)}&count=1&language=zh"));
        var place = geo?["results"]?[0];
        if (place is null)
        {
            return $"找不到城市 {city}";
        }

        // 2. 經緯度 → 每日天氣預報
        var forecast = JsonNode.Parse(await Http.GetStringAsync(
            $"https://api.open-meteo.com/v1/forecast?latitude={place["latitude"]}&longitude={place["longitude"]}" +
            $"&daily=weather_code,temperature_2m_max,temperature_2m_min,precipitation_probability_max" +
            $"&timezone=auto&forecast_days={daysFromToday + 1}"));
        var daily = forecast!["daily"]!;
        var i = daysFromToday;

        return $"{place["name"]} {daily["time"]![i]}:{WeatherText((int)daily["weather_code"]![i]!)}," +
               $"氣溫 {daily["temperature_2m_min"]![i]}~{daily["temperature_2m_max"]![i]}°C," +
               $"降雨機率 {daily["precipitation_probability_max"]![i]}%";
    }

    // WMO 天氣代碼 → 中文
    private static string WeatherText(int code) => code switch
    {
        0 => "晴",
        1 or 2 => "多雲",
        3 => "陰",
        45 or 48 => "霧",
        >= 51 and <= 57 => "毛毛雨",
        >= 61 and <= 67 => "雨",
        >= 71 and <= 77 => "雪",
        >= 80 and <= 82 => "陣雨",
        85 or 86 => "陣雪",
        >= 95 and <= 99 => "雷雨",
        _ => "未知",
    };
}

這個程式一開始將會讀取 API Key 與服務端點的數據,接著如同前面的範例相同,先建立一個 IChatClient 物件。接者建立 AIAgent 物件,這裡將會指定系統提示詞為 "你是穿搭顧問,用繁體中文精簡回答。需要天氣資訊時請使用工具查詢,不要自己猜測天氣" 其中,最後一句話,會讓代理人物件,嘗試到這個系統內,找到合適的工具來取得天氣資訊,而不是自行猜測。

從這個系統提示詞可以看出,這並不是僅僅想要讓 AI Agent 來使用工具來回答內容,而是想要透過工具來取得正確的天氣資訊,並且使用這些天氣預報資訊,讓 AI 模型產生出需要穿搭的建議。這樣的設計可以確保 AI 代理在提供穿搭建議時,所依據的天氣資訊是準確的,而不是憑空猜測,提升了建議的可靠性與實用性。

當在建立這個 AI Agent 的時候,有將 [AIFunctionFactory.Create(GetWeatherForecast)] 傳送給 ChatClientAgent 建構函式內,這個參數為讓 AI Agent 知道與註冊一個可用的工具函式,讓 AI 在需要天氣資訊時,可以呼叫這個工具來取得正確的天氣資料。

這樣的設計讓 AI Agent 能夠在對話過程中,根據使用者的需求,動態地呼叫工具函式來取得最新的天氣資訊,並將這些資訊整合到回應中,提供更精準且實用的建議。

AIFunctionFactory.Create 這個方法宣告在 Microsoft.Extensions.AI 命名空間中,用來建立可供 AI Agent 呼叫的工具函式。而傳入的引述為 GetWeatherForecast 這個方法,該方法的程式碼如下:

[Description("查詢指定城市在今天起第幾天的天氣預報")]
private static async Task<string> GetWeatherForecast(
    [Description("城市的英文名稱,例如 Taipei、Kaohsiung")] string city,
    [Description("從今天起算第幾天:0=今天,1=明天,最多 6")] int daysFromToday)
{
    _toolCallCount++;
    Console.WriteLine($"🔧 呼叫 GetWeatherForecast(city: {city}, daysFromToday: {daysFromToday})");

    daysFromToday = Math.Clamp(daysFromToday, 0, 6);

    // 1. 城市名稱 → 經緯度
    var geo = JsonNode.Parse(await Http.GetStringAsync(
        $"https://geocoding-api.open-meteo.com/v1/search?name={Uri.EscapeDataString(city)}&count=1&language=zh"));
    var place = geo?["results"]?[0];
    if (place is null)
    {
        return $"找不到城市 {city}";
    }

    // 2. 經緯度 → 每日天氣預報
    var forecast = JsonNode.Parse(await Http.GetStringAsync(
        $"https://api.open-meteo.com/v1/forecast?latitude={place["latitude"]}&longitude={place["longitude"]}" +
        $"&daily=weather_code,temperature_2m_max,temperature_2m_min,precipitation_probability_max" +
        $"&timezone=auto&forecast_days={daysFromToday + 1}"));
    var daily = forecast!["daily"]!;
    var i = daysFromToday;

    return $"{place["name"]} {daily["time"]![i]}:{WeatherText((int)daily["weather_code"]![i]!)}," +
            $"氣溫 {daily["temperature_2m_min"]![i]}~{daily["temperature_2m_max"]![i]}°C," +
            $"降雨機率 {daily["precipitation_probability_max"]![i]}%";
}

此方法使用 Description 屬性來標註其用途,在這裡使用了 "查詢指定城市在今天起第幾天的天氣預報" 字串,有了這樣的資訊,AI Agent 就能夠理解這個工具函式的用途,會將當時所提供的提示詞進行理解,決定是否要呼叫哪個方法,並且要傳遞甚麼參數到該方法內,當然,所要傳入進去的引數數值,會再提示詞中進行理解,如此一來,AI Agent 就能夠在需要時呼叫它來取得天氣預報。

同樣地,方法的參數也使用了 Description 屬性來標註其用途,例如 city 參數標註為 "城市的英文名稱,例如 Taipei、Kaohsiung",daysFromToday 參數標註為 "從今天起算第幾天:0=今天,1=明天,最多 6"。這些描述有助於 AI Agent 理解每個參數的意圖,從而正確地傳遞使用者的需求給工具函式。

在這個方法內,一旦取得了 城市名稱與從今天起第幾天的資訊 (當然,這些資訊將會從提示詞的上下文中解析而來),就可以進一步透過 geocoding-api.open-meteo.com 所提供的服務來查詢經緯度,當取得了經緯度之後,接著再度透過 api.open-meteo.com 服務來抓取該經緯度的天氣預報資訊,最後將結果組合成文字回傳給 AI Agent。這樣一來,AI Agent 就能夠根據使用者的需求,動態地取得正確的天氣資訊,並將其整合到回應中。

現在回到主程式進入點的 Main 方法內,這個程式將依序對 AI Agent 提出這三個問題:"我明天要去台北上班,該穿什麼?", "白色襯衫搭什麼顏色的褲子好看?", "我明天早上在台北、晚上到高雄,要帶哪些衣服?" 現在,在 GetWeatherForecast 方法的 _toolCallCount++; 敘述中設定中斷點,這樣在程式執行到呼叫天氣預報的時候,就能夠暫停,方便開發者觀察 AI Agent 的行為以及傳入的參數。

當這個專案執行後,螢幕顯示了 "❓ 我明天要去台北上班,該穿什麼?" ,這個專案將會停在中斷點上,此時的參數為 city = "Taipei", daysFromToday = 1 這個方法將會回傳 "台北市 2026-10-28:毛毛雨,氣溫 20.3~24.2°C,降雨機率 45%" 資訊給 AI Agent

接著系統將會進行送入第二個提示詞 "❓ 白色襯衫搭什麼顏色的褲子好看?" 由於這個提示詞內沒有任何地點與天氣的詢問意圖,所以,GetWeatherForecast 方法不會被呼叫,程式將直接由 AI Agent 生成回應。

最後,系統將會送入第三個提示詞 "❓ 我明天早上在台北、晚上到高雄,要帶哪些衣服?" 由於這個提示詞內包含了兩個地點與天氣的詢問意圖,因此,GetWeatherForecast 方法將會被呼叫兩次,分別查詢台北與高雄的天氣預報,方法收到的參數為 city = "Taipei", daysFromToday = 1 city = "Kaohsiung", daysFromToday = 1

這個方法將會回傳 "台北市 2026-10-28:毛毛雨,氣溫 20.324.2°C,降雨機率 45%" & "高雄市 2026-10-28:陰,氣溫 24.728.2°C,降雨機率 2%" 資訊給 AI Agent

現在,AI Agent收到這兩次呼叫的結果後,就能夠根據天氣資訊來生成最適合的穿搭建議。

執行結果

現在來看看這個範例程式碼的執行結果。

  • 在 Visual Studio 2026 下,按下 F5 鍵執行程式,將會看到程式輸出的結果。
使用的模型:gpt-5.6-luna

❓ 我明天要去台北上班,該穿什麼?
🔧 呼叫 GetWeatherForecast(city: Taipei, daysFromToday: 1)
明天台北約 **20–24°C、毛毛雨,降雨機率 45%**,建議:

- **上身**:長袖襯衫或薄針織衫,外搭防潑水薄外套
- **下身**:長褲,西裝褲或俐落牛仔褲皆可
- **鞋子**:防潑水樂福鞋、短靴或運動鞋
- **必備**:摺傘;早晚可加一件薄外套

整體以「**薄層次+防雨**」為主,辦公室若冷也方便調整。
(本題呼叫工具 1 次)

❓ 白色襯衫搭什麼顏色的褲子好看?
白色襯衫很百搭,推薦這幾種褲色:

- **黑色**:俐落正式,適合上班或正式場合
- **深藍/丹寧藍**:經典耐看,休閒又有精神
- **卡其/米色**:溫柔清爽,適合日常穿搭
- **灰色**:簡約成熟,整體很有質感
- **咖啡/駝色**:增加秋冬氛圍與層次
- **白色/奶油色**:同色系穿搭,乾淨時髦;建議材質或深淺有區別

想要最不出錯:**白襯衫+深藍牛仔褲**。
想要顯正式:**白襯衫+黑色西裝褲**。
(本題呼叫工具 0 次)

❓ 我明天早上在台北、晚上到高雄,要帶哪些衣服?
🔧 呼叫 GetWeatherForecast(city: Taipei, daysFromToday: 1)
🔧 呼叫 GetWeatherForecast(city: Kaohsiung, daysFromToday: 1)
明天台北 **20–24°C、毛毛雨**,晚上高雄 **25–28°C、陰天**,建議帶:

- **短袖上衣** 1–2 件
- **薄長袖或輕薄外套** 1 件:早上台北較涼,晚間高雄可脫掉
- **長褲** 1 件;怕熱可另帶薄裙/短褲
- **摺傘或防潑水外套**:台北有毛毛雨
- **防滑、好走的鞋子**,避免麂皮或布鞋

穿搭可選「短袖+薄外套+長褲」,抵達高雄後脫外套即可。
(本題呼叫工具 2 次)