AIコーディングの実力: Shiny for Pythonでの検証
LLMによるコード生成が広く用いられるようになってきました。
pythonから利用するケースがLLMでは多い前提で、pythonのフレームワークを対象としました。
フレームワークの選定
Streamlit は、手軽に始められるオープンソースのアプリケーションフレームワークです。
(原文: Streamlit is an open-source app framework that is a breeze to get started with. https://streamlit.io/)
自分で使ってみて、これには同意する部分がかなりあります。
しかしながら、前記のステートメントの範囲を超えると、フィットしないところが出てきました。
Streamlit は、ユーザー入力に変更があるたびに全体を再実行することによって、開発を単純化しています。この戦略は、最初期段階では効率的に機能しますが、スコープが拡大するにつれて、すぐに制約となってきます。
(原文: The idea of Streamlit is to simplify application development by rerunning the entire application script whenever any user input changes. This strategy leads to a great initial user experience, but quickly becomes constricting as your application grows in scope. https://shiny.posit.co/py/docs/comp-streamlit.html)
という指摘は、簡潔で的を射たものだと思っています。
この指摘をしているPositのフレームワークである、Shiny for Pythonを使ってみたいと思いました。
プログラムは生成AIに書いてもらうと、最初期段階も楽なのでは、という試みです。
最初に生成されたコード
次のプロンプトでコードの生成を依頼します。
shiny for pythonを使った、OpenAIのo3-miniをエンジンとしたチャットのプログラムを作成してください。
OPENAI_API_KEYは環境変数として定義済みです
返ってきたコードは次の通りです。
from shiny import App, ui, reactive
import openai
import os
# OpenAI APIキーを環境変数から取得
openai.api_key = os.getenv("OPENAI_API_KEY")
# UIの定義
app_ui = ui.page_fluid(
ui.h2("OpenAI Chat with o3-mini"),
ui.input_text("user_input", "Enter your message:", placeholder="Type your message here..."),
ui.output_text_verbatim("chat_response"),
ui.action_button("send", "Send")
)
# サーバーロジックの定義
def server(input, output, session):
@reactive.Effect
@reactive.event(input.send)
def handle_chat():
user_message = input.user_input()
if not user_message.strip():
output.chat_response.set("Please enter a valid message.")
return
try:
# OpenAI APIを使用して応答を取得
response = openai.Completion.create(
engine="o3-mini",
prompt=user_message,
max_tokens=150
)
output.chat_response.set(response.choices[0].text.strip())
except Exception as e:
output.chat_response.set(f"Error: {str(e)}")
# アプリケーションの作成
app = App(app_ui, server)
inputとoutputを明示的にわけて、デコレータを使って記述するのはわかりやすい印象です。
また、セッション変数やコールバック関数を書いていくよりも手間が少ないと感じました。
リアクティビティもミニマム(?)ではないかと。リアクティビティの詳細については、
チートシートに記載されています。
コードの修正
このコードを実行するにあたって、importされているパッケージをインストールします。
python3 -m pip install shiny openai
次のコマンドで実行すると、
shiny run app:app
エラーがでました。
(oaplatform) oaplatform $ shiny run app:app
Traceback (most recent call last):
File "/home/kit/act/oaplatform/bin/shiny", line 8, in <module>
sys.exit(main())
^^^^^^
File "/home/kit/act/oaplatform/lib/python3.12/site-packages/click/core.py", line 1161, in __call__
return self.main(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/kit/act/oaplatform/lib/python3.12/site-packages/click/core.py", line 1082, in main
rv = self.invoke(ctx)
^^^^^^^^^^^^^^^^
File "/home/kit/act/oaplatform/lib/python3.12/site-packages/click/core.py", line 1697, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/kit/act/oaplatform/lib/python3.12/site-packages/click/core.py", line 1443, in invoke
return ctx.invoke(self.callback, **ctx.params)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/kit/act/oaplatform/lib/python3.12/site-packages/click/core.py", line 788, in invoke
return __callback(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/kit/act/oaplatform/lib/python3.12/site-packages/shiny/_main.py", line 194, in run
return run_app(
^^^^^^^^
File "/home/kit/act/oaplatform/lib/python3.12/site-packages/shiny/_main.py", line 402, in run_app
uvicorn.run( # pyright: ignore[reportUnknownMemberType]
File "/home/kit/act/oaplatform/lib/python3.12/site-packages/uvicorn/main.py", line 580, in run
server.run()
File "/home/kit/act/oaplatform/lib/python3.12/site-packages/uvicorn/server.py", line 66, in run
return asyncio.run(self.serve(sockets=sockets))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/asyncio/runners.py", line 194, in run
return runner.run(main)
^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/asyncio/base_events.py", line 687, in run_until_complete
return future.result()
^^^^^^^^^^^^^^^
File "/home/kit/act/oaplatform/lib/python3.12/site-packages/uvicorn/server.py", line 70, in serve
await self._serve(sockets)
File "/home/kit/act/oaplatform/lib/python3.12/site-packages/uvicorn/server.py", line 77, in _serve
config.load()
File "/home/kit/act/oaplatform/lib/python3.12/site-packages/uvicorn/config.py", line 435, in load
self.loaded_app = import_from_string(self.app)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/kit/act/oaplatform/lib/python3.12/site-packages/uvicorn/importer.py", line 19, in import_from_string
module = importlib.import_module(module_str)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/importlib/__init__.py", line 90, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 995, in exec_module
File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
File "/home/kit/act/oaplatform/app.py", line 13, in <module>
ui.action_button("send", "Send")
^^^^^^^^^^^^^^^^
AttributeError: module 'shiny.ui' has no attribute 'action_button'
ui.action_buttonはないので、ui.input_action_buttonに修正すると画面を出力できました。
Positのサイト ( https://shiny.posit.co/py/api/core/ )によるとこちらが正でした。
ここで、生成AIについて教えてください、と入力してsendボタンを押すと、
エラーとなり、メッセージは、
Traceback (most recent call last):
File "/home/kit/act/oaplatform/app.py", line 28, in handle_chat
response = openai.Completion.create(
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/kit/act/oaplatform/lib/python3.12/site-packages/openai/lib/_old_api.py", line 39, in __call__
raise APIRemovedInV1(symbol=self._symbol)
openai.lib._old_api.APIRemovedInV1:
You tried to access openai.Completion, but this is no longer supported in openai>=1.0.0 - see the README at https://github.com/openai/openai-python for the API.
You can run `openai migrate` to automatically upgrade your codebase to use the 1.0.0 interface.
Alternatively, you can pin your installation to the old version, e.g. `pip install openai==0.28`
A detailed migration guide is available here: https://github.com/openai/openai-python/discussions/742
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/kit/act/oaplatform/lib/python3.12/site-packages/shiny/reactive/_reactives.py", line 584, in _run
await self._fn()
File "/home/kit/act/oaplatform/lib/python3.12/site-packages/shiny/_utils.py", line 279, in fn_async
return fn(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^
File "/home/kit/act/oaplatform/lib/python3.12/site-packages/shiny/reactive/_reactives.py", line 901, in new_user_fn
return user_fn()
^^^^^^^^^
File "/home/kit/act/oaplatform/app.py", line 35, in handle_chat
output.chat_response.set(f"Error: {str(e)}")
^^^^^^^^^^^^^^^^^^^^
AttributeError: 'Outputs' object has no attribute 'chat_response'
/home/kit/act/oaplatform/lib/python3.12/site-packages/shiny/reactive/_reactives.py:566: ReactiveWarning: Error in Effect: 'Outputs' object has no attribute 'chat_response'
await self._run()
Unhandled error: 'Outputs' object has no attribute 'chat_response'
INFO: connection closed
openaiのバージョンが上がって、インターフェースが変わったことが原因なので、
from openai import OpenAI
openai_client = OpenAI(
api_key=os.environ.get("OPENAI_API_KEY"),
)
を追加して、
response = openai_client.responses.create(
model="o3-mini",
input=input,
)
return response.output_text
の形式でメッセージを授受するようにします。
Shinyの書き方として、outputするオブジェクトを関数名にするので、
def chat_response():#handle_chat():
また、デコレータは@reactive.Effectではなく、@render.textが適切なため、renderもimport。
o3-miniも使えなくなっていたので、4.1-miniに変更して
# from shiny import App, ui, reactive
from shiny import App, ui, reactive, render
import openai
from openai import OpenAI
import os
# OpenAI APIキーを環境変数から取得
openai.api_key = os.getenv("OPENAI_API_KEY")
# UIの定義
app_ui = ui.page_fluid(
ui.h2("OpenAI Chat with 4.1-mini"),
ui.input_text("user_input", "Enter your message:", placeholder="Type your message here..."),
ui.output_text_verbatim("chat_response"),
# ui.action_button("send", "Send")
ui.input_action_button("send", "Send")
)
openai_client = OpenAI(
api_key=os.environ.get("OPENAI_API_KEY"),
)
# サーバーロジックの定義
def server(input, output, session):
#@reactive.Effect
@render.text
@reactive.event(input.send)
def chat_response():#handle_chat():
# user_message = input.user_input()
user_message = input.user_input.get()
if not user_message:
#if not user_message.strip():
# output.chat_response.set("Please enter a valid message.")
# return
return "Please enter a valid message."
try:
# OpenAI APIを使用して応答を取得
# response = openai.Completion.create(
# engine="o3-mini",
# prompt=user_message,
# max_tokens=150
# )
response = openai_client.responses.create(
#model="o3-mini",
model="gpt-4.1-mini",
input=user_message,
)
#output.chat_response.set(response.choices[0].text.strip())
return response.output_text
except Exception as e:
#output.chat_response.set(f"Error: {str(e)}")
return f"Error: {str(e)}"
# アプリケーションの作成
app = App(app_ui, server)
というプログラムが今回の着地です。
これを実行して、プロンプトを入力、sendボタンを押下した結果が次になります。
LLMの出力はマークダウン形式ですが、そのままテキストとして出力されています。
今回はここで終わりですが、パースしてきれいに出力していきたいところです。
おわりに
この試みでは、生成されたコードがそのまま使える、とはなりませんでしたが、
LLM以前と比べると、とても便利になったと感じています。
学習可能なコードの量が多いフレームワークやライブラリバージョンであれば、より手間のかからないコード生成が期待できそうです。
一方で、冒頭の記事中で、コードの半分をAIが書く、という記載がありました。
半分というのが行数の半分という意味だとすると、動かないコードが散らばっている場合は
うれしさも中くらいかもしれません。さらなる進化を期待です!
Discussion