TechSummary 2025-10-2
· 閱讀時間約 17 分鐘
如何利用 GitHub Copilot 在五小時內自動化實現無障礙合規性 🧑💻
- GitHub 過去的無障礙合規修復流程是手動且效率低下,導致反應時間慢、追蹤不一,且無法規模化。
- 透過 GitHub Copilot,團隊在約五到六小時內,將此流程轉變為自動化、可審計的循環,包括自動建立 GitHub Issues、更新現有問題、交叉引用 CRM 追蹤、自動關閉已解決的問題、同步指派者及提及相關利害關係人。
- Copilot 透過快速原型設計和測試,讓領域專家直接構建自動化解決方案,大幅減少傳統開發所需的數週時間,將工程師從基礎骨架工作解放,專注於安全、規模化與生產強化。
- 建構流程輕量化,包括:用簡潔語言定義規則、請 Copilot 調整程式碼而非從零開始、使用小型合成數據測試邏輯、審查輸出並優化提示、添加防護措施(如冪等性、減震機制)、記錄決策並重新運行測試以確保無回歸。
- 自動化成果包括:修復問題迅速出現、所有權和狀態集中化、減 少重複或過時的溝通、治理工作轉向高價值分析。Copilot 的賦能讓領域專家能夠直接構建原型,降低了未來合規工具的開發門檻。
從 Shell 腳本到科學代理:AI 代理如何轉變研究工作流程 🔬
Source: https://www.docker.com/blog/ai-science-agents-research-workflows/
- 問題點: 傳統科學研究工作流程由多種工具、腳本和格式拼湊而成,缺乏可重複性,且基礎設施通常是事後考量,導致效率低下。
- 科學代理的承諾: AI 驅動的系統能自主規劃、執行並迭代整個科學工作流程,無需人工不斷提示。例如,給定化合物和蛋白質的 CSV 文件,代理可自動搜尋文獻、預測 ADMET 性質並生成報告。
- 科學代理的定義: 區別於 ChatGPT 等聊天機器人,科學代理是自主系統,能理解目標、分解步驟、選擇工具、執行計算並反思結果,更像數位研究助理或協作者。
- 與 LLM(如 ChatGPT)的關鍵差異:
- 互動: LLM 是多輪對話,代理是長週期、自主工作流程。
- 角色: LLM 是助手,代理是明確執行特定任務的研究協作者。
- 自主性: LLM 需外部提示,代理是完全自主的規劃、工具選擇和迭代。
- 工具使用: LLM 透過插件,代理是明確的工具整合(API、模擬、Dockerized 工具)。
- 記憶: LLM 是短期上下文,代理是持久的長期記憶(向量資料庫、檔案日誌)。
- 可重複性: LLM 非常有限,代理是完全容器化、版本化的可重複工作流程。
- CrewAI 範例: 透過多代理系統(如 Curator、Researcher、Web Scraper、Analyst、Reporter)協同工作,自動化原本需數小時甚至數天的人工任務。
- Docker 實現: 提供一個雙容器範例,展示代理如何自動規劃和執行工作流程,包括攝取 CSV 文件、查詢 PubMed、生成文獻摘要、計算 ADMET 屬性、編譯 Markdown 報告。
此範例展示了代理無需手動干預即可進行決策、使用工具和協調任務的能力。
docker compose up - 基礎設施的重要性: AI 科學代理的瓶頸在於基礎設施。Docker 透過標準化環境、可重複工作流程、可組合代理和流暢的編排解決了重負載、可重複性混亂、工具鏈複雜性和版本控制等挑戰。
使用 Docker Offload 和 Unsloth 微調本地模型 🧠
Source: https://www.docker.com/blog/fine-tuning-models-with-offload-and-unsloth/
- 背景與挑戰: 小型本地模型在複雜任務中表現不足,而大型模型訓練成本高。微調環境設置複雜、易出錯,且對硬體有要求(如 GPU),尤其對於 MacBook 用戶而言更是挑戰。
- 解決方案: 結合 Unsloth(快速模型微調框架)和 Docker Offload(在雲端提供 GPU 資源)來實現本地模型的快速、高效微調。
- 範例目標: 微調一個小於 1GB 的 Gemma 3 270M 模型,使其能可靠地遮蔽個人身份資訊 (PII)。
- 原始模型表現: 對於 PII 遮蔽任務,原始 Gemma 3 270M 模型輸出不符預期,例如只輸出
[PERSON]而非詳細遮蔽。
- 原始模型表現: 對於 PII 遮蔽任務,原始 Gemma 3 270M 模型輸出不符預期,例如只輸出
- 微調步驟:
- 克隆專案:
專案包含用於微調 Gemma 3 的
git clone https://github.com/ilopezluna/fine-tuning-examples.git
cd fine-tuning-examples/pii-maskingfinetune.py腳本,使用 ai4privacy 的pii-masking-400k資料集。 - 啟動 Docker Offload (含 GPU):
選擇帳戶,並確認啟用 GPU 支援以獲得 NVIDIA L4 實例。
docker offload start - 運行 Unsloth 容器:
此命令會啟動包含 Jupyter 和 Unsloth 的容器,並將本地工作目錄掛載到
docker run -d -e JUPYTER_PORT=8000 \
-e JUPYTER_PASSWORD="mypassword" \
-e USER_PASSWORD="unsloth2024" \
-p 8000:8000 \
-v $(pwd):/workspace/work \
--gpus all \
unsloth/unsloth/workspace/work。 - 執行微調: 在容器內執行
finetune.py腳本。腳本使用 LoRA 技術(輕量級微調方法,只訓練小型適配器層)在不到 20 分鐘內完成訓練,將結果 LoRA 權重保存到掛載目錄。docker exec -it $(docker ps -q) bash
cd work
python finetune.py - 轉換為 GGUF 格式: 為便於 Docker Hub 分發,將微調後的模型轉換為 GGUF 格式。
cd ..
git clone https://github.com/ggml-org/llama.cpp
python ./llama.cpp/convert_hf_to_gguf.py work/result/ --outfile work/result.gguf - 打包並分享到 Docker Hub:
docker model package --gguf /Users/ilopezluna/Projects/fine-tuning-examples/pii-masking/result.gguf ignaciolopezluna020/my-awesome-model:version1 --push - 測試結果: 使用 Docker Model Runner 運行微調後的模型。
微調後的模型能精確地將 PII 遮蔽為
docker model run ignaciolopezluna020/my-awesome-model:version1 "Mask all PII in the following text. Replace each entity with the exact UPPERCASE label in square brackets (e.g., [PERSON], [EMAIL], [PHONE], [USERNAME], [ADDRESS], [CREDIT_CARD], [TIME], etc.). Preserve all non-PII text, whitespace, ' ' and punctuation exactly. Return ONLY the redacted text. Text: This is an example of text that contains some data. The author of this text is Ignacio López Luna, but everybody calls him Ignasi. His ID number is 123456789. He has a son named Arnau López, who was born on 21-07-2021"[GIVENNAME_1] [SURNAME_1]等格式,證明其有效性。
- 克隆專案:
- Docker 微調模型的優勢: 速度快(20 分鐘內)、無需本地 GPU、模型可移植、具備實用性。這使得小型通用模型能轉變為專注、可靠的工具,即使非 ML 專家也能輕鬆實現。
IntelliJ IDEA 2025.2.3 已發布! ✨
Source: https://blog.jetbrains.com/idea/2025/10/intellij-idea-2025-2-3/
- IntelliJ IDEA 2025.2 的最新次要更新 v2025.2.3 已發布。
- 用戶可透過 IDE 內部、Toolbox App 或 Ubuntu 的 snaps 進行更新,也可從官網下載。
- 此版本包含多項改進:
- Jira 任務伺服器整合在獲取任務時恢復正常。
- 在 Services 視圖中,啟用 ClassicUI 插件時,斷點功能如預期工作。
- 從 Find Usages 對話框中同時開啟多個文件的功能再次可用。
- 詳細問題解決清單可參考發布說明。
Koog × A2A:在 Kotlin 中構建互聯的 AI 代理 🤖
Source: https://blog.jetbrains.com/ai/2025/10/koog-a2a-building-connected-ai-agents-in-kotlin/
- 挑戰: 構建多 AI 代理系統時,各代理間因 API 介面、訊息格式和身份驗證差異,難以協同工作,導致需大量客製化整合程式碼。
- A2A (Agent2Agent) 協議: 提供標準化的跨代理通訊層,作為 AI 生態系統的通用翻譯器,實現即插即用連線、標準化訊息格式、內建編排及可擴展性。
- Koog 框架: 一個基於 Kotlin 的 AI 代理內部編排引擎,適用於 JVM、Android、iOS、WebAssembly 及瀏覽器應用。其優勢包括:複雜工作流程管理(圖形化策略、循環、分支、並行)、即用組件、工具編排、原生 MCP 整合、記憶與儲存支援、容錯性及可觀測性。
- Koog 與 A2A 結合: Koog 處理企業級 AI 編排的核心問題,A2A 則補足了代理間通訊的空白。Koog 的高級工作流程可作為 A2A 任務被其他代理請求,同時 Koog 代理也能利用 A2A 生態系統的完整能力。
- A2A 協議關鍵組成:
- 代理發現: 透過標準化的「代理卡片」(JSON 文件描述功能,如名稱、描述、技能、端點、安全方案)實現。
val agentCard = AgentCard(
name = "Blog Writer",
description = "AI agent that creates high-quality blog posts and articles",
url = "https://api.blog-writer.com/a2a/v1",
version = "1.0.0",
capabilities = AgentCapabilities(streaming = true),
defaultInputModes = listOf("text/plain"),
defaultOutputModes = listOf("text/markdown"),
skills = listOf(
AgentSkill(
id = "write-post",
name = "Blog Post Writing",
description = "Generate engaging blog posts on any topic",
tags = listOf("writing", "content", "blog"),
examples = listOf("Write a post about AI trends")
)
)
) - 通用訊息格式: 所有代理間通訊使用單一標準化訊息格式,統一了請求、回應及更新模式,支援文本 (TextPart)、文件 (FilePart) 和結構化數據 (DataPart) 等豐富內容。
val message = Message(
role = Role.User,
parts = listOf(
TextPart("Write a blog post about the future of AI agents")
),
contextId = "blog-project-456"
)
val request = Request(
data = MessageSendParams(
message = message,
configuration = MessageConfiguration(
blocking = false, // Get first response
historyLength = 5 // Include context
)
)
)
val response = client.sendMessage(request) - 任務生命週期: 根據複雜度和時長管理不同類型的工作,包括即時訊息、長時間運行任務和即時更新 (SSE)。
- 內建安全性: 依賴 OAuth2、API 密鑰和 HTTPS 等行業標準,無需學習新的身份驗證機制。
- 代理發現: 透過標準化的「代理卡片」(JSON 文件描述功能,如名稱、描述、技能、端點、安全方案)實現。
- Koog 代理與 A2A 整合:
- 將 Koog 代理封裝為 A2A 伺服器: 定義代理策略,使用 Koog 的轉換器和節點處理 A2A 訊息格式和交換。安裝
A2AAgentServer特性,使代理可被發現和訪問。fun blogpostWritingStrategy() = strategy<MessageSendParams, A2AMessage>("blogpost-writer-strategy") {
val blogpostRequest by node<MessageSendParams, A2AMessage> { input ->
val userMessage = input.toKoogMessage().content
llm.writeSession {
user {
+"Write a blogpost based on the user request"
+xml {
tag("user_request") {
+userMessage
}
}
}
requestLLM().toA2AMessage()
}
}
val sendMessage by nodeA2ARespondMessage()
nodeStart then blogpostRequest then sendMessage the nodeFinish
}
// ... (agent definition and server setup) - 從 Koog 代理呼叫其他 A2A 代理: 配置 A2A 客戶端以獲取代理卡片,並在策略中使用
nodeA2ASendMessage或nodeA2ASendMessageStreaming來呼叫這些客戶端。val a2aClient = A2AClient(transport = transport, agentCardResolver = cardResolver)
a2aClient.connect()
// ... (agent strategy using nodeA2AClientSendMessage)
- 將 Koog 代理封裝為 A2A 伺服器: 定義代理策略,使用 Koog 的轉換器和節點處理 A2A 訊息格式和交換。安裝