0
2

Gemini 1.5 Pro

2Mコンテキストに対応しているというGemini 1.5 Pro

どれだけの容量なのか・・・と調査してみるとOpenAIのHPが参考になりました。

You can think of tokens as pieces of words used for natural language processing. For English text, 1 token is approximately 4 characters or 0.75 words. As a point of reference, the collected works of Shakespeare are about 900,000 words or 1.2M tokens
To learn more about how tokens work and estimate your usage…

  • Experiment with our interactive Tokenizer tool(opens in a new window).
  • Log in to your account and enter text into the Playground. The counter in the footer will display how many tokens are in your text.

What’s a token? - OpenAI

シェイクスピアの作品集で1.2M。それをさばけるのですから恐ろしい量です。

大量の文字の処理となると何が挙げられるか。
自分が思いつくものは、先日からプレビューで実行できているPower AppsのYAMLコードです。

コードで可視化できることは便利ですが、全スクリーンのコードを抜き出すと容量が多くなります。

今回はPower AppsのテンプレートであるService Deskをもとにドキュメント作成ができないか、PythonGemini APIで試してみました。

image.png

Service Deskのトークン数

OpenAIのTokenizer ToolPower AppsのテンプレートのService DeskYAMLコードを入れてみると

  • Tokens 28,325
  • Characters 114,929

image.png

と出てきます。うーむ、案外ほかのモデルでも行けそうですね🧐
とりあえず今回はコチラの情報をGemini APIでさばいてみてみましょう。

Vertex AIでGemini APIを実行する

今回はGoogle Cloud PlatformVertex AIを使用します。
Vertex AIVertex AI Studioをクリックすると、Gemini 1.5 Proはスグに使うことができます。

image.png

事前に検証した結果

ほかのアプリで依存関係を表にしてほしい!というプロンプトを送った場合でも、Geminiは上手い具合に処理してくれていました。

ホントに恐るべしです。
これを更にAPIで実行出来たら夢が広がります。

Pythonで使用するために

Vertex AI Studioの右上にPythonのサンプルコードもあるため、入口は入りやすいです。

image.png

しかし、コードを試すために、まずはGoogle Cloud CLI が必要です。
cloud CLI をインストールするを参考にgcloud CLIを準備しましょう。

  • 環境変数にgcloud CLIが指定されない場合、コマンドからgcloud CLIを実行できません

完了後にVisual Studio Codeでフォルダを指定し、環境を設定します。

仮想環境の準備
python -m venv venv

環境を有効化して必要なライブラリのインストールと認証を実行します。

ライブラリのインストールと認証
pip install --upgrade google-cloud-aiplatform
gcloud auth application-default login

こちらをVisual Studio Codeのターミナルで実行し、認証を完了させます。
完了させると画面のように認証が完了したことを示す画面に遷移します。

image.png

Power Appsのドキュメントを作成する

今回はYAMLを使うため、pyyamlをインストールします。

pip install pyyaml
  • 実行ファイルと同一階層にYAMLファイルを格納するフォルダを設ける
    • 対象のフォルダのファイルをまとめて読み取る
  • Gemini APIを実行
  • 結果をMarkdownで返す

上記のシナリオで進めてみます。

コード

GPT-4oの力を借りまくって作成しています。
事前に環境変数の設定をしておいてください。

main.py
import os
import yaml
import vertexai
from vertexai.generative_models import GenerativeModel
import vertexai.preview.generative_models as generative_models
from pathlib import Path

# 環境変数の設定
project = os.getenv('PROJECT')
location = os.getenv('LOCATION')

# YAMLファイルの読み込み
def load_yaml(file_path):
    """
    指定されたファイルパスからYAMLファイルを読み込み、辞書として返す。
    もしYAMLエラーが発生した場合、もしくは辞書形式でない場合は
    ファイルの内容を単純なテキストとして辞書に格納して返す。

    :param file_path: 読み込むYAMLファイルのパス
    :return: YAML内容の辞書、もしくはテキスト内容を含む辞書
    """
    try:
        with open(file_path, 'r') as file:
            content = yaml.safe_load(file)
            if isinstance(content, dict):
                return content
            else:
                print(f"Warning: {file_path} did not contain a valid YAML dictionary. Treating as plain text.")
                return {'content': content}
    except yaml.YAMLError as e:
        print(f"YAML error in {file_path}: {e}")
        with open(file_path, 'r') as file:
            return {'content': file.read()}

# 複数のYAMLコンテンツを統合する関数
def merge_yaml_contents(yaml_contents):
    """
    複数のYAML内容を統合し、1つの辞書として返す。
    辞書でない内容は 'raw_content' キーに統合する。

    :param yaml_contents: YAML内容のリスト
    :return: 統合されたYAML内容の辞書
    """
    merged_content = {}
    for content in yaml_contents:
        if isinstance(content, dict):
            merged_content.update(content)
        else:
            merged_content['raw_content'] = merged_content.get('raw_content', '') + "\n" + content.get('content', '')
    return merged_content

# 依存関係を可視化する関数
def visualize_dependencies(yaml_content):
    """
    統合されたYAML内容をもとに依存関係を解析し、Markdown形式で返す。

    :param yaml_content: 統合されたYAML内容の辞書
    :return: 依存関係のMarkdown形式のテキスト
    """
    prompt = f"""
    Analyze the following Power Apps YAML configuration and visualize the dependencies between controls and functions. Highlight the dependencies clearly in Markdown format:
    ```yaml
    {yaml.dump(yaml_content)}
    ```
    Provide the dependencies in Markdown format.
    """

    vertexai.init(project=project, location=location)
    model = GenerativeModel("gemini-1.5-pro-001")
    response = model.generate_content([prompt], generation_config=generation_config, safety_settings=safety_settings)

    # `GenerationResponse`オブジェクトからテキストを取得
    markdown_output = response.candidates[0].content.parts[0].text

    return markdown_output

# Markdownをファイルに保存する関数
def save_markdown(file_path, content):
    """
    指定されたファイルパスにコンテンツをMarkdown形式で保存する。

    :param file_path: 保存するファイルのパス
    :param content: 保存するMarkdown形式のテキスト
    """
    with open(file_path, 'w') as file:
        file.write(content)

# "YAML"フォルダ内のすべてのYAMLファイルを処理し統合する関数
def process_yaml_files():
    """
    "YAML"フォルダ内のすべてのYAMLファイルを読み込み、統合し、依存関係を解析してMarkdownファイルに保存する。
    """
    current_dir = Path(__file__).parent
    yaml_folder = current_dir / 'YAML'
    yaml_files = yaml_folder.glob('*.yaml')
    
    # 各YAMLファイルを読み込み
    yaml_contents = [load_yaml(yaml_file) for yaml_file in yaml_files]
    # 読み込んだ内容を統合
    merged_yaml_content = merge_yaml_contents(yaml_contents)
    # 統合された内容から依存関係を可視化
    markdown_output = visualize_dependencies(merged_yaml_content)
    
    # Markdownファイルとして保存
    markdown_file_path = yaml_folder / 'merged_dependencies.md'
    save_markdown(markdown_file_path, markdown_output)
    print(f"Dependencies for merged YAML files saved to {markdown_file_path}")

# 依存関係の生成設定
generation_config = {
    "max_output_tokens": 8192,
    "temperature": 1,
    "top_p": 0.95,
}

# セーフティ設定
safety_settings = {
    generative_models.HarmCategory.HARM_CATEGORY_HATE_SPEECH: generative_models.HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
    generative_models.HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: generative_models.HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
    generative_models.HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: generative_models.HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
    generative_models.HarmCategory.HARM_CATEGORY_HARASSMENT: generative_models.HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
}

# メイン処理
if __name__ == "__main__":
    process_yaml_files()

実行前にproject-idlocationは一時的に環境変数として設定しています。

環境変数の設定
$env:PROJECT = "PROJECT_ID"
$env:LOCATION = "REGION"

単純にYAMLを読み込むとエラーが出るため、読み取れないファイルは、テキストとして読み取り、マージしています。

結果

非常に優秀な結果が出ました・・・(最後に記載しています)。
これもうAI使いまくって、本当にドキュメント作成を効率化したいですね。

依存関係の図式化の例もPPログさんが書かれています。

プロンプトを工夫すればテストケースもイケそう。

やってみるしかないですね。

Geminiの出力
## Power Apps Control & Function Dependencies Visualization

This Markdown document visualizes the dependencies between controls and functions within the provided Power Apps YAML configuration.

**Page:** TicketdetailsPage

**Controls and their Dependencies:**

- **TextBox5_57 (Label)**
    - **Text:** `EditRecord.DateCreated` (Data Source: EditRecord)
- **TextBox5_51 (Label)**
- **TextBox3 (Label)**
    - **Text:** `Today()` (Function)
    - **Visible:** `false`
- **Group21 (Group)**
    - **Children:**
        - **icon2_7 (Icon)**
            - **OnSelect:** 
                - `Navigate(PriorityPage, ScreenTransition.Fade, { priority: priority })` (Navigation, Data: priority)
        - **TextBox5_60 (Label)**
            - **Text:** `priority` (Data Source)
            - **OnSelect:** 
                - `Navigate(PriorityPage, ScreenTransition.Fade, { priority: priority })` (Navigation, Data: priority)
- **Group20 (Group)**
    - **Children:**
        - **icon2_6 (Icon)**
            - **OnSelect:** 
                - `Navigate(AreaPage, ScreenTransition.Fade, { Area: Area })` (Navigation, Data: Area)
        - **TextBox5_59 (Label)**
            - **Text:** `Area` (Data Source)
            - **OnSelect:** 
                - `Navigate(AreaPage, ScreenTransition.Fade, { Area: Area })` (Navigation, Data: Area) 
- **Group19 (Group)**
    - **Children:**
        - **icon2_5 (Icon)**
            - **OnSelect:** 
                - `Navigate(AssigntoPage, ScreenTransition.Fade, { assign: assign })` (Navigation, Data: assign)
        - **TextBox5_26 (Label)**
            - **Text:** `If(IsBlank(assign), "None", assign)` (Function, Data: assign)
            - **OnSelect:** 
                - `Navigate(AssigntoPage, ScreenTransition.Fade, { assign: assign })` (Navigation, Data: assign)
- **Group18 (Group)**
    - **Children:**
        - **icon2_4 (Icon)**
            - **OnSelect:** 
                - `Navigate(CasestatusPage, ScreenTransition.Fade, { type: type })` (Navigation, Data: type)
        - **TextBox5_25 (Label)**
            - **Text:** `type` (Data Source)
            - **OnSelect:** 
                - `Navigate(CasestatusPage, ScreenTransition.Fade, { type: type })` (Navigation, Data: type)
- **Rectangle7_14 (Rectangle)**
- **TextInput4 (TextInput)**
    - **Default:** `EditRecord.Description` (Data Source: EditRecord)
    - **DisplayMode:** `If(description_disabled, DisplayMode.Disabled, false, DisplayMode.View, DisplayMode.Edit)` (Function, Data: description_disabled)
    - **Fill:** `description_fill` (Data Source)
    - **Reset:** `textreset` (Data Source)
- **TextInput3 (TextInput)**
    - **Default:** `EditRecord.Comment` (Data Source: EditRecord)
    - **DisplayMode:** `If(commentdisabled, DisplayMode.Disabled, false, DisplayMode.View, DisplayMode.Edit)` (Function, Data: commentdisabled)
    - **Fill:** `commentfill` (Data Source)
    - **Reset:** `textreset` (Data Source)
- **TextInput2 (TextInput)**
    - **Default:** `EditRecord.Subject` (Data Source: EditRecord)
    - **DisplayMode:** `If(subjectdisabled, DisplayMode.Disabled, false, DisplayMode.View, DisplayMode.Edit)` (Function, Data: subjectdisabled)
    - **Fill:** `subjectfill` (Data Source)
    - **Reset:** `textreset` (Data Source)
- **Rectangle7_8 (Rectangle)**
- **TextBox5_56 (Label)**
    - **Text:** `EditRecord.ID` (Data Source: EditRecord)
- **TextBox5_50 (Label)**
- **TextBox5_58 (Label)**
    - **Text:** `EditRecord.Owner` (Data Source: EditRecord)
- **TextBox5_52 (Label)**
- **TextBox5_53 (Label)**
- **TextBox5_54 (Label)**
- **TextBox5_61 (Label)**
    - **Text:** `If(IsBlank(EditRecord.DateClosed), "--", EditRecord.DateClosed)` (Function, Data: EditRecord.DateClosed)
- **TextBox5_55 (Label)**
- **Button1 (Button)**
    - **DisplayMode:** `If(false, DisplayMode.View, DisplayMode.Edit)`
    - **OnSelect:**  (Complex function with dependencies on multiple controls & data)
        - `UpdateIf(TicketsCollect1, ID = EditRecord.ID, { Status: TextBox5_25.Text, AssignedTo: assign, DateClosed: "", AccountName: TextBox5_59.Text, Priority: TextBox5_60.Text, Subject: TextInput2.Text, Description: TextInput4.Text, Comment: TextInput3.Text })`
        - `UpdateIf(TicketsCollect1, ID = EditRecord.ID, { Status: TextBox5_25.Text, AssignedTo: assign, DateClosed: TextBox3.Text, AccountName: TextBox5_59.Text, Priority: TextBox5_60.Text, Subject: TextInput2.Text, Description: TextInput4.Text, Comment: TextInput3.Text })`
        - `Navigate(DashboardPage, ScreenTransition.Fade)` 
    - **Visible:** `If(type = "Completed", false, true)` (Data: type)
- **TextBox5_24 (Label)**
- **TextBox5_23 (Label)**
- **Rectangle7_7 (Rectangle)**
- **Rectangle7_6 (Rectangle)**
- **Rectangle7_5 (Rectangle)**
- **TextBox5_21 (Label)**
- **Rectangle7_4 (Rectangle)**
- **TextBox5_20 (Label)**
- **Rectangle7_3 (Rectangle)**
- **TextBox5_17 (Label)**
- **TextBox4_1 (Label)**
- **Rectangle1_1 (Rectangle)**
- **Rectangle5_7 (Rectangle)**
- **Rectangle1_14 (Rectangle)**
    - **OnSelect:** `If(type = "Completed", "", Navigate(AssigntoPage, ScreenTransition.Fade, { assign: assign }))` (Data: type, Navigation, Data: assign)
- **Group3 (Group)**
    - **Children:**
        - **icon4 (Icon)**
            - **OnSelect:** 
                - `UpdateContext({ textreset: false })` 
                - `UpdateContext({ textreset: true })`
                - `Navigate(DashboardPage, ScreenTransition.Fade)` 
        - **Rectangle1_3 (Rectangle)**
            - **OnSelect:** 
                - `UpdateContext({ textreset: false })`
                - `UpdateContext({ textreset: true })`
                - `Navigate(DashboardPage, ScreenTransition.Fade)` 

**Data Sources:**

- EditRecord
- priority
- Area
- assign
- type
- description_disabled
- description_fill
- commentdisabled
- commentfill
- subjectdisabled
- subjectfill
- textreset

**Functions:**

- Today()
- IsBlank()
- If()
- UpdateIf()
- Navigate()

This visualization shows the interconnectedness of controls and functions within the TicketdetailsPage. You can analyze how data is used, how user interaction (OnSelect events) triggers navigation or updates, and the overall flow of information within this screen. 

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2