iT邦幫忙

2025 iThome 鐵人賽

DAY 27
0
生成式 AI

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

Day 27: Semantic Kernel Multi-Agent 實戰 — 打造 PTT 熱門關鍵話題擷取系統

  • 分享至 

  • xImage
  •  

以 Semantic Kernel Agent Orchestration 為核心,結合 LLM 語言能力打造「輿情收集與初步分析系統」。以這樣的概念,來實作透過對 PTT 八卦版 RSS 資料的自動化擷取與處理,系統能即時蒐集熱門討論內容,並進行文字清理、關鍵詞分析及同義詞整併,最終輸出「當下熱門話題排行榜」,協助快速掌握話題動態,非常適用於企業品牌監測、話題追蹤、輿情偵測等多種應用場景。

整體流程採用 Semantic Kernel Agent 型能的 SequentialOrchestration 組合三個具明確職責的 Agent:

  • FetcherAgent:從 PTT 八卦版 RSS 來源自動擷取最新文章資料,作為分析的輸入來源。
  • CleanerAgent:對抓取的文本進行正規化與噪音移除,包含網址、Emoji、標點、重複標頭等處理,確保資料乾淨一致。
  • KeywordAgent:進行關鍵詞抽取與 LLM 驅動的同義詞合併,將不同寫法、別名統一歸類,最後依據詞頻與上下文代表性產生熱門話題排名報表。

這樣的系統還可以再擴充更多功能,例如:

  • SentimentAgent:進行中文情緒分析,輸出正/負/中立比例。
  • TrendAgent:分析關鍵詞出現頻率的時間序列變化,偵測爆量事件。
  • ReportAgent:將分析結果排程輸出至 Dashboard 資料。

系統實作

系統主要分為三個 Agent,分別負責資料擷取、清理與關鍵詞分析。以下是各 Agent 的核心程式碼與說明。

  • FetcherAgent:負責從 PTT 八卦版 RSS 擷取最新文章資料,並且這個 fetcherAgent 需要使用 RssPlugin 的 FetchFeed 函式來取得 RSS 內容,因此 FunctionChoiceBehavior 需要設為 Auto。
var fetcherAgent = new ChatCompletionAgent
{
    Name = "FetcherAgent",
    Description = "資料擷取代理,根據使用者的需求,取得RSS資料",
    Instructions =
    """
    你是資料擷取代理,根據使用者的需求,取得RSS資料。
    
    feedUrl:PTT RSS 網址,例如:https://www.ptt.cc/atom/Gossiping.xml
    take:要取幾筆文章,例如:10

    僅輸出RSS內容文字,不要多餘說明。
    """,
    Kernel = kernel,
    Arguments = new(new PromptExecutionSettings
    {
        // 自動函式選擇
        FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
    })
};
  • CleanerAgent:負責對抓取的文本進行清理,移除噪音與不必要的內容,並且這個 cleanerAgent 需要使用 TextCleanPlugin 的 CleanBatch 函式來進行文本清理,因此 FunctionChoiceBehavior 需要設為 Auto。
//資料清理代理
var cleanerAgent = new ChatCompletionAgent
{
    Name = "CleanerAgent",
    Description = "資料清理代理,負責清理從 RSS 擷取的文章內容",
    Instructions =
    """
    你是文本清理代理,負責清理從 RSS 擷取的文章內容。
    僅輸出「清理後的字串」。
    """,
    Kernel = kernel,
    Arguments = new(new PromptExecutionSettings
    {
        // 自動函式選擇
        FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
    })
};

  • KeywordAgent:負責關鍵詞抽取與同義詞合併,最終產出熱門話題排名。這個 keyworderAgent 不使用任何外部函式,完全透過 LLM 的語言能力來進行關鍵詞分析與同義詞合併。
// 關鍵詞代理
var keyworderAgent = new ChatCompletionAgent
{
    Name = "KeywordAgent",
    Description = "熱門話題分析代理,分析清理後的文章內容並找出熱門關鍵話題",
    Instructions =
    """
    你是熱門話題分析代理。
    請分析上一階段提供的清理後 JSON 文章資料,找出最熱門的關鍵話題。
    
    分析要求:
    1. 識別文章標題中的重要關鍵詞和主題
    2. 將相似或相關的話題合併(例如:「颱風」「天氣」可合併為「氣象」)
    3. 統計各話題的出現次數
    4. 列出前 15 個最熱門的話題
    
    輸出格式(純 Markdown):
    # PTT 八卦版熱門話題排行
    
    | 排名 | 關鍵話題 | 出現次數 | 相關文章標題範例  |
    | ---- | -------- | -------- | ----------------- |
    | 1    | [話題]   | X 次     | [1-2個代表性標題] |
    
    注意:僅輸出 Markdown 表格,不要其他說明文字。
    """,
    Kernel = kernel
};
  • RssPlugin:負責從 PTT RSS 擷取最新文章資料,由於 function 有提供參數,因此只要變更 feedUrl 便可以擴展至其他版的 RSS,例如八卦版、政治版等。
[KernelFunction("FetchFeed"), Description("讀取 RSS 資料")]
    public async Task<string> FetchFeedAsync(
        [Description("RSS 的網址")] string feedUrl,
        [Description("最多取得幾筆")] int maxItems = 50)
{
    using var httpClient = new HttpClient();
    httpClient.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");

    var xmlContent = await httpClient.GetStringAsync(feedUrl);
    var doc = XDocument.Parse(xmlContent);

    // 判斷是 Atom 還是 RSS 2.0
    var isAtom = doc.Root?.Name.LocalName == "feed";

    Console.WriteLine($"Feed Type: {(isAtom ? "Atom" : "RSS 2.0")}");

    var items = isAtom ? ParseAtomFeed(doc, maxItems) : ParseRssFeed(doc, maxItems);

    var sb = new StringBuilder();
    foreach (var item in items)
    {
        var pub = item.PublishDate?.ToString("yyyy-MM-dd HH:mm") ?? "n/a";
        sb.AppendLine($"- title: {item.Title}");
        sb.AppendLine($"  link: {item.Link}");
        sb.AppendLine($"  published: {pub}");
        sb.AppendLine($"  summary: {Trim(item.Content, 500)}");
        sb.AppendLine();
    }

    return sb.ToString();
}
  • TextCleanPlugin:負責對抓取的文本進行清理,移除噪音與不必要的內容,例如網址、Emoji、重複標頭等。這裡可以根據需求再擴充更多清理規則。
public class TextCleanPlugin
{
    private static readonly Regex UrlRx = new(@"https?://\S+", RegexOptions.IgnoreCase | RegexOptions.Compiled);
    private static readonly Regex EmojiRx = new(@"\p{Cs}", RegexOptions.Compiled);
    private static readonly Regex SpaceRx = new(@"\s+", RegexOptions.Compiled);

    [KernelFunction, Description("清理文本內容,移除網址、表情符號、多餘空白等")]
    public Task<string> CleanBatch(
        [Description("要清理的文本字串")]
        string rssContent)
    {
        // 移除網址
        var cleaned = UrlRx.Replace(rssContent, " ");

        // 移除 Emoji 表情符號
        cleaned = EmojiRx.Replace(cleaned, " ");

        // 移除零寬空白字元
        cleaned = cleaned.Replace("\u200B", " ");

        // 移除 Re: 和 Fw: 標記
        cleaned = cleaned.Replace("Re:", " ").Replace("Fw:", " ");

        // 將多個連續空白壓縮成單一空白
        cleaned = SpaceRx.Replace(cleaned, " ").Trim();

        return Task.FromResult(cleaned);
    }
}
  • 建立 Kernel 與 掛載 Plugin
Kernel kernel = Kernel.CreateBuilder()
    .AddOpenAIChatCompletion(
        apiKey: Config.OpenAI_ApiKey,
        modelId: Config.ModelId)
    .Build();

// 註冊工具
kernel.Plugins.AddFromType<RssPlugin>();
kernel.Plugins.AddFromType<TextCleanPlugin>();
  • 建立 Orchestration,將三個 Agent 依序串接起來
SequentialOrchestration orchestration = new(fetcherAgent, cleanerAgent, keyworderAgent)
{
    Name = "PttOrchestration",
    Description = "PTT 熱門關鍵話題擷取系統"
};

  • 執行 Orchestration
var runtime = new InProcessRuntime();
await runtime.StartAsync();

// 初始任務內容:提供 RSS 來源與取樣數(讓 FetcherAgent 知道要抓什麼)
string feed = "https://www.ptt.cc/atom/Gossiping.xml";
int take = 10;
var task = $"請用 feedUrl={feed}、take={take} 抓取、清理並輸出熱門關鍵話題排名。";

Console.WriteLine($"{task} \n");

// 建立 CancellationToken 防止無限等待
using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(15));

try
{
    var result = await orchestration.InvokeAsync(task, runtime);

    // 取得最終結果(KeywordAgent 的 Markdown)- 已有 timeout 保護
    string output = await result.GetValueAsync(TimeSpan.FromSeconds(90));
    Console.WriteLine(output);

    // 待所有訊息處理完畢
    await runtime.RunUntilIdleAsync();
}
catch (TimeoutException)
{
    Console.WriteLine("⚠️ 操作逾時,已自動取消執行以避免無窮等待。");
}
  • 系統執行結果
=== Semantic Kernel AI Agent 系統 ===
Hello, PTT Agent!


請用 feedUrl=https://www.ptt.cc/atom/Gossiping.xml、take=10 抓取、清理並輸出熱門關鍵話題排名。

Feed Type: Atom
Feed Total Items: 10
# PTT 八卦版熱門話題排行

| 排名 | 關鍵話題      | 出現次數 | 相關文章標題範例                                                                 |
| ---- | ------------- | -------- | -------------------------------------------------------------------------------- |
| 1    | 氣候/水資源   | 6 次     | [台灣多久沒下大雨了], [大停水的預備該做什麼準備], [如果水庫全部乾掉會多久沒下雨] |
| 2    | 政治          | 4 次     | [韓國瑜做國慶致詞如何], [柯文哲出馬機率高嗎]                                     |
| 3    | 股市/台積電   | 3 次     | [台積電股價明天還會繼續漲嗎], [大盤什麼時候才要崩盤]                             |
| 4    | 詐騙          | 2 次     | [現在什麼電話一定不要接], [如何防止自己被詐騙]                                   |
| 5    | 國際關係/軍事 | 2 次     | [中美關係會怎麼變化], [台灣被封鎖後物價會怎麼變]                                 |
| 6    | 娛樂/影劇     | 2 次     | [哪部台劇最近很紅], [韓劇為什麼那麼受歡迎]                                       |
| 7    | 中國政策      | 2 次     | [中國稀土出口限制影響], [中國為何不直接出兵]                                     |
| 8    | 民生物價      | 2 次     | [瓶裝水保存期限有多久], [台灣物價一直漲怎麼辦]                                   |
| 9    | 歷史/幫派     | 2 次     | [台灣黑幫歷史], [幫派在台灣的影響]                                               |
| 10   | 選舉          | 1 次     | [下屆總統誰最有希望]                                                             |
| 11   | 交通          | 1 次     | [哪裡車禍最多]                                                                   |
| 12   | 科技          | 1 次     | [最近AI晶片很夯嗎]                                                               |
| 13   | 租屋          | 1 次     | [台北租金什麼時候會跌]                                                           |
| 14   | 食安          | 1 次     | [最近食品安全新聞有哪些]                                                         |
| 15   | 網路          | 1 次     | [哪個論壇最八卦]                                                                 |

結語

這樣的一個 PTT 熱門關鍵話題擷取系統,透過 Semantic Kernel 的 Agent Orchestration 能力,將資料擷取、清理與分析等多個步驟模組化,並且利用 LLM 的強大語言理解與生成能力,實現自動化的輿情監測與話題分析,非常適合用於各種需要即時掌握話題動態的應用場景。雖然在這個範例中,我們只實作了三個主要 Agent,同時也僅以 PTT 為例,但實際上可以根據需求,擴展更多功能,例如情緒分析、趨勢偵測、報表生成等,或是多個資料收集來源,打造更完整的輿情分析系統。並且也可以將系統部署到雲端,結合排程與通知機制,實現全天候的話題監測與報告推送,進一步提升對資訊掌握能力與反應速度。


上一篇
Day 26: Semantic Kernel 無縫整合 Azure AI Foundry Agent - 企業助理實作
下一篇
Day 28: Semantic Kernel Multi-Agent 實戰 — 新聞內容嚴謹度評估系統
系列文
當 .NET 遇見 AI Agents:用 Semantic Kernel × MCP 打造智慧協作應用28
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言