지난 주에 저는 TheJam.dev 에서 하나를 발표하는 영광을 누렸습니다. 이것은 생성 AI에 대한 나의 첫 번째 프레젠테이션이었고, 글쓰기 과정에 도움이 되는 흥미로운 사용 사례라고 생각되는 내용을 공유하게 되었습니다.
분명히 말씀드리지만, GenAI를 사용하여 블로그 게시물을 작성한다는 의미는 아닙니다. 이는 끔찍한 생각입니다. (IMO!) 대신에 이것이 일부 프로세스에 어떻게 도움이 될 수 있는지 살펴보았습니다. 조금 백업하고 몇 가지 배경 정보를 제공하겠습니다.
나는 오랫동안 John Birmingham 의 팬이었습니다. 그는 군사/공상과학 등 장르의 글을 쓰는 작가이며 꽤 흥미로운 아이디어를 가지고 있습니다. 나는 처음에 현대 국제 해군 함대가 1942년으로 되돌아가는 아이디어를 다룬 그의 "시간의 축" 3부작을 통해 그를 발견했습니다.
지금은 그 자체로도 멋지지만, 나는 그가 군사적 측면에만 초점을 맞춘 것이 아니라 "업타임머"(미래에서 온 사람들)와 동시대인 사이의 문화 충돌에 대해 이야기하는 데 많은 시간을 할애했다는 점이 마음에 들었습니다.
Tom Clancy와 약간 비슷하지만 액션에만 집중하지 않았다고 말할 수 있을 것 같습니다. 나는 그의 책 중 무엇이든 추천하고 싶습니다. 이미 그의 책을 읽었다면 아래 댓글로 알려주세요.
그의 작품을 좋아하는 사람으로서 그의 Patreon을 구독했는데 정말 흥미로웠습니다. 그는 앞으로 나올 작품의 초안을 공유하지만, 더 중요한 것은 자신의 작업 과정에 대해서도 꽤 많이 이야기한다는 것입니다. 작가로서 저는 이것이 정말 매력적이라고 생각합니다.
최근 그는 자신의 GenAI 사용에 대해 이야기하고 좀 더 '프레임워크' 관점에서 이를 사용하는 방법에 대해 논의했습니다. 즉, 캐릭터의 동기를 적절한 시기에 어떻게 끌어오는지, 플롯 포인트를 어떻게 설정하는지 등이다. 이것은 여전히 '창의적인' 작업이지만 그 이상은... 모르겠습니다. 작품 관리?
하지만 말했듯이 저는 그것이 정말 흥미롭다고 생각했고, 생각하게 되었습니다. 글쓰기 과정을 돕기 위해 블로그에서 GenAI를 어떻게 사용할 수 있나요? 내가 생각 해낸 것은 다음과 같습니다.
여담이지만, 아래에서 논의하는 모든 내용은 Google의 Gemini API 와 Eleventy를 사용하지만 다른 곳에서도 확실히 유용할 것입니다.
제가 만든 첫 번째 데모에서는 블로그 게시물의 제목을 정하는 데 도움을 주었습니다. 저는 일반적으로 이 문제로 어려움을 겪지 않지만 GenAI가 더 나은 타이틀을 위한 대안을 제안할 수 있는지 궁금했습니다.
나는 프롬프트를 테스트하는 것으로 시작했습니다.
게미
블로그 게시물의 다음 제목이 주어지면 제목을 개선하고 게시물에 대한 트래픽을 유도할 수 있는 세 가지 제안을 공유하십시오: "제목 일부". 답변을 JSON 형식으로 제시하세요. JSON 결과의 최상위 키는 '제안'이어야 하며 각 제안은 제안된 제목에 '제목' 키를, 추론에 '추론' 키를 사용해야 합니다.
제가 구체적으로 세 가지 제안을 요청하고 더 많은 트래픽을 유도하는 데 도움이 되고 싶다고 말하는 것을 알 수 있습니다. 이제 솔직하게 말씀드리겠습니다. 좀 역겹고 스팸같은 느낌이 들거든요. 나는 클릭베이트 제목을 반드시 원하는 것은 아닙니다. 즉, 나는 내 타이틀에 대한 다른 아이디어를 보고 싶었습니다.
해당 프롬프트는 AI Studio 의 몇 가지 테스트에서 잘 작동하는 것 같아서 코드를 작성했습니다. Google에서 내보낸 코드를 가져와 다음과 같은 코드를 작성했습니다.
전체 스크립트는 다음과 같습니다.
#!/usr/bin/env node /* Given an input MD file, grab the title, and ask Google's AI APIs to offer suggestions. */ const fs = require('fs'); const fm = require('front-matter'); require('dotenv').config({path:__dirname + '/.env'}); const { GoogleGenerativeAI, HarmCategory, HarmBlockThreshold, } = require("@google/generative-ai"); const MODEL_NAME = "gemini-pro"; const API_KEY = process.env.GOOGLE_AI_KEY; async function runGenerate(title) { const genAI = new GoogleGenerativeAI(API_KEY); const model = genAI.getGenerativeModel({ model: MODEL_NAME }); 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: `Given the following title for a blog post, share three suggestions that may improve the title and drive traffic to the post: \"${title}\". Present your answer in JSON form. The top level key of the JSON result should be "suggestions" and each suggestion should use the key "title" for the suggested title and "reasoning" for the reasoning. The returned JSON should look like the following sample: [ { title: "First suggested title", reasoning: "This is the reason for the suggestion." } ] `}, ]; const result = await model.generateContent({ contents: [{ role: "user", parts }], generationConfig, safetySettings, }); const response = result.response; // remove backticks and parse. I'm seeing ```json sometimes too // console.log('DEBUG', response.text()); return JSON.parse(response.text().replace(/```/mg, '').replace(/```json/mg,'')); } (async () => { if(process.argv.length === 2) { console.log('Usage: titlesuggestions.js <<path to md>>'); process.exit(1); } const mdPath = process.argv[2]; if(!fs.existsSync(mdPath)) { console.log(`Unable to find ${mdPath}.`); process.exit(1); } const contents = fs.readFileSync(mdPath,'utf-8'); let title = fm(contents).attributes.title; console.log(`\nGenerating suggestions for: ${title}`); console.log('------------------------------------------------------------------------'); let suggestions = (await runGenerate(title)).suggestions; //console.log(suggestions); suggestions.forEach(s => { console.log(`Suggested Title: ${s['title']}\n\nReasoning: ${s['reasoning']}\n`); console.log('------------------------------------------------------------------------'); }); })();
어떻게 작동하나요? 이 블로그 게시물의 제목은 "제너레이티브 AI를 콘텐츠 도우미로 사용"입니다.
Gemini가 제안한 내용은 다음과 같습니다.
Suggested Title: The Power of Generative AI: Your New Content Assistant Reasoning: This title emphasizes the strength and potential of generative AI as a content creation tool, capturing the reader's attention and making them curious about the possibilities it offers. ------------------------------------------------------------------------ Suggested Title: Unlock Your Content Creation Potential with Generative AI Reasoning: This title suggests that generative AI can unlock new levels of productivity and creativity in content creation, attracting readers who are looking for ways to enhance their content strategy. ------------------------------------------------------------------------ Suggested Title: Revolutionize Your Content Creation: Meet Your Generative AI Assistant Reasoning: This title highlights the transformative potential of generative AI in content creation, appealing to readers who are eager to adopt cutting-edge technologies to streamline their workflow.
응... 꽤 괜찮은 것 같아. 그러나 이들 중 어느 것도 여기서 "나의" 목소리처럼 느껴지지 않습니다. 나는 제안에서 확실히 어느 정도 장점을 보고 생각할 거리를 주지만 분명히 원래 제목을 그대로 두었습니다.
다음 데모에서는 제가 정말 좋아하지 않는 글쓰기 과정의 일부를 살펴보며 머리말에 사용된 한 문장의 description
값을 작성했습니다. 이 문자열은 결국 내 <meta name="description">
태그로 들어가고 다른 곳에서는 사용되지 않습니다.
GenAI의 요약 기능을 활용하면 아주 좋을 것 같다고 생각했습니다. 나는 다음과 같은 프롬프트로 시작했습니다.
다음 블로그 게시물이 주어지면 설명으로 사용할 한 문장 요약을 작성하세요.
그리고 어떤 내용을 보낼지 고민했어요. 내 블로그 게시물에는 일반적으로 코드 샘플이 많아서 결국 소음이 될 것이라고 생각했습니다. 그래서 내 논리는 다음과 같습니다.
대부분은 첫 번째 예제의 수정된 버전이지만 정리 측면을 살펴보겠습니다.
function cleanup(str) { str = str.replace(/```(.*?)```/sg, ''); str = str.replace(/---(.*?)---/sg, ''); str = str.replace(/\n{3,}/g, '\n'); return str.trim(); }
이는 블로그 게시물의 전체 내용을 전달하므로 머리말과 코드 샘플을 제거했습니다. 그런 다음 여러 개의 빈 줄도 교체했습니다. 전체 스크립트는 다음과 같습니다.
#!/usr/bin/env node /* Given an input MD file, grab the text, scrub code, and ask for a summary. */ const fs = require('fs'); const fm = require('front-matter'); require('dotenv').config({path:__dirname + '/.env'}); const { GoogleGenerativeAI, HarmCategory, HarmBlockThreshold, } = require("@google/generative-ai"); const MODEL_NAME = "gemini-pro"; const API_KEY = process.env.GOOGLE_AI_KEY; async function runGenerate(text) { const genAI = new GoogleGenerativeAI(API_KEY); const model = genAI.getGenerativeModel({ model: MODEL_NAME }); 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: `Given the following blog post, write a one sentence summary to use as the description:\n${text} `}, ]; const result = await model.generateContent({ contents: [{ role: "user", parts }], generationConfig, safetySettings, }); return result.response.candidates[0].content.parts[0].text; } /* I'm responsible for 'cleaning' up the text before sending to Google. For now, I'll just remove code blocks, but in the future I may remove images too. Also remove double blank lines. Oh, also remove FM. */ function cleanup(str) { str = str.replace(/```(.*?)```/sg, ''); str = str.replace(/---(.*?)---/sg, ''); str = str.replace(/\n{3,}/g, '\n'); return str.trim(); } (async () => { if(process.argv.length === 2) { console.log('Usage: summarysuggestions.j <<path to md>>'); process.exit(1); } const mdPath = process.argv[2]; if(!fs.existsSync(mdPath)) { console.log(`Unable to find ${mdPath}.`); process.exit(1); } let contents = fs.readFileSync(mdPath,'utf-8'); let title = fm(contents).attributes.title; // Make it nicer! contents = cleanup(contents); console.log(`\nGenerating summary suggestion for: ${title}`); console.log('------------------------------------------------------------------------'); let suggestion = (await runGenerate(title)); console.log(suggestion); })();
며칠 전 "생성 AI를 사용하여 이미지 파일 이름 개선" 게시물에 표시된 내용은 다음과 같습니다.
This post explores how Generative AI can be used to enhance image filenames, making them more descriptive, accurate, and consistent.
내 말은, 정말 딱 맞는 것 같아! 그리고 나는 그것에 대한 '목소리'에 대해 그렇게 걱정하지 않습니다. 나는 계속해서 이 게시물을 위해 그것을 사용했고(완료 후) 이것을 얻었고 사용했습니다.
생성 AI를 가상 글쓰기 도우미로 활용하여 콘텐츠 제작 프로세스를 향상하세요.
이것이 당신에게 관심이 있지만 내가 LEGO Death Star를 보면서 산책하는 모습을 보고 싶다면 아래 프레젠테이션을 시청하세요.
위에 표시된 두 스크립트는 모두 내 저장소에 있으며 여기 스크립트 디렉터리에서 찾을 수 있습니다: https://github.com/cfjedimaster/raymondcamden2023/tree/main/scripts
아래 댓글로 여러분의 생각을 알려주세요!