최근 Generative AI에 대한 거의 모든 탐구에서 저는 프롬프트가 얼마나 중요한지 깨닫게 되었습니다. 그렇다고 해서 항상 더 나은 프롬프트를 작성하게 되는 것은 아니지만 문제를 인식하는 것은 문제를 해결하기 위한 최소한 한 단계입니다. 그렇죠? 개인적으로 도움이 될 것이라고 생각한 것 중 하나는 다양한 프롬프트를 신속하게 비교하고 대조할 수 있는 도구를 갖는 것입니다.
물론 브라우저에서 여러 탭을 열 수도 있지만, 저는 제 목적에 맞게 좀 더 직접적이고 단순화된 것을 원했습니다. 이를 염두에 두고 제가 만든 것을 공유하겠습니다.
프롬프트 테스터(Prompt Tester)는... 음... 프롬프트를 테스트하기 위해 믿을 수 없을 정도로 상상력이 풍부한 이름을 붙인 도구입니다. 하나의 프롬프트로 시작됩니다.
아이디어는 초기 프롬프트를 입력한 다음 한 번 이상 클릭하여 더 많은 프롬프트를 추가하는 것입니다. 준비가 되면 "결과 생성"을 클릭하면 됩니다.
몇 가지 예시 결과를 보여주기 전에, 그것이 어떻게 구축되었는지에 대해 조금 이야기하겠습니다.
백엔드는 정적 HTML을 처리하고 프런트엔드 API 요청에 응답하는 매우 간단한 Node.js 서버입니다. 다시 한 번 초기 코드를 나와 공유해 준 Todd Sharp에게 감사드립니다. 지루하고 간단한 처리 부분은 다음과 같습니다.
async function handler(req, res) { console.log('Entered handler.', req.method, req.url); if(req.method === 'GET' && req.url.indexOf('favicon.ico') === -1) { res.writeHead(200, { 'Content-Type':'text/html' }); res.write(fs.readFileSync('./demo.html')); res.end(); } else if(req.method === 'POST' && req.url === '/api') { let body = ''; req.on('data', chunk => { body += chunk.toString(); }); req.on('end', async () => { body = JSON.parse(body); console.log('BODY:\n', JSON.stringify(body, null, '\t')); let result = await callGemini(body.prompt); res.writeHead(200, { 'Content-Type':'application/json' }); res.write(JSON.stringify(result)); res.end(); }); } } const server = http.createServer(handler); server.listen(3000); console.log('Listening on port 3000');
그리고 Google의 Gemini API 에 프롬프트를 전달하는 callGemini
부분도 있습니다. 이론적으로는 ChatGPT 또는 다른 서비스로 변경될 수 있습니다.
const MODEL_NAME = "gemini-pro"; const API_KEY = process.env.GOOGLE_AI_KEY; const genAI = new GoogleGenerativeAI(API_KEY); const model = genAI.getGenerativeModel({ model: MODEL_NAME }); async function callGemini(text) { const generationConfig = { temperature: 0.9, topK: 1, topP: 1, maxOutputTokens: 2048, }; const safetySettings = [ { category: HarmCategory.HARM_CATEGORY_HARASSMENT, threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE, }, { category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE, }, { category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE, }, { category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE, }, ]; const parts = [ {text}, ]; const result = await model.generateContent({ contents: [{ role: "user", parts }], generationConfig, safetySettings, }); try { if(result.response.promptFeedback && result.response.promptFeedback.blockReason) { return { error: `Blocked for ${result.response.promptFeedback.blockReason}` }; } const response = result.response.candidates[0].content.parts[0].text; return { response }; } catch(e) { // better handling return { error:e.message } } }
이제 마침내 몇 가지 기본적인 오류 처리를 수행하고 프롬프트가 차단된 이유를 사용자(나)에게 노출시키려고 노력하고 있음을 자랑스럽게 생각합니다.
프런트 엔드는 비교적 간단합니다. 저는 핵심 CSS(Todd에게 다시 한 번 감사드립니다)에 Milligram을 사용하고 새로운 프롬프트 추가 및 호출을 처리하기 위해 일부 바닐라 JavaScript를 사용했습니다. HTML은 다음과 같습니다(인라인 CSS 제외).
<h1>Prompt Tester</h1> <p> This tool helps you see the result of multiple prompts at the same time. The idea is to use similar prompts with different phrasing to see how it impacts the result. </p> <div id="prompts"> </div> <template id="promptRow"> <div class="row"> <div class="column column-33"><textarea placeholder="Enter your prompt."></textarea></div> <div class="column column-67 result"></div> </div> <hr> </template> <div class="float-right"> <button id="addPrompt">Add Prompt</button> <button id="generateResults">Generate Results</button> </div>
그리고 자바스크립트입니다. 프롬프트 레이아웃을 처리하기 위해 위의 <template>
사용했습니다. 해당 태그를 사용한 것은 이번이 처음인데 비교적 쉬웠습니다.
let $template, $prompts, $addPromptsBtn, $generateResultsBtn; let promptCount = 0; document.addEventListener('DOMContentLoaded', init, false); async function init() { $addPromptsBtn = document.querySelector('#addPrompt'); $addPromptsBtn.addEventListener('click', addPrompt, false); $generateResultsBtn = document.querySelector('#generateResults') $generateResultsBtn.addEventListener('click', generateResults, false); $template = document.querySelector('#promptRow'); $prompts = document.querySelector('#prompts'); addPrompt(); } function addPrompt() { promptCount++; const newPrompt = $template.content.cloneNode(true); $prompts.append(newPrompt); } async function generateResults() { // disable add prompt and generate $addPromptsBtn.setAttribute('disabled', 'disabled'); $generateResultsBtn.setAttribute('disabled', 'disabled'); let $results = document.querySelectorAll('div.result'); // clear any previous results $results.forEach(r => r.innerHTML = ''); /* gather prompts from the inputs */ let prompts = []; let $fields = document.querySelectorAll('#prompts textarea'); for(f of $fields) prompts.push(f.value); let promises = []; for(p of prompts) promises.push(getResult(p)); let results = await Promise.all(promises); // assign results to result area for each, handle errors - later // there's a one to one correspondance between our array of results and the result div for(let i=0;i<results.length;i++) { if(results[i].response) $results[i].innerHTML = marked.parse(results[i].response); else if(results[i].error) $results[i].innerHTML = `<strong>Error with Prompt: </strong> ${results[i].error}`; } $addPromptsBtn.removeAttribute('disabled'); $generateResultsBtn.removeAttribute('disabled'); } async function getResult(p) { console.log('get result for',p); // quick return on empty prompt if(p.trim() === '') return { response:'<strong>Enter a prompt.</strong>' }; let req = await fetch('/api', { method:'POST', body: JSON.stringify({prompt:p.trim()})}); let result = await req.json(); return result; }
나는 그것이 모두 매우 간단한 DOM 조작 및 API 호출이라고 생각하지만 , 뭔가 이해가 되지 않는 경우 아래 댓글로 알려 주시기 바랍니다. 모든 API 호출을 한 번에 실행하고 Promise.all
완료될 때까지 기다립니다.
또한 marked
라이브러리를 사용합니다. Google의 결과에는 일반적으로 Markdown이 있으며 이는 내 작은 웹 앱에서 결과를 멋지게 렌더링하는 데 도움이 됩니다.
내 생각에는 이 모든 것이 그다지 흥미롭지는 않지만 몇 가지 테스트를 살펴보겠습니다. 앞서 말했듯이 나는 프롬프트 스타일이 중요하다는 것을 알고 있었습니다 . 도구를 통해 나에게 직접 제시하는 것은 이를 강화하는 데 엄청난 도움이 되었습니다.
중력을 설명하는 간단한 아이디어부터 시작해 보겠습니다. 다양한 연령대에 적용됩니다. 결과의 크기가 크기 때문에 여기서는 약간의 CSS를 사용하여 상황을 원활하게 유지하겠습니다.
프롬프트: 중력에 대해 설명하되 1학년 어린이에게 적절하게 설명하세요.
중력은 우리 행성이 가지고 있는 초능력입니다! 그것은 지구상의 모든 것을 땅에 머물게 만드는 보이지 않는 힘과 같습니다.
손에 공이 있다고 상상해보십시오. 떨어뜨리면 어떻게 되나요? 넘어지는 것 맞죠? 그건 중력 때문이에요. 중력은 공을 지구 중심 쪽으로 끌어당겨 땅에 떨어뜨립니다.
중력은 또한 우리를 땅에 붙잡아 두는 것이기도 합니다. 그것은 우리 발을 지구에 단단히 고정시키고 떠내려가는 것을 방지하는 것입니다!
중력은 매우 중요한 힘입니다. 모든 것을 제자리에 유지하고 일이 원래 방식대로 작동하도록 돕습니다. 중력이 없다면 우리 모두는 우주를 떠돌게 될 것이고 상황은 매우 혼란스러울 것입니다!
중력에 대해 생각하는 재미있는 방법은 다음과 같습니다. 지구가 거대한 자석이고 지구상의 모든 것이 금속으로 만들어졌다고 상상해 보세요. 자석은 금속 물체를 그쪽으로 끌어당겨서 땅에 고정시킵니다.
중력은 매우 중요하고 흥미로운 힘입니다. 그것은 우리 행성을 독특하고 특별한 살기 좋은 곳으로 만드는 것 중 하나입니다!
프롬프트: 중력에 대해 설명하되 7학년 어린이에게 적절하게 설명하세요.
공을 가지고 놀고 있다고 상상해 보세요. 공중에 던지면 다시 내려옵니다. 이는 중력 때문이다. 중력은 사물을 서로 끌어당기는 힘입니다. 그것은 당신을 땅에 머물게 하고 사물을 넘어지게 만드는 것입니다.
두 물체 사이의 중력의 양은 질량에 따라 달라집니다. 물체의 질량이 클수록 중력은 더 강해집니다. 그렇기 때문에 행성이나 별과 같은 물체는 사람이나 자동차와 같은 물체보다 중력이 더 큽니다.
중력은 또한 행성을 태양 주위의 궤도에 유지하는 것입니다. 태양의 중력은 지구보다 훨씬 강하므로 지구를 태양 쪽으로 끌어당깁니다. 이로 인해 지구는 태양 주위를 원으로 계속 움직입니다.
중력은 우리 우주에서 매우 중요한 힘입니다. 물건을 제자리에 유지하고 움직이게 만듭니다. 중력이 없으면 행성은 우주로 날아갈 것이고 우리 모두는 떠내려갈 것입니다.
중력에 관한 몇 가지 재미있는 사실은 다음과 같습니다.
아래 프롬프트의 오타에 유의하세요. 쌍둥이자리는 그것을 잘 처리했습니다.
프롬프트: 중력에 대해 설명하되 대학생에게 적합하게 설명하세요.
중력: 끌어당김의 힘
중력은 행성, 달, 별과 같은 천체를 계속 움직이는 역할을 하는 우주의 근본적인 힘입니다. 이는 또한 우리가 땅에 단단히 고정되도록 하고 물체를 떨어뜨렸을 때 떨어지도록 하는 것이기도 합니다.
뉴턴의 만유인력 법칙:
만유인력의 법칙에 따르면 우주의 모든 물체는 질량의 곱에 비례하고 거리의 제곱에 반비례하는 힘으로 다른 모든 물체를 끌어당긴다. 수학적으로는 다음과 같이 표현될 수 있습니다.
F = Gm1m2/r^2
어디:
F는 두 물체 사이의 중력입니다. G는 중력 상수(6.674 × 10^-11 N·m^2/kg^2)입니다. m1과 m2는 두 물체의 질량입니다. r은 두 물체의 중심 사이의 거리입니다.
중력의 중요성:
천체를 궤도에 유지: 더 큰 천체의 중력으로 인해 더 작은 천체가 그 주위를 계속 회전합니다. 예를 들어, 지구의 중력은 달을 궤도에 유지하는 반면, 태양의 중력은 지구와 다른 행성을 각각의 궤도에 유지합니다.
조수에 대한 책임: 지구와 달 사이의 중력으로 인해 바다의 조수가 발생합니다. 달이 지구를 공전할 때 달의 중력으로 인해 달을 바라보는 쪽의 바다 물이 부풀어오르게 됩니다. 관성력으로 인해 지구 반대쪽에 또 다른 돌출부가 형성됩니다. 지구가 회전함에 따라 이러한 돌출부는 행성 표면을 가로질러 이동하여 밀물과 썰물이 발생합니다.
물체를 지구에 유지: 중력은 물체를 지구 중심으로 끌어당겨 물체가 떠다니는 것을 방지합니다. 이 현상을 통해 우리는 걷고, 서고, 주변 환경과 상호 작용할 수 있습니다.
별과 은하의 형성: 중력은 별과 은하의 형성에 중요한 역할을 합니다. 거대한 가스와 먼지 구름의 중력 붕괴로 인해 새로운 별이 생성됩니다. 마찬가지로, 은하계의 중력은 은하계를 서로 묶어서 흩어지는 것을 방지합니다.
상대성 이론: 알베르트 아인슈타인의 일반 상대성 이론은 중력에 대한 보다 포괄적인 이해를 제공합니다. 중력은 힘이 아니라 질량이나 에너지의 존재로 인해 발생하는 시공간의 곡률이라고 설명합니다. 이 곡률은 객체의 움직임에 영향을 미치므로 직선이 아닌 곡선 경로를 따르게 됩니다.
결론적으로, 중력은 천체의 움직임, 조수 및 지구상 물체의 움직임을 유지하는 데 책임이 있는 근본적인 힘입니다. 이는 수세기에 걸쳐 광범위하게 연구되어 온 복잡한 현상이며, 오늘날까지도 계속해서 과학자와 연구자들을 매료시키고 있습니다.
두 번째 테스트에서는 기본 프롬프트로 시작한 다음 특정 유형의 세부 정보를 요청하는 더 나은 버전으로 시작했습니다. (빠른 편집 참고 사항 - 아래 표 형식 데이터는 약간 지저분하지만 원본 출력에서는 지저분하지 않았습니다 .)
프롬프트: 고양이가 개보다 훨씬 나은 이유는 무엇입니까?
고양이와 개는 모두 인기 있는 애완동물이며, 어느 것이 더 좋은지에 대한 명확한 답은 없습니다. 고양이와 개 모두 고유한 장점과 단점이 있으며, 귀하에게 가장 적합한 애완동물은 개인의 필요와 선호도에 따라 달라집니다.
어떤 사람들은 일반적으로 개보다 더 독립적이기 때문에 고양이를 선호합니다. 고양이는 일반적으로 스스로 즐겁게 지내며 산책을 하거나 운동을 위해 밖으로 나갈 필요가 없습니다. 이는 아파트에 거주하거나 일정이 바쁜 사람들에게 큰 이점이 될 수 있습니다.
고양이는 깨끗하고 깔끔한 동물로도 알려져 있습니다. 그들은 정기적으로 몸단장을 하고 일반적으로 아무 문제 없이 쓰레기통을 사용합니다. 이는 개의 지저분함과 냄새를 다루고 싶지 않은 사람들에게 큰 이점이 될 수 있습니다.
물론 고양이를 키우는 데에는 몇 가지 단점도 있습니다. 고양이는 개보다 비용이 더 많이 들 수 있고, 훈련시키기가 더 어려울 수 있습니다. 고양이는 가구나 기타 가정용품을 긁을 가능성도 더 높습니다.
결국 고양이를 키울 것인지, 개를 키울 것인지에 대한 결정은 개인의 몫입니다. 정답이나 오답은 없으며, 귀하에게 가장 적합한 애완동물은 개인의 필요와 선호도에 따라 달라집니다.
다음은 고양이와 개의 주요 차이점을 요약한 표입니다.
특성 | 고양이 | 개 |
---|---|---|
독립 | 더 독립적이다 | 덜 독립적 |
운동 필요 | 산책을 하거나 밖으로 나갈 필요가 없습니다. | 산책을 하거나 밖으로 나가야 함 |
청결 | 정기적으로 스스로 청소합니다. | 정기적으로 목욕을 시켜야 합니다 |
훈련 | 훈련이 어려울 수 있음 | 더 쉬운 훈련 |
긁기 | 가구나 기타 가정용품이 긁힐 수 있습니다. | 일반적으로 가구를 긁지 않습니다. |
비용 | 더 비싼 | 저렴 |
프롬프트: 고양이가 개보다 훨씬 나은 이유는 무엇입니까? 정서적 안녕과 재정적 측면을 포괄하는 이유를 글머리 기호 목록으로 설명하세요.
정서적 안정:
재정적 측면:
다음 테스트는 AWS re:Invent: AWS re:Invent 2023 - Amazon Bedrock의 LLM에 대한 프롬프트 엔지니어링 모범 사례(AIM377)의 훌륭한 프레젠테이션에서 나온 것입니다. 저는 이 세션에서 GenAI 엔진이 프롬프트를 더 잘 해결할 수 있도록 문제 해결에 사용되는 단계에 대한 정보를 제공하는 생각의 사슬 프롬프트에 대한 좋은 개요를 포함하여 많은 훌륭한 조언을 찾았습니다.
이제 해당 프레젠테이션에 제공된 예는 다음과 같습니다.
Q: Roger has 5 tennis balls. he buys 2 more cans of tennis balls. Each can has 3 tennis balls. How many tennis balls does he have now? A: The answer is 11. Q. The cafeteria had 23 apples. If they used 20 to make lunch and bought 6 more, how many apples do they have?
프레젠테이션에서 사용 중인 모델은 27을 잘못 반환했습니다. 테스트에서는 올바르게 작동했지만 지난 11월 생성 AI 블랙잭 조언 데모를 구축했을 때 수학에 문제가 있다는 것을 확실히 확인했습니다.
내가 말했듯이 Gemini는 이를 훌륭하게 처리했습니다. A: The answer is 9.
그러나 Prompt Tester에서는 제안된 CoT 프롬프트를 시도했습니다.
Q: Roger has 5 tennis balls. he buys 2 more cans of tennis balls. Each can has 3 tennis balls. How many tennis balls does he have now? A: Roger started with 5 balls. 2 cans of 3 tennis balls each is 6 tennis balls. 5 + 6 = 11. The answer is 11. Q. The cafeteria had 23 apples. If they used 20 to make lunch and bought 6 more, how many apples do they have?
그리고 결과가 개선되어 어떻게 답변을 얻었는지 보여줍니다.
A: The cafeteria had 23 apples. They used 20 apples for lunch, which means they have 3 apples left. They then bought 6 more apples, so they now have a total of 3 + 6 = 9 apples. The answer is 9.
그 차이는 꽤 상당합니다.
이것이 유용하다고 생각되면 여기( https://github.com/cfjedimaster/ai-testingzone/tree/main/prompt_tester) 에서 코드를 가져오세요. 앞서 언급했듯이 callChatGPT
등을 추가하고 다른 서비스를 사용하는 것은 그리 어렵지 않습니다. 그렇다면 알려주세요!