2025年11月27日 星期四

如何透過gemini + VSCode寫code

          這次的主題想做出自動下戴redmine 的csv檔案,因為要通知有需求上版的RD, 確認每個需求是否都已經merage到release,根據以往都需要返覆的確認redmine的每個分支的merge 狀態,再貼在teams的頻道上,有點繁瑣,所以想直接下戴csv,透過工具幫我整理好每個人的merage 狀態,直接output 內文在 teams,就方便許多了,於是我使用gemini AI ,不花一毛錢,只用免費的方案就可以使用的方式,所以需求越明確就可以成功用AI  自動產生程式碼,以下是我的範例和我的提示喔~ 


一、安裝 Gemini Cli

確保已安裝[**Node.js 18 或更高版本**](https://nodejs.org/en/download)。你可以執行以下命令進行檢查:


node -v

npm install -g @google/gemini-cli




選擇登入方式。我們推薦**「使用 Google 登入」** ,該方式**每分鐘最多可免費要求 60 次**,**每天最多可免費請求 1,000 次**。選擇該方式並按**Enter 鍵**。




二、透過 Visual Studio Code 的視窗,直接呼叫 Gemini Cli


可以針對專案的特別需求寫 gemni.md 可以針對專案作客製化的需求,比方排版的編好、命名的要求、針對大方向專案的描述都可以寫在裡面喔!


我的需求如下

程式語言為c#

可以從redmine 直接下戴csv

我可以直接給從瀏覽器取得 _redmine_session

幫我生成c#.exe的範例程式碼


      補充說明,因為remine 是透過 office 365 登入認證成功的,所以不像傳統的登入的帳號/密碼,所以要自動下戴,仍需要瀏覽器登入成功的資訊,_remind_session 的資訊,才能透過程式碼,正確的直接下戴csv,而不會變成亂碼


三、讀取CSV 檔案,並且讀進來整理redmine的內容,並且發送到teams 的內容

csv 範例如下:


希望整理如下的格式:

針對每個任務單,都依被分派任務的人為單任,依序顯示以下被分派的任務單有哪些,且是否已完成任務的狀態

**洪XX**
✅ [B2E] [[nlog] 富購spx:SPX_UpdProduct失敗時記錄相關資訊](https://redmine.etzone.net/issues/48677)

**翁XX**
⚠ [B2E] [EHub 變價、商品更新排程啟用](https://redmine.etzone.net/issues/48640)

**劉XX**
⏩ [B2E] [商品管理 > 審核變價 JS Error 修正](https://redmine.etzone.net/issues/48644)
⏩ [B2E] [調整RMQ批次Ack參數](https://redmine.etzone.net/issues/48591)

**劉XX**
✅ [B2B] [到廠併箱配送單拿掉注意事項](https://redmine.etzone.net/issues/48625)
✅ [B2E] [從富購同步回來,商品售價免稅設定改由富購主控](https://redmine.etzone.net/issues/48607)
✅ [B2B] [加鹽加密登入異常CreateJWT2方法修正](https://redmine.etzone.net/issues/48303)
✅ [B2B] [加鹽加密登入異常LoginValidationV2方法修正](https://redmine.etzone.net/issues/48302)
✅ [B2B] [加鹽加密登入異常CheckIdentity2方法修正](https://redmine.etzone.net/issues/48301)

**羅XX**
⏩ [B2E] [優化雄獅/旅天下斷碼規則](https://redmine.etzone.net/issues/48366)

基於以上的需求,我下的promp 如下:


我想建立一僤自動發佈到teams 的草稿的自動化工具, 用c# 程式語言

參數1: 版號的日期 ,例如 20251112

參數2: redminedb單號 ,例如 https://redmine.etzone.net/issues/48618

後續就能自動將內容範例如下:

標題: 20251112

上版 內文: 20251112 上版

1. 將此次需要上版的 Code 併入 [release_1029];請確認版本與 release 版本,還在待測試 => 請追相關人員。

2. 修改 DB 的 Redmine: https://redmine.etzone.net/issues/48618,請再把 SQL 放入。(11/12 週三上午會寄出)

3. 預計明日上午 (10/28) 佈版至 stage,故請在之前完成 merge

之後會自動讀取相對路徑下的一份 csv 的檔案,

欄位如下:

專案

蹤標籤

主旨

被分派者

版本 Release版本

是否有 Code Review

Merge into Release

Code Reviewer 1 將它讀入後, 可以依照欄位【Merge into Release】,
顯示成不同三種標記,
 ✅:已完成、已 Merge。 ⏩:開發中/尚未 Merge。 ⚠:僅環境設定或無需上版的調整。

請協助確認是否需安排上版或調整版次,謝謝! 我發佈到teams的方式,只有透過teams 的 link的方式 ,建立teams 的草稿,最後由我確認後才送出。


但問題來了~  讀 csv 檔案,不是UTF-8 格式,所以輸出的文章 就一直呈現亂碼的樣式,所以我又下的一個promp

為何會有亂碼該如何解決,因為csv 檔案來源是正常的UTF-8

它回的回覆如下:

原因:

你看到的 FFFD 是 Unicode Replacement Character,代表以 UTF-8 解碼時遇到非法位元組序列。

檔案實際編碼並非 UTF-8 (多半是 Big5/CP950 或 UTF-16),Excel 會依系統語系自動套用而正常。

被誤解碼後再傳給 CsvHelper,所有中文字成 FFFD,表頭對不到 → 讀不到資料,看起來像「亂碼」。

解法:

1. 先抓原始位元組,不直接假設 UTF-8。

2. 嘗試多種候選編碼 (UTF8 / UTF-16 LE / UTF-16 BE / CP950(Big5) / GB18030) 解碼。

3. 評估哪個解碼結果最少 FFFD 並含目標中文表頭 (專案, 主旨等)。

4. 用選出的編碼再交給 CsvHelper 解析。

使用方式:

1. 保留 Encoding.RegisterProvider(CodePagesEncodingProvider.Instance)(已存在)。

2. 重新執行程式;Console 會列出各編碼測試結果並選出最可能的。

3. 若仍顯示亂碼,檢查下載到的是否為 HTML (需登入 cookie)。可在 DownloadCsv 裡加判斷 <html。

若確定 Redmine 伺服器可設定輸出為 UTF-8,建議:

確認伺服器設定 (Redmine production.rb) config.encoding = 'utf-8'.

或在匯出 URL 加 ?encoding=utf-8 (若版本支援)。

接下來先測試上述自動偵測是否選出 CP950/Big5 並顯示正確中文。


最終編譯終於成功了,而已也能實作我直接發佈到teams 的內文,可以直接從redmine一鍵產出,而不是自已手動整理出來,,為了想瞭解 gitHub 做了哪些調整,我又下了一個promp

目前已正常顯示了,請問csv 這份檔案到底是什麼編碼? 最後是作了什麼調整才能顯示正常 請說明

它回覆如下

說明:

1. 原始檔非 UTF-8;根據正常顯示後程式輸出 (選用編碼: xxx) 可知實際是被偵測為 Big5/CP950 (若顯示 webName=cp950) 或另一候選 (UTF-16/GB18030)。你可再跑一次查看 Console 的「選用編碼:」行以確定,該行就是檔案真正被採用的編碼。大多在繁體中文 Windows 環境下 Redmine 匯出的 CSV 會是 CP950(Big5)。

2. 之前強制用 UTF-8 讀取,遇到非 UTF-8 多位元組序列 → 產生 Unicode 替代字元 FFFD 造成「亂碼」與表頭比對失敗。

3. 調整內容:

加入 Encoding.RegisterProvider(CodePagesEncodingProvider.Instance) 支援 Big5/GB18030 等非預設編碼。

以 DetectBestEncoding 讀入原始位元組,嘗試多個候選 (UTF8、UTF-16 LE/BE、CP950、GB18030) 計算表頭命中與替代字元數量選出最適編碼。

移除固定 UTF-8 假設,改用偵測結果建立 StreamReader。

Normalize 表頭 (去 BOM) 並 ClassMap 同時接受 “#” 與 “\uFEFF#”。

顯示各編碼測試結果與最終選用編碼,便於確認。

如何再次確認: 執行程式 → 查看 Console 第一個 “編碼測試 …” 列出分數,最後 “選用編碼: cp950 (Chinese (Traditional) Windows)” 或其他;那就是 CSV 真正的編碼。

產出的小工具如下:



產出的內文: 成果不錯!!!!


**20251126 上版**

1. 將此次需要上版的 Code 併入 [release_1126];請確認版本與 release 版本,還在待測試 => 請追相關人員。
2. 修改 DB 的 Redmine:https://redmine.etzone.net/issues/48682,請再把 SQL 放入。(11/26 週三上午會寄出)
3. 預計明日上午 (11/26) 佈版至 stage,故請在之前完成 merge。

---

以下為目前各單號是否已合併之狀態,請協助確認是否需安排上版或調整版次,謝謝!

**洪XX**
✅ [B2E] [[nlog] 富購spx:SPX_UpdProduct失敗時記錄相關資訊](https://redmine.etzone.net/issues/48677)

**劉XX**
⏩ [B2E] [商品管理 > 審核變價 JS Error 修正](https://redmine.etzone.net/issues/48644)
⏩ [B2E] [調整RMQ批次Ack參數](https://redmine.etzone.net/issues/48591)

**劉XX**
✅ [B2B] [到廠併箱配送單拿掉注意事項](https://redmine.etzone.net/issues/48625)
✅ [B2E] [從富購同步回來,商品售價免稅設定改由富購主控](https://redmine.etzone.net/issues/48607)
✅ [B2B] [加鹽加密登入異常CreateJWT2方法修正](https://redmine.etzone.net/issues/48303)
✅ [B2B] [加鹽加密登入異常LoginValidationV2方法修正](https://redmine.etzone.net/issues/48302)
✅ [B2B] [加鹽加密登入異常CheckIdentity2方法修正](https://redmine.etzone.net/issues/48301)

**羅XX**
⏩ [B2E] [優化雄獅/旅天下斷碼規則](https://redmine.etzone.net/issues/48366)

2025年7月18日 星期五

如何製作簡易版的MCP SERVER (最快3分鐘)

      MCP Server (Model Context Proptocol) 是為了LLM 可以外部擴充它的知識和相關可運用的工具的最新方式,例如你詢問LLM : 【今天的天氣如何?】,LLM 的知識是預訓練完成的,怎麼會得知今天或明天的天氣如何? 所以透過 相關的MCP SERVER 讓LLM 去調用相關內容的function 去取得相關的內容,並透過LLM 的分析,並將結果回傳。

如下圖所示: 

MCP Client :連接 LLM 和 MCP 服務器的橋樑。嵌入在 LLM 中


MCP Server: 具有MCP Protocol協定的 SERVER,負責根據 LLM 的請求,例如事先寫好相應的「工具」或「功能」(Function)。例如,如果 LLM 詢問天氣,MCP Server 會調用預先註冊好的「天氣查詢」功能。

前提安裝條件

    本人安裝的環境 .net 9 SDK 也能建置 MCP  SERVER 


一、安裝 MCP 伺服器範本

dotnet new install Microsoft.Extensions.AI.Templates


二、使用 dotnet 指令建立新的 MCP 伺服器應用程式的範本

dotnet new mcpserver -n SampleMcpServer

三、用Vistul studio 開啟專案

SampleMcpServer

├── 相依性(Dependencies

├── .mcp

   └── server.json

├── Tools

   └── RandomNumberTools.cs

├── Program.cs

└── README.md

這是一個典型的 C# 專案結構,說明如下: 

.mcp/server.json:是 MCP Server 的設定檔。 

Tools/RandomNumberTools.cs:應該是封裝隨機數工具的程式碼檔。 

Program.cs:主執行進入點。 

README.md:說明文件。



     沒有寫一行程式碼,這個專案SampleMCP SERVER 已具有基本的框架,也可以測試執行
接下來我們來看看進面的程式碼是什麼吧! 

檔名: Programe.cs
// 引入必要的命名空間
using Microsoft.Extensions.DependencyInjection;  // 用於注入服務(DI)
using Microsoft.Extensions.Hosting;             // 用於建立主機(主控程式架構)
using Microsoft.Extensions.Logging;             // 用於設定日誌系統(Logging)

// 建立主機建構器(HostBuilder),接收命令列參數
var builder = Host.CreateApplicationBuilder(args);

// 設定所有的 log 訊息輸出到標準錯誤(stderr),
// 因為 stdout 是用來傳輸 MCP 協定訊息的
builder.Logging.AddConsole(o => 
    o.LogToStandardErrorThreshold = LogLevel.Trace // 設定最細的日誌等級(Trace)
);

// 註冊 MCP 伺服器服務:
// - 加入 MCP 伺服器
// - 使用 stdio(標準輸入/輸出)當作資料傳輸通道
// - 註冊要提供的工具:RandomNumberTools
builder.Services
    .AddMcpServer()                 // 註冊 MCP server 基礎功能
    .WithStdioServerTransport()     // 使用 stdio 作為通訊方式(與 IDE / client 互動)
    .WithTools<RandomNumberTools>(); // 提供一個叫 RandomNumberTools 的工具(具體功能要看類別定義)

// 建立並啟動主機,非同步執行直到應用程式結束
await builder.Build().RunAsync();
------------------------------------------------------------------------------------------------------------------------------

檔名: RandomNumberTools.cs  : 建置MCP Server 的function 

// 引入用於工具描述與註解的命名空間
using System.ComponentModel;                  // 提供 DescriptionAttribute 等用來顯示說明文字
using ModelContextProtocol.Server;           // MCP server SDK 所提供的屬性與工具

/// <summary>
/// MCP 工具範例類別,用來展示 MCP server 可供 client 呼叫的方法。
/// MCP client 可以呼叫這些方法來執行對應功能。
/// </summary>
internal class RandomNumberTools
{
    // 定義一個 MCP 工具方法,會自動被 MCP Server 掃描並註冊
    [McpServerTool]
    [Description("在指定的最小值與最大值之間產生一個隨機數字(包含最小,不包含最大)")]
    public int GetRandomNumber(
        [Description("最小值(含)")] int min = 0,
        [Description("最大值(不含)")] int max = 100)
    {
        // 使用 .NET 內建的 Random.Shared 產生一個隨機整數
        return Random.Shared.Next(min, max);
    }
}
------------------------------------------------------------------------------------------------------------------------------
檔名:server.json :可以在NuGet 發佈 MCP套件的設定

{
  "$schema": "https://modelcontextprotocol.io/schemas/draft/2025-07-09/server.json",
  "description": "提供基本隨機數字工具的 MCP Server 範例",
  "name": "io.github.your-username/mcp-randomtools",
  "packages": [
    {
      "registry_name": "nuget",                  // 套件來源(這裡是 NuGet 套件註冊中心)
      "name": "Mcp.RandomTools.Server",          // 你在 NuGet 上發布的套件 ID
      "version": "0.1.0-beta",                   // 套件版本
      "package_arguments": [],                   // 若套件支援 CLI 引數,可列在這(目前留空)
      "environment_variables": []                // 若 MCP server 啟動時需要設定環境變數,可列於此
    }
  ],
  "repository": {
    "url": "https://github.com/your-username/mcp-randomtools",  // GitHub 原始碼位置
    "source": "github"                                          // 表示來源是 GitHub
  },
  "version_detail": {
    "version": "0.1.0-beta"  // MCP server 的版本資訊
  }
}

-----------------------------------------------------------------------------------------------------------------

    如果用戶不是用.net Framework 10 就會出現問題了,此時叫出你的好幫手 copilot ,
並下指令prompt : 我的專案build 不過,如何才能build成功,以下是截圖 ,
記得visual Studio 要更新最新版,才能使用copilot 代理人模式



    
四、如何讓copilot 調用MCP Server 的工具

請先建立資料夾.vscode, 並新增檔案 mcp.json以設定要測試的WEATHER_CHOICES環境變數。

檔名: mcp.json

{
  "servers": {
    // 定義一個 MCP server 名稱叫 "SampleMcpServer"
    // VS Code MCP 擴充功能會用這個名稱來顯示可啟動的伺服器
    "SampleMcpServer": {

      // MCP server 的通訊方式為 stdio(標準輸入/輸出)
      // 適合開發環境中使用,與 IDE 直接互動,不需開啟網路 port
      "type": "stdio",

      // 指定執行 MCP Server 的主命令,這裡使用 .NET CLI
      "command": "dotnet",

      // 執行 dotnet 指令時所附加的參數清單
      "args": [
        "run", // 執行 dotnet run
        "--project", // 指定要執行的專案路徑
        "<RELATIVE PATH TO PROJECT DIRECTORY>" // 專案的相對路徑(請替換為實際目錄)
      ],

      // MCP Server 啟動時設定的環境變數
      // 可以讓 server 程式根據這些變數的值改變行為(如工具回傳內容)
      "env": {
        "WEATHER_CHOICES": "sunny,humid,freezing"
        // MCP 工具可以透過讀取這個環境變數來決定回應的天氣選項
      }
    }
  }
}

透過copilot 的視窗 下面的【選擇工具】,並勾選 sampleMCTServer和get_random_number


        雖然我立刻在copilot 立刻下promp 提問: 【請幫我產生1~99的亂數】,它一直跟我鬼打牆,但後來我重新啟動visual studio ,就可以正常調用MCP Server , 所以不要鐵齒,重開治百病。







參考文章: 
https://blog.logto.io/zh-TW/what-is-mcp

https://www.ibest.com.tw/news-detail/what-is-mcp/

https://learn.microsoft.com/zh-tw/dotnet/ai/quickstarts/build-mcp-server








2025年4月7日 星期一

如何透過playWright作整合測試

 

                  大家有沒有經驗,明明都寫完了程式,也都單元測試完了,但一上線就炸了,QA也都測試過了… 怎麼還是有問題,如何讓程式品質提升或讓RD就時間打怪呢… 決定就是你了【PlayWright


前提大家都有安裝
VS Code ,身為RD 這應該是必備的工具,就不多講述,如果沒安裝,直接下戴安裝即可 https://code.visualstudio.com/download

一、
在擴充點選Playwright test for VSCode








二、在工具列中輸入

> install playwright 


預設值 會支援chromium / Firefox/ WebKit, 選擇OK 即可


呼叫command line : npx playwright codegen http://測址網址


自動會開啟瀏覽器,此時可以作操作畫面,右邊的視窗會自動錄下所有操作的行為


tests\example.spec.ts 將原本的程式碼拿掉,貼上新的代碼即可

點選 左下的水瓶 à 點右箭頭,然後一切就自動化的執行你剛才所做的操作行為囉!!! 























2024年1月18日 星期四

在 ASP.NET Core 中建立 gRPC 用戶端與伺服器

 GRPC 是什麼?  有別於傳統的client 和 server 端的連線,而是持續性且高效能的連線, 連線上的架構是HTTP2 協定,使用Protocol Buffers 作為介面描述語言,連線模式,可以1 (SERVER) 對多 (CLIENT),也可雙向溝通,而一般REST 連線只能作到單向構通

一、 建立GRPC的前置作業

先安裝GRPC的專案, 如果畫面上沒有出現asp.net Core gRPC的服務時,請搜尋GRPC













選擇asp.net Core gRPC服務























按下一步,就完成了…   建立GRPC Server端

二、建立GRPC Server





















Protos/greet.proto:會定義 `Greeter` gRPC,並會用來產生 gRPC 伺服器協定

以下是範例:  


syntax = "proto3";

 option csharp_namespace = "GrpcService1";

 package greet;

 

// server的名稱為 Greeter

service Greeter {

  // Sends a greeting

  rpc SayHello (HelloRequest) returns (HelloReply);

}

 

// The request 參數 name

message HelloRequest {

  string name = 1;

}

 // The response 回傳 message 

message HelloReply {

  string message = 1;

} 

三、建立GRPC Client


這用於client 與server端 的協定,所以clint端也需要相同的.proto, 所以從server端的 Protos/greet.proto copy一份到client端 Protos路徑下













以下是client的內容 ,但與server端略有不同,

選擇.proto 按下右鍵,在 gprc stub Classes 選擇 client Only



syntax = "proto3";

 option csharp_namespace = "GrpcGreeterClient"; //要自已設定clientOnly

 package greet;

 

// server端的server名稱一致

service Greeter {

  // Sends a greeting

  rpc SayHello (HelloRequest) returns (HelloReply);

}

 

// The request message containing the user's name.

message HelloRequest {

  string name = 1;

}

 

// The response message containing the greetings.

message HelloReply {

  string message = 1;

}


四、執行結果

server 端的執行檔




















client 端的執行檔