在前一篇文章中,我們已經學會如何透過 OpenAI API 建立一個基本的 CLI 聊天機器人,並進一步支援串流輸出與多輪對話。不過,實際使用時你可能會發現,AI 的回應有時過於簡短,有時又顯得冗長,甚至語氣或表達方式不符合期待。
這正是 提示工程(Prompt Engineering) 發揮作用的地方。我們可以透過精心設計的提示,讓 AI 扮演特定角色,同時控制回應的內容與風格,打造出更符合需求的智慧助理。
提示工程 指的是透過精心設計的輸入文字,來引導語言模型產生符合需求的回應。這些提示可能包含任務說明、語氣要求、角色設定、輸出格式限制,甚至是範例資料。其核心目的,就是讓模型能夠按照我們指定的方式思考並表達。
雖然 GPT 模型本身非常強大,能回答問題、生成文章、甚至轉換格式,但它並不會自動理解我們的偏好或期待。這時候,你就必須在提示中清楚告訴它:
舉個例子,如果我們只是單純問:
什麼是 API?
模型大多會給出一個通用的、較為中規中矩的解釋。但若你改寫成:
你是一位資深工程師,請用條列方式解釋 API 是什麼,並舉出實務應用的例子。
模型的回應就會更貼近工程師的角度,並依照條列格式,提供結構化且實用的答案。
換句話說,提示工程的本質就是「給模型下明確的指令」。當我們的 Prompt 設計得清楚,回應就能準確、專業、符合預期;反之,若提示模糊不明,AI 可能會偏題、冗長,甚至出現格式錯亂。
因此,學會設計 Prompt,不僅是我們開發任何 AI 應用的基礎功,也是讓模型真正成為你得力助手的關鍵技術。
在 OpenAI 的 Chat Completions API 中,一次對話請求是由一個 messages
陣列組成。這個陣列會按照時間順序依序列出所有訊息,而每則訊息都需要標註一個 role
,用來表明「說話者是誰」以及「這段話的用途」。
透過角色的區分,模型才能理解對話脈絡,並依據上下文生成最合適的回應。一般來說,最常見的角色包括:
system
:系統提示。用來定義模型的行為,例如應該扮演什麼角色、採取什麼語氣、遵循哪些規則。這是整段對話的基礎,對模型的回應風格影響很大。user
:使用者輸入。也就是我們真正要問的問題、下達的指令或任務描述。assistant
:模型的回應。我們也可以在這裡提供「範例回答」,幫助模型學習特定語氣或邏輯,讓後續回應更一致。以下是一個簡單範例,模擬我們希望模型用白話方式解釋 Node.js 的 Event Loop:
const messages = [
{ role: 'system', content: '你是一位幽默但專業的軟體工程師,擅長用生活化比喻解釋技術觀念。' },
{ role: 'user', content: 'Node.js 的 Event Loop 是什麼?' }
];
在這個例子中,我們先透過 system
設定模型的角色與語氣,再讓 user
提出問題。這樣的設計會讓模型以「軟體工程師」的身份回答,並遵循我們要求的幽默、生活化風格。當我們繼續輸入其他問題時,它也能根據上下文延續相同的角色設定。
這種多段訊息組合,就是提示工程的核心基礎。我們可以依需求自由調整 system
提示、加入上下文、補充範例問答,進一步打造出更貼近應用場景的 AI 助理。
設計 Prompt 的核心目標,是讓模型能更精準理解我們的需求,並生成格式清晰、語氣一致且內容正確的回應。以下介紹幾種常用且實用的設計技巧,它們可以單獨應用,也能彼此搭配,依照實際情境靈活組合。
在提示設計裡,最常見也有效的方法,就是利用 system
訊息替模型設定「角色」和「語氣」。透過這個方式,我們能明確告訴模型它是誰、要面對誰,以及該用什麼程度的專業度來回答,讓整段對話的風格自然一致。
以下是一個例子:
{ role: 'system', content: '你是一位資深前端工程師,擅長用簡潔明瞭的方式解釋 JavaScript 概念。' }
有了這段設定,模型會以「前端工程師」的身份回應,並傾向用結構化、邏輯清楚的方式解釋問題。
除了角色設定之外,在 user
的提示中也應該明確說明任務與輸出格式。
例如:
{ role: 'user', content: '請用條列方式說明 async/await 的優點,並搭配簡單的範例程式碼。' }
這樣能有效提升回應的結構性,避免模型生成過於冗長或鬆散的答案。
若希望模型模仿特定語氣或回答風格,可以提供一到兩組範例問答,這種方式稱為 Few-shot Prompting:
範例如下:
{ role: 'user', content: '什麼是 API?' },
{ role: 'assistant', content: 'Q: 什麼是 API?\nA: API 是應用程式之間溝通的標準介面。' },
{ role: 'user', content: '什麼是 HTTP?' }
透過範例,模型在回答最後一個問題時,就會自動模仿相同的格式與語氣。這種技巧特別適合需要保持輸出一致性的應用,例如問答系統、客服助理、學習平台等。
當你需要特定格式(例如 JSON、Markdown、CSV)時,可以直接在提示中提供樣板,模型通常會自動依照範本輸出。
以下是要求模型回傳 JSON 格式摘要的範例:
{ role: 'user', content: '請用以下格式回應:\n{\n "topic": "",\n "explanation": "",\n "example": ""\n}' }
這種方式特別適合應用程式開發情境,因為輸出結構穩定、容易解析,有助於系統自動化處理模型的回傳資料。
以上這些技巧,就是提示工程的基礎工具。透過角色設定、格式約束、範例引導,我們可以讓 AI 助理的回應更準確,風格更一致,也更容易整合進各種應用場景。
理解了提示工程的基本概念後,現在我們要將這些知識應用到 CLI 聊天機器人中。
這次的實作會在你昨天完成的 CLI Chatbot 基礎上,加入角色設定,讓使用者在啟動程式時就能決定 AI 助理要採取什麼樣的語氣與風格。
首先,我們需要為不同的角色設計提示語(prompt),並封裝成可重複使用的模板。
請在 src/
資料夾中新增一個名為 prompts.ts
的檔案,內容如下:
// src/prompts.ts
import type { ChatCompletionMessageParam } from 'openai/resources';
export const roles: Record<string, ChatCompletionMessageParam[]> = {
default: [
{ role: 'system', content: '你是一個樂於助人的 AI 助理。' }
],
software: [
{ role: 'system', content: '你是一位專業的軟體工程講師,擅長用條列方式解釋技術術語,語氣清楚、簡潔且具邏輯性。' },
{ role: 'user', content: '什麼是 API?' },
{ role: 'assistant', content: 'Q: 什麼是 API?\nA: API(應用程式介面)是讓不同系統交換資料的標準方式。' },
{ role: 'user', content: '什麼是 HTTP?' },
{ role: 'assistant', content: 'Q: 什麼是 HTTP?\nA: HTTP 是一種用於網頁資料傳輸的通訊協定,支援請求與回應的交換流程。' },
],
};
在這裡我們定義了兩種助理角色:
default
:中性通用型助理,風格友善但不偏特定任務。software
:技術導向助理,具備固定問答格式、偏好條列說明的軟體工程講師。接下來要讓 CLI 程式能透過參數指定角色。我們會使用 yargs
來處理命令列參數。
先安裝依賴:
npm install yargs
npm install --save-dev @types/yargs
然後修改 src/index.ts
:
// src/index.ts
import 'dotenv/config';
import readline from 'readline';
import yargs, { Arguments } from 'yargs';
import { hideBin } from 'yargs/helpers';
import { OpenAI } from 'openai';
import { roles } from './prompts';
interface Argv {
role: keyof typeof roles;
}
const argv = yargs(hideBin(process.argv))
.option('role', {
alias: 'r',
type: 'string',
choices: Object.keys(roles) as (keyof typeof roles)[],
default: 'default',
description: '指定助理角色',
})
.help()
.parseSync();
async function main(argv: Arguments<Argv>) {
const openai = new OpenAI();
const messages = [...roles[argv.role]];
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
console.log('GPT Chatbot 已啟動,輸入訊息開始對話(按 Ctrl+C 離開)。\n');
rl.setPrompt('> ');
rl.prompt();
rl.on('line', async (input) => {
messages.push({ role: 'user', content: input });
try {
const stream = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages,
stream: true,
});
let reply = '';
process.stdout.write('\n');
for await (const chunk of stream) {
const content = chunk.choices[0]?.delta?.content || '';
process.stdout.write(content);
reply += content;
}
process.stdout.write('\n\n');
messages.push({ role: 'assistant', content: reply });
} catch (err) {
console.error(err);
}
rl.prompt();
});
}
main(argv);
這段程式碼的重點調整如下:
--role
或簡寫的 -r
參數來指定要使用的助理角色。prompts.ts
中定義的 roles
物件中載入對應的提示訊息,作為對話的起始內容。messages
陣列中,持續維持對話上下文,實現多輪互動的效果。現在,你可以在執行時切換不同角色。
預設模式:
npm run dev
軟體工程師模式:
npm run dev -- --role software
輸入問題測試:
> 什麼是 RESTful?
可能輸出:
Q: 什麼是 RESTful?
A: RESTful 是一種基於 REST(Representational State Transfer)的架構風格,用於設計網路應用程式的 API,具有以下特點:
1. **無狀態性**:每個請求都包含所有必要的信息,伺服器不會存儲客戶端的狀態。
2. **資源導向**:主要透過 URL 定義資源,每個資源都有其唯一的標識符。
3. **統一介面**:使用標準的 HTTP 方法(如 GET、POST、PUT、DELETE)操作資源。
4. **可擴展性**:設計上易於擴展,支援多種資料格式(如 JSON、XML)。
5. **緩存能力**:可以對響應進行緩存,提升性能。
可以看到,software
角色的回答更有結構、風格一致,這就是透過 Prompt 模板設計出的效果。
你可以持續在 src/prompts.ts
中擴充角色設定,只要透過 --role
參數切換,就能快速測試不同風格的 AI 助理。
今天我們認識了 提示工程(Prompt Engineering) 技巧,並將其應用在 CLI 聊天機器人中,讓 AI 回應更符合需求與場景:
system
、user
、assistant
三種角色組成,能夠清楚定義模型的行為與對話上下文。提示工程是開發生成式 AI 應用的基本功,學會善用角色與格式設計,才能讓模型的回應真正貼近需求並具備一致性。