使用 Wordcraft 建構 AI 書寫助理

我們很喜歡故事。講故事和進行其他形式的創作,既有挑戰性又能帶來成就感。不過,從空白頁面開始建立自製故事,有時可能會讓人感到困惑,甚至不知從何下手。人工智慧 (AI) 生成式模型是能幫助你擺脫空白頁面,並建立敘事結構的絕佳工具。

本教學課程將說明如何擴充 Wordcraft,這是由 Google People and AI Research team 建構的 AI 技術輔助故事編寫工具。這個網頁應用程式會使用 Gemini API 協助你建構故事,透過產生點子、撰寫部分故事內容,以及修改內容來加入更多細節,逐步完成故事。您可以修改 Wordcraft,讓 Wordcraft 更符合您自己的寫作風格,並建立新的寫作控制項,以便更好地支援工作流程。

如要觀看有關專案的影片總覽和擴充方式,以及開發人員提供的洞察資料,請參閱「AI 寫作助理 - 使用 Google AI 技術建構」一文。否則,您可以按照下方說明開始擴充專案。

專案設定

本操作說明將逐步引導您設定 Wordcraft 專案,以便進行開發和測試。您需要安裝必要軟體、從程式碼存放區複製專案、執行設定安裝作業,以及設定幾個環境變數。完成這些步驟後,您可以執行專案來測試設定。

安裝必要條件

Wordcraft 專案會使用 Node 和 npm 管理套件並執行應用程式。以下安裝說明適用於 Linux 主機。

如何安裝必要軟體:

複製及設定專案

請下載專案程式碼,並使用 npm 安裝指令下載必要的依附元件,然後設定專案。您需要使用 git 原始碼管控軟體,才能擷取專案原始碼。
如要下載及設定專案程式碼,請按照下列步驟操作:

  1. 使用下列指令複製 Git 存放區。
    git clone https://github.com/PAIR-code/wordcraft
    
  2. 前往 Wordcraft 專案根目錄。
    cd wordcraft/
    
  3. 執行安裝指令,下載依附元件並設定專案:
    npm install
    

設定環境變數

設定 Wordcraft 程式碼專案執行所需的環境變數,特別是 Google Gemini API 金鑰。以下安裝操作說明適用於 Linux 主機。

如要設定環境變數,請按照下列步驟操作:

  1. 取得 Google Gemini API 金鑰,然後複製金鑰字串。
  2. 前往 Wordcraft 專案的根目錄。`
    cd wordcraft/
    
  3. 將 API 金鑰設為環境變數。在 Linux 主機上,請使用下列指令。
    touch .env
    echo "API_KEY="<YOUR_API_KEY>"" > .env
    

測試設定

您現在應該可以透過在裝置上執行 Wordcraft 來測試專案設定。這是選擇性步驟,但建議您採取。

Wordcraft 開啟畫面

如何測試安裝和設定:

  1. 前往 Wordcraft 專案根目錄。
    cd wordcraft/
    
  2. 在開發模式中透過它執行專案:
    npm run dev
    
  3. 在網路瀏覽器中前往 Wordcraft 使用者介面。特定地址會顯示在先前指令的輸出內容中,例如:
    http://localhost:3000/
    

修改提示範例文字

Wordcraft 指令使用者介面 Wordcraft 會根據每項寫作輔助作業的範例,為 AI 生成式模型建立提示,例如「start new story」和「generate text」指令。這些範例可引導生成式模型為故事產生文字,您可以修改操作的範例,讓輸出內容採用不同的模式或寫作風格。這種做法是讓 Wordcraft 以您想要的方式編寫的簡單方法。

以下範例說明如何修改 Wordcraft 中的 new_story 範例。這項修改的目標是指示 AI 生成模型使用內心獨白方法撰寫故事開頭,並採用更適合懸疑小說的風格。只要撰寫幾個這類故事的開頭,就能讓生成式模型遵循一般模式,產生不同主題的開頭。

如要修改 Wordcraft 中的新故事範例,請按照下列步驟操作:

  1. 開啟 wordcraft/app/context/json/new_story.json 檔案。
  2. 修改範例,同時保留 JSON 檔案的整體結構。以下是使用內心獨白風格修改的懸疑小說開頭範例。
    [
      {
        "topic": "scientist disappears and their research into a new technology is gone",
        "target": "I got the call from the chief early Tuesday morning, before I'd even had a second sip of coffee. Terrible timing. Something about a researcher disappearing from the local university. Unusual for the research lab to ask for assistance, so I headed over to main lab building."
      },
      {
        "topic": "a young woman wakes up with no memory",
        "target": "An unfamiliar ceiling with harsh, white lights greeted my eyes as I opened them. I looked around. White walls, medical equipment, a hospital? Wait. Where am I? How did I get here?!"
      },
      {
        "topic": "old man tries to recall an important task as his memories gradually fade away",
        "target": "What was I supposed to do today? Feels like it was important. I stared into the kitchen cabinet full of mismatched mugs, mirroring my own confusion. What was it? Like someone is...in danger? A chill shot down my spine, but the details skittered off and hid in some dark corner of my head."
      },
      {
        "topic": "billionaire is found dead in a hotel room",
        "target": "People meet their end every day, some naturally, some unnaturally. After 17 years of working as a homicide detective in Seattle, I'd seen a lot more of the unnatural variety than most. Comes with the job, along with a hard-learned sense of what side of the line any given incident sat on. This...was murder."
      },
      {
        "topic": "retired covert operative gets dragged back into an old mission",
        "target": "Steam rose gently off the cup of Earl Grey sitting in front of me as I sat at the cafe, pedestrians and light traffic rolling by. The city was slowly waking up around me and my perfect Paris morning routine was shaping up nicely. Then I noticed that old familiar and unwelcome tingling on the back of my neck. I was being watched."
      }
    ]
  3. 將變更儲存至 `new_story.json` 檔案。

如要測試經過修改的新故事作業,請按照下列步驟操作:

  1. 前往 Wordcraft 專案的根目錄。
    cd wordcraft/
    
  2. 在開發模式下執行專案。如果應用程式已在執行,您可能需要停止應用程式並重新啟動。
    npm run dev
    
  3. 在網路瀏覽器中前往 Wordcraft 使用者介面。特定地址會顯示在先前指令的輸出內容中,例如:
    http://localhost:3000/
    
  4. 前往 Wordcraft 主選單,然後選取「Start a New Story」
  5. 更新新故事提示或變更為您想要的內容,然後選取「開始新故事」

您可以使用這項技巧修改 Wordcraft 中所有現有的劇本編寫控制項。嘗試更新 wordcraft/app/context/json/ 目錄中的範例,變更其他故事控制項。

建立新的書寫控制項

Wordcraft 推出字元使用者介面 Wordcraft 應用程式可擴充,因此您可以新增寫作控制項來協助自己,類似於應用程式右側「控制項」分頁中的「產生文字」或「重寫句子」按鈕。雖然進行這些修改需要花費更多心力,但您可以根據自己的工作流程和目標調整 Wordcraft 的功能。

以下範例修改會為 Wordcraft 建立新的字元控制項。您可以使用此元素,在故事中介紹新角色,並說明該角色的屬性。這個控制項的基礎與其他 Wordcraft 控制項相同,例如前面討論的「start new story」控制項。您可以建立 JSON 檔案,其中包含幾個範例,說明您希望如何引入字元。其餘的變更則是新增使用者介面和 AI 提示管理功能。

可建立樣本

請寫幾個範例,說明您希望生成式模型如何介紹角色。舉例來說,您想以旁白的方式描述角色,還是想透過主角的經歷介紹角色?以下範例採用後者做法,從主角的角度介紹新角色。您可以使用新的 JSON 檔案新增這些範例:

如要為新控制項新增範例:

  1. 建立 wordcraft/app/context/json/new_character.json 檔案。
  2. 在 JSON 檔案中建立範例。在這個範例中,每個範例都包含一個 character 說明欄位,代表提示文字,以及一個 target 欄位,顯示預期輸出內容。
    [
      {
        "character": "A character who is helpful and modest.",
        "target": "\"You lost, buddy?\" came a voice from behind me. Turning, I discovered a man dressed in a simple but presentable outfit. Small signs of age and loose threads hinted that these clothes, and the man himself, had seen better days."
      },
      {
        "character": "A character who is attractive and devious.",
        "target": "Stepping out of the alley a little too quickly, I collided with something solidly muscular and surprisingly delicately scented. \"Sorry.\" I managed, regaining my balance. \"Easy there, buddy, you're gonna hurt yourself,\" came the reply from a man with an almost feline grace, further reinforced by a stare that reminded me of a hunting cat assessing its potential prey."
      },
      {
        "character": "A character who is old and hesitant.",
        "target": "\"Excuse me. Do you know the way to the train station from here?\" I looked up from my phone to see a elderly woman in a threadbare coat, purse clutched with two hands in front of her. \"I-I'm supposed to meet my nephew there. Do... do you think you can help me?\""
      },
      {
        "character": "A character who is intelligent and aloof.",
        "target": "Bookish. That was my immediate reaction to this person I now saw in front of me. \"You're finally here. Did you read the notes I sent you?\" The voice sat squarely in between feminine and masculine intonation. \"No, of course you didn't.\" Dismissing my answer before I'd even formulated one. Annoyance immediately flushed through me."
      },
      {
        "character": "A character who is clumsy and energetic.",
        "target": "\"Whoa!\" was the only warning I had before someone slammed into my back, almost knocking me off my feet. \"I'm so sorry! WOOO! These skates are a RUSH!\" The apology came from a rather loud redhead wearing rollerblades, dark glasses and a very beefy-looking pair of headphones. That explained the volume of the apology."
      }
    ]
  3. 將變更儲存至 new_character.json 檔案。

建立範例後,請修改 app/context/schema.tsindex.ts 檔案,以反映這個新字元控制項的提示內容。

如要將範例新增至 schema.ts 檔案,請按照下列步驟操作:

  • 修改 wordcraft/app/context/schema.ts 檔案,加入新的字元範例資料結構。
    export const newStorySchema = z.object({
      topic: z.string(),
      target: z.string(),
    });
    
    // add the following:
    export const newCharacterSchema = z.object({
      character: z.string(),
      target: z.string(),
    });

定義與這些新範例相關聯的作業類型。這類新類型可將提示範例連結至使用者介面和提示建構程式碼,您會在後續步驟中修改這些程式碼。

如何建立新的作業類型

  • 修改 wordcraft/app/core/shared/types.ts 檔案,新增字元運算類型。
    export const enum OperationType {
      ...
      NEW_CHARACTER = 'NEW_CHARACTER', // add to list of types
      ...
    }

如要在 index.ts 檔案中註冊示例,請按照下列步驟操作:

  1. wordcraft/app/context/index.ts 檔案中匯入新結構定義。
    import {
      continueSchema,
      ...
      newCharacterSchema // add new schema
    } from './schema';
  2. 將新的 JSON 檔案匯入為 newCharacterJson
    import newCharacterJson from './json/new_character.json';
  3. 在應用程式內容中註冊新的字元範例內容。
    export class WordcraftContext {
      constructor() {
      ...
        this.registerExamples(
          OperationType.NEW_CHARACTER,
          newCharacterSchema,
          newCharacterJson
        );
      ...
    }
  4. 匯出 NewCharacterExample 類型。
    export type NewCharacterExample = z.infer<typeof newCharacterSchema>;

建構使用者介面

建立並註冊內容產生範例後,您就可以為新控制項建立使用者介面。這個階段的主要工作是建立新的作業類別,然後將該類別註冊至 Wordcraft 應用程式的主程式碼。

如要建立新作業,請按照下列步驟操作:

  1. wordcraft/app/core/operations/ 目錄中,使用現有運算類別之一做為範本,建立新的運算類別。針對新的字元控制項,您可以複製 new_story_operation.ts 類別,並將其重新命名為 new_character_operation.ts
  2. 為類別命名,並藉由定義至少一個 OperationSite 值,指定控制項在使用者介面中顯示的時間。
    export class NewCharacterOperation extends ChoiceOperation {
      static override isAvailable(operationSite: OperationSite) {
        return (
          operationSite === OperationSite.END_OF_SECTION ||
          operationSite === OperationSite.EMPTY_SECTION
        );
      }
  3. 設定作業的 id
      static override id = OperationType.NEW_CHARACTER;
  4. 更新 getrun 函式,以反映結構定義參數的值。這段程式碼會處理從使用者介面取得提示文字,以便在 AI 提示中使用。
      private get character(): string {
        return NewCharacterOperation.controls.character.value;
      }
    
      async run() {
        const params = { character: this.character };
        const choices = await this.getModel().newCharacter(params);
    
        this.setChoices(choices);
      }
  5. 更新使用者介面文字和說明。
      static override getButtonLabel() {
        return 'introduce character';
      }
    
      static override getDescription() {
        return 'Introduce a new character at the cursor.';
      }
    
      static override controls = {
        character: new TextareaControl({
          prefix: 'prompt',
          description: 'A prompt to introduce a new character.',
          value: 'A new character.',
        }),
      };

如要在 Wordcraft 應用程式中註冊新作業,請按照下列步驟操作:

  1. wordcraft/app/core/operations/index.ts 檔案中,新增新作業的匯入作業。
    import {NewCharacterOperation} from './new_character_operation';
  2. 在同一個 index.ts 檔案中,為 NewCharacterOperation 類別新增匯出作業。
    export {
      ...
      NewCharacterOperation, // add this class
      ...
    };
  3. wordcraft/app/main.ts 檔案中,註冊新的作業。
    const operationsService = wordcraftCore.getService(OperationsService);
    operationsService.registerOperations(
      ...
      Operations.NewCharacterOperation, // add new operation
      ...
    );

建立提示處理程序

在建立新控制項的最後階段,您會建立程式碼,用於處理為 AI 生成式模型產生提示,以及處理回應。這項工作的重點是在 wordcraft/app/models/gemini/prompts/ 目錄中建立提示處理常式,該常式會從使用者介面取得輸入內容,並組合要傳遞給生成式模型的提示。

如要為提示參數定義介面,請按照下列步驟操作:

  • wordcraft/app/core/shared/interfaces.ts 檔案中,新增介面,用於顯示新作業提示參數。
    export interface NewCharacterPromptParams {
      character: string;
    }

如要定義新作業的提示處理程序,請按照下列步驟操作:

  1. wordcraft/app/models/gemini/prompts/ 目錄中,使用現有作業類別之一做為範本,建立新的提示處理常式類別。針對新的字元控制項,您可以複製 new_story.ts 類別,並將其重新命名為 new_character.ts,做為起點。
  2. 定義提示處理程序函式,並匯入 NewCharacterExample 類別。
    import { NewCharacterPromptParams } from '@core/shared/interfaces';
    import { NewCharacterExample, WordcraftContext } from '../../../context';
    import { OperationType } from '@core/shared/types';
    import { GeminiModel } from '..';
    
    export function makePromptHandler(model: GeminiModel, context: WordcraftContext) {
      ...
    }
  3. 建構 generatePrompt() 函式,取得 AI 模型提示的使用者介面輸入內容。
      function generatePrompt(character: string) {
        const prefix = "Here's a character description: ";
        const suffix = "Introduce this character in the story.";
    
        if (character.trim() === '') {
          return 'Introduce a new character to the story.';
        } else {
          return `${prefix}${model.wrap(character)}\n${suffix}`;
        }
      }
  4. 建立 getPromptContext() 函式,將使用者介面輸入內容與範例回應組合,並建立完整提示。
      function getPromptContext() {
        const examples = context.getExampleData(
          OperationType.NEW_CHARACTER
        );
        let promptContext = model.getPromptPreamble();
        examples.forEach((example) => {
          const { character, target } = example;
          const prompt = generatePrompt(character);
          promptContext += `${prompt} ${model.wrap(target)}\n\n`;
        });
        return promptContext;
      }

如要整合新的字元提示處理常式,請按照下列步驟操作:

  1. wordcraft/app/models/gemini/index.ts 檔案中,匯入新字元作業的提示處理常式。
    import {makePromptHandler as newCharacter} from './prompts/new_character';
  2. newCharacter 提示處理常式新增覆寫定義。
      override newCharacter = this.makePromptHandler(newCharacter);

如要將提示參數與模型定義登錄:

  1. wordcraft/app/models/model.ts 檔案中,為新的 NewCharacterPromptParams 介面新增匯入內容。
    import {
      ...
      NewCharacterPromptParams,
      ...
    } from '@core/shared/interfaces';
  2. newCharacter 提示參數新增至模型類別。
      async newCharacter(params: NewCharacterPromptParams): Promise<ModelResults> {
        throw new Error('Not yet implemented');
      }

測試新書寫控制項

您應該可以透過 Wordcraft 介面測試新控制項。請務必先檢查程式碼是否有編譯錯誤,再繼續操作。

如要測試新的角色控制項,請按照下列步驟操作:

  1. 前往 Wordcraft 專案的根目錄。`
    cd wordcraft/
    
  2. 在開發模式下執行專案:
    npm run dev
    
  3. 在網路瀏覽器中前往 Wordcraft 使用者介面。特定地址會顯示在先前指令的輸出內容中,例如:
    http://localhost:3000/
    
  4. 在 Wordcraft 應用程式中,建立新故事或開啟現有故事。
  5. 在故事編輯區域中,將游標移至故事結尾。右側的「控制項」分頁中應會顯示「引入角色」控制項。
  6. 在「introduce character」欄位中,輸入新角色的簡短說明,然後選取「introduce character」按鈕。

其他資源

如要進一步瞭解 Wordcraft 專案,請參閱程式碼存放區。您可以在這個提取要求中查看本教學課程中所述的變更。

正式版應用程式

如果您打算為大量使用者部署自訂版 Wordcraft,請注意,您使用 Google Gemini API 時可能會受到速率限制和其他使用限制。如果您考慮使用 Gemini API (例如 Docs Agent) 建構正式版應用程式,請參閱 Google Cloud Vertex AI 服務,進一步提升應用程式的可擴充性和可靠性。