OpenAIのサム・アルトマンCEOが自身のXアカウントに「o2がGPQAで105%のスコアを達成したと聞いた」と2024年11月3日(日)に投稿しました。「o2」の正式名称は不明ですが、OpenAIが開発中の次世代AIモデルが驚異的な性能を備えている可能性が濃厚となっています。
GPQAはAIの性能を測定するベンチマークの1種で、生物学・物理学・化学の専門家が作成した448問の選択問題で構成されています。GPQAの問題はかなりの高難度で、「専門家でない人間」がGoogle検索を駆使して挑んだ場合は34%、「博士号保持者または博士号の取得を目指す学生」の場合も65%のスコアしか獲得できません。
⇧ う~ん...
目指して欲しい方向が異なる気がするんよね...
選択問題のような事前に回答が明確な問題に対してではなく、回答が分かっていないような問題に対して、
- 回答に至った根拠
- 回答に参考にした一次情報
を明確にしていないのであるなら、「幻覚(ハルシネーション)」による虚偽が行われている可能性があるのであって、そのあたりを解決して欲しいんよね...
実業務におけるAIの利用で求めらているのは、そういうことな気がしますけど...
学術研究の域から出ていないんよね...
所謂、「概念実証(PoC:Proof of Concept)」を永久に続けている感じで、リリースのレベルになってはいないけど、リリースしてしまっている感じかな...
結局のところ、妥協点を決めるしかないわけだとは思うのだけど...
Gitのローカルのbare repositoryをgit diffし、変更の検知をシェルスクリプトで試す
本ブログで何回か話に出てきている
⇧「Oxidized」というRuby製のライブラリに触れる機会があり、「Oxidized」の内部で「Rugged」というライブラリが利用されている関係上、「Output」で「Git」を選択している場合、「Git」のローカルの「bare repository」が作成されますと。
で、「Oxidized」が、
- 「対象機器」の「設定情報」の取得に成功
- 「対象機器」の「設定情報」の取得に失敗
どちらの状態になっているのかを確認したいとした場合に、本当であれば、
- 最新のファイル
- 1つ前のファイル
の比較のようなことをしたいのだけど、「Git」の「bare repository」はファイルシステム上にファイルという形でデータを保持せず、「Git」の「bare repository」上の「object」という形で保存してしまっているので、通常の「シェル」に用意されているコマンドでのファイルの比較ができませんと。
一応、
■https://github.com/ytti/oxidized/blob/master/lib/oxidized/worker.rb
module Oxidized require 'oxidized/job' require 'oxidized/jobs' class Worker def initialize(nodes) @jobs_done = 0 @nodes = nodes @jobs = Jobs.new(Oxidized.config.threads, Oxidized.config.use_max_threads, Oxidized.config.interval, @nodes) @nodes.jobs = @jobs Thread.abort_on_exception = true end def work ended = [] @jobs.delete_if { |job| ended << job unless job.alive? } ended.each { |job| process job } @jobs.work while @jobs.size < @jobs.want Oxidized.logger.debug "lib/oxidized/worker.rb: Jobs running: #{@jobs.size} of #{@jobs.want} - ended: #{@jobs_done} of #{@nodes.size}" # ask for next node in queue non destructive way nextnode = @nodes.first unless nextnode.last.nil? # Set unobtainable value for 'last' if interval checking is disabled last = Oxidized.config.interval.zero? ? Time.now.utc + 10 : nextnode.last.end break if last + Oxidized.config.interval > Time.now.utc end # shift nodes and get the next node node = @nodes.get node.running? ? next : node.running = true @jobs.push Job.new node Oxidized.logger.debug "lib/oxidized/worker.rb: Added #{node.group}/#{node.name} to the job queue" end if cycle_finished? run_done_hook exit 0 if Oxidized.config.run_once end Oxidized.logger.debug("lib/oxidized/worker.rb: #{@jobs.size} jobs running in parallel") unless @jobs.empty? end def process(job) node = job.node node.last = job node.stats.add job @jobs.duration job.time node.running = false if job.status == :success process_success node, job else process_failure node, job end rescue NodeNotFound Oxidized.logger.warn "#{node.group}/#{node.name} not found, removed while collecting?" end def reload @nodes.load end private def process_success(node, job) @jobs_done += 1 # needed for :nodes_done hook Oxidized.hooks.handle :node_success, node: node, job: job msg = "update #{node.group}/#{node.name}" msg += " from #{node.from}" if node.from msg += " with message '#{node.msg}'" if node.msg output = node.output.new if output.store node.name, job.config, msg: msg, email: node.email, user: node.user, group: node.group node.modified Oxidized.logger.info "Configuration updated for #{node.group}/#{node.name}" Oxidized.hooks.handle :post_store, node: node, job: job, commitref: output.commitref end node.reset end def process_failure(node, job) msg = "#{node.group}/#{node.name} status #{job.status}" if node.retry < Oxidized.config.retries node.retry += 1 msg += ", retry attempt #{node.retry}" @nodes.next node.name else # Only increment the @jobs_done when we give up retries for a node (or success). # As it would otherwise cause @jobs_done to be incremented with generic retries. # This would cause :nodes_done hook to desync from running at the end of the nodelist and # be fired when the @jobs_done > @nodes.count (could be mid-cycle on the next cycle). @jobs_done += 1 msg += ", retries exhausted, giving up" node.retry = 0 Oxidized.hooks.handle :node_fail, node: node, job: job end Oxidized.logger.warn msg end def cycle_finished? if @jobs_done > @nodes.count true else @jobs_done.positive? && (@jobs_done % @nodes.count).zero? end end def run_done_hook Oxidized.logger.debug "lib/oxidized/worker.rb: Running :nodes_done hook" Oxidized.hooks.handle :nodes_done rescue StandardError => e # swallow the hook erros and continue as normal Oxidized.logger.error "lib/oxidized/worker.rb: #{e.message}" ensure @jobs_done = 0 end end end
⇧ 成功した場合は、ログを出力してくれるようにはなっているのだけど、何をもって成功と判定されるのかが、jobのステータスで判断されるっぽいのだけど、jobのステータスがどういう条件で設定されるのかがハッキリしませんと。
失敗した場合も、ログ出力されるようなのだけど、jobのステータスがどういう条件で設定されるかは成功の時と同じく分からないんよね...
話が脱線しましたが、通常の「シェル」のコマンドでファイルの比較ができない話に戻ると、どういうことかというと、Wikipediaによりますと、
Data Structure
The object store contains five types of objects:
- A blob is the content of a file. Blobs have no proper file name, time stamps, or other metadata (a blob's name internally is a hash of its content). In Git, each blob is a version of a file, in which is the file's data.
Some data flows and storage levels in the Git revision control system
⇧ とありますと。
で、「Git」のリポジトリの構成については、
⇧ 上記サイト様にありますように、何種類か用意されており、「git clone」の際のオプションによって異なりますと。
で、一般的に、「GitHub」のリモートリポジトリから「git clone」する時って、オプションとか何も付けずに行う感じになると思うので、上記サイト様の図で言うところの「normal」の構成になっているのが普通ですと。
対して、「bare repository」は「working tree」にあたる部分がないので、
No | Gitのリポジトリのタイプ | working tree | GIT DIR |
---|---|---|---|
1 | normal | あり | あり |
2 | bare | なし | あり |
⇧ のような違いがありますと。
ちなみに、
⇧「Git」には、環境変数「GIT_DIR」というものが存在するらしく、デフォルトだと「GIT_DIR」に何も設定されていないからなのか、「.git」ディレクトリとして作成される模様。
で、GitHubの公式のブログによると、
In your local Git repositories, your data is stored in the .git
directory. Inside, there is a .git/objects
directory that contains your Git objects.
https://github.blog/open-source/git/gits-database-internals-i-packed-object-store/
⇧ とあり、「GIT DIR」に該当する「.git」ディレクトリ配下には「objects」ディレクトリがあり、そこに「object」という形でデータが保存されてますと。
つまり、「Git」で用意されているコマンドで操作する想定になっていますと。
で、一般的な「シェル」については、
Basic shell architecture
Figure 2. Simple architecture of a hypothetical shell
In the Resources section, you can find links to learn about the architecture of the open source Bash shell.
⇧ 上記のような感じで、デフォルトで「ls -la」とかのコマンドを利用できるのだが、こやつらは、通常の「ファイル」に対しての利用を想定していますと。
で、「Git」の「bare repository」は「workig tree」を持たないので、通常の「ファイル」の形でデータを保持せず、「GIT DIR」の「object」としてしかデータを保持していないと。
少なくとも、「Oxidized」の「Rugged」の利用の仕方では、「ファイル」の形ではデータを保持していない。
というのも、
⇧ issueで、議論が上がっていたので。
話の脱線が長くなりましたが、ということで、表題の件の話になりますと。
ポイントは、
⇧ bare repositoryの場合「commit hash」でしか比較できないってことですかね。
あとは、
git diff
にquietオプションを指定すると、戻り値で判定できる。
差分がなければ0が返り、あれば1が返る。
⇧ 変更があったかどうかだけであれば、上記サイト様にありますように、「git diff」のオプションで「--quiet」というものを付けてあげるということでしょうか。
公式のドキュメントを見てみたけども、
⇧「Git」で用意されている定数っぽいものが返却されるとしか記載がないのだが、
⇧ C言語が分からんので何とも言えんのだけど、
strcmpは2つの文字列を比較 (compare) するC言語の関数である。 標準Cライブラリの文字列操作関数群が宣言されているヘッダーファイル string.h
に含まれる。 ストリングコンペア、ストリングコンプなどと呼ばれることが多い。
⇧ とあるので、とりあえず、「diff.trustexitcode」の値と異なる場合に、「0」を返しているっぽいが、よく分からん...
環境変数の「GIT_EXTERNAL_DIFF_TRUST_EXIT_CODE」の説明を見ると、
⇧ 環境変数の「GIT_EXTERNAL_DIFF_TRUST_EXIT_CODE」が「false」に設定されている場合、常に「0」を返すとなっている。
つまり、常に、差分なしと判定されてしまうと。
そして、環境変数の「GIT_EXTERNAL_DIFF_TRUST_EXIT_CODE」の現状の設定を確認する術が分からんという...
結局、実際に、「git diff」コマンドをオプション「--quiet」付きで実施して検証するしか無いってことか。
では、シェルスクリプトを試す環境としては、
⇧ 上記の記事の時に、導入していた「Oxidized」で生成された「Git」のローカルの「bare repository」を利用することにします。
以下のように、gitコマンドが利用できる環境であれば、何でも良いですが。
何はともあれ、シェルスクリプトのファイルを作成。
■viでシェルスクリプトのファイルを編集する(ファイルが無い場合は新規で作成される)
vi test-git-diff.sh
で、
- 戻り値をechoで返す場合、関数内でechoした内容が全て戻り値になる
- 戻り値をreturnで返す場合、trapを利用していると、0以外がtrapで捕捉されてしまう
⇧ といった感じで、かなり残念な仕様になっている。
以下の内容で、保存する。
コミットが実施された日時のチェックとかは、今回の変更の検知には関係ないですが、コミットして1日経過しているかとかのチェックとかもしています。
■/opt/app/oxidized/.config/oxidized/test-git-diff.sh
#!/bin/bash ################################################################ # # 関数定義など # ################################################################ #■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ #■ #■ エラーをスローする #■ params $1 エラーメッセージ #■ exit 1 trapの実行トリガーとなるように0以外の数値 #■ #■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ function execption_throw() { # 標準出力を標準エラーにリダイレクト echo $1 1>&2 # 戻り値 exit 1 } #■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ #■ #■ エラーハンドリングする #■ params $1 エラー行 #■ params $2 実行コマンド #■ params $3 シェルのステータス #■ return 99 0と1以外の数値なら何でも #■ #■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ function error_handler() { local line_no=$1 local exe_cmd=$2 local status=$3 echo "□status□${status}" 1>&2 # エラーが発生した場合のみ出力 if [[ "${status}" -ne 0 ]]; then # 標準出力を標準エラーにリダイレクト echo -e "□行□${line_no}\n□実行コマンド□${exe_cmd}□ステータス□${status}" 1>&2 # 戻り値 return 99 fi } #■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ #■ #■ ログ関数 #■ params $1 ログ出力先 #■ params $2 ログファイルに出力したいメッセージ #■ #■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ function output_logger() { local LOG_LOCATION=$1 local output_message=$2 echo "${output_message}" >> "${LOG_LOCATION}" } #■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ #■ #■ コミットの日時を現在の日時と比較する関数 #■ params $1 現在の日時 #■ params $2 最新のコミットされた日時 #■ return 0 true:コミットして1日以上(変更あり) #■ 1 false:コミットして1日未満(変更なし) #■ #■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ function compare_commit_time() { local current_time=$1 local commit_time=$2 # コミットの日時を秒に変換 commit_epoch=$(date -d "${commit_time}" +%s) current_epoch=$(date -d "${current_time}" +%s) # 1日の秒数 one_day_seconds=$((24 * 60 * 60)) # 日時の差を計算 time_difference=$((current_epoch - commit_epoch)) # 1日以上経過しているかどうかをチェック if [[ "${time_difference}" -ge "${one_day_seconds}" ]]; then # 1日以上経過している return 0 else # 1日未満 return 1 fi } #■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ #■ #■ 2つのコミットハッシュの日時をチェックする関数 #■ params $1 最新のコミットされた時間 #■ params $2 1つ前のコミットされた時間 #■ return 0 true:異常なし #■ 1 false:異常あり #■ #■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ function compare_latest_with_previous() { local latest_commit_time=$1 local previous_commit_time=$2 # 日時の比較 if [[ "${latest_commit_time}" > "${previous_commit_time}" ]]; then # 戻り値 return 0 else # 戻り値 return 1 fi } #■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ #■ #■ コミットの日時を取得し、比較する関数 #■ params $1 最新のコミットハッシュ #■ params $2 1つ前のコミットハッシュ #■ params $3 ログ出力先 #■ return 0 内容に差分なし(変更なし) #■ 1 内容に差分あり(変更あり) #■ #■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ function check_commit_times() { local latest_commit_hash=$1 local previous_commit_hash=$2 local LOG_LOCATION=$3 local is_all_green=0 # 処理開始 output_logger "${LOG_LOCATION}" "■■[start]check_commit_times■■" ### 現在の日時と最新のコミットの日時を比較する ### # 現在の日時を取得 current_time=$(date +"%Y-%m-%d %H:%M:%S") # ログ出力 #echo "■■Current DateTime: ${current_time}■■" output_logger "${LOG_LOCATION}" "■■Current DateTime: ${current_time}■■" # 最新のコミット日時を取得する latest_commit_time=$(git show -s --format=%ci "${latest_commit_hash}") # ログ出力 #echo "■■Latest Commit: ${latest_commit_hash}, Time: ${latest_commit_time}■■" output_logger "${LOG_LOCATION}" "■■Latest Commit: ${latest_commit_hash}, Time: ${latest_commit_time}■■" # 現在の日時と最新のコミットの比較 compare_commit_time "${current_time}" "${latest_commit_time}" RESULT_FUNC=$? # ログ出力 #echo "■■RESULT_FUNC: ${RESULT_FUNC}■■" output_logger "${LOG_LOCATION}" "■■RESULT_FUNC: ${RESULT_FUNC}■■" if [[ "${RESULT_FUNC}" -ne 0 ]]; then # 0か1が加算 is_all_green=$((is_all_green + RESULT_FUNC)) # ログを出力 #echo "Latest commit: ${latest_commit_hash} at ${latest_commit_time} is less than 1 day old." #echo "■■is_all_green: ${is_all_green}■■" output_logger "${LOG_LOCATION}" "■■Latest commit: ${latest_commit_hash} at ${latest_commit_time} is less than 1 day old.■■" output_logger "${LOG_LOCATION}" "■■is_all_green: ${is_all_green}■■" fi ### 最新のコミットの日時と1つ前のコミットの日時を比較する ### # 1つ前のコミット日時を取得する previous_commit_time=$(git show -s --format=%ci "${previous_commit_hash}") # ログ出力 #echo "■■Previous Commit: ${previous_commit_hash}, Time: ${previous_commit_time}■■" output_logger "${LOG_LOCATION}" "■■Previous Commit: ${previous_commit_hash}, Time: ${previous_commit_time}■■" # 最新のコミットが1つ前のコミットより新しいかをチェック compare_latest_with_previous "${latest_commit_time}" "${previous_commit_time}" RESULT_FUNC=$? # ログ出力 #echo "■■RESULT_FUNC: ${RESULT_FUNC}■■" output_logger "${LOG_LOCATION}" "■■RESULT_FUNC: ${RESULT_FUNC}■■" if [[ "${RESULT_FUNC}" -ne 0 ]]; then # 0か1が加算 is_all_green=$((is_all_green + RESULT_FUNC)) # ログを出力 #echo "Latest commit ${latest_commit_hash} is older than previous commit ${previous_commit_hash}." #echo "■■is_all_green: ${is_all_green}■■" output_logger "${LOG_LOCATION}" "■■Latest commit ${latest_commit_hash} is older than previous commit ${previous_commit_hash}.■■" output_logger "${LOG_LOCATION}" "■■is_all_green: ${is_all_green}■■" fi # 処理終了 output_logger "${LOG_LOCATION}" "■■[end]check_commit_times■■" # 戻り値 return "${is_all_green}" } #■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ #■ #■ コミットを比較する関数 #■ params $1 Gitのローカルのベアリポジトリのパス #■ params $2 ログ出力先 #■ return 0 内容に差分なし(変更なし) #■ 1 内容に差分あり(変更あり) #■ #■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ function compare_commits() { # Gitのローカルのベアリポジトリのパス local BARE_REPO_PATH=$1 local LOG_LOCATION=$2 # 処理開始 output_logger "${LOG_LOCATION}" "■■[start]compare_commits■■" # Gitのローカルのベアリポジトリのディレクトリに移動 output_logger "${LOG_LOCATION}" "■■BARE_REPO_PATH: ${BARE_REPO_PATH}■■" cd "${BARE_REPO_PATH}" # コミットの件数を取得 COMMIT_COUNT=$(git rev-list --count HEAD) # コミットが2件以上あるか確認 output_logger "${LOG_LOCATION}" "■■COMMIT_COUNT: ${COMMIT_COUNT}■■" if [[ "${COMMIT_COUNT}" -lt 2 ]]; then execption_throw "Error: Not enough commits to compare. Current commits: ${COMMIT_COUNT}" fi # 最新の2件のコミットハッシュを取得 COMMIT_HASHES=($(git rev-list --max-count=2 HEAD)) # コミットハッシュを表示 #echo "■■Comparing commits:■■" >&2 #echo "■■Commit 1: ${COMMIT_HASHES[0]}■■" >&2 #echo "■■Commit 2: ${COMMIT_HASHES[1]}■■" >&2 output_logger "${LOG_LOCATION}" "■■Comparing commits:■■" output_logger "${LOG_LOCATION}" "■■Commit 1: ${COMMIT_HASHES[0]}■■" output_logger "${LOG_LOCATION}" "■■Commit 2: ${COMMIT_HASHES[1]}■■" # コミットの日時をチェック check_commit_times "${COMMIT_HASHES[0]}" "${COMMIT_HASHES[1]}" "${LOG_LOCATION}" IS_ALL_GREEN=$? #echo "■■IS_ALL_GREEN: ${IS_ALL_GREEN}■■" >&2 output_logger "${LOG_LOCATION}" "■■IS_ALL_GREEN: ${IS_ALL_GREEN}■■" # 最新コミットから1日経過していない場合 if [[ "${IS_ALL_GREEN}" -ne 0 ]]; then # 戻り値 # 最終コミットから1日経過していない場合(差分なしとする) echo 0 # 最新のコミットから1日以上経過している場合 else # git diffを実行 # 最新のコミットと1つ前のコミットの内容を比較 git diff --quiet "${COMMIT_HASHES[0]}" "${COMMIT_HASHES[1]}" DIFF_EXIT_CODE=$? #echo "■■DIFF_EXIT_CODE: ${DIFF_EXIT_CODE}■■" >&2 output_logger "${LOG_LOCATION}" "■■DIFF_EXIT_CODE: ${DIFF_EXIT_CODE}■■" # 内容に差分なし(変更なし) if [[ "${DIFF_EXIT_CODE}" -eq 0 ]]; then # 戻り値 echo 0 # 内容に差分あり(変更あり) elif [[ "${DIFF_EXIT_CODE}" -eq 1 ]]; then # 戻り値 echo 1 else execption_throw "An error occurred during git diff." fi fi # 処理終了 output_logger "${LOG_LOCATION}" "■■[end]compare_commits■■" } ################################################################ # # メイン処理 # ################################################################ # ログ出力先 CURRENT_DIR=$(cd $(dirname $0) && pwd) LOG_LOCATION="${CURRENT_DIR}/logfile.log" # 処理開始 output_logger "${LOG_LOCATION}" "■[start]メイン処理■" # エラーハンドリング trap 'error_handler ${LINENO} ${BASH_COMMAND} $?' ERR # ログ出力 #output_logger #CURRENT_DIR=$(cd $(dirname $0) && pwd) #exec >> >(tee -a "${CURRENT_DIR}/logfile.log" 2>&1) # ベアリポジトリのパス BARE_REPO_PATH="/opt/app/oxidized/.config/oxidized/test-personal-github-app.git" # 関数を呼び出す RESULT_COMPARE_COMMITS=$(compare_commits "${BARE_REPO_PATH}" "${LOG_LOCATION}") echo "■RESULT_COMPARE_COMMITS: ${RESULT_COMPARE_COMMITS}■" output_logger "${LOG_LOCATION}" "■RESULT_COMPARE_COMMITS: ${RESULT_COMPARE_COMMITS}■" # 処理終了 output_logger "${LOG_LOCATION}" "■[end]メイン処理■"
シェルスクリプトを実行します。
■シェルスクリプトを実行
bash test-git-diff.sh
⇧ ログが分かり辛いが、git diffの比較はできてはいそう。
あくまで、シェルスクリプト以外で、リモートのGitリポジトリにgit pushされないという前提の形になってはいますが。
bashは、関数で戻り値を返すことに難があったり、いろいろと欠陥が多いので利用を回避できるなら回避したいのが正直なところですかね...
毎度モヤモヤ感が半端ない…
今回はこのへんで。