James君!〜ベイジアン・フィルター


概説

さて、これは応用編みたいなものだ。

ちょっとワケあって、例の「URL大量書き込み掲示板スパム」の被害を受けた、関係するサイトの応急処置をした時に、結構掲示板スパムに関しては研究してしまった。でまあ、「本家のスパム」で最近広く使われるベイジアン・フィルターなどというものにも、その研究が及んだわけだ。そうして見ると、「本家」側アプリである James にも、このベイジアン・フィルターを組み込んでみようか...という気にもなるわけだ。

まあ、「ベイジアン・フィルターって何だ!」という向きもいるだろうから、ごく簡単に説明しておこうか。

「通常メール(よく『ハム』なんて言う)の例」と「スパムの例」を収集して、その単語ベースの特徴を整理して保存しておく。それで判定すべき新しいメールで使われる単語の特徴を見て、どちらに近いか?によって「スパムかハムか?」を判定する。で、「スパム」と入力が判定されたら、「スパムの例」にそれを追加し、「通常メール」と判定されたら「通常メールの例」に追加する...といった、「学習型フィルター」である。

でちょいと調べたが、Perl で実装された sendmail や postfix 用のベイジアン・フィルターなどというものは別に珍しくもないが、「James に組み込む」ための Java 実装というのは、あまりないみたいだな。まあ、James を使うのはアプリ開発系だけで、そうそう一般 MTA として立っているものではない。というわけで、これなら書いてみて面白そうだ...ということになる。

とはいえ、紙幅の都合で、筆者フルスクラッチのベイジアン・フィルター自体のソース解説は特にしない。まあ、使い方の説明くらいのことである。興味のある人はjavadocとかソース自体を読んでくれたまえ。

ベイジアン・フィルターの仕様

どうせ作るんなら...で、実際にはベイジアン・フィルター自体の実装は完全にスタンドアロンなものだ。特徴はこんなとこかな。

フィルター自体
  1. フレームワーク風に完璧に interface を使い倒して設計されているので、後々差し替えて使えよう。
  2. たとえば、BayesSaver として、現状でも「ファイル保存」「DB保存」ができる。ちなみにあと James 対応で「Jamesの DataSource を使って DB 保存する」ものもある。
  3. 単語分解のためのパーサとして、正規表現でゴリゴリ単語を抽出する SplitParser と、もっとスマートに Sen を使う SenParser を用意している。ま、Sen は設定などがずいぶんヤヤコしいので、デフォルトは SplitParser だ。
  4. ロジック自体はフツーの頻度モデルでやっているが、これも書けば差し替えれるぞ。
  5. 設定ファイルはクラスパスに bayes.properties を置いておく。
James からの利用版
  1. とりあえず、Matcher として、BayesMatcher を、Mailet として BayesSave を用意してある。
  2. BayesMatcher の評価は、次のメールヘッダ& Mail Attribute にも保存される
    BayesPredict
    評価結果。「HAM」なら通常メール、「SPAM」ならスパムである。
    BayesPredictedHamValue
    ハム評価値
    BayesPredictedSpamValue
    スパム評価値
    なので、パイプラインの別 Mailet からも、この BayesMatcher の評価を使えるようにしてある。

bayes.propertiesの設定内容

というわけで、設定ファイル bayes.properties の説明だ。中身はこんなところである。

# データ永続化手段
# たとえばファイルに保存する
#jp.or.nurs.sug.bayes.saver=jp.or.nurs.sug.bayes.saver.FileSaver
# 保存ファイル名
# カレントディレクトリに保存するなどという暴挙をしているので、
# フルパスで書き換えることを勧める。
#jp.or.nurs.sug.bayes.saver.FileSaver.file=./bayes.dat

# DBに保存する
#jp.or.nurs.sug.bayes.saver=jp.or.nurs.sug.bayes.saver.JDBCSaver
# こいつはフツーの JDBC 使いだから、フツーのオプションがある
#jp.or.nurs.sug.bayes.saver.JDBCSaver.SQLDriver=org.gjt.mm.mysql.Driver
#jp.or.nurs.sug.bayes.saver.JDBCSaver.SQLURL=jdbc:mysql://127.0.0.1/bayes?useUnicode=true&characterEncoding=EUC-JP
#jp.or.nurs.sug.bayes.saver.JDBCSaver.SQLTable=bayes
#jp.or.nurs.sug.bayes.saver.JDBCSaver.SQLUser=bayes
#jp.or.nurs.sug.bayes.saver.JDBCSaver.SQLPasswd=bayes

# James のデータソースを使って、DB に保存する
# 当然他の設定項目は James の config.xml の datasource ブロックに書く
jp.or.nurs.sug.bayes.saver=jp.or.nurs.sug.bayes.saver.DataSourceSaver
# テーブル名は DataSource の管理外なので、ここでの設定項目
jp.or.nurs.sug.bayes.saver.DataSourceSaver.SQLTable=bayes
# config.xml 内で定義された datasource のIDを指定する
jp.or.nurs.sug.bayes.saver.DataSourceSaver.DatasourceId=bayes

# 差し替え可能なロジックのクラスを指定する
jp.or.nurs.sug.bayes.logic=jp.or.nurs.sug.bayes.logic.DefaultLogic

# 差し替え可能な単語分解パーサのクラスを指定する
# デフォルトは正規表現でゴリゴリ単語を抽出する SplitParser である
jp.or.nurs.sug.bayes.parser=jp.or.nurs.sug.bayes.parser.SplitParser
# もっとスマートに Sen を使うパーサもあるぞ!
# jp.or.nurs.sug.bayes.parser=jp.or.nurs.sug.bayes.parser.SenParser
# 必要ならば sen の設定ファイルを指定する
# jp.or.nurs.sug.bayes.parser.SenParser.conffile=/home/sug/public_html/soft/james/bayes/sen-1.2.1/conf/sen.xml

# 使うラベル名(ham,spam)を、ラベルの実装クラスと共に指定する
jp.or.nurs.sug.bayes.labels.spam=jp.or.nurs.sug.bayes.logic.DefaultLabel
jp.or.nurs.sug.bayes.labels.ham=jp.or.nurs.sug.bayes.logic.DefaultLabel
# ラベルham の場合は、重みを倍にする
jp.or.nurs.sug.bayes.label.ham.increment = 2

# 以降 BayesMatcher 用(Mailet の BayesSave は無視する)
# スパム判定の安全域の幅。
# 要するに「スパム評価値 - ハム評価値 > matchTreshold」の場合にスパム判定する
jp.or.nurs.sug.bayes.matchThreshold=0.1
# Spam だったら自動的に保存するか?
jp.or.nurs.sug.bayes.spamAutoSave=false
# Ham だったら自動的に保存するか?
jp.or.nurs.sug.bayes.hamAutoSave=false

# SPAM 判定されたデータを自動保存する場合に、最低必要なスパム評価値
jp.or.nurs.sug.bayes.spamSaveThreshold=0.6
# HAM 判定されたデータを自動保存する場合に、最低必要なハム評価値
jp.or.nurs.sug.bayes.hamSaveThreshold=0.6

# 自動保存するケースで、これより評価値が高い場合には、「すでにあるデータ」と
# 解釈して保存しないようにする閾値
jp.or.nurs.sug.bayes.upperThreshold=0.95

# もしこれが空文字列でなければ、スパムの場合にタイトルに文字列を追加する
jp.or.nurs.sug.bayes.spamAddSubject=**SPAM** 

まあ、こんな仕様である。実際には BayesSave Mailet は「特定のメールアドレスに送られたメールは Spam/Ham のトレーニング用のデータである!」という前提で、とにかくデータとして保存するようにしてあるので、これらの Matcher 用オプションはそもそも無視することになる。まあ、BayesSave はいろいろオプションの書きやすい Mailet だから、フツーにオプション設定すればよかろう。

config.xml の設定例

で、実際の config.xml の設定はこんな感じになる。ここではさっきの bayes.properties の設定で BayesMatcher の結果からのデータの「自動保存」はしない。

  <processor name="root">
   .........
   <!-- 特定のメールアドレス宛のメールはフィルタの調教とする -->
   <mailet match="RecipientIs=spam-train@mydomain.jp" class="BayesSave">
      <label>spam</label>
   </mailet>
   <mailet match="RecipientIs=ham-train@mydomain.jp" class="BayesSave">
      <label>ham</label>
   </mailet>

   <!-- 判定。ローカルな受取人だけについて判定すべき -->
   <mailet match="And=HostIsLocal,BayesMatcher" class="ToProcessor" >
        <!-- SPAM プロセッサに分岐する -->
        <processor>spam</processor>
   </mailet>

勿論同様な config.xml で、bayes.properties を

# Spam だったら自動的に保存するか?
jp.or.nurs.sug.bayes.spamAutoSave=true
# Ham だったら自動的に保存するか?
jp.or.nurs.sug.bayes.hamAutoSave=true

にすれば、BayesMatcher の結果に応じて、データを自動保存するようになるわけだ。しかし、まあ、これは設定の一例だ。自動保存をこういう風にして書いても問題はない。

  <!-- 判定。ローカルな受取人だけについて判定すべき -->
  <!-- Mailet は実質上何もしない(Info) -->
  <mailet match="And=HostIsLocal,BayesMatcher" class="Info">
     <message>This is SPAM!</message>
  </mailet>

  <!-- And が複雑だが、
       BayesPredict == SPAM
       BayesPredictedSpamValue > 0.6
       BayesPredictedSapmValue < 0.95
       のケースで「SPAMの例」として、データベースに保存する
  -->
  <mailet match="And=|HasAttributeWithValue=BaysPredict,SPAM
|CompareNumericHeaderValue=BayesPredictedSpamValue>0.6
|CompareNumericHeaderValue=BayesPredictedSpamValue<0.95"
                                       class="BayesSave" >
     <label>spam</label>
     <passthrough>true</passthrough>
  </mailet>

  <mailet match="And=|HasAttributeWithValue=BaysPredict,HAM
|CompareNumericHeaderValue=BayesPredictedHamValue>0.6
|CompareNumericHeaderValue=BayesPredictedHamValue<0.95"
                                        class="BayesSave" >
     <label>ham</label>
     <passthrough>true</passthrough>
  </mailet>

  <mailet match="HasAttributeWithValue=BaysPredict,SPAM" class="ToProcessor">
     <!-- スパム判定されていれば SPAM プロセッサに分岐する -->
     <processor>spam</processor>
  </mailet>

というように、前ページで紹介した「高階 And Matcher」と BayesMatcher がセットするヘッダ&アトリビュートをフル活用して、config.xml レベルで自動保存を書いちゃうこともできるわけだ。まあ、ライブラリに固まっている bayes.properties だと動的に変更するのは面倒だから、こっちの方が使いでがあるかな?

インストール手順

じゃあ、インストールの仕方の説明をしていこう。実際には Sen をパーサとして使わなければ、そのまま「Mailetの開発」で説明した手順だけでOKだ。とはいえ、Sen を使う SenParser でテキストを分解するとなると設定がかなりややこしい。だから、ここではまず、SplitParser を使う簡単な手順を先に書く。

  1. Mailetの開発 を読んで、そのままインストールする。ちなみに、ここから、bayesmailet.tgz というベイジアン・フィルターだけのパッケージも用意してあるから、これだけ欲しい人はそうしてくれ。
  2. で、開発環境側の設定ファイルなどをイジる。特にイジるべきなのは、src/bayes.properties、build/SAR-INF/config.xml あたりだろう。
  3. src/bayes.properties。配布パッケージでは FileSaver を使って、 James を起動したカレントディレクトリに bayes.dat というファイルを作ってここにデータを保存している。まあ、きっちりフルパスで保存ファイルを指定すべきだな。あと、自動保存はとりあえず切ってあるが、スパム判定されたメールのヘッダに「**SPAM**」という文言が追加されるようになっている。
  4. build/SAR-INF/config.xml。当り前のことだが、パイプラインに BayesMatcher, BayesSave を追加する。このページで書いた例を参考にしてくれ。
  5. ひょっとして Bayes の Saver として、DataSourceSaver を使うのならば、当然 config.xml にデータソースの設定が必要だ。まあ、これは「DB連携Mailet〜DBの用意」を見て、適切に書いてくれ。あと当然DB自体の用意も要るわけだ。
  6. DB 保存をする Saver(DataSourceSaver,JDBCSaver)の場合は、当然DB自体の用意が必要だ。テーブル仕様はこんなものである。
    CREATE TABLE bayes (
       label VARCHAR(30) NOT NULL,
       name  VARCHAR(100) NOT NULL,
       count INTEGER );
    

    bayes.properties や config.xml のデータソース指定に対応した Database の上で、対応したユーザ・パスワードで作成するのは当然だな。
  7. で、コンパイルして固める。開発環境の build.xml そのままで大丈夫だから、単に「ant install」で固めてインストールしてしまってくれたまえ。

とはいえ、ややこしい Sen を使うとなると、いくつか準備が必要だ。ポイントに絞ってここでは解説するが、Sen のホームページも読んでおいてくれたまえ。

  1. とりあえず、現在の build.xml では、
       <!-- 注意! SenParser をコンパイルするには、sen.jar が必要だ。
       だから、これはデフォルトではコンパイルされない。もし、SenParser 
       をコンパイルするのならば、次の行をコメントアウトせよ! -->
       <property name="src.excludes" value="jp/or/nurs/sug/bayes/parser/SenParser.java" />
      .......
      <target name="compile" depends="init">
         ......
         <javac srcdir="${src}" destdir="${classes}" debug="${debug.flag}" 
            classpathref="lib.classpath" excludes="${src.excludes}" />
      </target>
    

    としてあって、コンパイルしないようになっている。だから、これをコメントアウトしてくれ。
  2. SenParser は日本語形態素解析に Sen を使っている。だから、Sen をダウンロードしてインストールする必要がある。ダウンロード先はここである。
  3. で、適当なディレクトリでダウンロードした sen-*.*.zip を展開する。これは James 開発のパスの外部の方が良いだろう。
  4. そうすると、sen を解凍したディレクトリの下、sen-1.2.1/lib/ に、sen.jar がある。これが Sen のライブラリだ。あと、Sen は commons-logging.jar を必要とするので、この2つのライブラリを、開発ディレクトリの build/SAR-INF/lib にコピーしておく。ここはコンパイル時のクラスパスが通っているのと同時に、james.sar を作る際に一緒に固められるディレクトリである。
  5. src/bayes.properties や config.xml を適当に修正する。それに加えて、SenParser の場合は src/commons-logging.properties をちょっと見てくれるといい。これは言うまでもなく Commons-Logging の設定ファイルだが、要するに Sen が Commons-Logging を使って Sen のログを出す設定のわけだ。で、実際これはウザいだけのことが多かろう。だから、配布パッケージでは NoOpLog(出さない)が指定してある。James は Avalon のアプリだから、「ひょっとして LogKitLoggerとか AvalonLoggerとか使える?」という気にはなるのだが、やってみたが James のログとは連動できない(ちなみに LogKitLogger か Jdk14Logger を使えば標準出力に出してくれる)。まあ、DEBUG で出したらホントにウザいし、本質的にはどっちでもイイことなので、NoOpLog が一番適切だろう。
  6. これで一応コンパイルは通るようになる。ここでコンパイルしてみてよい。
  7. 実は Sen にはもう一仕事残っている。それは「辞書を作る」なのだが、これは sen-1.2.1/dic に移動すると、build.xml があるので、ここで ant を実行する。そうすると、ネット越しにいろいろなデータをゲットしてきて、Sen 用の辞書を作り上げてくれる。ここらへんうまく行かなかったら、プロキシとかの問題かもしれない。Sen Projectのドキュメントをしっかり読んでくれ。
  8. で、最後に忘れちゃならないことがある。それは「Sen の辞書」を作ったわけだけど、James の上から「Sen の辞書とかがどこにあるか?」というのが検索できなきゃならない。このため、起動スクリプトをちょっと細工する。james-2.2.0/bin/phoenix.sh を編集し、赤字の
    # Get the run cmd
    RUN_CMD="$JAVA_HOME/bin/java $JVM_OPTS \
        $JVM_OPTS \
        $DEBUG \
        -Djava.security.policy=jar:file:$PHOENIX_HOME/bin/phoenix-loader.jar!/META-INF/java.policy \
        -Dsen.home=/home/sug/public_html/soft/james/bayes/sen-1.2.1 \
        $PHOENIX_JVM_OPTS \
        -Dphoenix.home="$PHOENIX_HOME" \
        -Djava.io.tmpdir="$PHOENIX_TMPDIR" \
        -jar "$PHOENIX_HOME/bin/phoenix-loader.jar" $*"
    

    を、Sen をインストールしたディレクトリを指すように追加してくれたまえ。
  9. これでOK。James を起動してくれたまえ。お疲れさん。

まあ、SplitParser と SenParser と、どっちを使うべきか?というと、筆者の軍配は SenParser だ。何やかんや言って、Sen がややこしいのはそれなりの理由があって、キッチリ日本語を理解して分析をしているわけだ。が、SplitParser はここらへんいい加減である....Sen をインストールするのはちょっとレベルが高いので、「試してみようか..」というノリだと面倒だ。だから、デフォルト採用を見送ったわけだが、試してみるとやや SplitParser を使うと判定が甘いような気がする....調教度を上げれば SplitParser でも問題ないとは思うが、ここはまあ、実務だったら SenParser にお任せ!の方が安心だ。

実際、SplitParser のロジックはこんなものだ。

  1. Unicode の字種を見て、半角アルファベット・全角アルファベット・全角カタカナ・漢字以外の文字は、すべて空白文字に置換する。言い替えると、ひらがなを主要なデリミタとして扱うわけだ。(ひらがなばっかりのスパムメールだったらどうしよう! 想像するだけで笑えるが....)
  2. で、空白文字をデリミタとして、String#split() で切り出す。

....手抜きである。それに対して、Sen は「日本語」を理解しているから、日本語の単語を分離して、動詞なら原形にして頻度マップに追加する。まあ、あまりトリビアルな品詞は頻度マップに追加しないようにはしてあるが...ここらへん、どっちにせよ暫定だ。皆さんの御意見も伺いたいな。関心のある方はソースの方に当たってくれ。

パッケージ構成

一応念のために、クラスパッケージ構成くらいは書いておくか。こんな感じだ。太字はスタンドアロン用の bayes.tgz には含まれていない。これはスタンドアロンだとコンパイルできないソースになるからね。

jp-or-nurs-sug-+-bayes -+- Bayes  フロントエンド&ドライバ
               |        +- BayesException 例外クラス
               |        +- BayesItem  データ表現のBean
               |        +- BayesLogic Logic 用の interface
               |        +- BayesManager 同一インスタンスを返すための Manager
               |        +- BayesParser Parser 用の interface
               |        +- BayesSaver  Saver 用の interface
               |        +- BayesTester スタンドアロンのテスター
               |        +- Label       各 Label 用の interface
               |        +- ReadWriteLock  教科書的 ReadWriteLock
               |        +- logic -+- DefalutLabel  ラベルのオブジェクト
               |        |         +- DefaultLogic  頻度型ロジック実装
               |        +- parser -+- SenParser   Sen を使った日本語パーサー
               |        |          +- SplitParser 正規表現を使った日本語パーサ−
               |        +- saver -+- DataSourceSaver James用のDB保存
               |                  +- FileSaver   ファイル保存
               |                  +- JDBCSaver   一般的なDB保存
               +- james -+- mailets  -- BayesSave  データ更新
                         +- matchars -- BayesMatcher Matcher実装

問題点&改善プラン

で、ちょっと問題点に触れよう。

実はスパムメールの場合、うまく内部のテキストをひきずり出せないケースがないわけではない。いろいろやってみたが、その本質的な原因はというと、

スパムメールはRFCを無視したメールを送ってくる可能性が高い!

ということなのだ。RFCも守れない奴に一般商業道徳が守れるわけがないな。実際にはこういうメールが来る。

  1. Base64でエンコードされているが、メール本文は Shift_JIS。しかも「Content-Type: text/plain」だけで、Charset を使って中身が SJIS だと断っていない。
  2. しかも、MS特有の外字を使っている。これだと Java の Unicode 変換がコケて IOException を投げてしまうので、対応しようがない...
  3. Content-Type が text/plain なのに、HTML メール。

まあ、ここらへん、勘ぐると「RFCを守らないのはスパムフィルターを回避するため?」ということかもしれない。まあ、現実的には「まともなメールの99%は JIS でコーディングされたプレーンメール」だという日本の現実から考えれば、単に Base64 でエンコードされている text を含むメールはそのままハネてしまってもイイのかもしれない(極論だが...)

あと、改善プランとして次のようなものがある。

  1. 今は id ベースで、複数のデータベースを併用できるような仕様になっているが、これを「メールの宛先」ごとに自動的にデータベースを振り分けるようにする。

実際には、こういうこともできる。

  <mailet match="RecipientIs=useA-spam@mydomain.jp" class="BayesSave">
    <label>spam</label>
    <conf>userA-bayes.properties</conf>
    <id>userA</id>
  </mailet>
  <mailet match="RecipientIs=userA-ham@mydomain.jp" class="BayesSave">
    <label>ham</label>
    <conf>userA-bayes.properties</conf>
    <id>userA</id>
  </mailet>

  <mailet match="RecipientIs=userB-spam@mydomain.jp" class="BayesSave">
    <label>spam</label>
    <conf>userB-bayes.properties</conf>
    <id>userB</id>
  </mailet>
  <mailet match="RecipientIs=userB-ham@mydomain.jp" class="BayesSave">
    <label>ham</label>
    <conf>userB-bayes.properties</conf>
    <id>userB</id>
  </mailet>

  <mailet match="And=RecipientIs=userA@mydomain.jp,BayesMatcher=id=userA" class="ToProcessor" >
    <processor>spam</processor>
  </mailet>

  <mailet match="And=RecipientIs=userB@mydomain.jp,BayesMatcher=id=userB" class="ToProcessor" >
    <processor>spam</processor>
  </mailet>

これは要するに、受取ユーザごとに別々の Bayes インスタンスを確保し、それぞれ別のデータベースを使ってスパム判定をする、というやり方である。この時、「id=id」で、それぞれ「どのデータベースか」を特定する。まあ、「conf=設定ファイル名」で、それぞれ個別の設定ファイルを指定できるので、その中で保存ファイルなりDBのテーブル名なりを切替えてやればイイのである。ここらのロジックは BayesManager がインスタンス管理をうまくやっていてくれて、要するに「すでに id で特定される Bayes のインスタンスがあれば、それを設定変更なしで使い、初登場の id の場合は指定された設定ファイルを使ってインスタンスを作って返す」という動作をする。だから、パイプラインの先頭にある Bayes 関連 Mailet&Matcher の最初のもので指定した設定ファイルが、同じIDである限り使われるということになる。まあ、こうしたのは、Mailet だと設定をイジりやすいが、Matcher だと1行ですべての設定を書かなきゃならないので、設定がヤヤコシくなるということの配慮だ。多分 BayesSave の方をパイプラインの上で先に書くと思うから、ほとんどのケースで書きにくい BayesMatcher の側は id を指定するだけで良いと思う。

一応狙いはOKなのだが、ユーザ数が多い場合にはちょっと厄介だ。これは

  <mailet match="RecipientIsRegex=(.*)-spam@mydomain.jp" class="BayesSave">
    <label>spam</label>
  </mailet>
  <mailet match="RecipientIsRegex=(.*)-ham@mydomain.jp" class="BayesSave">
    <label>ham</label>
  </mailet>

  <mailet match="And=UserIsLocal,BayesMatcher" class="ToProcessor" >
    <processor>spam</processor>
  </mailet>

と書くだけで、受け取りユーザ個別のデータベースを使う...なんてのもイイのかなあ?と思うのだ。しかし、ローカルユーザにこれを徹底するのも難しいし、メンテは逆に厄介になりそうだ...というわけで、やるとしても「Experimental」な別Mailetの方が良さそうな気もする。

最後にちょっとだけまとめ。BayesSave で受け入れ可能なオプションだ。

<mailet match="****" class="BayesSave">
   <label>必須:保存すべきラベル名。プロパティファイルで定義されたラベル名の
          どれかを指定すべきなのは当然だ。</label>
   <id>Bayes のインスタンスのID。Match する時に使うインスタンスと、ここで調教
       するインスタンスは同一でなければならないよね! 省略されれば default と
       いうIDになる。</id>
   <conf>任意:初登場ならばインスタンスの初期化に使われる設定ファイル。
         ID 初登場でこれが省略されていると、bayes.properties が指定されている
         ものとする。ID が初登場でなければ無視されて使われることはない。</conf>
   <debug>true|false。true ならば解析すべきテキストをログ出力する。テキスト
          抽出に??となった時にだけ true にすべきだな。
          デフォルトは false。</debug>
    <passThrough>true|false。false ならばパイプラインはここで打ち切り。
          デフォルトは false で、この Mailet はここで終るのが自然だと思う。
    </passThrough>
</mailet>

BayesMatcher も BayesSave と同様に、id, conf, debug のオプションをサポートしている。また、debug オプションは設定ファイルの中に、「jp.or.nurs.sug.bayes.debug」で指定することもできる。が、まあフルに BayesMatcher のオプションを指定すると、

<mailet match="And=|UserIsLocal|BayesMatcher=id=test,conf=mybayes.properties,debug=true"
     class="ToRepository">

とかなって、冗長なことはなはだしい。まあ、id だけを指定する、という格好で大概設定ファイル読み込みができるのでは?とは思うぞ。



copyright by K.Sugiura, 1996-2006