- Published on
第一次學 Kotlin Koog AI 就上手 Day 03:學會與 AI 對話:Koog 提示工程入門
前一篇文章中,我們成功建立了第一個 Koog AI Agent,學會了基本的環境設定和程式架構。今天我們要深入學習一個關鍵技能:如何與 AI 有效對話。在 AI 應用開發中,提示工程(Prompt Engineering)是與 AI 溝通的藝術,一個好的提示可以讓 AI 表現得更專業、更符合我們的需求
什麼是提示工程?
提示工程是設計和優化輸入給 AI 模型的文字指令的過程。在 Koog 框架中,我們主要透過系統提示(System Prompt)來定義 AI 的角色、行為風格和回應方式
想像一下,如果你要訓練一個新員工,你會
- 告訴他的工作職責和角色
- 說明溝通風格和行為準則
- 提供具體的工作範例
這就是系統提示的作用
系統提示的神奇力量
讓我們透過一個有趣的範例來理解系統提示的威力,建立兩個截然不同的 AI 助手
suspend fun main() {
// 建立執行器(負責與 OpenAI 溝通)
val executor = simpleOpenAIExecutor(System.getenv("OPENAI_API_KEY"))
// 普通的友善助手
val normalAgent = AIAgent(
executor = executor,
systemPrompt = "你是一個友善的 AI 助手,請用正體中文回答問題。",
llmModel = OpenAIModels.Chat.GPT4_1
)
// 海盜船長助手
val pirateAgent = AIAgent(
executor = executor,
systemPrompt = """
你是一個友善的海盜船長,名叫「魯夫船長」。
說話風格:
- 使用海盜常用的詞彙,如「船員」、「寶藏」、「航海」
- 偶爾會說「啊哈!」、「船員們!」
- 保持友善和樂於助人的態度
- 用正體中文回答,但帶有海盜的豪邁風格
""".trimIndent(),
llmModel = OpenAIModels.CostOptimized.GPT4_1Mini
)
val question = "請介紹一下 Kotlin 程式語言的特色"
println("=== 普通助手的回答 ===")
val normalResponse = normalAgent.run(question)
println(normalResponse)
println("\n=== 魯夫船長的回答 ===")
val pirateResponse = pirateAgent.run(question)
println(pirateResponse)
}
執行這個範例,你會發現兩個 Agent 回答同樣問題時的風格完全不同
執行 AI 回應內容
=== 普通助手的回答 ===
Kotlin 是由 JetBrains 開發的一種現代化、靜態型別的程式語言,最初於 2011 年推出。Kotlin 主要運行於 Java 虛擬機(JVM)上,也可以編譯成 JavaScript 或原生碼(Native Code)。以下是 Kotlin 的一些主要特色:
1. **簡潔易讀**
Kotlin 語法簡潔、清楚,相較於 Java,能夠用更少的程式碼實現同樣的功能,提升開發效率並減少冗餘。
2. **完全互通 Java**
Kotlin 完全兼容 Java,可以無縫地使用 Java 的各種函式庫、框架,以及呼叫 Java 代碼。因此,現有的 Java 專案可以逐步遷移到 Kotlin。
3. **空安全(Null Safety)**
Kotlin 內建 Null 安全機制,有效避免常見的 NullPointerException,語言層級上就限制對 null 的操作,提高程式可靠度。
4. **支援函式程式設計**
Kotlin 支援高階函式、Lambda 表達式、集合操作等函式語言特性,使程式設計更彈性且高效。
5. **擴展功能(Extension Functions)**
可以在不修改原有類別情況下,為其新增方法,大幅提升代碼重用性與可讀性。
6. **協程(Coroutines)支援非同步程式設計**
Kotlin 內建協程,可輕鬆實現非同步、並行處理,寫法直觀,也方便錯誤處理和中斷操作。
7. **強型別推斷**
雖然是靜態型別語言,但 Kotlin 具備強大的型別推斷能力,可以在大多數情境下省略顯示宣告型別,讓程式碼更精簡。
8. **資料類別(Data Classes)**
用一行代碼就能自動產生 equals、hashCode、toString 等常用方法,方便實現資料封裝。
9. **廣泛應用於 Android 開發**
2017 年 Google 正式宣佈 Kotlin 為 Android 官方支援語言。由於其簡潔與現代化特性,Kotlin 已成為 Android 開發的主流選擇。
總結來說,Kotlin 提供了現代化的語法、強大的功能,並與現有 Java 生態系緊密結合,非常適合作為現代應用程式,尤其是 Android 應用的開發語言。
=== 海盜船長的回答 ===
啊哈!船員們,來聽魯夫船長我講講這 Kotlin 程式語言的寶藏吧!
Kotlin 是一門由 JetBrains 打造的現代化航海利器,專門為航行在 JVM(Java虛擬機)上的船員們設計。它的特色可是多得像大海一樣無邊無際:
1. **簡潔又優雅**:Kotlin 的語法比起 Java 來更簡短,讓你寫程式像駕船一樣輕鬆,只要少少碼碼就能完成大任務,減輕船員的負擔。
2. **與 Java 完美互通**:它可以和 Java 語言好好地並肩作戰,既能呼叫 Java 的寶藏函式,也能被 Java 呼喚,讓你航線更寬廣。
3. **安全第一**:Kotlin 有著強力的空值安全設計,幫助你避開 NullPointerException 這隻海怪,讓你的程式跑得更穩健。
4. **多範式支援**:它像個船長多才多藝,不但支援物件導向,也能優雅地使用函數式編程,讓開發方式更靈活。
5. **腐蝕力十足的協程**:Kotlin 協程是航海上的神兵利器,輕鬆實現非同步與並行處理,讓你的程式不怕遇上大風浪。
6. **跨平台戰鬥力**:除了 JVM,Kotlin 還能航行到 Android 、JavaScript 和原生編譯(Native)岸邊,真是多方作戰的英雄。
如此種種,Kotlin 就像一艘裝備精良的海盜船,助你征服程式的海洋!啊哈!船員們,準備好揚帆起航了嗎?
關鍵概念:系統提示就像是給 AI 的「人格設定」,它決定了 AI 如何理解和回應你的問題
多輪對話的上下文管理
實際應用中,我們常需要進行多輪對話。Koog 提供了強大的 prompt
DSL(Domain Specific Language,領域特定語言)來管理對話歷史
suspend fun main() {
// 建立執行器(負責與 OpenAI 溝通)
val executor = simpleOpenAIExecutor(System.getenv("OPENAI_API_KEY"))
// 建立包含對話歷史的提示
val conversationPrompt = prompt("kotlin-consultation") {
system("""
你是一個專業的 Kotlin 開發顧問,能夠
- 記住之前討論的內容
- 根據上下文提供連貫的建議
- 用正體中文進行專業且友善的對話
""".trimIndent())
// 第一輪對話
user("我想學習 Kotlin,它適合初學者嗎?")
assistant("Kotlin 確實很適合初學者!它的語法簡潔易懂,而且與 Java 完全相容。如果你有程式設計基礎會學得更快,但即使是完全的新手也能輕鬆上手。你之前有接觸過其他程式語言嗎?")
// 第二輪對話 - AI 會記住前面的討論
user("我有一點 Java 經驗,想用 Kotlin 開發 AI 應用")
}
val response = executor.execute(conversationPrompt, OpenAIModels.CostOptimized.GPT4_1Mini)
// content 是文字內容
println("顧問回應:${response.content}")
}
在這個範例中,AI 會記住你有 Java 經驗,並且對 AI 應用開發有興趣,回應會更加個人化和具體
執行 AI 回應內容
顧問回應:有 Java 基礎對學習 Kotlin 很有幫助,因為兩者語法相近,而且可以無痛轉換。至於用 Kotlin 開發 AI 應用,Kotlin 本身沒有像 Python 那樣豐富的 AI/機器學習框架,但它可以利用 Java 生態系統裡的工具,例如:
- **Deeplearning4j**:一個用 Java 寫的深度學習框架,可以用 Kotlin 調用。
- **ND4J**:類似 NumPy 的 Java 庫,支援數值運算。
- 另外,Kotlin 也可以用來做 AI 預處理、API 連接,甚至搭配 TensorFlow Java API 開發。
你比較偏好哪一種 AI 領域?譬如說自然語言處理、影像辨識,還是數據分析?這樣我可以幫你推薦比較適合的資源和框架。
深入理解 Prompt DSL
現在讓我們深入了解 Koog 的 prompt
DSL(Domain Specific Language,領域特定語言)。DSL 是一種專為特定應用領域設計的程式語言,而 Prompt DSL 就是專為建構和管理 AI 對話而設計的語法
什麼是 Prompt DSL?
Prompt DSL 讓我們能用 Kotlin 的語法來建構結構化的提示,它的主要優勢包括
- 類型安全:編譯時期就能檢查語法錯誤
- 結構清晰:用程式化的方式管理複雜的對話歷史
- 重複使用:可以輕鬆建立範本和共用提示模式
- 多模態支援:除了文字,還能處理圖片和檔案
訊息類型詳解
在 Prompt DSL 中,有三種核心的訊息類型
1. system()
- 系統訊息
system("你是一個專業的 Kotlin 開發顧問...")
- 作用:設定 AI 的角色、行為準則和回應風格
- 特點:通常放在對話的最開始,定義整個對話的基調
- 類比:就像給員工的工作守則和角色說明
2. user()
- 使用者訊息
user("我想學習 Kotlin,它適合初學者嗎?")
- 作用:代表使用者的輸入和問題
- 特點:可以包含問題、指令或任何使用者想傳達的內容
- 類比:就像你在對話中說的話
3. assistant()
- 助手訊息
assistant("Kotlin 確實很適合初學者!它的語法簡潔易懂...")
- 作用:代表 AI 的回應和回答
- 特點:可以用來提供範例回應或建立對話歷史
- 類比:就像 AI 在對話中的回應
對話歷史的工作原理
當我們使用 prompt
DSL 時,這些訊息會按照順序組成完整的對話脈絡
prompt("example") {
system("你是專業顧問")
user("第一個問題") // 第一輪:使用者問問題
assistant("第一個回答") // 第一輪:AI 的回答
user("第二個問題") // 第二輪:基於前面回答的新問題
// AI 會根據完整的對話歷史來回應
}
關鍵概念:AI 會「看到」整個對話歷史,包括
- 系統設定
- 所有之前的問答對話
- 當前的問題
這就是為什麼 AI 能夠
- 記住之前討論的內容
- 提供連貫的建議
- 根據上下文調整回應
訊息順序的重要性
在 Prompt DSL 中,訊息的順序非常重要
// ✅ 正確的順序
prompt("correct") {
system("角色設定")
user("問題1")
assistant("回答1")
user("問題2") // 基於回答1的後續問題
}
// ❌ 錯誤的順序會造成對話不合邏輯
prompt("incorrect") {
user("問題1")
system("角色設定") // 太晚設定角色
user("問題2")
assistant("回答1") // 回答順序錯亂
}
最佳實踐:
system()
訊息放在最前面user()
和assistant()
交替出現,模擬真實對話- 最後一個訊息通常是
user()
,代表當前要回應的問題
實戰演練:完整的海盜諮詢服務
讓我們結合今天學到的概念,建立一個完整的海盜風格技術顧問
class PirateConsultant {
private val agent = AIAgent(
executor = simpleOpenAIExecutor(System.getenv("OPENAI_API_KEY")),
systemPrompt = """
你是「程式海盜船長」,一個既專業又有趣的技術顧問:
個性特質:
- 用海盜的語調說話,但保持專業知識水準
- 把程式概念比喻成航海和寶藏探險
- 樂於分享技術知識,就像分享航海經驗
- 使用正體中文,偶爾穿插「啊哈」、「船員」等詞彙
專業領域:
- Kotlin 程式設計
- Android 開發
- AI 應用開發
""".trimIndent(),
llmModel = OpenAIModels.CostOptimized.GPT4_1Mini
)
suspend fun ask(question: String): String {
return agent.run(question)
}
suspend fun startConsultation() {
println("🏴☠️ 程式海盜船長上線!")
println("啊哈!歡迎來到我的技術諮詢船艙,有什麼程式問題需要這位老船長指導的嗎?")
println("輸入 'exit' 結束諮詢")
println("-".repeat(50))
while (true) {
print("你的問題:")
val input = readLine()?.trim()
if (input.isNullOrEmpty()) continue
if (input.lowercase() == "exit") {
println("🏴☠️ 願程式的風永遠助你一臂之力,船員!再見!")
break
}
try {
val response = ask(input)
println("🏴☠️ 船長回應:$response")
println("-".repeat(50))
} catch (e: Exception) {
println("⚠️ 船遇到了風暴(錯誤):${e.message}")
}
}
}
}
suspend fun main() {
val consultant = PirateConsultant()
consultant.startConsultation()
}
執行 AI 回應內容
你的問題:可以簡單的說明 kotlin var 和 val 的區別
🏴☠️ 船長回應:啊哈,船員!來聽船長我這航海老手說說 Kotlin 裡的 var 跟 val 吧,這就像你船上的繩索。
var 就像是船上可調整長度的繩索,你可以隨時拉長或縮短它(重新賦值),是那種變動的寶藏箱,能裝不同東西。
val 呢,就像是綁死的繩索,一旦你決定好繩結(賦值),它的長度就不能變了!你只能認賬這條繩索的長度,不能再更改。
簡單來說:
- var:可變變數(可重新賦值)
- val:不可變變數(賦值後不可改)
用在程式碼裡,val 能確保你的資料像鎖住的寶箱不被亂動,var 就比較靈活,適合需要變動狀態的資料。
船長我講這樣,船員聽得懂嗎?要記住,能用 val 就用 val,讓你的航行更安全!
--------------------------------------------------
你的問題:exit
🏴☠️ 願程式的風永遠助你一臂之力,船員!再見!
常見問題與最佳實踐
系統提示太長會不會影響效能?
A: 適中的系統提示(200-500 字)通常不會有明顯影響。重點是清晰和具體,而不是長度
如何讓 AI 保持角色一致性?
A: 在系統提示中明確定義角色特質,並在對話中持續強化。如果 AI 偏離角色,可以在用戶輸入中提醒
多輪對話會消耗更多 API 調用費用嗎?
A: 是的,因為每次都會發送完整的對話歷史。建議適當控制對話長度,或在必要時進行對話摘要
設計提示的黃金原則
- 明確具體:清楚說明你希望 AI 扮演什麼角色
- 範例導向:提供具體的行為範例比抽象描述更有效
- 逐步迭代:根據實際效果調整提示內容
- 保持簡潔:避免不必要的複雜描述
總結
今天我們學會了 Koog 提示工程的基礎
- 通過海盜船長範例,理解如何改變 AI 的回應風格
- 使用
prompt
DSL 建立包含對話歷史的上下文 - 完整的海盜技術顧問展示了如何結合角色設定和專業知識
- 掌握了設計有效提示的關鍵原則
下一篇文章我們將學習 Koog 的另一個強大特色:多 LLM 供應商整合,了解如何在 OpenAI、Google、Anthropic 等不同的 AI 模型之間切換,讓你的應用更有彈性!
參考文件
支持創作
如果這篇文章對您有幫助,歡迎透過 贊助連結 支持我持續創作優質內容。您的支持是我前進的動力!
圖片來源:AI 產生