WordCloudでChatGPTのチャット履歴を解析してみる

お疲れ様です。

会社の勉強会でChatGPTのチャット履歴を取得して解析をしました。 その内容を記事として残しておこうと思います。

全体観としては、取得したチャット履歴からユーザの入力内容のみを抽出し、その文書情報を使ってWordCloudを作成するというものになります。

WordCloudについて詳しくはこちら。
チャット履歴の入力内容の全体的な傾向をみることができ、実装もそれほど難しくないので採用しました。 aiacademy.jp



ChatGPTから履歴をエクスポート

ChatGPTのサイドバー下部のユーザの項目から、設定->データコントロール->エクスポートを順に選択します。その後登録したメールアドレスに履歴データのダウンロードリンクがついたメールが届くのでそこからダウンロードします。
(以前書いた記事 でも書いた方法と同じにはなります。)
export

詳細はこちらが参考になります。
www.goatman.co.jp

ダウンロードしたデータのうち、conversations.jsonを使用します。 このファイルにチャット履歴のデータが記録されています。
json

ソースコード

ソースコードはこちらにありますので、詳細はこちらをご確認ください。 なお、実際に勉強会で作成したコードを整理して再度作成したものになります。
github.com

コード解説

ベースとしてこちらの記事のコードを使用しています。
qiita.com

実装する場合下記のライブラリをインストールしてください。

pip install wordcloud janome matplotlib

コードの全体は以下のようになっています。

# ①
# conversations.jsonを読み込み
json_path = "./conversations.json"
with open(json_path, mode='r', encoding='utf-8') as f:
    json_contents = json.load(f)

# ユーザの入力内容のみを取得
text = ""
for json_content in json_contents:
    for chat_id in json_content["mapping"].keys():
        if json_content["mapping"][chat_id]["message"]:
            message = json_content["mapping"][chat_id]["message"]
            if message["author"]["role"] == "user":
                part = message["content"]["parts"][0]
                if isinstance(part, str):
                    text += part

# ②
# 正規表現を使った文字列の削除
text = re.sub(r"[^\w\s\u3000-\u9FFF]", "", text, flags=re.UNICODE)
# 文字列の正規化
text_norm = unicodedata.normalize('NFKC', text)

# 文書のtoken化(janomeで分かち書きする)
t = Tokenizer()
tokenized_text = t.tokenize(text_norm)

# 文字列のうち名詞のみを抽出
words_list=[]
for token in tokenized_text:
    tokenized_word = token.surface
    hinshi = token.part_of_speech.split(',')[0]
    hinshi2 = token.part_of_speech.split(',')[1]
    if hinshi == "名詞":
        if (hinshi2 != "数") and (hinshi2 != "代名詞") and (hinshi2 != "非自立"):
            words_list.append(tokenized_word)

# 抽出した単語のリストをスペース区切りの文字列に変換
words_wakachi = " ".join(words_list)

# ③
# 頻出単語を確認
for word_count in Counter(words_list).most_common():
    print(word_count)

# ストップワードの設定
stopwords = ["_", "u"]

# ④
# WordCloudを作成
word_cloud = WordCloud(
    font_path=r"C:\Windows\Fonts\BIZ-UDGOTHICB.TTC", 
    width=1500, height=900, stopwords=set(stopwords),
    min_font_size=5, collocations=False, background_color='white', max_words=400
)
word_cloud_img = word_cloud.generate(words_wakachi)

# 表示
figure = plt.figure(figsize=(15, 10))
plt.imshow(word_cloud_img)
plt.tick_params(labelbottom=False, labelleft=False)
plt.xticks([])
plt.yticks([])
plt.show()
figure.savefig("wordcloud.png")

①はconversations.jsonの読み込みの段階です。
チャット履歴のデータを含みますが構造がかなり複雑なので、ループを駆使してrole="user"のテキストのみを抽出しています。 ChatGPTで画像生成をさせている場合などにデータ構造が変化する場合があるのでそこは注意してください。

②はテキストの前処理の段階。下記の3つの処理を適用しています。
最終的に抽出した単語を半角スペース区切りで1つの文字列にしています。

  1. 記号を正規表現で除く処理

  2. 全角半角を統一するための正規化処理

  3. 分かち書きし名詞のみを抽出する処理

③ではWordCloud作成前の準備段階です。
単語を出現頻度順に並べて表示する処理と、ストップワードの設定の部分になります。 頻出単語の表示させ、意味のない単語があればストップワードのリストに追加するイメージです。 こちらで検討した際は、"_"と"u"があったのでストップワードに設定しています。(前処理の段階でうまく取り除けなかったようですね…。)

words

④でようやくWordCloudを作成しています。
インストールしたwordcloudのライブラリをインポートし、ここまでで作成したデータを投入すると画像として生成できます。 その後、matplotlibで画面表示させるような形になっています。

出力結果

出力結果が以下のようになりました。
私自身のチャット履歴を使用して出力してみたのですが、プログラム関係の内容をよく質問していることがわかります。
あと、「下記の内容について答えて」のような質問の仕方をよくするので「下記」という単語も頻出単語として出ています。 自分の質問の書き方の癖が可視化できるのも面白いなと思いました。

wordcloud

gpt-oss:20bでRAGを試してみた

お疲れ様です。

前回Ollamaを使用してgpt-oss:20bを動かしてみたのですが、ここまでできたらRAGも試してみたいということで実装してみました。

  • 前回の記事

fallpoke-tech.hatenadiary.jp

RAGの実装については以前GeminiAPIを使って実装したコードをベースに作成しています。 使用したベクトルDBも同様です。(データ自体は増えていますが。)
その時の記事と実装も併せてどうぞ。

fallpoke-tech.hatenadiary.jp

コード

早速にはなりますが、実際のRAGのコードを下記に記載します。

ソースコード全体はこちらにあります。 github.com

def response_generator_langchain_ollama_rag() -> str:
    """langchain_ollamaを使用+RAG
    """
    llm = OllamaLLM(model='gpt-oss:20b')
    
    # 直前のユーザの入力を取得
    user_input = st.session_state.messages[-1]["content"]
    
    # ベクトル化する準備
    model_kwargs = {
        "device": "cuda" if torch.cuda.is_available() else "cpu", 
        "trust_remote_code": True
    }
    embedding = HuggingFaceEmbeddings(
        model_name="pfnet/plamo-embedding-1b",
        model_kwargs=model_kwargs
    )
    
    # DBを読み込んで知識データ取得
    vectorstore = Chroma(collection_name="elephants", 
                         persist_directory="chat/chroma", 
                         embedding_function=embedding)
    docs = vectorstore.similarity_search(query=user_input, k=10)
    context = "\n".join([f"Content:\n{doc.page_content}" for doc in docs])
    
    messages = [
        ROLES[msg["role"]](content=msg["content"]) 
        for msg in st.session_state.messages[:-1]
    ] + [HumanMessage(content=RAG_PROMPT.format(question=user_input, context=context))]
    
    response = llm.invoke(messages)
    
    response_html = Markdown().convert(response)
    
    return response_html

下記、以前作成のGeminiAPI版との違いを中心に説明します。

  • モデルについて

モデルはOllamaを使用しているのでlangchain_ollamaOllamaLLMを使用します。 こちらは個別にpip installが必要です。

pip install langchain_ollama

参考: https://python.langchain.com/docs/integrations/llms/ollama/

実際の実行時にはollama serveでOllamaのサーバを立ち上げておきます。
今回はgpt-oss:20bを使用するので引数modelに指定します。

  • データについて

StreamlitでUIを簡易的に作成して表示しているので、チャット履歴のデータはst.session_state.messagesに格納されています。 データの形式は以下。

st.session_state.messages = [
  {"role": "user", "content": "..."},
  {"role": "assistant", "content": "..."},
]

このデータの形式をLangChainのモデル入力形式に変換して入力します。 直前のユーザの入力はベクトルDBの検索結果も含めてRAG用のプロンプトに埋め込み入力としています。

出力結果

出力結果としては以下になります。
表示が崩れているのはStreamlitの問題だと思います。(そろそろfrontendはReactでちゃんと作ろうかな…。)

output_gpt-oss

GeminiAPIとの比較

以前からベクトルDBのデータが増えているので比較用に同条件でGeminiAPI版も試してみました。 2025/8/10現在は無料版のAPIなのでモデルをgemini-2.5-flashに設定しています。

output_gemini

データが同じなので内容に大差はありませんが、gpt-ossの方が出力の情報を綺麗にまとめようとしているイメージです。 モデルごとの特性がでているようで面白いですね。

gpt-ossを動かしたくてOllama環境をDevcontainerで作成した、試した記録

お疲れ様です。 昨日(2025/8/6)にgpt-ossが発表されました。界隈がめちゃくちゃ盛り上がっていますね。

GPUのVRAMが16GBあれば一般のPCでも動かせる、その上かなりの高性能とのことで私もめちゃくちゃ気になっていたので試してみました。

環境について

OSがWindows11GPURTX4046tiのVRAM16GBです。
WSLを使用し、DockerおよびDevcontainerを立ち上げました。

gpt-ossの実行環境にはOllamaを使用しています。
HuggingFaceのモデルをPythonで動かすこともできるのですが、VRAM16GB以内で動かすには私のGPUでは要件を満たしておらず断念しました…。

Devcontainerの設定ファイルはGitHubに置いてあります。
ドライバのバージョンの兼ね合い等あるかもですが、これさえ使えば動かせるはず…!
以前作成したuv環境のDevcontainerをベースに作成したものなので、Pythonも使用可能です。

github.com

立ち上げ後の設定

コマンドラインでOllamaを設定、実行します。

  • サーバ立ち上げ
    ダウンロードや実行などの操作の際このサーバを立ち上げた状態にする必要があります。

ollama serve

serve

  • モデルダウンロード
    今回は目的の「gpt-oss:20b」を選択。他のモデルも指定可能です。

ollama pull gpt-oss:20b

pull

  • モデル確認
    ダウンロード済みのモデルの一覧表示ができます。

ollama list

list

  • 実行
    「gpt-oss:20b」を指定して実行します。

ollama run gpt-oss:20b

長かったので返答の全文は載せていません。
run

ollama-pythonでも試す

Pythonも使用可能な環境なのでollama-pythonも試しました。 ollama-pythonはDevcontainer立ち上げ時に自動でインストールされるはず。 ない場合は下記のコマンドでインストールしてください。

uv add ollama

  • コード
from ollama import chat
from ollama import ChatResponse

response: ChatResponse = chat(model='gpt-oss:20b', messages=[
  {
    'role': 'user',
    'content': 'ollamaとは何ですか?',
  },
])

print(response.message.content)
  • 実行結果

文字数が多かったのでGitHubにmdファイルで個別に保管しています。 全文を確認する場合は下記URLを参照ください。

https://github.com/muetaek0321/uv-ollama-devcontainer-template/blob/master/example.md

ollama-python

Pythonのデータのキャッシュ保存モジュール比較【備忘録】

お疲れ様です。

Pythonにはオブジェクトをキャッシュデータとしてファイル保存するモジュールがいくつかあります。 今回はそれらのモジュールをそれぞれ使用して比較してみたという内容です。

コードはこちらに残してあります。 github.com

  • 今回使用するデータについて

データについては、私が個人的に使用しているChatGPTからエクスポートした履歴データを使用しています。 このうちのチャット履歴データのjsonファイル(conversations.json)を読み込んで、このデータをキャッシュとして保存して比較しています。
ChatGPTの履歴のエクスポートについてはこちらが参考になると思います。
https://www.goatman.co.jp/media/chatgpt-export-data/

実際に使用するデータはこちらになります。 json



pickle

docs.python.org
Pythonの標準モジュールで、Pythonオブジェクトの保存では一番よく見るモジュールかなと思います。 有名なところだとPytorchのモデル保存(torch.save)でも使われています。
データをバイト列に変換してファイル保存(シリアライズ)します。

import pickle

# 保存
with open("conversations_cache.pickle", mode='wb') as f:
    pickle.dump(data, f)

# 読み込み
with open("conversations_cache.pickle", mode='rb') as f:
    data = pickle.load(f)

実際に保存したデータは以下になります。 元のjsonファイルと比較してもファイルサイズが小さくなっていることがわかります。 (1486KB -> 876KB) pickle

実行時間はこんな感じ。

  • 保存: 3.16 ms

  • 読み込み: 6.12 ms

shelve

docs.python.org Pythonの標準モジュールで辞書形式でデータを格納して保存するイメージです。 簡易的なデータベースとしても扱えます。 ちなみに、内部的にはpickleが使われているようです。

import shelve

# 保存
with shelve.open("conversations_cache.shelve") as db:
    db['data'] = data

# 読み込み
with shelve.open("conversations_cache.shelve") as db:
    data = db['data']

他とは違い3つのデータが保存されます。 .datのデータがpickleのファイルサイズと同じですね。 shelve

実行時間はこんな感じ。 pickleの処理+shelve特有の処理で多少実行時間が伸びている感じでしょうか。

  • 保存: 8.94 ms

  • 読み込み: 26.1 ms

joblib

joblib.readthedocs.io 一般的には処理の並列化に使われるライブラリですが、シリアライズの機能も持っています。 joblibのシリアライズは他と違いデータの圧縮の機能もあります。 下記コードのdumpの引数compressで圧縮率を設定できます。

こちらはサードパーティライブラリなのでpip等で個別にインストールする必要があります。

pip install joblib

import joblib

# 保存
joblib.dump(data, "conversations_cache.joblib", compress=3)

# 読み込み
data = joblib.load("conversations_cache.joblib")

出力ファイルが以下になります。 圧縮しているのでファイルサイズは他と比べても小さくなっていますね。 joblib

実行時間はこんな感じ。 処理時間については他のモジュールと比べて明確に遅いですね。

  • 保存: 92.0 ms

  • 読み込み: 44.2 ms

cloudpickle

github.com 基本機能はpickleと同じですが、関数やクラスなどpickleでは扱えないオブジェクトについても保存できます。

こちらもサードパーティライブラリなのでpip等で個別にインストールする必要があります。

pip install cloudpickle

import cloudpickle

# 保存
with open("conversations_cache.cloudpickle", mode='wb') as f:
    cloudpickle.dump(data, f)

# 読み込み
with open("conversations_cache.cloudpickle", mode='rb') as f:
    data = cloudpickle.load(f)

出力されたファイルが以下になります。 保存したのが同じデータなので容量的にもpickleとも変わらないですね。
cloudpickle

実行時間はこんな感じ。 今回のデータと環境では、保存はpickleと大差なく、読み込みはこちらの方が速いという結果でした。 ただ、一般的にはpickleの拡張ということもあってcloudpickleの方がやや遅いらしいです。

  • 保存: 2.99 ms

  • 読み込み: 3.42 ms

比較まとめ

最後に結果をまとめました。 実行時間については何度か実行しましたが結構ばらつきがあったので参考程度に。

元データ(json) 1486KB

モジュール サイズ[KB] 保存[ms] 読み込み[ms]
pickle 876 3.16 6.12
shelve 876 8.94 26.1
joblib 250 92.0 44.2
cloudpickle 876 2.99 3.42

今回4種試した中で個人的にはjoblibが一番いいかなと思いました。やっぱり圧縮機能が便利。

開発に関わるライセンス周りの個人的な知見まとめ【備忘録】

お疲れ様です。

仕事上で開発を行う際に付きまとうライセンス周りの問題について、個人的な知見もふくめてまとめておこうと思います。 (今後も追記する可能性ありです。)

以下2点ご注意ください。あくまで参考程度に考えてください。

  • ライセンス周りは私が調べた範囲内の話にはなるので間違いがあるかもしれません

  • 私の業務上の運用方法に近い話にはなりますので、詳しくは個々の会社の運用に従ってください



OSSライセンスについて

全体観としてはこちらの記事が参考になると思います。
www.hitachi-solutions.co.jp

基本的にはMITBSDApache-2.0 licenseを選べばトラブルは少ない。
GPL系は使用した場合ソースコードの開示が必要になるので、実際に使用する場合は注意が必要。
上記以外にも開発元が独自に設定したライセンスもあるので、内容はよく確認する必要があります。 (最近では機械学習のモデルアーキテクチャなどで商用利用不可のものがあったりする。)

確認方法

ソースコードと各プログラム言語のライブラリのOSSライセンスについて記載します。
ライブラリに関しては私がよく使うPythonやTypeScriptのもののみ記載。 他の言語でも同様に確認することはできると思います。

ソースコード

とりあえずGitHubリポジトリが無いか探すのが良いと思います。
「about」の項目に記載されているのと、LICENSEファイルを確認することもできます。 独自ライセンスの場合はLICENSEファイルの内容をよく確認した方が良いです。 github about license file 例:https://github.com/facebookresearch/Mask2Former

Python(pip, conda)

Pythonライブラリの場合はPyPIで確認することができます。
pypi.org
例えばNumpyならこんな感じ。GitHubのリンクもあるのでそちらを見てもOKです。 numpy pip
https://pypi.org/project/numpy/

condaの場合も同様に公式サイトからライブラリを探して確認できます。 anaconda.org numpy conda https://anaconda.org/anaconda/numpy

JavaScript/TypeScript(npm)

JavaScript/TypeScriptのライブラリの場合はnpmの公式を見ると良いと思います。
インストールのコマンドとしてyarnもありますが、インストールできるライブラリに違いは無いのでnpm公式を見れば確認可能です。
www.npmjs.com 例えばReactならこんな感じです。 react https://www.npmjs.com/package/react

特定のライブラリについての注意点

ライブラリとしては商用利用可能ばライセンスなのに、使用するライブラリのソースコードによっては別のライセンスがかかる場合があります。

例)HuggingFace(transformers) ライブラリのライセンス「Apache-2.0 license」ですが、使用するモデルアーキテクチャによっては別でライセンスがかかる場合があります。
ややこしいですが、よく確認しないと知らないうちにライセンス違反になることもあるので注意。

VisualStudio関係

基本的にはProfessional版を使うようにする。 会社の規模によっては制限はありますが無料のCommunity版を使うこともできるようです。
詳しくは下記のライセンス条項を確認してください。 visualstudio.microsoft.com

一方でC++ Build Toolsについては、一部無償で使用することができるようになっています。 一部のPythonライブラリやCUDAなどを使いたいときもこれだけインストールすれば使えます。
cpp build tool 下記が参考になります。ビルドツールのライセンス条項についても要チェック。 qiita.com

機械学習のデータセット

機械学習のデータセットの多くにもライセンスや条件が付与されています。
例えば、画像分類のデータセットで有名なImageNetは非営利目的のみとされています。 有名なデータセットであれば公式サイトがあり、そこに記載があると思うので確認しましょう。 imagenet www.image-net.org

HuggingFaceのDatasetsから利用する場合はデータセットのページを調べて確認することができます。 huggingface dataset https://huggingface.co/datasets/mrtoy/mobile-ui-design

特定のデータセットを使った事前学習モデル

上記のように非営利目的でしか使用できないデータセットを使った事前学習モデルは多く出回っています。 このようなモデルを営利目的で使用しても良いかは曖昧なところではあります…。
一応以下の論文や記事のような知見はありますので、参考にどうぞ。 https://www.jstage.jst.go.jp/article/alis/11/0/11_110/_pdf/-char/jawww.jstage.jst.go.jp note.com

【FastAPI】APIから別のAPIをリダイレクトで呼び出し(RedirectResponse)【備忘録】

お疲れ様です。

今回はAPIから別のAPIをリダイレクトで直接呼び出すような実装をしたので、その復習も兼ねてメモを書いておきます。

以前requestsモジュールを使ってAPIの処理の中で別のAPIを呼び出してその結果を処理の中で使うということもやったのですが、その時とは少しイメージが異なります。
自作APIとの通信をするPythonコードのメモ(requestsモジュール)

FastAPIでのリダイレクトについて

fastapi.tiangolo.com

FastAPIでリダイレクトをするときはこのRedirectResponseを使用します。 クライアントから呼び出したAPIから,さらに別の呼び出したいAPIのURLを指定して直接APIを呼び出し、そのAPIのレスポンスを自身のレスポンスとしてクライアントに返してくれます。

デフォルトでは307ステータスコード (Temporary Redirect) になります。 他のリダイレクトのステータスコードも指定できます。以下参考に。
qiita.com

今回はデフォルトの307ステータスコードのTemporary Redirectを扱います。 こちらについて詳しくは下記サイトが参考になります。
developer.mozilla.org

特徴としては以下の2点が挙げられるかなと思います。
- 一時的にURLを指定した別のURL変える
- HTTPメソッド(post,getなど)はそのまま保持する

コード

例によってソースコードGitHubに置いてありますので併せてご参考ください。 記事内ではRedirectResponseを使用したコードの説明のみとしますが、ソースコードの方には比較用に通常のAPI呼び出しも記載しています。
github.com

では、実際に動かしてみます。
下記で2つのエンドポイントを同時に立ち上げます。コマンドは1,2どちらでもOKです。

  • endpoint_1.py
     1. python endpoint_1.py
     2. uvicorn endpoint_1:app --reload --port 8000

  • endpoint_2.py
     1. python endpoint_2.py
     2. uvicorn endpoint_2:app --reload --port 8080

エンドポイント1の方からHTMLResponseで表示したクライアントを開きます。

http://localhost:8000/

いくつかボタンがあるうちの以下を使用します。
クライアント

<p>
  <h4><b>endpoint_1</b>から<b>endpoint_2</b>の画像取得APIを呼び出し</h4>
  <a href="http://localhost:8000/img_2" target="_blank">
    <button>画像ダウンロード</button>
  </a>
</p>

エンドポイント1経由でエンドポイント2の画像ダウンロードAPIを呼び出します。 それぞれのAPIが以下。エンドポイント1でRedirectResponseを使用しています。

  • エンドポイント1
api_url = "http://localhost:8080"

@app.get("/img_2")
def show_img_2() -> RedirectResponse:
    """ファイルをダウンロードするAPI
    """
    return RedirectResponse(api_url+"/img", status_code=307)
  • エンドポイント2
@app.get("/img")
def show_img() -> FileResponse:
    """ファイルをダウンロードするAPI
    """
    filepath = Path("./data/cat.png")
    
    return FileResponse(filepath, media_type="image/png", filename=filepath.name)

この設定でWeb画面の「画像ダウンロード」ボタンを押すと、エンドポイント2の"cat.png"がダウンロードされました。 ダウンロード

エンドポイント1のコマンドラインでは「307 Temporary Redirect」の表示があります。 endpoint_1

エンドポイント2ではしっかり"/img"のAPIが呼び出されています。 endpoint_2

PythonからGeminiCLIを呼び出していろいろ活用したかった

お疲れ様です。

最近はCLIのAIエージェントが流行っていますよね…! 私も「GeminiCLI」をよく使っています。ほぼ無料で使えるのが良い!

この「GeminiCLI」をPythonのコード上でうまく活用できないかいろいろと考えてみました。



Python上でコマンドを呼び出す方法

PythonCLIのコマンドを呼び出したい場合は標準モジュールの「subprocess」を使うのが一般的かなと思います。

docs.python.org

基本的にsubprocess.run()でコマンドを実行する形になります。 subprocess.Popenで別プロセスで呼び出すこともできますが、今回の場合は必要ないかなと思います。

import subprocess

command = ["dir", "/B"] # フォルダ内のファイルとフォルダを表示

# コマンドを実行します
result = subprocess.run(
    command,
    capture_output=True,
    text=True,
    check=True,
    encoding='cp932' if sys.platform == "win32" else "utf-8",
    shell=True 
)

作成したコード

作成したコードはこちらにあります。 ちなみに、READMEのファイルの説明部分はGeminiCLIで作成しました。 github.com

Python+GeminiCLIを試す

実際にGeminiCLIを使ってみます。
GeminiCLIの導入方法や使用可能なコマンド等はこちらがめちゃくちゃ参考になります。 zenn.dev

イメージとしては、Pythonで書いた処理の途中で特定の操作をGeminiCLIにやってもらう感じです。 具体的な活用方法は思いついていませんが、一旦は使えるかどうか試してみようと思います。

単純に「Gemini」コマンドを実行したときの挙動確認

結論から言うと、通常の「gemini」コマンドだと対話モードになり、立ち上がったエージェント上で質問内容を入力する必要があるので、今回やりたいこととしては使えなさそうです。(想定内です。)

まずは単純にcommand=["gemini"]で動かしてみましたが、動作せず。 標準出力にGeminiCLI起動時の表示が残っているのみでした。 ちなみにencoding='cp932'だと文字コードのエラーになりました。

command=["gemini"]

result = subprocess.run(
    command, capture_output=True, text=True, 
    check=True, encoding='utf-8', shell=True 
)

geminiコマンドのみ

次にcommand=["start", "gemini"]で動かしてみました。「start」は指定されたコマンドを実行するために別のウィンドウを表示するコマンドです。
こちらは別ウィンドウが開いていつも通りの操作ができるのですが、出力を取り出すといったことはできなさそうです。

command=["start", "gemini"]

result = subprocess.run(
    command, capture_output=True, text=True, 
    check=True, encoding='utf-8', shell=True 
)

start gemini

非対話モードを使ってみる

GeminiCLIの非対話モードはgemini --prompt [質問文]で実行できます。
結果的にはこちらが使えそうです。

コードとしてはこのように書いてみました。
質問文を外部ファイル(prompt.txt)に記載しそれを読み込み、必要があれば文字列のフォーマットで変数を埋め込み、改行文字を除いた後、gemini --prompt [質問文]の形でコマンド実行という感じです。

今回は作成したPythonファイル自体を説明させています。

  • コード
target = "execute_gemini.py"
    
# プロンプトを読み込み
with open("./prompt.txt", mode="r", encoding="cp932") as f:
    prompt_text = f.read()
    
# パスの指定を埋め込み
prompt_text = prompt_text.format(target=target)
# 改行を除く(改行コードまでの文字列しか参照されないため)
prompt_text = prompt_text.replace("\n", "")

print("--- 入力 ---")
print(prompt_text)

# 実行するCLIコマンド
command = ["gemini", "--prompt", prompt_text]

# コマンドを実行します
result = subprocess.run(
    command, capture_output=True, text=True, 
    check=True, encoding='utf-8', shell=True 
)
  • プロンプト
現在のフォルダ内にある{target}の内容を説明してください。

出力はこんな感じになりました。 output

所感

このように一応PythonでGeminiCLIを実行して結果を取得することができました。
ただ、ファイル作成など実行の許可を入力しないといけない場合などは、非対話モードではうまく動作できませんでした。何か方法はあるんですかね…? allow

結局これをどう活用するかはあまり良いアイデアは浮かびませんでした…。
普通にAIと対話するだけならAPIを使った方がいいので、CLIツールならではの使い方ができればなお良さそうです。
また、GeminiCLIについてもいろいろと勉強しないといけないですね…!