以 Semantic Kernel Agent Orchestration 為核心,結合 LLM 語言能力打造「輿情收集與初步分析系統」。以這樣的概念,來實作透過對 PTT 八卦版 RSS 資料的自動化擷取與處理,系統能即時蒐集熱門討論內容,並進行文字清理、關鍵詞分析及同義詞整併,最終輸出「當下熱門話題排行榜」,協助快速掌握話題動態,非常適用於企業品牌監測、話題追蹤、輿情偵測等多種應用場景。
整體流程採用 Semantic Kernel Agent 型能的 SequentialOrchestration 組合三個具明確職責的 Agent:
這樣的系統還可以再擴充更多功能,例如:
系統主要分為三個 Agent,分別負責資料擷取、清理與關鍵詞分析。以下是各 Agent 的核心程式碼與說明。
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()
})
};
//資料清理代理
var cleanerAgent = new ChatCompletionAgent
{
Name = "CleanerAgent",
Description = "資料清理代理,負責清理從 RSS 擷取的文章內容",
Instructions =
"""
你是文本清理代理,負責清理從 RSS 擷取的文章內容。
僅輸出「清理後的字串」。
""",
Kernel = kernel,
Arguments = new(new PromptExecutionSettings
{
// 自動函式選擇
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
})
};
// 關鍵詞代理
var keyworderAgent = new ChatCompletionAgent
{
Name = "KeywordAgent",
Description = "熱門話題分析代理,分析清理後的文章內容並找出熱門關鍵話題",
Instructions =
"""
你是熱門話題分析代理。
請分析上一階段提供的清理後 JSON 文章資料,找出最熱門的關鍵話題。
分析要求:
1. 識別文章標題中的重要關鍵詞和主題
2. 將相似或相關的話題合併(例如:「颱風」「天氣」可合併為「氣象」)
3. 統計各話題的出現次數
4. 列出前 15 個最熱門的話題
輸出格式(純 Markdown):
# PTT 八卦版熱門話題排行
| 排名 | 關鍵話題 | 出現次數 | 相關文章標題範例 |
| ---- | -------- | -------- | ----------------- |
| 1 | [話題] | X 次 | [1-2個代表性標題] |
注意:僅輸出 Markdown 表格,不要其他說明文字。
""",
Kernel = kernel
};
[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();
}
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 kernel = Kernel.CreateBuilder()
.AddOpenAIChatCompletion(
apiKey: Config.OpenAI_ApiKey,
modelId: Config.ModelId)
.Build();
// 註冊工具
kernel.Plugins.AddFromType<RssPlugin>();
kernel.Plugins.AddFromType<TextCleanPlugin>();
SequentialOrchestration orchestration = new(fetcherAgent, cleanerAgent, keyworderAgent)
{
Name = "PttOrchestration",
Description = "PTT 熱門關鍵話題擷取系統"
};
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 為例,但實際上可以根據需求,擴展更多功能,例如情緒分析、趨勢偵測、報表生成等,或是多個資料收集來源,打造更完整的輿情分析系統。並且也可以將系統部署到雲端,結合排程與通知機制,實現全天候的話題監測與報告推送,進一步提升對資訊掌握能力與反應速度。