Programming」カテゴリーアーカイブ

Amazon KendraのWebCrawlerでRedmineのサイトを学習させる

社内の問い合わせをAIチャットボットにやらせるプロジェクトを行うことになった。
最初はChatGPT連携を謳っているクラウドサービスを契約して使っていたんだけど、精度や運用面での課題が多く、あまり活かせていない状況だった。
そこで、乗り換えを検討していたところ、AWSで割と簡単にAIチャットが構築できることが分かった。
https://catalog.workshops.aws/generative-ai-use-cases-jp/ja-JP/overview

本当に、これに書いていることをそのまんまやればAIチャットボットが作成できる。
ただし、AWSは従量課金なので要注意。利用状況にもよるが、試算すると月額15~20万円ぐらいになった。
計算方法は忘れた。

で、さらにこれは学習データを用意して読み込ませることができる(RAGチャット)。
なので、社内の各種情報を読み込ませればそれで社内用AIチャットボットが完成する。

データの読み込みはAmazon Kendraというドキュメント検索エンジンを用いる。
最初は各種ドキュメントファイルをAmazon S3にアップロードし、それをKendraに読み込ませることを想定していたのだが、以下の二点の問題点が生まれた。

そこで、ナレッジ共有サイト(Redmine)を直接読み込ませることにした。
KendraではWebCrawlerをデータソースに出来るので、それで行ける筈だった。

結論としては、問題なくいけた。
ただ、認証面に関してうっかりしてドツボにはまったので、同じことが起こらないようにメモを残す。

【認証情報の設定方法】
(Webcrawlerv2を用いる。基本的な部分は省略する)

  1. Source URLsにRedmineのサイトURLを入力する。
  2. AuthenticationでForm authenticationを選択する。
  3. AWS Secrets Manager secretでCreate and add new secretをクリックする。
  4. 下記の様に各種情報を入力する。
    XPathは下記サイトを参考にしてログインしたRedmineの情報を確認する。
    (多分、デフォルトならこの画像と同じと思われる。)
    https://dietwork.net/winactor-xpath/
  5. 後はダイアログに従って設定する。

恥ずかしい話なんだけど、何故かBasic認証で通そうとして悪戦苦闘してしまった。
しかも、上手くいかなかったのでRedmineのapiキーを使うという斜め下の取り組みをしてしまい、結局1日無駄にしてしまった。。。

とりあえずこれで形は出来た。
ただ、微妙に回答の的がズレていたり、回答とソースが整合しなかったりで、もう少し調整が必要そう。
どう調整するかは全く分からないので、トライアンドエラーの年末年始になりそう。

SQL Serverの照合順序を変更する

SQL Serverでは文字列比較の基準となる照合順序が設定できる。
例えば「大文字小文字を区別する」等である。

これはデータベース毎に設定可能で、特に指定していない場合はSQL Serverセットアップ時に設定したサーバーデフォルト値が適用される。

デフォルトと異なる照合順序をデータベースに設定した場合、トラブルが発生しやすい。
例えば、一時テーブルと作成し、それとデータベース内のテーブルをJOINするようなクエリはエラーになる。
これは、一時テーブルの照合順序はサーバーのデフォルト値になっており、
異なる照合順序でのon Table.col1=temptable.col1という比較が行えないためである。
on Table.col1=temptable.col1 collate Japanese_CI_ASのように、クエリの中で照合順序を指定すればエラーは発生しない。)

仕事の都合で照合順序の変更を余儀なくされたことがあったので、その手順をメモする。
なお、実際に行ったのはSQL Server 2012だが、2008 R2や2014でも同じだと思う。

・データベース単体の照合順序変更方法

以下のクエリを実行すれば良い。
対象のデータベース名をSampleDataBase、照合順序をJapanese_CI_AS_KS_WSとする。

・SQL Server(=システムデータベース)の照合順序変更方法

データベースが存在すると実行できないため、システムデータベースを除く全てのデータベースを削除する。
(復元が必要な場合はバックアップをとっておく。)

SQLのインストールディレクトリ内にSetup.exeがあるので、そこに移動する。
規定ではC:\Program Files\Microsoft SQL Server\110\Setup Bootstrap\Releaseである。
コマンドプロンプトからSetup.exeを以下のコマンドで実行する。

例えば、インスタンス名は規定のMSSQLSERVER、Windows認証でAdministratorがSQL Serverにログイン可能で、
照合順序をJapanese_CI_AS_KS_WSに変更したい場合は以下のようなコマンドを実行する。


なお、実行できない場合、インストールメディアのSetup.exeで試すとうまくいく場合がある。
理由は不明で、偶然かもしれないが、そういう場合もあった。


参考
データベースの照合順序の設定または変更
システム データベースの再構築

Macでファイルを定期的にバックアップする設定

僕はiTunesのメディアファイルを始め、動画、画像ファイルを全て外付けのHDDに保存して管理している。
昔はNASだったんだけど、引っ越してから無線の速度が遅くなったので単純な外付けHDDに回帰した。

で、それはいいんだけど、最近ファイルが壊れていたりすることに気付いた。
古いHDDのせいだろうか。
幸か不幸か、大して重要じゃないファイルなのでよかったが、流石にこの大容量の資産にバックアップがないこの状況が少し怖くなってきた。
今までもたまに気が向いたときに別の外付けHDDにバックアップを取ったりしていたが、気が向いた時だけじゃ心もとなさ過ぎる。
バックアップを毎晩、自動実行しようと思うに至った。

で、Windowsのようにrobocopyコマンドを使おうとしたけど、当然Macにはrobocopyなんてコマンドは無い。
その上、定期実行しようにもWindowsで言うタスクスケジューラが無い。Windows脳の僕はこれで少しパニックになった。

そんな色んな障害を乗り越えた(乗り超えるほど大きい壁じゃない)作業メモを残そうかなと思う。
なお、前提として僕はシェルスクリプトなんて書いたこともない素人である。
cron?良くワカンネ」というレベルである。
実際、良くわからなくて今回使わなかった。今度また挑戦する。


1. バックアップを行うコマンド(シェルスクリプト)を書く

最悪手動でも、バックアップを1クリックで行えるコマンドファイルを作成できれば、
Macでバッチファイル的なものを作成する
のようにして1クリックバックアップシステムが作れるはず。
まずはバックアップを行えるようなコマンドを探した。

2秒で見つかった。

rsync

差分更新まで行ってくれるイカした奴。
僕の望む同期の形を考え、以下のようなオプションを設定した。

-a…元の権限を保持する
-t…元のタイムスタンプを保持する
-u…追加、更新されたファイルだけをコピーする
-v…処理中の経過ファイルを表示する(後述のログ出力のため)
–delete…コピー元にないファイルがコピー先にあった場合、削除する

要するに、バックアップ元と寸分たがわぬ構成をコピーする、という趣旨だ。
それで、最初は単純に以下のような1行コマンドで済ませていた。

だけど、あんまり好きじゃない点が一つあった。
Macで外付けHDDに接続した場合、”.”から始まる不可視ファイルが作成されるが、これらまでバックアップ先にコピーされてしまう。
なので、バックアップ元の外付けHDD内の通常フォルダのみに対してrsyncを行うようなコマンドに修正した。
ついでにrsyncの出力をログとしてファイルに残すようにした。

これでバックアップ元HDD直下のフォルダについて、全てバックアップ先にコピーされる。
ただ、上記スクリプトの欠点は、バックアップ元HDD直下にスペースを含むフォルダが有る場合、上手く動作しないことだ。
つまり、”test folder”というフォルダがあると、”test”と”folder”という2フォルダと認識されてしまう。
工夫すればどうにでもなると思うけど、スクリプトをシンプルにしたかったのでこのままにしておいた。
工夫する知識も気力も無いです。

2. シェルスクリプトを定期実行する

さて、スクリプトはできたが、定期実行はできるのか。
ちゃんとやるならcronやlaunchで設定するのが良いらしい。
でも、よくわからないので、原始的な方法を採った。
Macの標準カレンダー(旧iCal)とAutomatorを用いる方法だ。

MacのAutomatorで新規作成を行う。
書類の種類は「カレンダーアラーム」にする。(アプリケーションでも良い。その場合は後述の設定が必要)
アクションで、「ユーティリティ」→「シェルスクリプトを実行」を選択する。
上記のスクリプトを記述し、保存する。

以上により、Macの標準カレンダーにイベントが登録される。
時刻や繰り返しの設定を適宜変更すれば、定期タスクの設定が簡単に可能だ。

ちなみに、「カレンダーアラーム」でなく、「アプリケーション」を作成した場合、appファイルが作成される。
このappファイルを開くと、シェルスクリプトが実行されるので、後はこのappを定期的に実行するように、手動で標準カレンダーに登録すれば良い。
カレンダーで普通にイベントを追加し、
「通知」→「カスタム」を選択する。
初期状態では通知が「メッセージ」となっていると思うので、ここを「ファイルを開く」にする。
ファイルの種類がカレンダーになっているので、ここで「その他」を選択し、先程のappファイルを選択する。
2015-03-02 22.53.29
ちなみに、GoogleカレンダーやiCloudのカレンダー等、クラウド上のカレンダーではこの通知の設定が保存されなかった。
未検証だが、クラウド上と動機する過程で「ファイルを開く」通知が消えてしまうのだろう。
ローカルのMac上のイベントとして設定したほうが確実だ。

後は時刻や繰り返しを設定することで、定期的にスクリプトが実行できる環境が完成する。

基本的に「カレンダーアラーム」で設定したほうが簡単だと思う。
ちなみに「カレンダーアラーム」で作成した場合、そのappファイルは以下の場所に保存されている。
/Users/UserName/Library/Workflows/Applications/Calendar
appファイルを常に手元においておきたい場合は「アプリケーション」で作成するのもありだと思う。

・参考にしたページ
rsync – 高速なファイル同期(バックアップ) – Linuxコマンド
iCalで指定時刻にアプリを呼び出す
【Linux】bashで指定したディレクトリ内のファイルを取得する
lsでディレクトリのみ,ファイルのみ,表示する


追記(2015/05/30)

定期実行でカレンダーを使うのは負けた気がしてきたので結局launchdを使うことにしました。
MacでScriptを定期実行する方法

VirtualBoxの仮想環境で容量を増やす方法とその注意点

僕はMac使いだが、C#しか基本的に使わないので、コーディングするときはVirtualBoxでWindows8.1を起動してその中でやっている。
Windowsもクリーンインストールだとスッキリしていて悪くない。仕事関係の勉強にもなるし。

で、どんどん使っていると容量が無くなってくる。
当初は20GBもあればいいだろ、と思っていたのに、今では40GBに増やしたくなっている。
VirtualBox上では「容量の拡張」みたいなメニューが無いので無理だと思っていたが、調べてみたらコマンドから実行可能だった。

やり方自体は簡単。Macなのでターミナルから実行することを想定しているが、Windowsでもコマンドプロンプトから同じように実行できる。

ちなみに、一つ落とし穴がありハマったので、それについては後述する。


VirtualBox仮想環境(Windows)のディスク拡張方法

  1. 仮想ディスクファイルのバックアップをとっておく
  2. VBoxManage modifyhd 仮想マシンの仮想ディスクのパス —-resize 拡張後のディスク容量(MB単位)
    を実行する。
    ex)VBoxManage modifyhd /Users/Administrator/VirtualBox/VMs/TestMachine/TestMachine.vdi --resize 40960
  3. コマンドが完了したら、仮想マシンを起動する。(この時点ではまだ容量が増えていない)
  4. 「コントロールパネル」→「システムとセキュリティ」→「ハードディスク パーティションの作成とフォーマット」を開く
  5. 画面下部で、Cドライブに割り当てられている領域を右クリックし、「ボリュームの拡張」をクリックする。

以上で仮想マシンの容量が拡張される。
2以降の手順はWindows固有の操作なので、Windows以外の場合はそのOSのパーティション割り当て方法に従ってください。

・・・で、1で仮想ディスクを拡張したとき、仮想マシンが一切起動しなくなる場合がある。
どうやら原因は仮想ディスクファイルの形式のようだ。
VHD形式の仮想ディスクファイルは上記コマンドで拡張すると壊れてしまい、起動できなくなるのだ。

VHD形式の仮想ディスクを拡張するには、一度別形式に変換してから拡張を行うと良い。

VBoxManage clonehd --format VDI 仮想マシンの仮想ディスクのパス 変換後の仮想ディスクのパス

ex)VBoxManage clonehd --format VDI /Users/Administrator/VirtualBox/VMs/TestMachine/TestMachine.vhd /Users/Administrator/VirtualBox/VMs/TestMachine/TestMachine.vdi
上記コマンドでVDI形式に変換された仮想ディスクファイルが作成されるので、それを拡張すれば良い。
拡張後は形式をVHD形式に戻してもいいし、戻さなくてもいいし、好みにすればいいと思う。


しかし、最近自分にITの適正が無いのが分かり始めた。
何かこう、薄給で責任の少ない仕事がしたい。
田舎の公園の清掃員とかが理想なのだ。
(清掃員を下に見る意図ではありません。ただ、田舎に帰るとどうしても羨ましくなってしまうのです。)

Monoのアンインストール方法

さて、前回書いたように、Monoがインストール済みの状態ではHomebrewの導入が上手くいかない。
そこで、Monoのアンインストール方法を備忘のためにメモする。

以下のスクリプトをターミナルで実行する。
その際、管理者権限で実行する。
この「管理者権限で」というのが英語の読めない僕は気付かなかった。

ターミナルで管理者権限、即ちrootユーザーになるには、suコマンドを実行すればいい。
もし一度もrootユーザーになったことがないなら、以下の操作を行う。

  1. ターミナルでsudo passwd rootを実行する
  2. 「Changing password for root.New password:」と表示されるので、rootにログインするためのパスワードを設定する。勿論任意のパスワードで良い。
  3. 「Retype new password:」と表示されるので、同じパスワードを入力する。
  4. 「su -」を実行する。
  5. 先ほど設定したrootのパスワードを設定する。
  6. カーソル左の「$」が「#」に変わったら管理者権限への変更完了。

この状態で、上のスクリプトをコピペして実行すればMonoのアンインストールができる。


最近、プロジェクト管理ツールの「asana」が割と気に入っている。
使いやすい。その一点だけで素晴らしい。
しかも軽い。
仕事で使ってたけど、個人用でも使おうか考えている。

Macでバッチファイル的なものを作成する

仕事上、Windowsのbatファイルを作成することがよくある。
パソコンをまともに触るまではコマンドラインすら使いもしなかったが、
意外といろんな操作を自動化できて楽しい。
で、batファイル的なものをMacでも作ろうかと思ったが、意外と落とし穴があったのでメモ。


  1. シェルスクリプトで実行内容をテキストに記述
    以下の様なスクリプトを任意のテキストエディタで記述する。

    このとき、改行コードをUNIX(LF)にする。
    そのため、テキストエディタは何でも良いが、改行コードを設定できることにすること。
    記述したら、このファイルの拡張子を”.command”として保存する。
  2. ターミナルからコマンドファイルの実行権限を付ける
    上記のファイルをそのまま実行しようとしても、「適切なアクセス権限がないため実行できません。」となり、実行できません。
    そこで、ファイルを実行できるようにアクセス権を設定します。

    1. ターミナルを開き、先ほど作成したファイルを保存したディレクトリに移動する。
    2. 以下のコマンドを実行し、実行権限を付ける。

後は.commandファイルを実行するだけでターミナルが開かれ、記述した操作内容が実行される。
実行後、ターミナル画面がそのまま残ると味が悪いので、終了後画面が閉じるようにターミナルを設定すると良い。

  1. ターミナルから、「ターミナル」→「環境設定」を開く
  2. 「設定」→「シェル」タブで、「シェルの終了時」を「シェルが正常に終了した場合は閉じる」にする

以上。
僕はC#で作ったファイル整理系コンソールアプリをmonoで実行させるのによく使っています。
コマンド内容は以下の通り。


来月には帰国できる・・・はず。

C#でEvernoteアプリを作る

Evernoteは言わずとしてたクラウドメモツールだ。
使い道は人それぞれだが、僕はwebクリップでプログラミング関連のページを保存したり、
2chで面白いと思ったスレやレスを保存したり、
画像スレの画像をページごと保存したり、
ロクでもない使い方をしている。
で、EvernoteはAPIを公開しており、これを使えば誰でもEvernote関連アプリケーションが作成できる。
C#でも公開されており、C#しか使わない僕にも使えた。
テキスト投稿アプリならすぐに作れてしまう程分かりやすい仕組みになっている。
何より日本語化されているのがいい。リファレンスは英語だけど。
備忘の為、0からの使い方を以下にまとめた。

  1. Developer登録をする
    1. https://dev.evernote.com/intl/jp/に行き、「APIを使いはじめる」をクリックする。
    2. APIドキュメントのページが表示されるので、「APIキーを取得」をクリックする。
    3. 申し込みフォームが表示されるので、必要事項を入力する。
      (API権限は作りたいアプリ種類によって選択する。投稿アプリならベーシックアクセスでいいし、既存Noteの編集を行うならフルアクセスが必要。
      とりあえず試すだけならフルアクセスにしておけばいいと思う)
    4. コンシューマーキーとコンシューマーシークレットが発行されるので、メモしておく。
  2. SDKを取得する
    1. APIドキュメントでC#の「SDKをインストール」をクリックする。
    2. Githubのページがでてくるので、右下の「Download ZIP」をクリックし、ソースコードを取得する。
      (勿論クローンでもいいです。お好きな方で)
  3. EvernoteSandbox(開発環境)のアカウントを取得する
    1. EvernoteのAPIはいきなり通常のアカウントに対しては使えず、
      まず開発環境内で動作検証をして、OKだと思ったら本番環境での使用許可を得る、という形になっています。
      開発環境はEvernote社が提供しているので、そのアカウントを取得します。
    2. https://sandbox.evernote.com/で「アカウントを作成」する。
      以下、手順にしたがってアカウントを作成してください。
      このときのアカウントとパスワードは自分のEvernoteアカウントとは全く無関係なものでOKです。
  4. デベロッパトークンを取得する
    1. いちいち自分の環境に対してユーザーIDとパスワードを入力して認証するのは
      面倒ですが、デベロッパトークンを用いることでその作業を省略できます。
    2. ここのページ中段の「サンドボックス用のデベロッパトークンを取得」をクリックする。
    3. 先程取得したSandboxのアカウントとパスワードを入力する。
    4. 「Creat a developer token」とクリックし、表示されたデベロッパトークンをメモする。
  5. Sampleアプリを動かしてみる
    1. 2でダウンロードしたSDKをZIPなら展開し、sample→client内のEvernote.slnをVisualStudioで開く。
    2. SampleAppプロジェクト内のEDAMTest.csを開く。これがサンプルアプリのコードです。
    3. 40行あたりにauthTokenが定義されているので、先程取得したデベロッパトークンに設定してください。
    4. VisualStudio上で実行してください。上手く実行されればsandbox環境内に新規ノートが作成されます。

後はソースを眺めてAPIの仕組みを大まかに把握し、色々変更してみたりして弄ってみてください。
なお、自分のアプリでAPIを利用する場合は、EDAMプロジェクトをビルドしてできた「Evernote.dll」と「Thrift.dll」をコピーし、アプリのプロジェクトのの参照設定に追加してください。
細かい使い方やリファレンスは他のページにまとまっていますので、そちらを参照したほうが良いと思います。
ちなみに、地味に躓くのが認証する所だと思います。
僕は下記のソースを参考にしています。
参考というか、利用させてもらっています。
MITライセンスだからね。本当に感謝しています。
https://github.com/matchy2/EvernoteOAuth
Sandbox上の検証が十分に完了したら、プロダクション用のデベロッパトークンを使って自分の本アカウントEvernoteに使ってもいいし、アクティベートしてコンシューマーキーをプロダクト環境で利用可能にして、一般公開してもいいと思う。
僕が作ったAppも一応このサイトに置いてあります。
クソコードなので中身はあまり見せられません。

InvokeでFormをCloseして、その後すぐに同じFormインスタンスを生成するときの注意点

タイトルが長くなった。
ちなみに検証できないので内容的に間違っているかもしれません。
解決方法も最善解ではないです。
完全に自分の覚え書き用の記事です。

おおまかに言うと、上記のようなコードを実装していた。
InvokeしてるのはこのメソッドがUIスレッドでない別スレッドで実行されるからです。
簡単に言うと「今表示されているSampleFormを閉じて、初期化して再度表示する」という実行内容。

で、これがいざ実行するとハングすることがある。
困ったのは、100%でなく、起こったり起こらなかったりするところである。

form.Show()の直前でブレイクしてデバッグすると、どうやらここでformがDisposeされていることがあるということが分かった。
しかし、// (中略2)や// (中略3)のどこにもformをDisposeする要素がない。
より詳細に見ようと、// (中略2)にブレイクポイントを置いてデバッグしたところ、問題が一切発生しなくなった。

しばらく頭を捻っていたが、ようやく尤もらしい仮説が見つかった。

InvokeされたCloseによるDispose処理が、「form=new SampleForm()」後に実行され、
折角初期化したformがDisposeされてしまった。

シナリオはこうだ。

1)(処理スレッド)CloseをUIスレッドに託す
2)(UIスレッド)formをCloseする
3)(処理スレッド)処理スレッドformを初期化する
4)(UIスレッド)Closeしたことだし、formをDisposeする
5)(処理スレッド)formをShowする

formはDisposeされてるから無理ですよ〜だ。

4が3と5の間に起こると本件が発生する。
そのタイミングは環境の状態によるので、問題が発生したりしなかったりした訳だ。

で、以下のように1行追加して問題は発生しなくなった。

一度たまっている処理(Close含む、ってか多分Closeしかない)を全て実行させればいいのだ。
Mutexを使う等して排他制御をした方が正確だが、バグの温床に成りかねないのでこの方法にした。
ちょっと不安ではあるけど、これで問題なさそうならこれで行こうと思う。

昔「おまじないだ」と言われたApplication.DoEvents()を使うことになるとはなぁ。
ネットで調べたらあまり勧められない方法らしいけど、そこまで必死に直す箇所じゃないからね。仕方ないね。

DataViewでRowFilterを設定する場合の注意点

DataViewはDataTableと似たようなクラスだが、
インスタンス生成時に既にインデックスを貼ってくれていたり、
まぁ色んな違いがある(投げやり)。

で、最近いじっていたあるコードがある。

DataViewにRowFilterを設定し、その後の処理結果によってまた同じ操作を繰り返すというものだ。
(実際のコード上は無限ループにならないようになっています。念のため)
これがやけに時間がかかる。
原因はRowFilterがRowFilterを何度も行ったことだった。

なぜなら理由は単純で、RowFilterが実行された場合、DataViewは内部的にインデックスを設定してしまっているのだ。
インデックスを設定するために毎回全データを取得しに行っているわけで、これでは大変時間がかかる。

で、幸い「// (dvを使った実行内容)」の内容をRowFilterでなく、FindRowsに置き換えることが可能だったので、それでひとまず解決した。
FindRowsメソッドはインデックスを再設定しないのだ。
(参考:http://msdn.microsoft.com/ja-jp/library/bb669089(v=vs.110).aspx

RowFilterで遅くなっている可能性を全く考えておらず、分かるのに時間がかかった。
ぱっと見、プロパティ設定してるだけに見えるからね、仕方ないね。

・・・修正したはいいけど、割と条件分岐が複雑なメソッドなので、テストに時間かかりそう。
(現に実装終わった後にバグを指摘されまくった)

C#で素因数分解(工夫なし)

案の定というか、更新停止していた。

ネタは書きためているのだけど、書き溜めるだけじゃ何もできん。
プログラミングを学んで一番最初に自分で1から作成したのは素因数分解を行うプログラムの作成だった。
ついさっき、そのコードを見つけたのだけど、これが酷すぎる。
見ていられない。
というわけで、ついさっき書きなおしてみたら5分でできた。それも以前のものより段違いにシンプルで速い。
こんなサンプルに意味はないのだけど、なんとなく載せてみる。

全然関係ないけど、最近、SmartWatchのPebbleのアプリ”Simply.js”でjavascriptだけで色々とできることが分かったので、Twitter投稿アプリを作成しようと目論んでる。
・・・javascript単体だと難しそうなのでサーバーにexe置いてjavascriptで叩いて起動、というフローを想定しているんだけど、できるのかなこんなこと。
どうも、プログラム初心者です。