🚀 在 VS Code 中

製作語言模型提示

您可以使用字串串連來建立語言模型提示,但很難組合功能並確保您的提示保持在語言模型的內容視窗內。為了克服這些限制,您可以使用 @vscode/prompt-tsx 程式庫。

@vscode/prompt-tsx 程式庫提供下列功能

  • 以 TSX 為基礎的提示呈現:使用 TSX 元件撰寫提示,使其更易於閱讀和維護
  • 以優先順序為基礎的修剪:自動修剪提示中較不重要的部分,以符合模型的內容視窗
  • 彈性的權杖管理:使用 flexGrowflexReserveflexBasis 等屬性來協同使用權杖預算
  • 工具整合:與 VS Code 的語言模型工具 API 整合

如需所有功能的完整概觀和詳細使用說明,請參閱完整 README

本文說明使用此程式庫進行提示設計的實務範例。這些範例的完整程式碼可以在 prompt-tsx 儲存庫中找到。

管理對話記錄中的優先順序

在提示中包含對話記錄非常重要,因為它讓使用者可以針對先前的訊息提出後續問題。但是,您需要確保適當地處理其優先順序,因為記錄可能會隨著時間推移而變得龐大。我們發現,最有意義的模式通常是依序優先處理

  1. 基本提示指示
  2. 目前使用者查詢
  3. 最近幾輪的聊天記錄
  4. 任何支援資料
  5. 盡可能容納剩餘的記錄

因此,請在提示中將記錄分成兩個部分,其中最近的提示輪次優先於一般內容資訊。

在此程式庫中,樹狀結構中的每個 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 {
	SystemMessage,
	UserMessage,
	PromptElement,
	BasePromptElementProps,
} from '@vscode/prompt-tsx';

interface IMyPromptProps extends BasePromptElementProps {
	history: ChatContext['history'];
	userQuery: string;
}

export class MyPrompt extends PromptElement<IMyPromptProps> {
	render() {
		return (
			<>
				<SystemMessage priority={100}>
					Here are your base instructions. They have the highest priority because you want to make
					sure they're always included!
				</SystemMessage>
				{/* 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 system message 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 屬性來協同調整檔案內容的大小,以符合權杖預算。

步驟 1:定義基本指示和使用者查詢

首先,您定義一個 SystemMessage 元件,其中包含基本指示。此元件具有最高優先順序,以確保始終包含它。

<SystemMessage priority={100}>Here are your base instructions.</SystemMessage>

然後,您可以使用 UserMessage 元件來包含使用者查詢。此元件具有高優先順序,以確保在基本指示之後立即包含它。

<UserMessage priority={90}>{this.props.userQuery}</UserMessage>

步驟 2:包含檔案內容

您現在可以使用 FileContext 元件來包含檔案內容。您為其指派 flexGrow1,以確保在基本指示、使用者查詢和歷史記錄之後呈現它。

<FileContext priority={70} flexGrow={1} files={this.props.files} />

使用 flexGrow 值,元素會在傳遞至其 render()prepare() 呼叫的 PromptSizing 物件中取得任何未使用的權杖預算。您可以在 prompt-tsx 文件中閱讀有關彈性元素行為的詳細資訊。

步驟 3:包含歷史記錄

接下來,使用您先前建立的 History 元件來包含歷史訊息。這有點棘手,因為您確實希望顯示一些歷史記錄,但同時也希望檔案內容佔用提示的大部分空間。

因此,請為 History 元件指派 flexGrow2,以確保它在所有其他元素(包括 <FileContext />)之後呈現。但是,也將 flexReserve 值設定為 "/5",以為歷史記錄保留總預算的 1/5。

<History
	history={this.props.history}
	passPriority
	older={0}
	newer={80}
	flexGrow={2}
	flexReserve="/5"
/>

步驟 3:組合提示的所有元素

現在,將所有元素組合到 MyPrompt 元件中。

import {
	SystemMessage,
	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 (
			<>
				<SystemMessage priority={100}>Here are your base instructions.</SystemMessage>
				<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 儲存庫中查看它。

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 來協同調整檔案內容的大小,以符合權杖預算。

透過遵循此模式,您可以確保始終包含提示中最重要的部分,而較不重要的部分會根據需要進行修剪,以符合模型的內容視窗。如需 getExpandedFiles 方法和 FileContextTracker 類別的完整實作詳細資訊,請參閱 prompt-tsx 儲存庫