正解率はそのままで、別の案件が壊れる
目標
ノードを包んで、どこで何をしたかを帳簿に残し、ゴールデンセットで正解率と間違えた件のリストを出します。ルールを直した2つ目のバージョンと比べて、新しく壊れたものと新しく直ったものを名前で分け、コストと経路の変化まで測ります。
なぜ重要なのか
エージェントを直したあとに、「良くなったか」をスコア1つで尋ねることがよくあります。ところが、スコアが同じでも、中で1つが直って1つが壊れていたなら、それは良くなったのではなく、変わったのです。スコアは個数で、私たちが知る必要があるのは、名前です。 観測は、ノードを1枚包めば済みます。このとき、重要な選択が1つあります。時間も測っておきますが、判定には使いません。同じコード・同じ入力でも、時間はマシンと負荷によって変わり、時間で判定するリグレッション検査は、ぶれて、結局オフにされます。判定には、回数とサイズを使います。 ゴールデンセットは小さくてよいですが、毎回同じものである必要があります。そして、今間違える件を残しておいてこそ、直したときに直ったものが見えます。全部当たるゴールデンセットは、何も見つけられません。 品質がそのままでも、コストと経路はリグレッションすることがあります。ノードの呼び出し回数は、同じ入力でいつも同じ値なので、リグレッション指標として使え、足跡が変わったなら、答えが同じに見えても別の動作です。 採点ツールは、書かれた説明を信用しません。書かれたモジュールを実際に読み込んで、任意の問い合わせで動かしてみて、ゴールデンセットの判定と2つのバージョンの差を、採点ツールが別に計算した値と突き合わせます。
ステップ
- /root/work/ageval/evalkit.pyに
LABELS・ANSWER・classify_v1・State・5つのノード(normalize・refund・shipping・other・finish)・ROUTE・build_graph(version="v1")・run_one(text, version="v1")を作成してください。 PROFILE・reset_profile()・instrument(name, fn)を追加して、build_graphがすべてのノードを包んで追加するようにしてください。hotspots(texts, version="v1")を追加して、ノードごとの呼び出し回数と、最も忙しいノードを出すようにしてください。GOLDENとevaluate(version="v1")を追加して、正解率と間違えた件のリストを出すようにしてください。classify_v2とcompare(old="v1", new="v2")を追加して、新しく壊れた件と新しく直った件を分けるようにしてください。cost_delta(old="v1", new="v2")を追加して、ノードの呼び出し回数の変化を測るようにしてください。path_changes(old="v1", new="v2")を追加して、足跡が変わった件を探すようにしてください。- 測ったことを記録してください(保存先: /root/work/ageval/eval_report.json、/root/work/ageval/eval_report.md)。
参考
- 実行の契約: 採点ツールは、
/root/work/ageval/evalkit.pyをPythonモジュールとして読み込んで、上に書いた名前を直接使います。スクリプトとして実行しません。 classify_v1(text):"환불"があれば"환불"、なければ"배송"があれば"배송"、なければ"기타"(3つの韓国語の値は、順に「返金」「配送」「その他」を意味するラベルです)。classify_v2(text):"배송"があれば"배송"、なければ"환불"か"반품"があれば"환불"、なければ"기타"(3つ目に出てくる語は、韓国語で「返品」を意味します)。検査の順序がv1と違います。それがこのラボの材料です。- 状態のキー:
text・clean・label・answer・trace。traceだけが連結するリデューサーを使います。ノード名と状態のキーは、重なってはいけません。重なると、コンパイルのときにValueError: 'x' is already being used as a state keyが出ます。 normalizeは、textの空白を1つに縮めてcleanに入れます。分類はcleanを見ます。refund・shipping・otherは、labelとANSWER[라벨](プレースホルダーはラベルです)を入れ、3つともfinishに集まります。5つのノードすべてが、traceに自分の名前を書きます。ROUTE = {"환불": "refund", "배송": "shipping", "기타": "other"}を、条件付きエッジのパスマップとして使います(キーは、韓国語で順に「返金」「配送」「その他」を意味します)。run_one(text, version)の答え:{"label": 문자열, "answer": 문자열, "trace": [...]}(プレースホルダーは文字列です)。PROFILEは{노드이름: {"calls": 정수, "written": 정수, "ms": 실수}}です(プレースホルダーはノード名、整数、実数です)。writtenは、そのノードが返したディクショナリのキーの数を足した値です。msは、測っておくだけで、判定には使いません。hotspots(texts, version)の答え:{"calls": {...}, "total_calls": 정수, "busiest": 문자열, "timed": [...정렬된 노드 이름]}(プレースホルダーは整数、文字列、ソート済みのノード名を意味する語です)。呼び出すたびに、帳簿を新しく広げます。busiestは、呼び出し回数が最も多いノードで、同点なら名前の昇順です。GOLDENは(문의, 정답라벨)(プレースホルダーは問い合わせと正解ラベルです)の6組です:("환불 절차 알려 주세요", "환불")、("반품하고 싶어요", "환불")、("배송비 환불되나요", "환불")、("배송 언제 오나요", "배송")、("영수증 좀 보내 주세요", "기타")、("반품 배송비는 누가 내나요", "배송")(6組はそれぞれ、問い合わせが「返金の手順を教えてください」でラベルが返金、「返品したいです」で返金、「配送料は返金されますか」で返金、「配送はいつ届きますか」で配送、「領収書を送ってください」でその他、「返品の配送料は誰が払いますか」で配送、という意味です)。evaluate(version)の答え:{"total": 정수, "correct": 정수, "accuracy": 실수, "wrong": [{"text":…, "gold":…, "got":…}, …]}(プレースホルダーは整数と実数です)。wrongは、ゴールデンセットの順序に従います。compare(old, new)の答え:{"old_accuracy": 실수, "new_accuracy": 실수, "newly_broken": [...정렬됨], "newly_fixed": [...정렬됨]}(プレースホルダーは実数と、ソート済みを意味する語です)。cost_delta(old, new)の答え:{"old_calls": 정수, "new_calls": 정수, "delta": 정수}(プレースホルダーは整数です)。ゴールデンセット全体を1回ずつ動かしたあとの呼び出し回数です。path_changes(old, new)の答え:[{"text":…, "old": [...], "new": [...]}, …]をtextの昇順で。足跡が同じ件は入れません。- このPodにはインターネットがありません。langgraph 0.2.60がすでに入っています。
- 公式ドキュメント: Graph API overview・Use the graph API・Streaming
- よくある間違い: 帳簿を呼び出すたびに空にしないこと(前の実行の数値が混ざります)、時間でリグレッションを判定すること、間違えた件の名前を残さないこと、ゴールデンセットを実行のたびに新しく選ぶことです。
問い合わせをブランチに送る
/root/work/ageval/evalkit.pyにLABELS・ANSWER・classify_v1・State・5つのノード・ROUTE・build_graph(version="v1")・run_one(text, version="v1")を作成してください。
normalizeが作ったcleanを見て分類します。条件関数はラベルを返し、ROUTEがどのノードへ行くかを決めます。ラベル名とノード名を同じにしないのは、パスマップの役割をはっきり見せるためです。version引数は、今は"v1"だけを受け付けます。
ノードを包んで帳簿を残す
PROFILE・reset_profile()・instrument(name, fn)を追加して、build_graphが5つのノードをすべて包んで追加するようにしてください。帳簿にはcalls・written・msを残します。
包む関数は、元のノードを呼び出して、その結果をそのまま返しながら、横に数値を書きます。writtenは、そのノードが返したディクショナリのキーの数です。時間も一緒に測っておきますが、判定には使わないことをコメントとして書いておいてください。なぜそうなのかが、このステップの核心です。
どのノードが最も忙しいか
hotspots(texts, version="v1")を追加して、{"calls": {...}, "total_calls": 정수, "busiest": 문자열, "timed": [...]}(プレースホルダーは整数と文字列です)を出すようにしてください。呼び出すたびに、帳簿を新しく広げます。
帳簿を空にしないと、前の実行の数値が混ざって、何を測っているのかわからなくなります。busiestは、呼び出し回数が最も多いノードで、同点なら名前の昇順で分けて、毎回同じ答えが出るようにしてください。timedは、時間が記録されたノードの名前のリストです。
手で正解を付けた6件
GOLDENの6組とevaluate(version="v1")を追加して、{"total", "correct", "accuracy", "wrong"}を出すようにしてください。wrongにはtext・gold・gotを入れます。
ゴールデンセットは小さくてよいですが、毎回同じものである必要があります。そして、今間違える件を残しておいてこそ、直したときに直ったものが見えます。全部当たるゴールデンセットは、何も見つけられません。正解率だけを出さず、間違えた件の名前も一緒に出してください。
スコアは同じなのに、別の件が間違う
classify_v2とcompare(old="v1", new="v2")を追加してください。答えは{"old_accuracy", "new_accuracy", "newly_broken", "newly_fixed"}で、2つのリストはソートします。
classify_v2は、"반품"(韓国語で「返品」を意味する語です)を返金として見るようにしつつ、検査の順序を変えたバージョンです。2つのバージョンの正解率を先に測って、その数値だけで判断できるかを問いかけてください。旧バージョンで合っていたものが新バージョンで間違う件と、その反対を、それぞれ集めると、スコアが隠していたものが見えます。
品質が同じでもコストは変わる
cost_delta(old="v1", new="v2")を追加して、{"old_calls", "new_calls", "delta"}を出すようにしてください。ゴールデンセット全体を1回ずつ動かしたあとのノードの呼び出し回数です。
呼び出し回数は、同じ入力でいつも同じ値なので、リグレッション指標として使えます。時間と違う点が、それです。前のステップで作ったhotspotsをそのまま使えば済みます。このグラフでは、どのブランチへ行ってもノード数が同じですが、それならdeltaが何を語っているのかも、考えてみてください。
同じ答えが別の道で出た
path_changes(old="v1", new="v2")を追加して、足跡が変わった件だけを、[{"text", "old", "new"}, ...]としてtextの昇順で出すようにしてください。
ラベルが同じでも、通ってきたノードが違えば、別の動作です。今は答えが同じに見えても、次の変更で違う方向に分かれます。足跡が同じ件は入れないでください。リストが短いと、人が見ます。
何を出すかを判断する
/root/work/ageval/eval_report.jsonにold_accuracy・new_accuracy・newly_broken・newly_fixed・cost・path_changes・busiest・total_callsを、/root/work/ageval/eval_report.mdに## 어디서 무엇을 했는지 어떻게 기록했나 ## 점수만 보면 놓치는 것 ## 비용과 경로도 회귀한다 ## 다음에 무엇을 하겠는가の4つの節を書いてください(4つの見出しは、順に韓国語で「どこで何をしたかをどう記録したか」「スコアだけを見ると見逃すもの」「コストと経路もリグレッションする」「次に何をするか」を意味します)。
costはcost_deltaの答えをそのまま、path_changesは、変わった件の個数です。busiest・total_callsは、ゴールデンセット全体をv1で動かしたときの値です。4番目の節には、この結果を見てどちらのバージョンを出すかについての、自分の判断を書いてください。正解は1つではありません。