コンテキスト適応型プロンプトヒントシステム(トークン最適化)
TypeScript
AI
会話履歴からキーワードマッチングでヒントを自動選択し、システムプロンプトに動的追加する設計
基本構造
type ToolHintId = "uiInteraction" | "dataSearch" | "reportGeneration"; const KEYWORD_RULES: Record<ToolHintId, RegExp[]> = { uiInteraction: [/クリック/, /押して/, /開いて/, /入力/], dataSearch: [/検索/, /探して/, /絞り込/, /抽出/], reportGeneration: [/レポート/, /集計/, /分析/], }; const DEFAULT_HINT_IDS: ToolHintId[] = ["dataSearch"];
ヒント選択ロジック
function selectToolHints(messages: UIMessage[]): ToolHint[] {
// 直近3メッセージのみを分析対象
const recentMessages = messages
.filter(m => m.role === "user")
.slice(-3);
const combinedText = normalise(
recentMessages.map(extractText).join("\n")
);
const matchedIds = new Set<ToolHintId>();
// キーワードマッチング
Object.keys(KEYWORD_RULES).forEach((hintId) => {
if (KEYWORD_RULES[hintId].some(regex => regex.test(combinedText))) {
matchedIds.add(hintId);
}
});
// マッチなしの場合はデフォルト
if (matchedIds.size === 0) {
DEFAULT_HINT_IDS.forEach(id => matchedIds.add(id));
}
// 最大3つまで
return dedupe(ORDERED_HINTS.filter(h => matchedIds.has(h.id))).slice(0, 3);
}
テキスト正規化
const extractText = (message: UIMessage): string => {
const { content } = message;
if (typeof content === "string") return content;
if (Array.isArray(content)) {
return content.map(part =>
typeof part === "string" ? part : part.text ?? ""
).join("\n");
}
return content?.text ?? "";
};
const normalise = (text: string) => text.replace(/\s+/g, "").toLowerCase();
システムプロンプトへの統合
function buildHintSection(hints: ToolHint[]): string {
if (hints.length === 0) return "";
const lines = ["## 現在のタスクに有用なヒント"];
hints.forEach(hint => {
lines.push(`### ${hint.title}`);
lines.push(hint.summary);
lines.push("- 推奨ツール: " + hint.recommendedTools.join(", "));
lines.push("- 手順:");
hint.steps.forEach((step, i) => {
lines.push(` ${i + 1}. ${step}`);
});
});
return lines.join("\n");
}
// 使用例
const toolHints = selectToolHints(messages);
const hintSection = buildHintSection(toolHints);
const systemPrompt = [BASE_SYSTEM_PROMPT, hintSection]
.filter(s => s.trim().length > 0)
.join("\n\n");
最適化のポイント
- 直近3メッセージのみ分析(古い履歴は無視)
- 最大3つまでのヒント(トークン制限)
- 空白除去と小文字化で正規化
- デフォルトヒントでゼロマッチを回避