構建語言模型提示
您可以透過字串拼接來構建語言模型提示,但這很難組合功能並確保您的提示保持在語言模型的上下文視窗內。為了克服這些限制,您可以使用 @vscode/prompt-tsx 庫。
@vscode/prompt-tsx 庫提供以下功能
- 基於 TSX 的提示渲染:使用 TSX 元件組合提示,使其更具可讀性和可維護性
- 基於優先順序的修剪:自動修剪提示中不太重要的部分,以適應模型的上下文視窗
- 靈活的 token 管理:使用
flexGrow、flexReserve和flexBasis等屬性來協同使用 token 預算 - 工具整合:與 VS Code 的語言模型工具 API 整合
有關所有功能的完整概述和詳細用法說明,請參閱 完整的 README。
本文介紹了使用該庫進行提示設計的實用示例。這些示例的完整程式碼可以在 prompt-tsx 儲存庫中找到。
管理對話歷史記錄中的優先順序
在提示中包含對話歷史記錄很重要,因為它使使用者能夠對先前的訊息提出後續問題。但是,您需要確保其優先順序得到適當處理,因為歷史記錄會隨著時間的推移而增長。我們發現,最有意義的模式通常是按以下順序確定優先順序
- 基礎提示指令
- 當前使用者查詢
- 最近幾次對話歷史記錄
- 任何支援資料
- 儘可能多的剩餘歷史記錄
因此,在提示中將歷史記錄分成兩部分,其中最近的提示輪次優先於一般的上下文資訊。
在此庫中,樹中的每個 TSX 節點都有一個優先順序,概念上類似於 zIndex,數字越大表示優先順序越高。
步驟 1:定義 HistoryMessages 元件
要列出歷史訊息,請定義一個 HistoryMessages 元件。此示例提供了一個良好的起點,但如果您處理更復雜的資料型別,可能需要對其進行擴充套件。
此示例使用了 PrioritizedList 輔助元件,該元件會自動為其每個子項分配升序或降序的優先順序。
import {
UserMessage,
AssistantMessage,
PromptElement,
BasePromptElementProps,
PrioritizedList,
} from '@vscode/prompt-tsx';
import { ChatContext, ChatRequestTurn, ChatResponseTurn, ChatResponseMarkdownPart } from 'vscode';
interface IHistoryMessagesProps extends BasePromptElementProps {
history: ChatContext['history'];
}
export class HistoryMessages extends PromptElement<IHistoryMessagesProps> {
render(): PromptPiece {
const history: (UserMessage | AssistantMessage)[] = [];
for (const turn of this.props.history) {
if (turn instanceof ChatRequestTurn) {
history.push(<UserMessage>{turn.prompt}</UserMessage>);
} else if (turn instanceof ChatResponseTurn) {
history.push(
<AssistantMessage name={turn.participant}>
{chatResponseToMarkdown(turn)}
</AssistantMessage>
);
}
}
return (
<PrioritizedList priority={0} descending={false}>
{history}
</PrioritizedList>
);
}
}
步驟 2:定義 Prompt 元件
接下來,定義一個 MyPrompt 元件,其中包括基礎指令、使用者查詢和具有適當優先順序的歷史訊息。優先順序值在同級之間是本地的。請記住,您可能希望在觸及提示中的任何其他內容之前修剪歷史記錄中較舊的訊息,因此需要將兩個 <HistoryMessages> 元素分開
import {
UserMessage,
PromptElement,
BasePromptElementProps,
} from '@vscode/prompt-tsx';
interface IMyPromptProps extends BasePromptElementProps {
history: ChatContext['history'];
userQuery: string;
}
export class MyPrompt extends PromptElement<IMyPromptProps> {
render() {
return (
<>
<UserMessage priority={100}>
Here are your base instructions. They have the highest priority because you want to make
sure they're always included!
</UserMessage>
{/* Older messages in the history have the lowest priority since they're less relevant */}
<HistoryMessages history={this.props.history.slice(0, -2)} priority={0} />
{/* The last 2 history messages are preferred over any workspace context you have below */}
<HistoryMessages history={this.props.history.slice(-2)} priority={80} />
{/* The user query is right behind the based instructions in priority */}
<UserMessage priority={90}>{this.props.userQuery}</UserMessage>
<UserMessage priority={70}>
With a slightly lower priority, you can include some contextual data about the workspace
or files here...
</UserMessage>
</>
);
}
}
現在,在庫嘗試修剪提示的其他元素之前,所有較舊的歷史訊息都會被修剪。
步驟 3:定義 History 元件
為了使使用更輕鬆,請定義一個 History 元件來包裝歷史訊息,並使用 passPriority 屬性充當直通容器。透過 passPriority,其子項在優先順序方面被視為直接子項。
import { PromptElement, BasePromptElementProps } from '@vscode/prompt-tsx';
interface IHistoryProps extends BasePromptElementProps {
history: ChatContext['history'];
newer: number; // last 2 message priority values
older: number; // previous message priority values
passPriority: true; // require this prop be set!
}
export class History extends PromptElement<IHistoryProps> {
render(): PromptPiece {
return (
<>
<HistoryMessages history={this.props.history.slice(0, -2)} priority={this.props.older} />
<HistoryMessages history={this.props.history.slice(-2)} priority={this.props.newer} />
</>
);
}
}
現在,您可以單獨使用並重用此元素來包含聊天曆史記錄
<History history={this.props.history} passPriority older={0} newer={80}/>
擴充套件檔案內容以適應
在此示例中,您希望在提示中包含使用者當前正在檢視的所有檔案的內容。這些檔案可能很大,以至於包含所有檔案會導致其文字被修剪!此示例演示瞭如何使用 flexGrow 屬性來協同調整檔案內容的大小,以適應 token 預算。
步驟 1:定義基礎指令和使用者查詢
首先,您需要定義一個包含基礎指令的 UserMessage 元件。
<UserMessage priority={100}>Here are your base instructions.</UserMessage>
然後,您可以使用 UserMessage 元件包含使用者查詢。此元件具有高優先順序,以確保其緊隨基礎指令之後。
<UserMessage priority={90}>{this.props.userQuery}</UserMessage>
步驟 2:包含檔案內容
現在,您可以使用 FileContext 元件包含檔案內容。為其分配 flexGrow 值為 1,以確保其在基礎指令、使用者查詢和歷史記錄之後呈現。
<FileContext priority={70} flexGrow={1} files={this.props.files} />
使用 flexGrow 值,該元素將在傳遞到其 render() 和 prepare() 呼叫中的 PromptSizing 物件中獲得任何未使用的 token 預算。您可以在 prompt-tsx 文件中瞭解有關 flex 元素行為的更多資訊。
步驟 3:包含歷史記錄
接下來,使用您之前建立的 History 元件包含歷史訊息。這有點棘手,因為您確實希望顯示一些歷史記錄,但也希望檔案內容佔據提示的大部分。
因此,將 History 元件的 flexGrow 值設定為 2,以確保它在所有其他元素(包括 <FileContext />)之後呈現。但是,還將 flexReserve 值設定為 "/5",以將總預算的 1/5 保留給歷史記錄。
<History
history={this.props.history}
passPriority
older={0}
newer={80}
flexGrow={2}
flexReserve="/5"
/>
步驟 3:組合提示的所有元素
現在,將所有元素組合到 MyPrompt 元件中。
import {
UserMessage,
PromptElement,
BasePromptElementProps,
} from '@vscode/prompt-tsx';
import { History } from './history';
interface IFilesToInclude {
document: TextDocument;
line: number;
}
interface IMyPromptProps extends BasePromptElementProps {
history: ChatContext['history'];
userQuery: string;
files: IFilesToInclude[];
}
export class MyPrompt extends PromptElement<IMyPromptProps> {
render() {
return (
<>
<UserMessage priority={100}>Here are your base instructions.</UserMessage>
<History
history={this.props.history}
passPriority
older={0}
newer={80}
flexGrow={2}
flexReserve="/5"
/>
<UserMessage priority={90}>{this.props.userQuery}</UserMessage>
<FileContext priority={70} flexGrow={1} files={this.props.files} />
</>
);
}
}
步驟 4:定義 FileContext 元件
最後,定義一個 FileContext 元件,其中包含使用者當前正在檢視的檔案的內容。由於您使用了 flexGrow,因此可以透過使用 PromptSizing 中的資訊,實現邏輯以獲取“有趣”行周圍的儘可能多的行。
為了簡潔起見,省略了 getExpandedFiles 的實現邏輯。您可以在 prompt-tsx repo 中檢視它。
import { PromptElement, BasePromptElementProps, PromptSizing, PromptPiece } from '@vscode/prompt-tsx';
class FileContext extends PromptElement<{ files: IFilesToInclude[] } & BasePromptElementProps> {
async render(_state: void, sizing: PromptSizing): Promise<PromptPiece> {
const files = await this.getExpandedFiles(sizing);
return <>{files.map(f => f.toString())}</>;
}
private async getExpandedFiles(sizing: PromptSizing) {
// Implementation details are summarized here.
// Refer to the repo for the complete implementation.
}
}
總結
在這些示例中,您建立了一個 MyPrompt 元件,其中包含具有不同優先順序的基準指令、使用者查詢、歷史訊息和檔案內容。您使用 flexGrow 來協同調整檔案內容的大小,以適應 token 預算。
透過遵循此模式,您可以確保您的提示中最重要的部分始終被包含,而不太重要的部分則根據需要進行修剪,以適應模型的上下文視窗。有關 getExpandedFiles 方法和 FileContextTracker 類的完整實現詳細資訊,請參閱 prompt-tsx repo。