Skip to content

Commit

Permalink
fix
Browse files Browse the repository at this point in the history
  • Loading branch information
tbrandaws committed Dec 4, 2024
1 parent 7a8d2bc commit a6a9a00
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 27 deletions.
91 changes: 76 additions & 15 deletions packages/web/src/components/useCaseBuilder/UseCaseBuilderHelp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,11 @@ const PromptSample: React.FC<PromptSampleProps> = (props) => {
<PiCaretUp className={`${isOpen ? 'rotate-180' : ''} transition-all`} />
</div>
{isOpen && (
<pre className="relative m-2 mt-1 whitespace-pre-wrap break-words rounded bg-gray-100 p-1 text-sm">
<pre className="m-2 mt-1 whitespace-pre-wrap break-words rounded bg-gray-100 p-1 text-sm">
{props.prompt}
<ButtonCopy
text={props.prompt}
className="absolute bottom-2 right-2"
/>
<div className="flex w-full justify-end">
<ButtonCopy text={props.prompt} className="" />
</div>
</pre>
)}
</div>
Expand All @@ -51,28 +50,76 @@ const UseCaseBuilderHelp: React.FC<Props> = (props) => {

<div className="flex flex-col gap-4">
<div>
<div className="text-lg font-bold">
プロンプトテンプレートについて
</div>
<div className="text-lg font-bold">プロンプトテンプレートとは?</div>
<div className="mt-1 text-sm">
ユースケースごとに、プロンプトテンプレートを定義することができます。ユースケースを実行すると、ここで定義したプロンプトテンプレートをもとに、生成
AI が推論を行います。
生成AIに指示を出すための「ひな形」のようなものです。目的に応じて、あらかじめ指示文の型を用意しておくことができます。
</div>
</div>
<div>
<div className="text-lg font-bold">可変項目の設定</div>
<div className="text-lg font-bold">可変項目</div>
<div className="mt-1 text-sm">
プロンプトテンプレートの中には、可変項目を設定することができます。この可変項目を設定すると、自動的に画面上に入力欄が配置されます。ユースケースを実行すると、プロンプトテンプレート内の可変項目が対応する画面の入力値で置換されます。
プロンプトテンプレートの中に「後から内容を入れられる場所」(可変項目)を作れます。例えるなら、穴埋め問題の空欄のようなものです。
可変項目を定義すると、自動的に入力用の欄が画面に表示されます。
また、入力された内容が実行時にテンプレート内の該当箇所に自動で入ります。
</div>
</div>
<div>
<div className="text-base font-bold">可変項目の設定方法</div>
<div className="mt-1 text-sm">
プロンプトテンプレート内に、
<span className="rounded bg-gray-200 px-2 py-0.5 font-light">
プロンプトテンプレート内に以下の形式で記述します。
<br />
<span className="rounded bg-gray-200 px-1 py-0.5 font-light">
{'{{入力タイプ:見出し}}'}
</span>
<br />
※「見出し」は入力欄のラベルとなります(省略可)
</div>
</div>
<div>
<div className="text-base font-bold">可変項目一覧</div>
<div className="mt-3 text-sm">
<div className="mb-1 w-fit rounded bg-gray-200 px-1 py-0.5 font-light">
{'{{text:見出し}}'}
</div>
自由入力を受け付けます。入力された内容をそのまま可変項目が定義された場所に埋め込みます。
</div>
<div className="mt-3 text-sm">
<div className="mb-1 w-fit rounded bg-gray-200 px-1 py-0.5 font-light">
{'{{retrieveKendra:見出し}}'}
</div>
Amazon Kendra から Retrieve
した結果を可変項目が定義された場所に埋め込みます。Retrieve
した結果は Amazon Kendra の Retrieve API の
<a
href="https://docs.aws.amazon.com/kendra/latest/APIReference/API_Retrieve.html#kendra-Retrieve-response-ResultItems"
className="text-aws-smile"
target="_blank">
ResultItems を JSON にした文字列
</a>
です。
<span className="font-bold">
この機能を利用するためには、GenU で RAG チャット (Amazon Kendra)
が有効になっている必要があります。
</span>
</div>
<div className="mt-3 text-sm">
<div className="mb-1 w-fit rounded bg-gray-200 px-1 py-0.5 font-light">
{'{{retrieveKnowledgeBase:見出し}}'}
</div>
Knowledge Base から Retrieve
した結果を可変項目が定義された場所に埋め込みます。Retrieve
した結果は Knowledge Base の Retrieve API の
<a
href="https://docs.aws.amazon.com/bedrock/latest/APIReference/API_agent-runtime_Retrieve.html#API_agent-runtime_Retrieve_ResponseSyntax"
className="text-aws-smile"
target="_blank">
retrievalResults を JSON にした文字列
</a>
です。
<span className="font-bold">
この機能を利用するためには、GenU で RAG チャット (Knowledge Base)
が有効になっている必要があります。
</span>
の形式で入力してください。「見出し」は画面上に表示される入力項目のラベルとなります。
</div>
</div>
<div>
Expand Down Expand Up @@ -113,6 +160,20 @@ const UseCaseBuilderHelp: React.FC<Props> = (props) => {
{{text:CDK で生成したい構成の概要}}
</構築したいシステムの構成>`}
/>
<PromptSample
title="Knowledge Base を利用した基本的な RAG"
prompt={`あなたはユーザーの質問に答える AI アシスタントです。
ユーザーの質問と取得した情報を与えるので、情報を元に質問に答えてください。
必ず与えられた情報のみを参照してください。既成事実や憶測で答えてはいけません。
<質問>
{{text:質問}}
</質問>
<情報>
{{retrieveKnowledgeBase:検索クエリ}}
</情報>`}
/>
</div>
</div>
</div>
Expand Down
92 changes: 83 additions & 9 deletions packages/web/src/components/useCaseBuilder/UseCaseBuilderView.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useMemo } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import Select from '../Select';
import Button from '../Button';
import useChat from '../../hooks/useChat';
Expand All @@ -21,6 +21,12 @@ import {
extractPlaceholdersFromPromptTemplate,
getItemsFromPlaceholders,
} from '../../utils/UseCaseBuilderUtils';
import useRagKnowledgeBaseApi from '../../hooks/useRagKnowledgeBaseApi';
import useRagApi from '../../hooks/useRagApi';

const ragEnabled: boolean = import.meta.env.VITE_APP_RAG_ENABLED === 'true';
const ragKnowledgeBaseEnabled: boolean =
import.meta.env.VITE_APP_RAG_KNOWLEDGE_BASE_ENABLED === 'true';

type Props = {
modelId?: string;
Expand Down Expand Up @@ -90,6 +96,7 @@ const UseCaseBuilderView: React.FC<Props> = (props) => {
getModelId,
setModelId,
loading,
setLoading,
messages,
postChat,
clear: clearChat,
Expand All @@ -104,6 +111,9 @@ const UseCaseBuilderView: React.FC<Props> = (props) => {
const { modelIds: availableModels } = MODELS;
const { setTypingTextInput, typingTextOutput } = useTyping(loading);
const { updateRecentUseUseCase } = useMyUseCases();
const { retrieve: retrieveKendra } = useRagApi();
const { retrieve: retrieveKnowledgeBase } = useRagKnowledgeBaseApi();
const [warnMessages, setWarnMessages] = useState<string[]>([]);

const placeholders = useMemo(() => {
return extractPlaceholdersFromPromptTemplate(props.promptTemplate);
Expand Down Expand Up @@ -141,26 +151,78 @@ const UseCaseBuilderView: React.FC<Props> = (props) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [messages]);

const onClickExec = useCallback(() => {
useEffect(() => {
const hasKendra =
items.filter((i) => i.inputType === 'retrieveKendra').length > 0;
const hasKnowledgeBase =
items.filter((i) => i.inputType === 'retrieveKnowledgeBase').length > 0;
const tmpWarnMessages = [];

if (hasKendra && !ragEnabled) {
tmpWarnMessages.push(
'プロンプトテンプレート内で {{retrieveKendra}} が指定されていますが GenU で RAG チャット (Amazon Kendra) が有効になっていません。'
);
}

if (hasKnowledgeBase && !ragKnowledgeBaseEnabled) {
tmpWarnMessages.push(
'プロンプトテンプレート内で {{retrieveKnowledgeBase}} が指定されていますが GenU で RAG チャット (Knowledge Base) が有効になっていません。'
);
}

setWarnMessages(tmpWarnMessages);
}, [setWarnMessages, items]);

const onClickExec = useCallback(async () => {
if (loading) return;

setLoading(true);
setText('');

let prompt = props.promptTemplate;

items.forEach((item, idx) => {
for (const [idx, item] of items.entries()) {
let placeholder;

if (item.label !== NOLABEL) {
placeholder = `{{${item.inputType}:${item.label}}}`;
} else {
placeholder = `{{${item.inputType}}}`;
}
prompt = prompt.replace(new RegExp(placeholder, 'g'), values[idx]);
});

if (item.inputType === 'text') {
prompt = prompt.replace(new RegExp(placeholder, 'g'), values[idx]);
} else if (item.inputType === 'retrieveKendra') {
if (ragEnabled) {
const res = await retrieveKendra(values[idx]);
const resJson = JSON.stringify(res.data.ResultItems);
prompt = prompt.replace(new RegExp(placeholder, 'g'), resJson);
}
} else if (item.inputType === 'retrieveKnowledgeBase') {
if (ragKnowledgeBaseEnabled) {
const res = await retrieveKnowledgeBase(values[idx]);
const resJson = JSON.stringify(res.data.retrievalResults);
prompt = prompt.replace(new RegExp(placeholder, 'g'), resJson);
}
}
}

postChat(prompt, true);
if (!props.previewMode) {
updateRecentUseUseCase(props.useCaseId);
}
}, [loading, items, postChat, props, updateRecentUseUseCase, values]);
}, [
loading,
items,
postChat,
props,
updateRecentUseUseCase,
values,
setLoading,
retrieveKendra,
retrieveKnowledgeBase,
setText,
]);

// リセット
const onClickClear = useCallback(() => {
Expand Down Expand Up @@ -217,6 +279,15 @@ const UseCaseBuilderView: React.FC<Props> = (props) => {
)}
</div>

{warnMessages.length > 0 &&
warnMessages.map((m, idx) => (
<div
key={idx}
className="text-aws-squid-ink mb-2 rounded bg-red-200 p-2 text-sm">
{m}
</div>
))}

{props.description && (
<div className="pb-4 text-sm text-gray-600">{props.description}</div>
)}
Expand Down Expand Up @@ -247,7 +318,7 @@ const UseCaseBuilderView: React.FC<Props> = (props) => {
<div key={idx}>
<Textarea
label={item.label !== NOLABEL ? item.label : undefined}
rows={2}
rows={item.inputType === 'text' ? 2 : 1}
value={values[idx]}
onChange={(v) => {
setValue(idx, v);
Expand Down Expand Up @@ -281,11 +352,14 @@ const UseCaseBuilderView: React.FC<Props> = (props) => {
)}
</div>
<div className="flex shrink-0 gap-3 ">
<Button outlined onClick={onClickClear} disabled={props.isLoading}>
<Button
outlined
onClick={onClickClear}
disabled={props.isLoading || loading}>
クリア
</Button>

<Button onClick={onClickExec} disabled={props.isLoading}>
<Button onClick={onClickExec} disabled={props.isLoading || loading}>
実行
</Button>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ const UseCaseBuilderEditPage: React.FC = () => {
? item.label
: undefined
}
rows={2}
rows={item.inputType === 'text' ? 2 : 1}
value={
inputExample.examples
? inputExample.examples[item.label]
Expand Down
10 changes: 8 additions & 2 deletions packages/web/src/utils/UseCaseBuilderUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
// 空文字だと DynamoDB に inputExample を挿入した際にエラーになる
export const NOLABEL = 'NOLABEL';

const SUPPORTED_TYPES: string[] = ['text'];
const SUPPORTED_TYPES: string[] = [
'text',
'retrieveKendra',
'retrieveKnowledgeBase',
];

export const extractPlaceholdersFromPromptTemplate = (
promptTemplate: string
Expand Down Expand Up @@ -30,7 +34,9 @@ export const getItemsFromPlaceholders = (placeholders: string[]) => {
.filter((item) => SUPPORTED_TYPES.includes(item.inputType))
.filter(
(elem, idx, self) =>
self.findIndex((e) => e.label === elem.label) === idx
self.findIndex(
(e) => e.inputType === elem.inputType && e.label === elem.label
) === idx
) ?? []
);
};
58 changes: 58 additions & 0 deletions packages/web/tests/use-case-builder/items.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,26 @@ describe('入力タイプを正しくパースできる', () => {
]);
});

test('retrieveKendra', () => {
expect(getItemsFromPlaceholders(['{{retrieveKendra:xxx}}'])).toEqual([
{
inputType: 'retrieveKendra',
label: 'xxx',
},
]);
});

test('retrieveKnowledgeBase', () => {
expect(getItemsFromPlaceholders(['{{retrieveKnowledgeBase:xxx}}'])).toEqual(
[
{
inputType: 'retrieveKnowledgeBase',
label: 'xxx',
},
]
);
});

test('不正なタイプ', () => {
expect(getItemsFromPlaceholders(['{{hoge:xxx}}'])).toEqual([]);

Expand Down Expand Up @@ -104,4 +124,42 @@ describe('複数のラベルを正しくパースできる', () => {
},
]);
});

test('異なる入力タイプで同じラベルの時正しくパースできる', () => {
expect(
getItemsFromPlaceholders([
'{{text}}',
'{{retrieveKendra}}',
'{{retrieveKnowledgeBase}}',
'{{text:xxx}}',
'{{retrieveKendra:xxx}}',
'{{retrieveKnowledgeBase:xxx}}',
])
).toEqual([
{
inputType: 'text',
label: NOLABEL,
},
{
inputType: 'retrieveKendra',
label: NOLABEL,
},
{
inputType: 'retrieveKnowledgeBase',
label: NOLABEL,
},
{
inputType: 'text',
label: 'xxx',
},
{
inputType: 'retrieveKendra',
label: 'xxx',
},
{
inputType: 'retrieveKnowledgeBase',
label: 'xxx',
},
]);
});
});

0 comments on commit a6a9a00

Please sign in to comment.