「ありがとう」「お願いします」という感謝や丁寧な言葉は、円滑な人間関係を構築する上で欠かせません。
ChatGPTに使う「〇〇」「△△」という言葉が実はOpenAI社の負担になっていた【優しい言葉は無駄なのか】 - ナゾロジー
では、相手がChatGPTだった場合は、どうでしょうか。
ChatGPTに使う「〇〇」「△△」という言葉が実はOpenAI社の負担になっていた【優しい言葉は無駄なのか】 - ナゾロジー
2025年4月17日、OpenAIのCEOサム・アルトマン氏はXにて、ChatGPTに投げかけられる「please」や「thank you」といった言葉が大きな損失を生んでいることを認めました。
ChatGPTに使う「〇〇」「△△」という言葉が実はOpenAI社の負担になっていた【優しい言葉は無駄なのか】 - ナゾロジー
⇧ 根拠となるデータが示されていないので憶測の域を出ていないようなのだが...
そもそも、実際に「電力消費」に影響していて、コストが上昇しているのであれば、公式サイトでアナウンスがあるような気がしますけど、アナウンスが無いのであれば、「OpenAI」社にとっては全くもって負担になることの無い微々たる影響しかないってことになるんですかね?
利用者側からしたら、「幻覚(ハルシネーション)」が抑制されていてくれる方が重要事項ですからな...
「幻覚(ハルシネーション)」の発生が0になれば、膨大な裏付けの作業コストが無くなって、本質的な作業に注力できますからな...
OxidizedはローカルのGitリポジトリが存在しない場合git init相当をしておりリモートと同期されない罠
「Ruby」製のライブラリ「Oxidized」というものがあるのだが、公式のドキュメントの情報が不足している、且つ、整理されていないが過ぎてカオスなのだが、公式のドキュメントに載っていないが把握していないと危険な情報について備忘録として記載しておく。
ネットの情報を漁っていたところ、
⇧ とあり、バグではなく「Oxidized」の仕様というのが「Oxidized」の開発に関係する側の主張でありますと。
つまり、デリバリ手順的には、必ず「Oxidized」を起動させる前に、明示的に「git clone 」して最新の「.git/」に差し替えておく必要があるということらしい。
正確には、「Oxidized」の内部で「Rugged」というライブラリで「Git」の「bare repository」を扱っているようなので、「.git/」以外は削除しておく必要があるという部分に言及が無いため、「issue#2033」の回答は不十分なのだが...
ちなみに、
- git init
- git clone
の違いについては、
⇧ 上記サイト様がまとめてくださっています。
「Oxidized」に話を戻すと、ザックリ言うと、「Oxidized」は「リモート」の「Gitリポジトリ」の内容を事前に同期できないという残念な仕様になっているらしい。
つまり、「リモート」の「Gitリポジトリ」の「コミット履歴」が無かったことにされてしまうという、あり得ない仕様になっているのである。
「リモート」の「Gitリポジトリ」で「private repository」になっているものに対しては「認証情報」が必要になって来るのだが、「Oxidized」の「ソースコード」の「変更容易性」が低いためなのか、理由は定かでは無いのだが、
- git clone相当の処理(Oxidizedではbare repositoryを扱っているため、実際には、git cloneは利用できない)の追加
を「Oxidized」側で対応できていないというのが実情のようだ...
「Oxidized」としては、
⇧「git fetch」相当の処理が為されていないということみたいね...
ここで今一度「https://github.com/ytti/oxidized/issues/2033」の回答部分に着目してみると、
Need more information about your migration of Oxidized to a new server. You should be able to copy everything as-is, including the .git directory. Why are you manually initialising a new repo on your new server? Why aren't you letting Oxidized do it?
I assume it's because you want to retain git history, in which case you only need to clone your remote repo to your local oxidized.git. That's how I've done it many times myself.
⇧『Why are you manually initialising a new repo on your new server? Why aren't you letting Oxidized do it?』が疑問形になっているのが全く理解できないのだが、大事故に繋がるということを強調して欲しい気はする...
『I assume it's because you want to retain git history, in which case you only need to clone your remote repo to your local oxidized.git. That's how I've done it many times myself.』については、正しい手順を公式のドキュメントに記載しておいて欲しい気はする...
ちなみに、「Ruby」に詳しくないので推測になってしまいますが、「Oxidized」で「Gitリポジトリ」の操作部分は、以下で行われているっぽい。
■https://github.com/ytti/oxidized/blob/master/lib/oxidized/output/git.rb
module Oxidized module Output class Git < Output using Refinements class GitError < OxidizedError; end begin require 'rugged' rescue LoadError raise OxidizedError, 'rugged not found: sudo gem install rugged' end attr_reader :commitref def initialize super @cfg = Oxidized.config.output.git end def setup if @cfg.empty? Oxidized.asetus.user.output.git.user = 'Oxidized' Oxidized.asetus.user.output.git.email = 'o@example.com' Oxidized.asetus.user.output.git.repo = File.join(Config::ROOT, 'oxidized.git') Oxidized.asetus.save :user raise NoConfig, "no output git config, edit #{Oxidized::Config.configfile}" end if @cfg.repo.respond_to?(:each) @cfg.repo.each do |group, repo| @cfg.repo["#{group}="] = File.expand_path repo end else @cfg.repo = File.expand_path @cfg.repo end end def store(file, outputs, opt = {}) @msg = opt[:msg] @user = opt[:user] || @cfg.user @email = opt[:email] || @cfg.email @opt = opt @commitref = nil repo = @cfg.repo outputs.types.each do |type| type_cfg = '' type_repo = File.join(File.dirname(repo), type + '.git') outputs.type(type).each do |output| (type_cfg << output; next) unless output.name # rubocop:disable Style/Semicolon type_file = file + '--' + output.name if @cfg.type_as_directory? type_file = type + '/' + type_file type_repo = repo end update type_repo, type_file, output end update type_repo, file, type_cfg end update repo, file, outputs.to_cfg end # Returns the configuration of group/node_name # # #fetch is called by Nodes#fetch # Nodes#fetch creates a new Output object each time, so it not easy # to cache the repo index in memory. But as we keep the repo index up # to date on disk in #update_repo, we can read it from disk instead of # rebuilding it each time. def fetch(node, group) repo, path = yield_repo_and_path(node, group) repo = Rugged::Repository.new repo # Read the index from disk index = repo.index repo.read(index.get(path)[:oid]).data rescue StandardError 'node not found' end # give a hash of all oid revisions for the given node, and the date of # the commit. # # Called by Nodes#version def version(node, group) repo_path, node_path = yield_repo_and_path(node, group) self.class.hash_list(node_path, repo_path) rescue StandardError 'node not found' end # give the blob of a specific revision def get_version(node, group, oid) repo, path = yield_repo_and_path(node, group) repo = Rugged::Repository.new repo repo.blob_at(oid, path).content rescue StandardError 'version not found' end # give a hash with the patch of a diff between 2 revision and the stats (added and deleted lines) def get_diff(node, group, oid1, oid2) diff_commits = nil repo, = yield_repo_and_path(node, group) repo = Rugged::Repository.new repo commit = repo.lookup(oid1) if oid2 commit_old = repo.lookup(oid2) diff = repo.diff(commit_old, commit) diff.each do |patch| if /#{node.name}\s+/ =~ patch.to_s.lines.first diff_commits = { patch: patch.to_s, stat: patch.stat } break end end else stat = commit.parents[0].diff(commit).stat stat = [stat[1], stat[2]] patch = commit.parents[0].diff(commit).patch diff_commits = { patch: patch, stat: stat } end diff_commits rescue StandardError 'no diffs' end # Return the list of oids for node_path in the repository repo_path def self.hash_list(node_path, repo_path) update_cache(repo_path) @gitcache[repo_path][:nodes][node_path] || [] end # Update @gitcache, a class instance variable, ensuring persistence # by saving the cache independently of object instances def self.update_cache(repo_path) # initialize our cache as a class instance variable @gitcache ||= {} # When single_repo == false, we have multiple repositories unless @gitcache[repo_path] @gitcache[repo_path] = {} @gitcache[repo_path][:nodes] = {} @gitcache[repo_path][:last_commit] = nil end repo = Rugged::Repository.new repo_path walker = Rugged::Walker.new(repo) walker.sorting(Rugged::SORT_DATE) walker.push(repo.head.target.oid) # We store the commits into a temporary cache. It will be prepended # to @gitcache to preserve the order of the commits. cache = {} walker.each do |commit| if commit.oid == @gitcache[repo_path][:last_commit] # we have reached the last cached commit, so we're done break end commit.diff.each_delta do |delta| next unless delta.added? || delta.modified? hash = {} # We keep :date for reverse compatibility on oxidized-web <= 0.15.1 hash[:date] = commit.time.to_s # date as a Time instance for more flexibility in oxidized-web hash[:time] = commit.time hash[:oid] = commit.oid hash[:author] = commit.author hash[:message] = commit.message filename = delta.new_file[:path] if cache[filename] cache[filename].append hash else cache[filename] = [hash] end end end cache.each_pair do |filename, hashlist| if @gitcache[repo_path][:nodes][filename] # using the splat operator (*) should be OK as hashlist should # not be very big when working on deltas @gitcache[repo_path][:nodes][filename].prepend(*hashlist) else @gitcache[repo_path][:nodes][filename] = hashlist end end # Store the most recent commit @gitcache[repo_path][:last_commit] = repo.head.target.oid end # Currently only used in unit tests def self.clear_cache @gitcache = nil end private def yield_repo_and_path(node, group) repo, path = node.repo, node.name path = "#{group}/#{node.name}" if group && !group.empty? && @cfg.single_repo? [repo, path] end def update(repo, file, data) return if data.empty? if @opt[:group] if @cfg.single_repo? file = File.join @opt[:group], file else repo = if repo.is_a?(::String) File.join File.dirname(repo), @opt[:group] + '.git' else repo[@opt[:group]] end end end begin repo = Rugged::Repository.new repo update_repo repo, file, data rescue Rugged::OSError, Rugged::RepositoryError => e begin Rugged::Repository.init_at repo, :bare rescue StandardError => create_error raise GitError, "first '#{e.message}' was raised while opening git repo, then '#{create_error.message}' was while trying to create git repo" end retry end end # Uploads data into file in the repository repo # # update_repo caches the index on disk. An index is usually used in a # working directory and not in a bare repository, which confuses users. # The alternative would be to rebuild the index each time, which a little # time consuming. Caching the index in memory is difficult because a new # Output object is created each time #store is called. def update_repo(repo, file, data) oid_old = repo.blob_at(repo.head.target_id, file) rescue nil return false if oid_old && (oid_old.content.b == data.b) oid = repo.write data, :blob # Read the index from disk index = repo.index index.add path: file, oid: oid, mode: 0o100644 repo.config['user.name'] = @user repo.config['user.email'] = @email @commitref = Rugged::Commit.create(repo, tree: index.write_tree(repo), message: @msg, parents: repo.empty? ? [] : [repo.head.target].compact, update_ref: 'HEAD') index.write true end end end end
⇧ 上記のクラスにおいて、処理的には「git commit」相当までを担当しているようで、「git push」部分に関しては、
■https://github.com/ytti/oxidized/blob/master/lib/oxidized/hook/githubrepo.rb
require 'rugged' class GithubRepo < Oxidized::Hook def validate_cfg! raise KeyError, 'hook.remote_repo is required' unless cfg.has_key?('remote_repo') end def run_hook(ctx) unless ctx.node.repo log "Oxidized output is not git, can't push to remote", :error return end repo = Rugged::Repository.new(ctx.node.repo) creds = credentials(ctx.node) url = remote_repo(ctx.node) if url.nil? || url.empty? log "No repository defined for #{ctx.node.group}/#{ctx.node.name}", :error return end log "Pushing local repository(#{repo.path})..." log "to remote: #{url}" if repo.remotes['origin'].nil? repo.remotes.create('origin', url) elsif repo.remotes['origin'].url != url repo.remotes.set_url('origin', url) end remote = repo.remotes['origin'] begin fetch_and_merge_remote(repo, creds) remote.push([repo.head.name], credentials: creds) rescue Rugged::NetworkError => e if e.message == 'unsupported URL protocol' log "Rugged does not support the git URL '#{url}'.", :warn unless Rugged.features.include?(:ssh) log "Note: Rugged isn't installed with ssh support. You may need " \ '"gem install rugged -- --with-ssh"', :warn end end # re-raise exception for the calling method raise end end def fetch_and_merge_remote(repo, creds) result = repo.fetch('origin', [repo.head.name], credentials: creds) log result.inspect, :debug their_branch = remote_branch(repo) unless their_branch log 'remote branch does not exist yet, nothing to merge', :debug return end result = repo.merge_analysis(their_branch.target_id) if result.include? :up_to_date log 'nothing to merge', :debug return end log "merging fetched branch #{their_branch.name}", :debug merge_index = repo.merge_commits(repo.head.target_id, their_branch.target_id) if merge_index.conflicts? log("Conflicts detected, skipping Rugged::Commit.create", :warn) return end Rugged::Commit.create(repo, parents: [repo.head.target, their_branch.target], tree: merge_index.write_tree(repo), message: "Merge remote-tracking branch '#{their_branch.name}'", update_ref: "HEAD") end private def credentials(node) Proc.new do |_url, username_from_url, _allowed_types| # rubocop:disable Style/Proc git_user = cfg.has_key?('username') ? cfg.username : (username_from_url || 'git') if cfg.has_key?('password') log "Authenticating using username and password as '#{git_user}'", :debug Rugged::Credentials::UserPassword.new(username: git_user, password: cfg.password) elsif cfg.has_key?('privatekey') pubkey = cfg.has_key?('publickey') ? cfg.publickey : nil log "Authenticating using ssh keys as '#{git_user}'", :debug rugged_sshkey(git_user: git_user, privkey: cfg.privatekey, pubkey: pubkey) elsif cfg.has_key?('remote_repo') && cfg.remote_repo.has_key?(node.group) && cfg.remote_repo[node.group].has_key?('privatekey') pubkey = cfg.remote_repo[node.group].has_key?('publickey') ? cfg.remote_repo[node.group].publickey : nil log "Authenticating using ssh keys as '#{git_user}' for '#{node.group}/#{node.name}'", :debug rugged_sshkey(git_user: git_user, privkey: cfg.remote_repo[node.group].privatekey, pubkey: pubkey) else log "Authenticating using ssh agent as '#{git_user}'", :debug Rugged::Credentials::SshKeyFromAgent.new(username: git_user) end end end def rugged_sshkey(args = {}) git_user = args[:git_user] privkey = args[:privkey] pubkey = args[:pubkey] || (privkey + '.pub') Rugged::Credentials::SshKey.new(username: git_user, publickey: File.expand_path(pubkey), privatekey: File.expand_path(privkey), passphrase: ENV.fetch("OXIDIZED_SSH_PASSPHRASE", nil)) end def remote_repo(node) if node.group.nil? || cfg.remote_repo.is_a?(String) cfg.remote_repo elsif cfg.remote_repo[node.group].is_a?(String) cfg.remote_repo[node.group] elsif cfg.remote_repo[node.group].url.is_a?(String) cfg.remote_repo[node.group].url end end # Returns a Rugged::Branch to the remote branch or nil if it doen't exist def remote_branch(repo) head_branch = repo.branches[repo.head.name] repo.branches['origin/' + head_branch.name] end end
⇧ で行っているようなのだが、「HTTP」リクエストの際の「プロキシ」には対応していないらしい...
「HTTP」リクエストの際の「プロキシ」に対応したい場合は、独自に「git push」部分を実装する必要があるという残念な仕様になっている...
で、「Rugged」について、
⇧ とあることから、「Oxidized」では「リモート」と同期できていないことが分かる。
「Oxidized」に生殺与奪を託してしまうと、コミット履歴が無かったことにされる、つまり「リモート」の「Gitリポジトリ」が破壊されるという恐ろしい罠があるという...
「Oxidized」の起動前に必ず、「git clone」相当の処理を行うようにして、「リモート」の「Gitリポジトリ」の内容を「ローカル」の「Gitリポジトリ」に反映しておくのを忘れないように注意ですね...
とりあえず、公式のドキュメントの情報が不足し過ぎていて、「ファインダビリティ(Findability)」以前の問題なのだが、「アーキテクチャ図」を公開して欲しいですな...
毎度モヤモヤ感が半端ない…
今回はこのへんで。