<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://shemol.tech/feed.ja-JP.xml</id>
    <title>Shemol's Blog</title>
    <updated>2026-04-22T02:38:03.976Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <author>
        <name>Shemol</name>
        <email>shemol106@gmail.com</email>
        <uri>https://shemol.tech</uri>
    </author>
    <link rel="alternate" href="https://shemol.tech/"/>
    <link rel="self" href="https://shemol.tech/feed.ja-JP.xml"/>
    <subtitle>Sneak through holes and climb over fences.</subtitle>
    <icon>https://shemol.tech/favicon.svg</icon>
    <rights>All rights reserved 2026, Shemol</rights>
    <entry>
        <title type="html"><![CDATA[Claude Code の Memory 管理]]></title>
        <id>https://shemol.tech/claude-code-memory-management-ja</id>
        <link href="https://shemol.tech/claude-code-memory-management-ja"/>
        <updated>2026-02-27T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Claude Code ネイティブの memory（Auto memory、CLAUDE.md）、階層構造、オンデマンド読み込みなどの仕組みを整理する。]]></summary>
        <content type="html"><![CDATA[<h1>Claude Code の Memory 管理</h1>
<p>新しい小シリーズ、エージェントの memory。まずは CC（Claude Code）のネイティブ memory 管理から。</p>
<p>CC ネイティブの memory 管理：<https://code.claude.com/docs/en/memory#auto-memory>  </p>
<p>読んだ印象としては、面接で「こういうシステムを組みました」と言っても、絶対に通らないだろうな、という感じだった。</p>
<p>CC のネイティブ memory 管理はファイルシステム経由。</p>
<p>セッションをまたいで永続化される記憶には次の 2 種類がある。</p>
<li><strong>Auto memory</strong>：Claude が有用な文脈（プロジェクトのパターン、重要なコマンド、ユーザーの好みなど）を自動保存する。セッション間で保持される。</li>
<li><strong>CLAUDE.md ファイル</strong>：自分で書き・保守する Markdown。Claude に従わせる指示・ルール・好みを書く。</li>
<p>どちらも各セッション開始時に Claude のコンテキストに読み込まれるが、自動記憶はメインファイルの<strong>先頭 200 行だけ</strong>が読み込まれる。</p>
<p>切り捨てはあまり良い方法ではない気がする…</p>
<p>CC の memory の階層構造：</p>
<strong>組織スコープ</strong>（組織の全ユーザー向け）
<li><strong>Managed policy</strong>：IT / DevOps が管理する組織全体の指示。コーディング規約、セキュリティ方針、コンプライアンスなど。</li>
<strong>チーム・プロジェクトスコープ</strong>（そのプロジェクトの全メンバー向け）
<li><strong>Project memory</strong>：チーム共有のプロジェクト指示。アーキテクチャ、コーディング標準、よくあるワークフローなど。</li>
<li><strong>Project rules</strong>：モジュール化されたトピック別のプロジェクト指示。言語別ガイド、テスト規約、API 標準など。</li>
<strong>個人スコープ</strong>（個人向け）
<li><strong>Project memory</strong>：個人のプロジェクト固有の好み。サンドボックス URL、優先するテストデータなど。</li>
<li><strong>Auto memory</strong>：プロジェクトのパターン、デバッグの気づき、アーキメモなど。</li>
<p>Auto memory には次のようなものが含まれる。</p>
<li>プロジェクトのパターン：ビルドコマンド、テストの慣習、コードスタイルの好み</li>
<li>デバッグの洞察：厄介な問題の解き方、よくあるエラーの原因</li>
<li>アーキテクチャのメモ：重要なファイル、モジュール関係、重要な抽象概念</li>
<li>個人の好み：コミュニケーションスタイル、ワークフロー、ツール選び</li>
<p>これらの保存場所の詳細はドキュメントを参照。</p>
<li>前述のとおり <code>MEMORY.md</code> は先頭 200 行だけ読み込まれる。また Claude Code は簡潔に書くよう指示されており、詳しいトピックは別ファイルに移す。</li>
<li><strong>オンデマンド読み込み</strong>：<code>debugging.md</code> や <code>patterns.md</code> のようなトピックファイルは起動時には読み込まれない。必要なときに標準のファイルツールで読む。</li>
<li>Claude はセッション中に memory ファイルを読み書きするので、作業しながら memory が更新されるのを見られる。</li>
<p>Claude Code は memory を<strong>再帰的</strong>に読む：カレントワーキングディレクトリから上に向かってルート <code>/</code> まで（ただし <code>/</code> 自体は含めない）辿り、見つかった <code>CLAUDE.md</code> や <code>CLAUDE.local.md</code> を読む。大きなコードベースで便利。例：<code>foo/bar/</code> で Claude Code を動かしていて、<code>foo/CLAUDE.md</code> と <code>foo/bar/CLAUDE.md</code> の両方がある場合。</p>
<p>またカレント以下のサブツリー内の <code>CLAUDE.md</code> も見つける。これらは起動時には読み込まれず、Claude がそのサブツリー内のファイルを読むときに初めて取り込まれる。</p>
<p>他ディレクトリからの memory 読み込みや編集、モジュール化ルールなどもあるが、ここでは省略。</p>
<h2>他言語</h2>
<li><a href="/claude-code-memory-management">中文</a></li>
<li><a href="/claude-code-memory-management-en">English</a></li>]]></content>
        <author>
            <name>Shemol</name>
            <email>shemol106@gmail.com</email>
            <uri>https://shemol.tech</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[陳皓（耗子）氏と「3.25」——知恵袋の記録（転載）]]></title>
        <id>https://shemol.tech/about-chen-hao-325-ja</id>
        <link href="https://shemol.tech/about-chen-hao-325-ja"/>
        <updated>2026-02-17T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[知乎で見かけた陳皓（耗子）氏に関する匿名回答の転載・整理メモ。]]></summary>
        <content type="html"><![CDATA[<h1>陳皓（耗子）氏と「3.25」——知恵袋の記録（転載）</h1>
<p>ノート整理のときに見つけ、ブログに残しておきたかった。</p>
<p>作者：匿名ユーザー  </p>
<p>リンク：https://www.zhihu.com/question/29614511/answer/45025842  </p>
<p>出典：知乎  </p>
<p>著作権は作者に帰属。商用転載は作者の許可が必要。非商用転載は出典明記のうえで。</p>
<hr />
<p>匿名にしなければならない。利害関係者で、耗子（陳皓）氏のチームメンバーと近く付き合っており、いくつか知っている。</p>
<p>事情はこうだったと思う。当時アリババクラウドの ECS で VPC というプロジェクトがあり、一年ほどやっているのにまだ正式リリースになっていなかった。最初から方向を誤ったらしい。友人の話では、最初から耗子は ECS 側の技術案が間違っていると争ったが、プロジェクトが大きすぎ、三十〜四十人・複数チームだったという。耗子一人ではコントロールできず、友人の言葉を借りれば「アリババには神様が多すぎた」。</p>
<p>このプロジェクトは初日から非人道的な残業で、月曜から日曜まで、毎日深夜二〜三時まで、三〜四か月続いた。そんな残業が信じられるか。</p>
<p>友人もこのプロジェクトにいて、毎日のように技術的な問題を愚痴っていた。相当おかしいものもあり、まったくの素人が犯すようなミスだった。</p>
<p>耗子はプロジェクトをコントロールできないので、自チームには残業をさせなかった。低レベルなミスは残業が原因だと考え、彼が残業反対で知られていたからだ。友人の話では、当時チームに半分冗談でこう言ったという。残業が八時を過ぎたら評価 3.25、価値観 C だ、と。（KPI のために命を削る連中を皮肉ったのだと思う。）</p>
<p>結局プロジェクトは失敗し、今も大規模なやり直し中だという。当時三か月やってバグだらけで本番に上げられず、上層部が動いたらしい。そこで責任追及があり、プロジェクト責任者が上のボスに「一部は耗子のチームが力不足で、残業してくれなかったからだ」と言った。翌日、ボスは友人と耗子チームの他のメンバーを、毎日深夜まで残るチームに異動させようとした。一日かけて話したが、誰一人行きたがらなかった。</p>
<p>実際には？ 耗子チームの二人は残業なしで自モジュールを期限どおり終え、発生バグの数もごく一部にすぎなかった。</p>
<p>それでもボスは強引に決めた。人は移さなくてよいが、仕事の割り当ては向こう側が行い、耗子は完全に空権となった。耗子のチームは事実上なくなった。</p>
<p>この二日、耗子が微博で「寒波」について論理破綻だらけの広報文を批判したあと、会社の広報部が動き、新しい上司は耗子のチームを直接引き剥がした。友人も含めチーム全体、耗子本人も知らなかった。ここを見ると、アリババのマネジメントは暴力的だろう。</p>
<p>これが耗子が微博で書いていた、「価値観」の違いによる迫害の一例なのだろう。</p>
<h2>他言語版</h2>
<li><a href="/about-chen-hao-325">中文</a></li>
<li><a href="/about-chen-hao-325-en">English</a></li>]]></content>
        <author>
            <name>Shemol</name>
            <email>shemol106@gmail.com</email>
            <uri>https://shemol.tech</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[2026.1.31]]></title>
        <id>https://shemol.tech/2026-1-31-ja</id>
        <link href="https://shemol.tech/2026-1-31-ja"/>
        <updated>2026-01-31T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[直近二週間ほどの考えと出来事。]]></summary>
        <content type="html"><![CDATA[<h1>2026.1.31</h1>
<p>水曜に研究室の忘年会が終わって正式に休みに入ってから書こうと思っていたが、延びに延びて、論文を投げ終わってからにしようと思い、今日ようやく寮の片付けが終わって時間ができた。</p>
<p>実際、何を書けばいいのかもはっきりしない。忙しいようで何に忙しかったのかもよくわからない。これまで学んだことを軽くまとめ、これからの学習の計画にするくらいのつもりだ。</p>
<p>まず全体のトーンを決める。AI は能力の増幅器なら、学習にもっと時間をかけ、アイデアをより沈殿させる必要がある。なので当面の主戦場は学習、より細かく深い学習だ。インターンや就活のことも焦りはあるが、どうもそれほど重要ではない気もする。どれも外からの評価軸だ。インターンで一社に入り、将来また一社に入るのは、他人のプロダクトを作り、他人のアイデアを実現することになる。なら自分のプロダクトを試してみたらどうか。何でもいい、良くても悪くても。自分のものを持ってからインターンに行くのも、きっと役に立つ。</p>
<h1>フルスタック（当面はフロントを）</h1>
<p>最近フロントを学んでいて、TypeScript、React、実プロジェクトを触っている。アイデアはいくつかあるがまだ手をつけていない。やるとなると時間がかかる。進めながら知識を復習し、インターン対策で暗記も必要かもしれない。来学期の重点になりそうだ。最終目標はフルスタック。一歩ずつ。まず Web、次にアプリ、やがてバックエンドやデータベースへ。</p>
<h1>Agent</h1>
<p>Agent はまだ粗いと感じる。もっと掘り下げたい。博杰先生の講座や配信、LangChain などのフレームワーク、SDK、API も見る。hook や skills なども追い、新しい技術をフォローする。</p>
<p>多くの先輩が出している frontend skills なども含めて。</p>
<h2>Memory</h2>
<p>細分化すると memory だ。memory はかなり重要だと思う。以前はファイルシステムか RAG くらいに考えていたが、浅すぎた。インターンで白婷先生の memory に関するレビュー論文を読み、いろいろな memory プロダクトに触れて、そう単純ではないとわかった。まだ掘るべきことがある。</p>
<h1>論文</h1>
<p>1 月 29 日が ICML の締切で、ここ数日は先輩に連れられて論文を猛スピードで書いた。図表をたくさん描き、先輩に図の描き方、表の作り方、レイアウトと論文の書き方をたくさん教わった。先輩と徹夜もした。忘れられない。先輩の論文の組み立て方はこれからも咀嚼したい。来学期は卒業のためにまた論文が必要だろうが、いったん脇に置く。当面はそこまで優先度は高くない。関連することは memory から disk に移してよい。</p>
<h1>インターン</h1>
<p>論文締切の八時以降、インターン用の書類を印刷し、翌日（三十日、昨日）memory 系スタートアップで一日インターンした。仲間もよい人ばかりで、この日も多くを学んだ。memory プロダクトや、みんなが使っているツールも見せてもらった。ただ自分の事情で、来週からは行かないことにした。自分のことに集中する。サマーインターン、日常インターンもいったん保留し、まず自分のプロダクトを作る。</p>
<p>インターンも外の評価軸だ。日常インターンがどれほど大事か、サマーが本採用にどれほど効くか、みんな言うが、本質は自分に構築力・実戦力があるかどうかだ。他人の OSS にコントリビュートするのも、いったん止めて、自分のものを試したい。時間はない。創り始める。ポートフォリオを持って応募したい。AI がジュニアエンジニアを減らしている流れとも合うと思う。もちろん基礎は固めないと。</p>
<h1>オープンであること</h1>
<p>もっとオープンになり、多くの人と接するよう意識してきた。しばらく前は論文のことで先輩とやりとりしたり、一日だけのインターンの経験も、その考えを強めた。人と関わる機会を増やし、先入観で線を引かない。</p>
<p>接し方もいろいろ試している。Kubernetes release team の shadow に入りたいなら、今から準備を始める。</p>
<p>表現力も鍛えたい。音声入力の typeless や autotyper などをもっと使えば、吃音も少し直せるかもしれない。</p>
<h1>運動</h1>
<p>今朝はスクワットを少しした。できる範囲から運動を積み上げるのも大事だと思う。彼女はよく「男は二十五を過ぎたら六十五」と言う……先輩も運動の大切さを強調する。きちんと向き合う。</p>
<h1>最後に</h1>
<p>あとは特にない。ときどきまとめるのも悪くない。打つのは速いが、だいたい無駄話。</p>
<p>最近は Things で Todo とプロジェクト管理をしている。悪くない。習慣にしたい。Telegram には読んだ記事のリンクを溜めている。</p>
<p>あとで Agent memory プロダクトを整理するかもしれない。</p>]]></content>
        <author>
            <name>Shemol</name>
            <email>shemol106@gmail.com</email>
            <uri>https://shemol.tech</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[2026.1.4 — Agent]]></title>
        <id>https://shemol.tech/2026-1-4-agent-ja</id>
        <link href="https://shemol.tech/2026-1-4-agent-ja"/>
        <updated>2026-01-05T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Cursor 公式サイトの模写に挫折してから、また Agent の話に戻ったメモ。コンテキスト設計、Skills、評価など。]]></summary>
        <content type="html"><![CDATA[<h1>2026.1.4 — Agent</h1>
<p>Cursor の公式サイトのページを真似しようとして挫折し、話題を Agent に戻した。</p>
<https://01.me/2025/12/silicon-valley-ai-insights-2025/>
<p>「AI プログラミング」のところで、大企業の日常開発の話がなかなかおかしい。コードを書いている時間が 15% しかない、というのは本当に好きになれない。</p>
<p>Research Code はエージェントやスクリプトを書くのに使える。</p>
<p>インフラ寄りのコード——Linux カーネルやコンセンサスプロトコルなど——は、どれもまだいまいち。</p>
<p>vibe coding のベストプラクティスは<strong>分割</strong>で、一度に生成するコードはできるだけ少なくする。  </p>
<p>もう一つは <strong>TDD（テスト駆動開発）</strong>。正直こちらのほうが Ralph 開発より信頼できる気がする…</p>
<p>大規模リファクタでは <strong>spec</strong> が重要だ。spec に基づいて Linux カーネルのファイルシステムを書く、という論文もあるらしいが、効果はまだ分からない。</p>
<p>厳密な <strong>evaluation system</strong> も、コードデータを積み上げるプロセスだ。今は誰もがデータの重要性を知っていて、各社がデータセットを作っている。  </p>
<p>シリコンバレー巨大企業の事情は本当に勉強になる。  </p>
<p>スタートアップの示唆もかなり参考になる。実際、スタートアップは業界全体の中で自分の<strong>エコロジカル・ニッチ</strong>を見つける必要がある。汎用領域は大手がやるので、かなり細かい垂直領域を取りに行く。</p>
<strong>工程実践から離れてはいけない</strong>。自分の手で試して初めて、その仕事に対する一番リアルな感覚がつかめる。vibe coding でもモデル学習でも、噂話だけでなく自分で試すこと。
<h2>技術実践</h2>
<h3>Context Engineering の枠組み</h3>
<li>System Prompt  </li>
<li>Tools  </li>
<li>Data Retrieval  </li>
<li>Long Horizon Optimizations（長期タスクの最適化）  </li>
<strong>Data Retrieval</strong> のパラダイムシフト：新しいやり方は <strong>just in time（必要なときに）</strong> の読み込み。
<li>戦略 1：軽量な識別子  </li>
<li>漸進的開示（progressive disclosure）  </li>
<li>自律的探索  </li>
<p>すべてのモデルは長いコンテキストで性能が落ちる。  </p>
<p>コンテキストウィンドウの容量を超えたときの対処：</p>
<li>コンテキスト圧縮  </li>
<li>Agent が<strong>明示的な記憶アーティファクト</strong>を持ち、「作業メモ」として判断・学習・状態を保存する。必要なときに検索し、すべてをコンテキストに載せ続けない  </li>
<li><strong>Sub-Agent</strong>。複雑なタスクを専門の Agent に分割し、各 Sub-Agent は集中した、明確で狭いコンテキストを持つ。main agent がオーケストレーションし結果を統合する  </li>
<h3>Skills 機構の仕組み</h3>
<p>Claude が動的に発見して読み込める。</p>
<p>``<code>markdown</p>
<p>pdf/SKILL.md（メイン）</p>
<p>├── YAML Frontmatter (name, description)</p>
<p>├── Overview（概要）</p>
<p>└── References: "For advanced features, see /reference.md"</p>
<p>pdf/reference.md（詳細リファレンス）</p>
<p>└── Advanced PDF processing features...</p>
<p>pdf/forms.md（専用機能）</p>
<p>└── PDF form filling instructions...</p>
</code>``
<li>Memory（記憶）  </li>
<li>Sub Agents & Collaboration（サブ Agent と協調）  </li>
<li>Dynamic Tool Calls（動的ツール呼び出し）  </li>
<li>Code Generation & Execution（コード生成と実行）  </li>
<li>Web Search（ウェブ検索）  </li>
<li>Agentic Search（エージェント型検索）  </li>
<li>Long Running Tasks（長時間タスク）  </li>
<h2>他言語</h2>
<li><a href="/2026-1-4-agent">中文</a></li>
<li><a href="/2026-1-4-agent-en">English</a></li>]]></content>
        <author>
            <name>Shemol</name>
            <email>shemol106@gmail.com</email>
            <uri>https://shemol.tech</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[year-review-2025-ja]]></title>
        <id>https://shemol.tech/year-review-2025-ja</id>
        <link href="https://shemol.tech/year-review-2025-ja"/>
        <updated>2026-01-01T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[2025 年の振り返り。]]></summary>
        <content type="html"><![CDATA[<h1>年末の振り返り——2025</h1>
<p>また一年が終わった。12 月 31 日に書こうと思っていたが、昨日は午前と午後に面接が一つずつあり、そのあと彼女のところへ行き、彼女の家で食事をしてから友人たちと合流して年を越し、時間がなかった。今日ようやく座って書き始める。</p>
<p>fedi で 2025 年初めの自分の投稿を探し、時間を 2025 年 1 月に戻した。2024 年末はアニメや漫画をたくさん観た。カウボーイビバップ、EVA を再視聴、ファイアパンチ、チェンソーマンを再視聴、ルックバックを再視聴。2025 年初めも映画やアニメを続けた。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867598746.png" alt="" />
<p>2025 年は自分の干支の年で、家族が法要で太歳を鎮めてくれ、お守りももらった。私は二週間もしないうちに完全に寮に置きっぱなしにした。一方でそういうものは信じないし、占いの先生のことも好きではなかった。2025 年全体を振り返っても、運命がもたらした思いがけない喜びや悲しみはあまり感じない。多くの出来事はよく観察すれば、その原因が辿れる。これからもよく観察する習慣を続け、引き続き connect the dots していきたい。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867599669.png" alt="" />
<p>自分で手を動かしてやってみたが、いくつか失敗もあったははは。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867600558.png" alt="" />
<p>いくつかの夢。</p>
<p>1 月に初めてコスプレに挑戦し、ルームメイトと一緒に行った。私は碇シンジ、彼は渚カヲル。一日中歩き回って疲れたが楽しかった。アスカに声をかけてもらった。</p>
<p>連絡を交わして、その方は安颯先生だと分かった。</p>
<p>17 日のイベントにも行った。ずっと前に買ったチケットで、行かないのは惜しいから。もう一つは、安颯先生にもう一度会いたかったから（笑）。</p>
<p>その後も QQ でやりとりし、学校に戻ってからも会う約束をした。</p>
<p>彼女の友人が占いで「2 月に必ず恋人ができる」と言われたという。私はその予言に乗っかり、2 月の最後の日に告白し、付き合うことになった。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867601527.png" alt="" />
<p>『四畳半神話大系』の言葉どおり。「恋人たちがめでたく結ばれることほど、取るに足りないことはない。」</p>
<p>今年の生活は例年と大きく変わった。ほとんど生活も儀式もなく、二人の予定を立て、祝日の段取りを考え、いろいろな角度から物事を考える必要が出てきた。信頼の課題、成長の見方、親密な関係の扱い方など、たくさんの課題があった。急に十キロ痩せて、元より十キロ太った。親密な関係は二人のことだが、向き合っているのは結局いつも自分自身だと思う。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867602537.png" alt="" />
<p>だから今年は、いろいろな面で成長した一年だった。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867603510.png" alt="" />
<p>英語は少しは上達したが、まだ足りない…日本語は年初に少し触っただけで、その後は続けていない…</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867604495.png" alt="" />
<p>北京で shrik3 サイトの管理者さんに会い、食事をしながら二、三時間話した。とてもいいサイト管理者の方だった！お土産もありがとう！</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867606085.png" alt="" />
<p>4 月の考えはまだこうだったが、下半期はエージェントと合意プロトコルに没頭した…</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867607192.png" alt="" />
<p>では、自分はどうすべきか。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867608179.png" alt="" />
<p>今年観たアニメは多くない。GQuuuuuuX は彼女とも好きな作品だった。夏休みは研究に没頭し、コスの時間もなかった。彼女が転職後、週末休みがない仕事になり、イベントにもあまり行けなくなった。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867609122.png" alt="" />
<p>4 月頃の株の波動で、少しだけ利益が出た。その後から年末まで、株はおおむね穏やかに上がり続けた。バリュー投資を実践する身として、持ち続けるつもりだ。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867610110.png" alt="" />
<p>不安になるとインターンに応募して水を試す癖がある。今年の年末も同じで、応募して中小三社から面接の機会をもらった。12 月最終日に二つ面接して、自分の課題がはっきりした。相変わらずアルゴリズムと面接で問われる定型的な基礎知識、プロジェクト経験もポジションに合わせる必要がある。面接前は緊張しても、面接官の前に出ると緊張が消える。面接のフローに入り、答えることだけに集中し、他は考えない。終わるとぐったりする。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867611159.png" alt="" />
<p>おおむね上の計画は満たせた。論文の作業もほぼ進み、実験の仕上げと論文執筆が残る程度になった。負荷が下がったので、インターン準備も並行できる。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867612269.png" alt="" />
<p>言い忘れていた。付き合ってから一緒にコスプレにも行った。一回目は彼女がレイ、私がトガタ（『ファイアパンチ』）。もう一回は EVA の朝阳大悦城イベント「潮流と芸術展」で、私が碇シンジ、彼女がアスカ。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867613303.png" alt="" />
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867614401.png" alt="" />
<p>自分にそんな才能があるのだろうか。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867615302.png" alt="" />
<p>自分の夢はどこにある？</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867616306.png" alt="" />
<p>今でもこう問いたい。Aaron Swartz は今日の LLM を喜ぶだろうか。知識へのアクセスは楽になった一方、Rob Pike が言うように、オープンソースのコードが学習に使われて逆に技術独占を強めるのは、Aaron が望んだ姿ではないだろう。</p>
<p>5 月末から 6 月初旬に、指導教員が先輩二人と私を食事に連れて行ってくれた。一人は NUS で博士課程にいる。とても外向的な先輩で、食事中は行程から学術ゴシップまで話が尽きなかった。体力のピークでは徹夜のあとでも 1000m を 3 分半以内で走れたという。記憶力もよく、ゴシップの細部まで覚えている。私が学校近くで映画を観に行くと言うと、すぐに映画館の名前を二つ挙げた。印象に残ったのはこの二点と、自分との差を感じた二点。自分はそこまで体力がなく、記憶力も弱く、枠組みだけ残って細部は忘れがちだ。</p>
<p>先輩にも苦しい時期があった。話してくれる範囲は聞いたが、本人以外誰も触れられない部分もあるに違いない。</p>
<p>その食事から間もなく、先輩についてエージェントと合意プロトコル関連の研究を始め、最近ようやく筋道が見えてきた。年末の振り返りで研究内容を長々とは書かないが、今年は研究でも成果が出せればいい。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867617196.png" alt="" />
<p>彼女とのやりとりのちょっとした笑い話。</p>
<p>vibe coding、いわゆる AI 支援プログラミングについても書きたい。2024 年は Cursor の補完でオープンソースの夏のタスクを終えた。それ以降の AI 支援には、正直あまり意識を向けていなかった。5 月までは、やはり手で書くべきで、AI に任せると実装の細部を取りこぼす、と思っていた。</p>
<p>指導教員の横断プロジェクトを手伝うとき、Trae で AI にコード生成を任せてみた。一気に数万行を書かれ、驚いた。正しいかどうかは別として（もちろんバグもある）、一気に万行を出せること自体が、注意すべきだと思った。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867618360.png" alt="" />
<p>あとの展開は周知のとおり。年末には汎用ソフトウェアの領域（Linux カーネルや合意プロトコルが絡むところでは AI の役割はまだ限定的）で、純手書きコードを標榜する人はもういない。</p>
<p>ただ、プログラマと vibe coder を分けるのは、やはりプログラミングへの深い理解だと思う。良い道具は効率を十倍にできるが、まずプログラマとしての素養が要る。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867619325.png" alt="" />
<p>今年のレストラン・オブ・ザ・イヤーは浜寿司だろう。何度も行った。スシローは後から一回行って人民元 300 元を超えるほどかかり、もう怖くてあまり行けない。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867620261.png" alt="" />
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867621202.png" alt="" />
<p>幼なじみが 7 月に北京でインターンし、一か月ほど再会した。その後はオーストラリアの学校に戻った。彼が語る異郷の孤独を自分がどこまで共感できるかは分からないが、自分にできることは限られていて、できる範囲で応えた。</p>
<p>才能の在り処：</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867622183.png" alt="" />
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867623336.png" alt="" />
<p>相変わらず不安だ…が、正しい方向には進んでいる。頑張ろう。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867624285.png" alt="" />
<p>Hawstein さんの文章を読んでから、何かが芽生えた。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867625158.png" alt="" />
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867626033.png" alt="" />
<p>8 月は一週間ちょっとしか帰省せず、威海で数日海を見て、食べ歩きした。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867626973.png" alt="" />
<p>どの道を選んでも、結局今の自分になる。</p>
<p>9 月には姪も幼稚園に入った。清明に帰省して従兄弟の結婚式に出たとき、姉がまた妊娠していると聞き、その後男の子だと教えてもらい、12 月末に無事出産した。姉と義兄におめでとう。</p>
<p>9 月と 10 月はアルゴリズム問題を集中的にやったが、その後研究でまた止まった。昨日の面接でアルゴリズムが書けず、暇を見つけてまた刷り直している。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867627990.png" alt="" />
<p>vibe coding の別の側面も見ておくとよい。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867629256.png" alt="" />
<p>母のスマホが重すぎたので替えてあげたく、結局 17 Pro Max を買い、自分は母の OPPO を使っている。OPPO は Google フレームワークが入っていて、とても快適だ。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867630264.png" alt="" />
<p>10 月末にようやく LeetCode Hot 100 を終えた…</p>
<p>12 月に開題答弁（研究計画の口頭審査）があり、なんとか無事通過した。次は 2026 年 12 月の中期答弁。そのあいだに論文を発表できれば、審査員も無理難題はしてこないだろう。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93-2025_1770867631378.png" alt="" />
<p>いくつかの考え。</p>
<p>12 月末に先輩が北京に来て、食事をし、よく喋った。先輩のことがより分かり、彼らの付き合い方の一端も窺えた。</p>
<p>今年の大きな変化の一つは、多くの時間を彼女と過ごしたことだ。クリスマスも、友人たちと年越しも、フェイシャルケアも一緒だった。来年も一緒に歩こう。</p>
<p>そして 2026 年は論文を出し、良いインターンを見つけたい。まずはそこを越えてから他の話をしたい。基礎を固める。</p>
<p>新しい年も、目でよく見、耳でよく聞き、小さな細部を逃さず、考え続けよう。</p>]]></content>
        <author>
            <name>Shemol</name>
            <email>shemol106@gmail.com</email>
            <uri>https://shemol.tech</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[2025.12.28]]></title>
        <id>https://shemol.tech/2025-12-28-ja</id>
        <link href="https://shemol.tech/2025-12-28-ja"/>
        <updated>2025-12-28T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[2025 年最後の一週間：論文サーベイ、食事会、OSS の PR、クリスマスとブログ。]]></summary>
        <content type="html"><![CDATA[<h1>2025.12.28</h1>
<p>月曜は手元の仕事に関連する論文をあさっていて、検索すればするほど増え、ついでにサーベイ論文を一篇書けるかもと思った。先輩に聞いたら、IJCAI に出せるとのこと。火曜に整理した文献リストを送った。</p>
<p>月曜の夜は先輩三人と中关村の「庭院江南菜」で食事。研究の話をたくさん聞いた。一人は研究が強く、外向的で気配りもでき、社交がうまく、十時過ぎまで話してから一緒に地下鉄で帰った。</p>
<p>水曜はフロントエンドをまた触り、Dify と Cherry Studio に PR を出した。小さく始めるつもりで、まずテストを書き、バグを直し、少しずつ深く入っていく。</p>
<p>木曜はクリスマス。午前中は彼女と三里屯でホラーではない（微ホラーすらもう無理で、地雷が多い）脱出ゲームをした。そのあと国貿で歩き、写真を撮り、スイーツとコーヒー、王府井付近のイルミも見た。夜は日本料理、高校の同級生と合流してアニメグッズ店（いわゆる「グッズ店」）へ。閉店後はカラオケに二時間、解散は午前一時近く——後半はみんなのテンションも下がり、典型的な低エネルギー勢はこの私である。</p>
<p>土曜の昼にその先輩の一人とビデオ通話し、コードを書くことに重心を置くよう勧められた。サーベイや Infocom のポスター原稿はたぶん向こう側で進める、とのこと。夜は大学の同窓会に出て、近況の話が弾んだ。</p>
<p>金曜から土曜は引き続き Dify／Cherry Studio への PR。日曜は部屋の片付けと個人ブログの体裁調整で、なかなか気に入った。</p>
<p>振り返ると、この一週間は妙に長く感じる——月曜に外食したのがつい最近のはずなのに、ずいぶん前のようにも思える。</p>
<a href="https://shemol.tech/">https://shemol.tech/</a>
<h2>他言語版</h2>
<li><a href="/2025-12-28">中文</a></li>
<li><a href="/2025-12-28-en">English</a></li>]]></content>
        <author>
            <name>Shemol</name>
            <email>shemol106@gmail.com</email>
            <uri>https://shemol.tech</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[2025.12.21]]></title>
        <id>https://shemol.tech/2025-12-21-ja</id>
        <link href="https://shemol.tech/2025-12-21-ja"/>
        <updated>2025-12-21T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[今週の週報。]]></summary>
        <content type="html"><![CDATA[<h1>2025.12.21</h1>
<p>表現力を上げる必要があると感じ、週記をまた書き始めることにした……</p>
<p>あと、記事を読んでも一度流し読みして終わりで、あまり定着していない。書けば考えももう少し沈殿するかもしれない。</p>
<p>先週末は AI Maker Summit に行った。きっかけは李博杰先生が fedi で講演に行くと書いていて、サミットのサイトを見たらチケットが三百元台だった。少し迷って痛い目で申し込んだ。現地に着いてみると博杰先生の講演はなく、翌日 fedi で李博杰先生はアメリカに行ったらしいと知った。金曜に彼がブログでシリコンバレーの AI 見聞を書いていて面白く、二、三度読み返した。</p>
<a href="https://01.me/2025/12/silicon-valley-ai-insights-2025/">https://01.me/2025/12/silicon-valley-ai-insights-2025/</a>
<p>個人的に役立ったのは AI コーディングのベストプラクティスの部分。タスクを分割し、AI に書かせるコードを一タスク 500 行以内に抑える。異なるモデルでコードレビューに回すのもよい。</p>
<p>AI Maker Summit 全体を通して、島姐（ダオジェイ）の講演、ポストトレーニング関連の一つ、投資家の講演がいちばん面白かった。投資家の「我々はものすごくたくさんのプロジェクトを見てきた……」という一言が、どっしりした感じがして好きだった。</p>
<p>島姐の講演で、隣のホールのあとに予定されていた agent memory system の話が出てきた。大規模モデルが記憶問題を完全に解決したら、そのようなプロジェクトはそもそも存在し得ない、という趣旨だった。</p>
<p>かなり刺さった。博杰先生のブログとも重ねて、インディー開発や小規模事業は自分のニッチをはっきりさせ、製品がモデルの強化で飲み込まれないようにしつつ、大手に先を越されないようにする必要がある。エンジニアであっても、大規模モデルの動向を押さえるために AI 論文や各社の tech report を読むべきだと思う。</p>
<p>終了から一、二日後に AI Maker Summit のグループに入れられ、翌日ある創業者が Product Hunt の宣伝を流した。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/2025.12.21_1770867667048.png" alt="" />
<p>読書そのものは AI に置き換えられない、という考え自体は自分も同意なので、Product Hunt を覗いた。有名な人と一冊の本を読む、というサービス。アプリを試したら本当に面白かった。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/2025.12.21_1770867668061.png" alt="" />
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/2025.12.21_1770867669345.png" alt="" />
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/2025.12.21_1770867670502.png" alt="" />
<p>横に Jobs と Munger の発言が並んでいて、以前読んだ『Poor Charlie's Almanack』や『スティーブ・ジョブズ』などの本を思い出した。過去に読んだ本の復習にもなり、つながりも見えやすい。Discord で 50% オフをもらって、躊躇なく Annual Plan を契約した。まとめ買いは得だから。</p>
<p>Readever で Tony Dinh の <em>My Indie Book</em> を続けて読んでいる。後半は少し飽きて、早く読み終えたくなってきた。今夜か明日には読み終えるかもしれない。</p>
<p>さきほどまた面白い例を見た。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/2025.12.21_1770867671927.png" alt="" />
<p>Jason Young 氏が OpenRouter のチャット形式を Claude Code 互換に直すのに多大な労力とお金をかけたが、すぐに OpenRouter が互換 API を出した、という話。</p>
<p>Tony Dinh の本にも似た選択が出てくる。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/2025.12.21_1770867673193.png" alt="" />
<p>今週の最初の三日は開題（研究計画）の発表準備に費やした。十分準備したつもりだったが、審査の先生には「プロダクトマネージャーが製品を説明しているようだ」と言われた。後から発表した同門によると、ある先生は七十点台をつけたらしい。院で再度の口頭審査が必要かはまだわからず、結果は来週……</p>
<p>今年の年次総括は 31 日まで待つことにした。毎日サプライズがあり得るから、一年の終わりは最終日まで待ちたい。</p>]]></content>
        <author>
            <name>Shemol</name>
            <email>shemol106@gmail.com</email>
            <uri>https://shemol.tech</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[bytedance-frontend-eg-camp-ja]]></title>
        <id>https://shemol.tech/bytedance-frontend-eg-camp-ja</id>
        <link href="https://shemol.tech/bytedance-frontend-eg-camp-ja"/>
        <updated>2025-11-10T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[バイトダンス（ByteDance）フロントエンド工程トレーニングキャンプのオンライン試験メモ。]]></summary>
        <content type="html"><![CDATA[<h1>バイトダンス フロントエンド工程トレーニングキャンプ オンライン試験</h1>
<p>単一選択とプログラミングに分かれていた。おおまかな知識点を記録する。</p>
<h1>単一選択</h1>
<p>主にデータ構造とアルゴリズム、コンピュータネットワーク、HTML・CSS・JavaScript の基礎。</p>
<h2>データ構造とアルゴリズム</h2>
<p>大学一年か二年のデータ構造・アルゴリズム試験を思い出す感じ…</p>
<p>ソート（バブル、クイックなど）が二、三問。</p>
<p>アルゴリズムの計算量はごく簡単な一問。</p>
<p>二分木の走査も二、三問あった印象。</p>
<p>スタックに関する問題が一問。</p>
<h2>コンピュータネットワーク</h2>
<blockquote>トランスポート層の非信頼プロトコルは何か</blockquote>
<p>UDP。</p>
<blockquote>DNS パケットに関する問題も一問</blockquote>
<p>覚えているのは、選択肢 A が「QR が 0 ならクエリ、1 ならレスポンス」だったことだけ。</p>
<h2>HTML、CSS、JS</h2>
<blockquote>ブラウザごとの margin / padding をどう揃えるか</blockquote>
<p>CSS Reset</p>
<p>``<code>css</p>
<p>* {</p>
<p>  margin: 0;</p>
<p>  padding: 0;</p>
<p>}</p>
</code>`<code>
<p>Normalize.css もあった気がする。あとでちゃんと学ぶ。</p>
<blockquote>CSS の float に関する問題で、誤った使い方はどれか</blockquote>
<p>選択肢に</p>
<p>A. float: **</p>
<p>B. float: none</p>
<p>C. float: left</p>
<p>D. float: right</p>
<p>とあって、とにかく A を選んだ。</p>
<blockquote>親要素の高さが潰れる（height collapse）のをどう直すか</blockquote>
<p>選択肢は忘れた。まだよく分かっていない。</p>
<p>1. </code>::after<code> 疑似要素でクリア。</p>
<p>2. モダンな方法：</code>display: flow-root<code>。</p>
<p>3. BFC を発火させる：</code>overflow<code> 系。欠点：</code>overflow: hidden<code> の本業は「はみ出しを隠す」こと。ドロップダウン、シャドウ、ツールチップなど親の外に出したい子があると切れる。</p>
<blockquote>次のうち BFC の応用に当たるのは</blockquote>
<p>BFC（Block Formatting Context）、ブロック整形コンテキスト。</p>
<p>BFC の応用：</p>
<p>1. 内部フロートのクリア（いちばんよくある）：子に </code>float: left/right<code> があり親の高さが潰れる。<strong>親</strong>に </code>overflow: hidden<code> や </code>display: flow-root<code> を付ける。</p>
<p>2. 垂直マージンの相殺（Margin Collapse）の防止：通常のフローでは<strong>隣接</strong>する兄弟ブロックの<strong>垂直マージン</strong>（</code>margin-top<code> と </code>margin-bottom<code>）が折りたたまれ、大きい方だけが残る。どちらか一方（または両方）を新しい親で包み、その親で BFC を発火させる（例：</code>overflow: hidden<code>）。</p>
<p>3. 適応型の二段／三段レイアウト：片側固定幅、もう片側が残りを埋める（左メニュー </code>float: left<code>、右メイン）。左を </code>float: left<code>（固定幅）。右のメイン側で BFC を発火（例：</code>overflow: hidden<code> や </code>display: flow-root<code>）。</p>
<p>面接では「次の属性のうち BFC を<strong>発火できる</strong>のはどれ？」という言い方もある。</p>
<p>よくある発火の仕方：</p>
<li></code>overflow: hidden<code><strong></code>;<code></strong> / </code>auto;<code> / </code>scroll;<code>（定番のハック）</li>
<li></code>display: flow-root;<code>（いちばんモダンで意味としてきれいな「BFC トリガー」）</li>
<li></code>float: left;<code> / </code>right;<code>（浮動要素自身が BFC を作る）</li>
<li></code>position: absolute;<code> / </code>fixed;<code>（絶対配置要素が BFC を作る）</li>
<li></code>display: inline-block;<code></li>
<li></code>display: table-cell;<code></li>
<li>Flex / Grid の子（</code>flex item<code> / </code>grid item<code>）</li>
<p>面接で </code>overflow: hidden<code> や </code>display: flow-root<code> が「高さの潰れ」「マージンの折りたたみ」「二段レイアウト」の文脈で出てきたら、それは BFC の応用と考えてよい。</p>
<blockquote>JS の </code>requestAnimationFrame<code></blockquote>
<p>JS の赤本を眺めたがまだよく分からない。また後で。</p>
<blockquote></code>setTimeout<code> と </code>Promise.then()<code> の出力順</blockquote>
<p>JS は実行時にタスクを三種に分ける。</p>
<li>同期タスク（Synchronous Code）：コールスタックですぐ走るコード</li>
<li>マイクロタスク：同期が終わった直後に走る。</code>Promise.then()<code> と </code>.catch()<code> のコールバックが代表例。</li>
<li>マクロタスク：同期とすべてのマイクロタスクのあと、キューから一つずつ。</code>setTimeout()<code> と </code>setInterval()<code> のコールバックがマクロタスク。</li>
</code>`<code>css
<p>console.log('1. 同步代码：开始');</p>
<p>// 安排一个宏任务</p>
<p>setTimeout(() => {</p>
<p>  console.log('2. 宏任务：setTimeout 1');</p>
<p>}, 0);</p>
<p>// new Promise 的执行器是同步的</p>
<p>new Promise((resolve, reject) => {</p>
<p>  console.log('3. 同步代码：Promise Executor');</p>
<p>  </p>
<p>  // 在 Promise 内部安排一个宏任务</p>
<p>  setTimeout(() => {</p>
<p>    console.log('4. 宏任务：setTimeout 2 (在Promise内部)');</p>
<p>    resolve(); // 在这个宏任务中，Promise 状态变为 fulfilled</p>
<p>  }, 0);</p>
<p>}).then(() => {</p>
<p>  // 当 promise被 resolve() 时，这个 .then() 才会被放入微任务队列</p>
<p>  console.log('5. 微任务：Promise.then 1');</p>
<p>});</p>
<p>// 安排一个立即 resolve 的 Promise</p>
<p>Promise.resolve().then(() => {</p>
<p>  console.log('6. 微任务：Promise.then 2');</p>
<p>});</p>
<p>console.log('7. 同步代码：结束');</p>
</code>`<code>
<p>1. </code>console.log('1. 同步代码：开始')<code> を実行する</p>
<p>2. </code>setTimeout 1<code> に遭遇し、そのコールバックをマクロタスクキューに入れる</p>
<p>3. </code>new Promise<code> に遭遇し、<strong>すぐに同期実行</strong>される </code>executor<code> 関数に入る。</code>console.log('3. 同步代码：Promise Executor')<code> を実行する</p>
<p>4. </code>setTimeout 2<code> に遭遇し、そのコールバックをマクロタスクキューに入れる</p>
<p>5. </code>Promise.resolve().then()<code> に遭遇。この Promise はすぐ </code>resolved<code> なので、その </code>.then<code> コールバックをマイクロタスクキューに入れる</p>
<p>6. </code>console.log('7. 同步代码：结束')<code> を実行する</p>
<p>7. マイクロタスクキューを空にする</p>
<p>8. 最初のマクロタスクを取り出して実行する</p>
<p>9. 再度マイクロタスクキューをチェックする</p>
<p>10. 次のマクロタスクを実行する。同一タスク内で </code>resolve()<code> が呼ばれる。</code>resolve()<code> が関連する </code>.then<code> をトリガーし、そのコールバックをマイクロタスクキューに入れる</p>
<p>11. マイクロタスクキューを空にする</p>
<h1>プログラミング問題</h1>
<p>ACM 形式はまだ慣れない。牛客で増やす。</p>
<blockquote>与えられた連立方程式から A、B、C の値が与えられたとき、実数解がいくつあるか</blockquote>
<blockquote>形：</blockquote>
<blockquote>X² + A²Y² + C = 0</blockquote>
<blockquote>Y² + Z² + B = 0</blockquote>
<blockquote>Z² + A = 0</blockquote>
<p>数学問題…式を整理して場合分けすればよさそう。</p>
<blockquote>桁数が k の整数のうち、各桁の和が m になるものはいくつあるか。</blockquote>
<blockquote>例：k=2、m=3 なら 12、21、30 の三つ</blockquote>
</code>`<code>python
<p>import functools</p>
<p>def solve_digit_sum(k:int,m:int)->int:</p>
<p>	@functools.lru_cache(None)</p>
<p>	def count_sequences(digits:int,target_sum:int)->int:</p>
<p>		if target_sum < 0:</p>
<p>			return 0</p>
<p>			</p>
<p>		if target_sum > 9*digits:</p>
<p>			return 0</p>
<p>			</p>
<p>		if digits == 0:</p>
<p>			return 1 if target_sum==0 else 0</p>
<p>			</p>
<p>		total_ways = 0</p>
<p>		for d in range(10):</p>
<p>			total_ways += count_sequences(digits - 1,target_sum - d)</p>
<p>		</p>
<p>		return total_ways</p>
<p>		</p>
<p>		</p>
<p>	if k<=0:</p>
<p>		return 0</p>
<p>		</p>
<p>	final_count = 0</p>
<p>	</p>
<p>	for d1 in range(1,10):</p>
<p>		final_count += count_sequences(k-1,m-d1)</p>
<p>		</p>
<p>	return final_count</p>
</code>``
<p>もう一問はたしか：</p>
<p>「コスト」を単純パス上の辺の重みの最大値と定義する。無向の単純連結グラフで、ノード対（u, v）のうち、最小コストがちょうど k になるものがいくつあるか、という趣旨だった。</p>
<p>難しすぎるので、いったん保留にした。</p>]]></content>
        <author>
            <name>Shemol</name>
            <email>shemol106@gmail.com</email>
            <uri>https://shemol.tech</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Context-Engineering-for-AI-Agents-with-LangChain-and-Manus-ja]]></title>
        <id>https://shemol.tech/Context-Engineering-for-AI-Agents-with-LangChain-and-Manus-ja</id>
        <link href="https://shemol.tech/Context-Engineering-for-AI-Agents-with-LangChain-and-Manus-ja"/>
        <updated>2025-10-20T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[LangChain と Manus を手がかりにしたコンテキストエンジニアリングの学習メモ。]]></summary>
        <content type="html"><![CDATA[<h1>LangChain と Manus に見る AI エージェントのコンテキストエンジニアリング</h1>
<p>数か月前、Manus がコンテキストエンジニアリングについて書いていた。  </p>
<a href="https://manus.im/blog/Context-Engineering-for-AI-Agents-Lessons-from-Building-Manus">https://manus.im/blog/Context-Engineering-for-AI-Agents-Lessons-from-Building-Manus</a>  
<p>エージェントのメッセージ履歴にすべてのコンテキストを載せる必要はないので、<strong>コンテキストのオフロード</strong>が要になる。</p>
<h1>LangChain 側の話</h1>
<h2>コンテキストをファイルシステムへオフロード</h2>
<p>定番のアイデアのひとつが <strong>ファイルシステム</strong> を使うこと。ツールメッセージの出力を例にすると、全文をそのまま履歴に戻さずファイルに書き出し、エージェントには必要最小限（参照用の手がかりだけ）を返す。Web 検索のようにトークンが重い結果を、ずっとコンテキストウィンドウに載せ続けない。</p>
<h2>コンテキストの削減（リダクション）</h2>
<p>要約や圧縮でコンテキストを減らす。ツール呼び出し結果の要約は直感的な一手。古いツール呼び出しと出力を剪定する考え方は、Claude の SDK などに組み込まれつつある。Cognition（エージェントアプリ）も、エージェント間ハンドオフでの要約に言及している。</p>
<h2>コンテキストの取得（リトリーブ）</h2>
<p>Claude Code は主にファイルシステムと単純な検索（glob / grep）に寄せている。オンデマンドでコンテキストを取りにいく方法はいろいろある。インデックス＋意味検索と、ファイル＋単純検索の両方が有効になりうる。</p>
<h2>コンテキストの分離（アイソレーション）</h2>
<p>特にマルチエージェントでは重要。サブエージェントごとにコンテキストウィンドウを分け、関心の分離ができる。</p>
<h2>コンテキストのキャッシュ</h2>
<p>langchain の open deep research：  </p>
<a href="https://github.com/langchain-ai/open_deep_research">https://github.com/langchain-ai/open_deep_research</a>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/Context+Engineering+for+AI+Age_1770869899254.png" alt="" />
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/Context+Engineering+for+AI+Age_1770869900279.png" alt="" />
<p>三フェーズ：<strong>研究範囲のスコーピング</strong>、マルチエージェントで行う <strong>研究本体</strong>、最後の <strong>ワンショット執筆</strong>。オフロードで研究計画のブリーフをコンテキストにべったり載せず別に保存し、状態やファイルシステムから参照する。研究計画を作り→オフロード→作業のあと必要なときだけメッセージ末尾に戻して執筆フェーズに使う。観察の要約でトークン重いサーフ系ツール呼び出しを削減し、研究内のサブエージェント間ではアイソレーションも使う。複数プロジェクトにまたがるアイデアの要約になっている。</p>
<h1>Manus 側の話</h1>
<p>早い段階から専用モデルに寄りすぎず、できるだけ長く <strong>汎用モデル＋コンテキストエンジニアリング</strong> に頼るべき、というスタンス。</p>
<h2>コンテキスト削減：コンパクション vs 要約</h2>
<p>Manus では各ツール呼び出しと結果に <strong>フル形式</strong> と <strong>コンパクト形式</strong> の二通りがある。コンパクト版は、ファイルシステムや外部状態から <strong>再構成できる情報を削る</strong>。例：ファイル書き込みツールが <code>path</code> と <code>content</code> を返したあと、環境にはファイルが既にあるなら、コンパクトでは長い <code>content</code> を落として <code>path</code> だけ残せる。エージェントが賢ければ必要時にパスから読み直せる。<strong>可逆性</strong>が重要で、過去の行動・観察に連鎖して予測するため、どの過去が後で効いてくるかは事前にわからない。コンパクションはこの意味で可逆な削減。  </p>
<p>一方、コンパクションだけではいずれ上限に当たるので、<strong>要約</strong>と組み合わせる。要約の前に重要部分をファイルへオフロードしたり、要約前の全文をログとして捨てず保存したりする。glob/grep でログから復元も可能。  </p>
<strong>コンパクションは可逆だが要約は不可逆</strong>。両方とも長さは減るが振る舞いは違う。
<p>共存のため <strong>コンテキスト長の閾値</strong> を追う。ハード上限（例：100 万トークン）の手前で、実用上 <strong>コンテキスト腐敗</strong>（繰り返し・遅い推論・品質低下）が始まる帯（だいたい 128k〜200k 前後を評価で特定）を <strong>削減トリガ</strong> にする。まずコンパクションから。コンパクションは <strong>古いツール呼び出しの半分だけ</strong> など、新しい方はフルのまま残してツールの使い方の「生きた例」を失わないようにする（さもないとモデルがコンパクト形式を真似てフィールド欠落を出す）。コンパクション後の <strong>実際の空き</strong> を測り、利益が小さければ要約へ。要約には <strong>常にフル版データ</strong> を使い、直近のツール呼び出し・結果は数件は要約せずフルで残して「どこまで進んだか」を維持する。</p>
<h2>コンテキスト分離：「共有メモリで通信するな」</h2>
<p>Cognition のブログでも、マルチエージェント間の情報同期は悪夢になりがち、とある。Go の言い回し：<strong>「共有メモリで通信するな。代わりに通信によってメモリを共有せよ」</strong>  </p>
<a href="https://chatgpt.com/share/68f4f8c3-baac-8004-9cf7-421375260909">https://chatgpt.com/share/68f4f8c3-baac-8004-9cf7-421375260909</a>  
<p>エージェントに置き換えると <strong>「通信」</strong> はメインがプロンプトだけをサブに渡し、サブのコンテキストはその指示だけ、という古典的サブエージェント。<strong>「共有メモリ」</strong> はサブがこれまでのツール履歴全体を見るパターン。タスクが短く明確で最終出力だけ欲しい（コードスニペット検索など）は通信型で単純に。複雑なディープリサーチのように中間の検索・メモが最終レポートに効くなら共有コンテキスト型の方が、全部ファイルに落として読み直すより遅延・トークン的にも合理的なことが多い。ただし共有は <strong>prefill が太くなり、システムプロンプトとアクション空間が違うと KV キャッシュを再利用しにくくコストが上がる</strong>。</p>
<h2>コンテキストオフロード：階層化されたアクション空間</h2>
<p>「オフロード」は作業コンテキストを外部ファイルへ、が基本だが、規模が大きくなると <strong>ツール定義自体</strong> がコンテキストを食い、<strong>コンテキスト混乱</strong>（誤ツール・存在しないツールの呼び出し）を招く。動的 RAG でツール説明を都度読み込む手もあるが、ツール定義が先頭にあると KV が毎回リセットされがちで、過去の「ツール削除」呼び出しが履歴に残るとモデルを混乱させうる。</p>
<p>Manus は <strong>三層</strong> を試している：(1) <strong>関数呼び出し</strong> — スキーマ安全（制約付きデコード）だがツール多すぎは混乱。だから <strong>固定数の原子関数</strong>（ファイル読み書き、シェル、検索、ブラウザ操作など）に絞る。(2) <strong>サンドボックスユーティリティ</strong> — セッションはフル VM。シェルから事前インストール済みコマンド（変換、音声認識、MCP を CLI 経由で叩く <strong>MCP CLI</strong> など）を使う。関数空間に MCP ツールを直注入しない。新能力はモデルの呼び出し空間を触らずにコマンド追加で足せる。大きい出力はファイルへ。grep/cat/less で処理できる。対話的 UI との低遅延往復には向きにくい。(3) <strong>パッケージと API</strong> — Python スクリプトで事前承認 API やパッケージを呼ぶ。株価の年間データのように、数値全部をモデルに入れずスクリプトで集計し要約だけ戻す。コードは合成しやすいが <strong>スキーマ安全ではない</strong>。インタプリタ／ランタイムで捌けるものはコード、それ以外はユーティリティや関数呼び出し、のハイブリッド。</p>
<p>モデルから見ると三層とも最終的には <strong>標準的な関数呼び出し</strong>（シェル経由、ファイル＋シェル実行など）に収斂し、インタフェースは単純でキャッシュにも有利。</p>
<h2>五つの次元をつなぎ、過剰設計を避ける</h2>
<p>オフロード・削減・取得・分離・キャッシュは独立ではない。<strong>オフロードと取得が効率的な削減を可能にし、安定した取得が分離を安全にする。一方で分離はコンテキストを遅くし削減頻度も変える</strong>。分離と削減はキャッシュ効率や出力品質にも効く。コンテキストエンジニアリングは <strong>相反しうる目標のバランスの科学であり技術</strong>。</p>
<p>最後に逆のことを言う：<strong>コンテキストの過剰設計は避けてほしい</strong>。Manus 公開から半年ほどで最大の伸びは、凝った管理層やトリッキーな検索より <strong>単純化と不要な細工の削除、モデルをもう少し信頼すること</strong> にあった。アーキテクチャを簡略化するたびに速く、安定し、賢くなった。目的はモデルの仕事を <strong>簡単にすること</strong> であり、難しくすることではない。<strong>build less and understand more</strong> を一つ持ち帰るならそれ。</p>
<h1>Q&A</h1>
<p>講演後の質疑応答を、元の英語対話の流れに沿って日本語訳したものです（話者の補足・Lance 氏のフォローも含みます）。</p>
<h2>Q&A — シェルツールとサンドボックス</h2>
<strong>Q:</strong> LLM はさまざまなシェルツールをどう呼び出すのか。どんなツールが存在し、どう起動するかをどう知るのか。Manus の多層サンドボックスの話も少し聞きたい。
<strong>A:</strong> まずシステムプロンプトにヒントがあり、プリインストール済みのコマンドラインユーティリティが特定フォルダにある、と伝えている。よく使うものについてはシステムプロンプトにコンパクトに埋め込む。ただし各ツールの <strong>使い方までは書かない</strong>。一覧だけ示し、すべて自前チーム開発で形式が揃っているので、<code>--help</code> フラグを安全に使える、とエージェントに伝えている。
<h2>Q&A — インデックスとファイルシステムによる文脈取得</h2>
<strong>Q:</strong> ファイルシステムの話が多かったが、インデックス化についてはどう考えるか。扱う文脈が十分に大きくなったときに、オンデマンドでベクタストアを立ち上げるようなことはするか。
<strong>A:</strong> この領域に唯一の正解はない。Manus では <strong>インデックス DB は使っていない</strong>。各サンドボックスはセッションごとに新しく、ユーザーは素早い対話を求めるので、オンフライでインデックスを構築する時間がない。だから Claude Code に近く、grep や glob に頼る。一方、長期記憶やエンタープライズナレッジベースのように <strong>情報量が桁違い</strong> の場合は、外部のベクターインデックスが依然として必要になる。規模と用途による。
<strong>Q:</strong> ユーザーが Manus アカウントで多くのセッションをまたいで使うとき、記憶という概念はあるか。Claude Code の Claude MD のようにセッション横断で残るものは。
<strong>A:</strong> Manus には <strong>knowledge</strong> と呼ぶ、ある種の <strong>明示的メモリ</strong> がある。例えば「毎回 Excel で渡して」と言うと、自動でメモリに挿入されるのではなく、「以前の会話からこう学びました。保存しますか？」というダイアログが出て、ユーザーが承認か拒否かを選ぶ。より自動に近づける方法も研究中。エージェントはチャットボットよりユーザーが訂正することが多く、例えばデータ可視化で中日韓フォントの問題が起きると、ユーザーが「CJK フォントを使え」と繰り返し指摘する。こうした <strong>集合的フィードバック</strong> を、パラメータを増やさないオンライン学習で活かせないか、という方向もある。
<h2>Q&A — 進化するモデルへの適応</h2>
<strong>Q:</strong> 終盤、足場を削ったことで得たものが大きかった、とあった。モデル能力が上がるからだとも思う。時間とともにモデルが良くなり足場を外せるのは大きな課題だ。数か月ごとにアーキテクチャを見直してモデルが良くなるたびに削除していくのか。
<strong>A:</strong> とても良い質問。Manus はすでに <strong>5 回大きくリファクタ</strong> している。3 月ローンチから 10 月までで 5 回。止まれない。モデルは強くなるだけでなく <strong>挙動そのものが変わる</strong>。モデルプロバイダと密に働くのも一手だが、内部では <strong>エージェントアーキテクチャを固定したまま弱いモデルと強いモデルを切り替える</strong> 評価をしている。弱い→強いで大きく伸びる設計は、将来「今日の強モデル＝明日の弱モデル」になっても持ちやすい。1〜2 か月ごとにレビューし、OSS や早期アクセスのプロプライエタリモデルで次のリリース前に準備することも多い。
<h2>Q&A — データ保存フォーマット</h2>
<strong>Q:</strong> データを保存するときのベストプラクティス。Markdown、プレーン、ログなど、何を優先するか。
<strong>A:</strong> プレーンか Markdown かというより、<strong>行指向（line-based）</strong> を優先する。モデルが grep や行範囲読みをしやすい。Markdown は便利だが、モデルによっては <strong>箇条書きを出しすぎる</strong> 癖がある（名前は伏せる）。だからプレーンを増やす方向も取る。
<h2>Q&A — 要約のプロンプト設計</h2>
<strong>Q:</strong> コンパクションと要約の話に続き、要約のプロンプトはどうするか。要約は不可逆なので、プロンプトが悪いと情報を失う。<strong>再現率（recall）</strong> を高めるプロンプト、どう考えるか。
<strong>A:</strong> 要約プロンプトはかなり最適化を試したが、うまくいったのは <strong>自由形式で AI に全部書かせない</strong> こと。スキーマ（フォーム）を決め、フィールドを埋めさせる。例：変更したファイル、ユーザーの目的、どこまで進んだか。こうすると出力が安定し、反復改善しやすい。<strong>自由形式の要約だけは避ける</strong>、が実務的な答え。
<h2>Q&A — 検索結果のコンパクション</h2>
<strong>Q:</strong> コンパクションについて確認したい。検索ツールの生出力が生メッセージで、コンパクション後はファイル名だけ、という理解で合っているか。
<strong>A:</strong> そう。ツール呼び出しだけでなく <strong>ツール結果</strong> にも適用する。Manus では、ファイルシステムや外部状態にオフロードできれば <strong>ほぼすべての操作が可逆</strong> に近い。多くのタスクにはすでに一意な識別子がある。ファイル操作ならパス、ブラウザなら URL、検索ならクエリ。
<strong>Lance:</strong> 検索を使うエージェントで、トークン過多のツール結果をメッセージ履歴に全部戻したくない。要約やコンパクションで要約だけ返すこともしたが、次の判断に全部の情報が必要なこともある。巨大なブロックを履歴に置き続けたくない。全体を戻して後で消す（Claude のやり方）、先に要約、全部載せてからコンパクションでファイルへのリンクだけ残す、など選択肢があるが、どう考えるか。
<strong>A:</strong> シナリオ次第。複雑な検索（単一クエリではない、複数クエリで重要なものだけ集める等）は <strong>サブエージェント</strong>（内部では agent as tool）に任せる。モデルから見ると <code>advanced_search</code> のような関数だが、裏は固定出力スキーマのワークフロー型サブエージェント。単純な Google 検索などは <strong>フル詳細をコンテキストに載せ、あとからコンパクションに頼る</strong>。また、コンパクションが思ったより早く来るかもしれないので、<strong>中間の洞察や重要発見をファイルに書かせる</strong> ように指示している。これがうまくいけば、時間が経って無関係になった古いツール呼び出しをコンパクションしても損失は小さい。
<h2>Q&A — エージェント間通信と MapReduce</h2>
<strong>Q:</strong> agent as tool は有効だが、エージェント間通信の問題も出る。Cognition の Walden Yen 氏も Devin で大きな課題だと書いていた。十分な情報を渡しつつ、サブの prefill を過剰にしないには。
<strong>A:</strong> 先月 <strong>Wide Research</strong> を出した。内部では <strong>agentic MapReduce</strong> と呼んでいる。Manus はセッション背後にフル VM があるので、メインからサブへ文脈を渡す一つの方法は <strong>同じサンドボックスを共有</strong> し、パスだけ渡すこと。サブへの情報送信自体はそれほど難しくない。難しいのは <strong>各エージェントから正しい出力を得ること</strong>。メインがサブを複数起動するときは、<strong>出力スキーマをメインが定義</strong> し、サブ側に <code>submit_result</code> という特別なツールを用意し、<strong>制約付きデコード</strong> でメインが定義したスキーマどおりに返すようにする。スプレッドシートのように集約され、スキーマで形が決まる。
<strong>Lance:</strong> Manus の設計で繰り返し出るテーマだ。要約でもエージェント間でも <strong>スキーマを契約</strong> にして、構造化された十分な情報を渡す。
<h2>Q&A — モデル選択とオープンモデル</h2>
<strong>Q:</strong> Anthropic を使っていると思うが、オープンモデルやファインチューニング、KV キャッシュの観点は。
<strong>A:</strong> 現状オープンソースモデルは使っていない。面白いことに理由は品質ではなく <strong>コスト</strong>。OSS は安くなるイメージがあるが、Manus の規模で <strong>本物のエージェント</strong>（入力が出力より遥かに長い）を作ると <strong>KV キャッシュが極めて重要</strong> で、分散 KV は OSS だけでは実装が難しい。フロンティア提供側の方がグローバル分散キャッシュの基盤が固く、計算すると <strong>フラッグシップの方が安い</strong> こともある。Anthropic だけでなく Gemini や OpenAI の進展も見ている。コーディングは Claude、マルチモーダルは Gemini、複雑な数学・推論は別、など <strong>タスクやサブタスク単位でルーティング</strong> できるのがアプリ会社の強み。
<strong>Lance:</strong> KV キャッシュで、プロバイダのどの機能を使っているか。Anthropic の input caching など。
<p>（※本パートでは具体的プロダクト名のキャッシュ機能について、講演では Anthropic の input caching 等に言及していた。）</p>
<h2>Q&A — ツール選択と階層化アクション空間（再訪）</h2>
<strong>Q:</strong> ツール説明のインデックスや意味類似で都度取得は使わない、とあった。ツールが多すぎる閾値は。ツール選択は古典的な難題だが。
<strong>A:</strong> まずモデルによる。経験則として <strong>30 を超えるツールは避けたい</strong>（頭の中の雑な数字だが）。汎用エージェントなら <strong>ネイティブ関数は極限まで原子化</strong> するので、実際アクション空間に入れる原子関数はそれほど多くない。Manus ではだいたい <strong>10〜20 個</strong>、あとはサンドボックス側。動的に引っ張る必要が薄い。
<strong>Lance:</strong> エージェントが直接呼べるツールが 10 個でも、スクリプトを書いて実行すれば実質的なアクション空間は爆発的に広がる。汎用ツールでそれを賄う、という理解でよいか。
<strong>A:</strong> だから Manus を汎用エージェントと言い切れる。<strong>コンピュータ上で動く</strong> から。コンピュータはチューリング完全で、ジュニアインターンが PC でできることは理論上エージェントも可能。シェルとテキストエディタがあればかなり完結し、あとはサンドボックスにオフロードできる。
<strong>Lance:</strong> コードエージェントでは毎回スクリプト生成→サンドボックス実行がツール呼び出しのように見える。Manus は直接ツールとサンドボックスのハイブリッドか。
<strong>A:</strong> 非常に重要。<strong>全部コード（Codec）に寄せる</strong> のも試したが、コードだと <strong>制約付きデコードが使えず</strong> 破綻しやすい。コードは先に述べたように大量データ処理など特化用途に強い。だから <strong>ハイブリッド</strong> が良い。
<h2>Q&A — プランニングと To‑Do</h2>
<strong>Q:</strong> Manus の to‑do ツールや、冒頭でタスクリストを作る話。
<strong>A:</strong> 当初は <code>to-do.md</code> パラダイムだったが、婉曲に言うと <strong>ターンをかなり浪費</strong> する。3〜4 月頃のログでは、行動の <strong>3 分の 1 が To‑Do 更新</strong> だった。今は <strong>構造化プランニング</strong>。画面下のプランナーは内部では agent as tool で別エージェントが計画管理。最新版では <strong><code>to-do.md</code> 依存はやめた</strong>。<code>todo.md</code> でも良い結果は出るが、トークン節約なら別手段を。
<h2>Q&A — マルチエージェント設計と役割</h2>
<strong>Q:</strong> プランニングエージェントが計画オブジェクトを出しサブを起動する、といった分割。推奨するサブエージェントの数は。
<strong>A:</strong> 設計次第だが、Manus は <strong>典型的な「役割別マルチエージェント」ではない</strong>。デザイナ／プログラマ／マネージャのように分けるのは <strong>人間会社のやり方の比喩</strong> で、人間のコンテキスト限界の反映に過ぎない。Manus もマルチエージェントだが <strong>役割で分割しない</strong>。巨大な汎用実行、プランナー、ナレッジ管理、データ API 登録など <strong>ごく少数</strong>。サブを増やすと通信が地獄になるので、増やす代わりに <strong>agent as tool</strong> に寄せる。
<strong>Lance:</strong> 人間組織図の比喩でサブエージェントを分けるのは見かけるが、無理なアナロジーかもしれない。ナレッジマネージャのタスクは何か。
<strong>A:</strong> ナレッジシステムがあり、ナレッジエージェントは <strong>ユーザーとエージェントの会話をレビューし、長期記憶に何を残すべきか</strong> を判断する。
<h2>Q&A — サンドボックス環境の安全とガードレール</h2>
<strong>Q:</strong> インターネット接続サンドボックスの安全、ガードレール。
<strong>A:</strong> インターネット付きサンドボックスは <strong>何でも危険</strong>。ガードレールに力を入れ、少なくとも <strong>情報がサンドボックス外に出ない</strong> ようにしている。プロンプトインジェクション対策で <strong>外向きトラフィックを検査</strong> し、トークン類が外に出ないようにする。ユーザーが意図的に外に出したい場合は、情報を落とす処理などでサンドボックス外への流出を防ぐ。ブラウザはさらに難しく、ログイン状態の永続化とページ内容の悪意（プロンプトインジェクション）が絡む。<strong>コンピュータ利用モデル</strong> のプロバイダ（Anthropic、Google 等）と密に連携し、ガードレール強化を待つ部分もある。今はブラウザやサンドボックス内の敏感操作で <strong>手動確認</strong> を求め、ユーザーが承認しないと自分で操作し続ける必要がある。モデル側のガードレールが良くなれば手動は減らせる、という漸進的アプローチ。
<h2>Q&A — 評価（Eval）</h2>
<strong>Q:</strong> 評価の話。Claude Code はコード eval は飽和気味で非公式 eval とドッグフード、など。Eval は有用か、何が有用か。
<strong>A:</strong> ローンチ当初は Gaia など学術ベンチを使ったが、<strong>ユーザー好みとずれる</strong> ことがわかった。今は三種類。(1) <strong>最重要</strong>：完了セッションごとのユーザー <strong>1〜5 星</strong>。平均評価がゴールドスタンダード。(2) 正解が検証可能な <strong>内部自動テスト</strong> と、読み取り専用中心の既存ベンチではなく <strong>実行・トランザクション系</strong> に寄せた自前データセット（サンドボックスで環境をリセットしやすい）。(3) <strong>インターン等の人間評価</strong> — サイト生成やデータ可視化は「見た目が良いか」の報酬モデルを設計しにくい。<strong>味（taste）の問題</strong>。
<h2>Q&A — 検証可能報酬 RL とツール呼び出しエージェント</h2>
<strong>Q:</strong> 検証可能報酬付き RL の潮流と、ツール呼び出しエージェントの対比。Claude Code はハーネス上で RL できる。自前 RL は。オープンモデルとの関係。
<strong>A:</strong> 事前学習・ポスト学習・RL は長くやってきたが、リソースが十分なら試す価値はある。ただし <strong>MCP はゲームチェンジャー</strong> で、固定行動空間でないと <strong>報酬設計が極めて難しく</strong>、ロールアウトとフィードバックのバランスも崩れる。MCP 対応モデルを自前で作るのは <strong>事実上ファウンデーションモデル開発</strong> に等しい。コミュニティのモデル企業が同じことをしている。今は RL にそれほど時間をかけず、<strong>パラメータフリー</strong> の個人最適化や集合フィードバックを探っている。
<strong>Lance:</strong> Anthropic が Claude Code のツール群で検証可能報酬 RL をしたとすれば、<strong>同じツール名・同じ説明</strong> をハーネスに載せれば同様の能力を引き出せるか。
<strong>A:</strong> 明確な答えはある。<strong>同じ名前は使わない方がよい</strong>。自前関数は要件や引数が違う。モデルがポストトレーニングで見た「公式ツール」と <strong>混同</strong> させたくないから。]]></content>
        <author>
            <name>Shemol</name>
            <email>shemol106@gmail.com</email>
            <uri>https://shemol.tech</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[context-engineering-ai-agents-langchain-manus-ja]]></title>
        <id>https://shemol.tech/context-engineering-ai-agents-langchain-manus-ja</id>
        <link href="https://shemol.tech/context-engineering-ai-agents-langchain-manus-ja"/>
        <updated>2025-10-20T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[コンテキスト工学の学習メモ：LangChain と Manus の実践と Q&A。]]></summary>
        <content type="html"><![CDATA[<h1>AI エージェントのためのコンテキスト工学（LangChain と Manus）</h1>
<p>数か月前、Manus は Context Engineering について書いたブログを公開した。  </p>
<a href="https://manus.im/blog/Context-Engineering-for-AI-Agents-Lessons-from-Building-Manus">https://manus.im/blog/Context-Engineering-for-AI-Agents-Lessons-from-Building-Manus</a>
<p>すべてのコンテキストをエージェントのメッセージ履歴に積む必要はない。だから<strong>コンテキストのオフロード（offloading）</strong>が要る。</p>
<h1>LangChain 側の知見</h1>
<h2>コンテキストをファイルシステムへ卸す</h2>
<p>よくあるのは<strong>ファイルシステム</strong>の利用だ。例：ツールの返り値が重い——全文をディスクに書き、エージェントの推論に必要な最小限の要約だけ会話に戻す。細部が要るときだけオンデマンドで読む。こうすればウェブ検索のようにトークンが爆発する結果が、ウィンドウを永遠に占有しない。</p>
<h2>コンテキストの圧縮</h2>
<p>要約や圧縮で負荷を下げる。ツール出力の要約は自然な一手だ。古いツール呼び出しとその出力を削る（Claude などの SDK でもますます一般的）。Cognition などのエージェントアプリも、エージェントの引き継ぎで要約により「承認／ハンドオフ」する話をしている。</p>
<h2>オンデマンドでコンテキストを取り戻す</h2>
<p>Claude Code はほぼファイルシステムと簡単な検索ツール（特に glob／grep）だけに頼る。インデックス＋意味検索でも、純粋なファイルシステム検索でも、十分効く。</p>
<h2>コンテキストの分離</h2>
<p>マルチエージェントでは特に重要。各サブエージェントが独自のコンテキストウィンドウを持ち、<strong>関心の分離</strong>しやすい。</p>
<h2>コンテキストのキャッシュ</h2>
<p>LangChain の open deep research：  </p>
<a href="https://github.com/langchain-ai/open_deep_research">https://github.com/langchain-ai/open_deep_research</a>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/Context+Engineering+for+AI+Age_1770869899254.png" alt="" />
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/Context+Engineering+for+AI+Age_1770869900279.png" alt="" />
<p>三段階：研究スコープの確定 → マルチエージェントで研究 → 最後に一括ライティング。オフロードを使う：まず研究 brief で計画を定義し、外部に保存しておき、brief を主コンテキストにずっと載せない（後から他の情報に薄まる）。brief は独立保存し、グラフ状態やファイルシステムから取り戻せる——研究を大きく回したあと、必要に応じてメッセージリスト末尾へ再投入し、執筆段階に使う。研究フェーズ内部では、reduction 要約でサーフ系ツールの大量観察を圧縮し、サブエージェント間の context isolation で隔離する——一つのプロジェクトに複数の手法をまとめた形だ。</p>
<h1>Manus 側の知見</h1>
<p>早すぎる専用モデル化は避け、スタートアップはできるだけ長く<strong>汎用モデル＋コンテキスト工学</strong>に寄せるべきだ、という立場。</p>
<h2>コンテキスト圧縮：Compaction と Summarization</h2>
<p>Manus では、各ツール呼び出しと結果に<strong>完全版</strong>と<strong>コンパクト版</strong>がある。コンパクト版は、ファイルシステムや外部環境から<strong>再構築</strong>できる情報を落とす。例：ファイル書き込みツールの返り値に path と content がある。ディスクに書いたあとは、コンパクト版では長大な content を捨て path だけ残せる——エージェントが十分賢ければ必要時にファイルを読めばよく、情報は失われていない。単に<strong>外部化</strong>しただけだ。この<strong>可逆性</strong>が重要。エージェントは過去の行動と観察を連鎖的に参照し、十歩先でどの行動が急に効いてくるか分からない。</p>
<p>Compaction だけでは限界があり、コンテキストは依然として上限に当たる。そこで従来の summarization と組み合わせるが、非常に慎重に——例：要約の前に重要段落をファイルへオフロードする。攻めた場合は要約前の<strong>コンテキスト全体</strong>をテキスト／ログにダンプし、後から復元可能にする。glob／grep でログを辿るだけの人もいる。モデルが十分賢ければ「要約の前」のコンテキストまで取り戻せる。</p>
<p>要点：<strong>compaction は可逆、summarization は不可逆</strong>。どちらも長さは縮むが、振る舞いはまったく違う。</p>
<p>両立には<strong>コンテキスト長の閾値</strong>の追跡が要る。モデルのハード上限は 1M トークンに届いても、だいたい 200k 付近から「context rot」（繰り返し、遅さ、品質低下）が始まる。評価で<strong>腐る手前の閾値</strong>（よく 128k–200k 付近）を見つけ、近づいたら負荷を下げ、<strong>先に compaction、次に summarization</strong>。Compaction も履歴全体である必要はない——最も古い 50% のツール呼び出しだけ compact し、新しい方は完全版のままにして、モデルが「正しいツールの使い方」をまねできるようにする。さもなくと、最悪、モデルがフィールド欠損の compact 形式を吐くようになり、逆に壊れる。Compaction 後は<strong>実際にどれだけ空いたか</strong>を確認し、数ラウンドで効果が小さいなら summarization。Summarization は<strong>常に完全データを使い、compact 版は使わない</strong>。直近のツール呼び出し／結果のいくつかは要約せず完全版のまま残し、「今どこまでやったか」をモデルに知らせ、トーンのドリフトを防ぐ。</p>
<h2>コンテキスト分離：通信と共有メモリ</h2>
<p>Cognition のブログの注意：マルチエージェント間の情報同期は悪夢になりうる。<strong>マルチプロセス／マルチスレッドの協調</strong>は古典的に難題で、Go コミュニティの格言を借りるなら「共有メモリで通信するな。通信によってメモリを共有せよ」。  </p>
<a href="https://chatgpt.com/share/68f4f8c3-baac-8004-9cf7-421375260909">https://chatgpt.com/share/68f4f8c3-baac-8004-9cf7-421375260909</a>
<p>エージェントに常にそのまま当てはまるわけではないが、二つのパターンを示す。「メモリ」を「コンテキスト」に置き換えると、<strong>「通信による」</strong>のは古典的なサブエージェント：親が指示を送り、子のコンテキストはほぼそのタスクだけ——<strong>短く明確で、最終結果だけ欲しい</strong>仕事向け（コードベースの一片を探すなど）。親は子の探索過程より結果だけ欲しい。Claude Code の task tool はよくこう使う。</p>
<strong>「共有メモリ／共有コンテキスト」</strong>は、子が前の完全なコンテキスト（全ツール履歴）を見られるが、system prompt と行動空間は別、という形。最終レポートのための深い調査では、中間の検索やメモが多く、メモをファイルに書いて子が全部読むより、子に<strong>直接コンテキストを共有</strong>した方が遅延とトークンを節約できる場合がある。代償は各サブエージェントの prefill が大きく入力トークンが高くなり、system／行動空間が違うと<strong>KV キャッシュを共有できず</strong>フル料金になること。
<h2>コンテキストオフロード：階層化された行動空間</h2>
<p>オフロードは作業コンテキストを外部ファイルへ、とよく言う。システムが大きくなり特に MCP を繋ぐと<strong>ツール定義そのもの</strong>がコンテキストを食い、ツールが多いと「context confusion」になり、モデルが誤ったツールや存在しないツールを呼ぶ。よくあるのはタスクに応じたツール記述の動的 RAG だが、ツール定義は文脈の前寄りにあり、<strong>KV が何度もリセット</strong>される。履歴にはもう無いツールへの呼び出しが残り、モデルを迷わせる。</p>
<p>Manus は<strong>階層化された行動空間</strong>を試している：モデルが三つの抽象レベルから選ぶ——(1) 関数呼び出し (2) サンドボックス内のユーティリティ (3) パッケージと API。</p>
<p>第一層の関数呼び出し：スキーマは安全（制約付きデコード）だが、ツールが多いと混乱しキャッシュも壊れる。Manus は<strong>固定数の原子関数</strong>：ファイル読み書き、シェル実行、ファイル／ネット検索、ブラウザ操作など。境界が明確で複雑なワークフローを組み立てられる。残りの能力は第二層の<strong>サンドボックスツール</strong>へ。各セッションはフル Linux VM で、シェルからプリインストール済みツール（変換、音声認識、コマンドライン経由の MCP——MCP ツールを関数空間に詰め込まず CLI で処理）を呼べる。新能力はユーティリティ追加で足せ、モデルの呼び出し空間を変えなくてよい。大きな出力はファイルへ書くかページングし、<code>grep</code>／<code>cat</code>／<code>less</code> で処理。代償はフロントエンドとの低遅延往復では可視化のチェーンが長くなること。</p>
<p>第三層の<strong>パッケージと API</strong>：Python でプリ承認 API やカスタムパッケージ（サブスクに API キー込み）を書く。大量メモリ計算が要るが生データを全部モデルに入れたくないタスク向け——例：一年分の株価をスクリプトで集計し要約だけ返す。コードと API を組み合わせ、一ステップに多くの呼び出しを連ねる（Code Act 論文に近い）。欠点は<strong>関数呼び出しほどスキーマが安全でなく</strong>制約デコードが難しいこと。Manus の方針：コンパイラ／インタプリタの実行時に任せられるものはコード。そうでなければサンドボックスツールか関数呼び出し。モデルから見れば三層とも統一された関数インタフェースで、UI が単純でキャッシュに効き、関数同士が直交する——例：サンドボックスツールもシェル関数経由、第三者 API はファイルに書いてシェル実行、など。</p>
<h2>五つの軸をつなぎ、過剰設計を避ける</h2>
<p>Offload、reduce、retrieve、isolate、cache は独立ではない：<strong>offload＋retrieve が reduction を効かせる。安定した retrieve が isolation を安全にする。isolation は連絡を遅くし reduction の頻度を変える</strong>。isolation と reduction はキャッシュ効率と出力品質にも効く。コンテキスト工学は複数目標のあいだのバランスの科学であり芸術だ。</p>
<p>最後の一文は一見矛盾するが：<strong>コンテキストの過剰工学は避けよ</strong>。Manus 公開から六、七か月で、最大の飛躍はしばしば、より凝ったコンテキスト層や検索テクニックを足すことではなく、<strong>単純化し、不要な細工を削り、モデルをもう少し信頼すること</strong>だった。アーキテクチャを簡素化するたび、システムは速く、安定し、賢くなる——コンテキスト工学の目的はモデルの仕事を<strong>簡単</strong>にすることであって、難しくすることではない。一つだけ持ち帰るなら：<strong>足場を少なく、問題を多く理解せよ（build less and understand more）</strong>。</p>
<h1>Q&A（要旨訳。原文の意図に合わせる）</h1>
<h2>シェルツールとサンドボックス</h2>
<strong>問</strong>：LLM はどのシェルツールがあり、どう呼ぶかをどう知る？多層サンドボックスはどう設計？  
<strong>答</strong>：システムプロンプトにプリインストール CLI のディレクトリを書き、よく使うツールはコンパクトに列挙。使い方は教えず名前だけ。<code>--help</code> を安全に使える旨を書く（フォーマットは統一）。
<h2>インデックス対ファイルシステム検索</h2>
<strong>問</strong>：ベクトル DB、オンザフライで索引？  
<strong>答</strong>：絶対の正解はない。Manus のサンドボックスセッションはたいてい新規で素早い対話が要り、<strong>索引をその場で建てる時間がない</strong>。Claude Code の grep／glob に近い。長期記憶や企業ナレッジなら外部ベクトル索引がまだ要る場合も。情報規模とコードベース／サンドボックスが長期かどうかによる。
<h2>セッション横断の記憶</h2>
<strong>問</strong>：Claude の CLAUDE.md のようなセッション横断の永続化？  
<strong>答</strong>：Manus には <strong>knowledge</strong> があり、明示的記憶に近い。「今後ずっと覚えて」と言うとポップアップで採用確認。より自動的な方法も探索中——例：可視化での中日韓フォントの修正が繰り返されるような<strong>集合的フィードバック</strong>を、パラメータ更新なしのオンライン改善に使う。
<h2>モデル進化に合わせたアーキ更新</h2>
<strong>問</strong>：モデルが強くなったら足場を外す。どのくらいの頻度で見直す？  
<strong>答</strong>：モデルは良くなるだけでなく<strong>振る舞いも変わる</strong>。ベンダーと密に連携。内部評価の一つ：<strong>エージェントアーキを固定したまま強弱モデルを切り替える</strong>——強モデルで伸び大きければアーキはより「未来向き」。Manus は三月公開から十月まで大きな改版を約五回。一、二か月ごとに振り返り、オープンソースや初期クローズドモデルで事前研究もする。
<h2>データ保存形式</h2>
<strong>問</strong>：Markdown、プレーンテキスト、ログ？  
<strong>答</strong>：<strong>行指向</strong>を優先し、<code>grep</code> や行範囲読みをしやすくする。Markdown はモデルがリスト記号を使いすぎることがある。
<h2>要約のプロンプト</h2>
<strong>問</strong>：summarization は不可逆。どうプロンプトする？  
<strong>答</strong>：凝ったプロンプトはいろいろ試したが、シンプルで効くのは<strong>完全自由形式にしない</strong>こと。複数フィールドのスキーマで表を埋めさせる（どのファイルを変えたか、ユーザーの目的、どこまで進んだか等）。出力が安定し、反復しやすい。
<h2>検索結果の compaction</h2>
<strong>問</strong>：検索ツールが重い。情報を残しつつ履歴を抑えるには？  
<strong>答</strong>：複雑な多段検索は <strong>sub-agent／agent as tool</strong> で、外からは一つの「高度な検索」関数に見せ、内部は固定出力スキーマのワークフロー。単純検索はまず完全結果を渡し compaction に頼る。モデルに中間の洞察をファイルへ書かせ、早すぎる compact で失わないようにする。
<h2>エージェント間通信と MapReduce</h2>
<strong>問</strong>：agent as tool は効くが、エージェント間は情報をどう渡す？  
<strong>答</strong>：Manus の <strong>Wide Research</strong> は内部的に agentic MapReduce：親子エージェントが<strong>同一サンドボックスファイルシステムを共有</strong>し、パスで引数を渡す。難所は多路出力のマージ。親は先に<strong>出力スキーマ</strong>を定義し、子は制約デコード付きの <code>submit_result</code> で返す。スキーマ付きの「表」を生成するイメージ。
<h2>モデル選択とオープンソース</h2>
<strong>問</strong>：オープンソース、微調整？KV cache？  
<strong>答</strong>：現状オープンソースは使っていない。<strong>品質だけでなくコスト</strong>の理由も。エージェントは入力が出力より遥かに大きく、分散 KV cache が重要。大手クラウドの基盤の方が割に合うことも。Anthropic だけでなく、コードは Claude、マルチモーダルは Gemini、複雑推理は OpenAI など、タスクやサブタスク単位でルーティングも可能。
<h2>ツール数と階層行動空間（再掲）</h2>
<strong>問</strong>：ツールが多すぎる？  
<strong>答</strong>：モデル次第だが、経験上<strong>一度に 30 を超えるツールは避ける</strong>（おおまかな目安）。汎用エージェントの原子関数は絞る。Manus はだいたい十個台の原子関数で、残りはサンドボックス側。ツール定義テーブルを動的に丸ごと引っ張るのは避ける。
<h2>プランニングと todo.md</h2>
<strong>問</strong>：to-do ツール？  
<strong>答</strong>：初期は <code>to-do.md</code> パターンで、<strong>ラウンドを浪費</strong>した（ときに三分の一が todo 更新）。今はより構造化された planner（根底も agent as tool）。最新版は <code>todo.md</code> に依存しない。使っても動くがトークンはかさむ。
<h2>マルチエージェントの役割分担</h2>
<strong>問</strong>：プランナー、ナレッジ管理？  
<strong>答</strong>：Manus はマルチエージェントだが<strong>「デザイナー／プログラマー／マネージャー」といった人間組織の硬い分割はしない</strong>——それは人間のコンテキスト限界から来る分業が多い。Manus のサブエージェントは少ない：大きな実行器、プランニング、knowledge 管理、データ API 登録など。追加は慎重。ナレッジエージェントは会話を見て、長期記憶に入れるべきか決める。
<h2>サンドボックスの安全</h2>
<strong>問</strong>：ネット接続サンドボックスのガードレール？  
<strong>答</strong>：少なくとも機密がサンドボックス外に出ないこと。外向きトラフィックは検査。ブラウザはログイン状態やページ注入など複雑で、企業の能力境界とコンピュータ利用モデルのベンダーとの協業に関わる。敏感な操作は手動確認が多い。ガードが良くなれば人手を減らす。
<h2>評価</h2>
<strong>問</strong>：正式な eval はまだ意味がある？  
<strong>答</strong>：初期は Gaia など公開ベンチを使ったが、<strong>ユーザー嗜好と大きくズレた</strong>。今は三種：(1) セッション終了ごとのユーザー 1–5 星（最重要）；(2) 検証可能な答えの自動テスト＋実行寄りの自前セット；(3) <strong>大量のインターン</strong>がサイト生成や可視化など「趣味」のタスクを評価——報酬モデルで美しさを判定するのは難しい。
<h2>RL と検証可能な報酬</h2>
<strong>問</strong>：自前 harness での RL との比較？  
<strong>答</strong>：長年 post-training／RL だが、MCP を支えると<strong>行動空間が固定でなく</strong>安定報酬と均衡 rollout を設計しづらく、ほぼ自前基盤になる。コミュニティモデル企業がその部分を担う。無パラメータのパーソナライズ／オンライン学習（集合的フィードバックなど）に関心。Claude Code と同名ツールを再利用するか：<strong>意図的に同名にしない</strong>。post-training データの内部ツールと混同し、パラメータ不一致で誤誘導するのを避ける。
<h2>コードエージェント：純スクリプトと混合</h2>
<strong>問</strong>：コードエージェントは毎ステップスクリプト生成してサンドボックス実行？Manus はツール直叩きとサンドボックスが混在？  
<strong>答</strong>：重要——Manus を<strong>コードだけ</strong>にしようとしたが、コード経路は<strong>制約デコードが効きにくく</strong>制御しづらい。コードは大量データをランタイムメモリで処理し結果だけ返す等に向く。関数呼び出し／サンドボックスツールと<strong>併用</strong>すべき。
<h2>Claude Code と「同名ツール」</h2>
<strong>問</strong>：Anthropic が Claude Code ツールで RL している。自社 harness で<strong>同名同記述</strong>なら似た能力が「解放」される？  
<strong>答</strong>：<strong>意図的に同名を避ける</strong>。自前関数のパラメータと要件が違い、post-training で見た内部ツールと現在実装を混同させたくない。]]></content>
        <author>
            <name>Shemol</name>
            <email>shemol106@gmail.com</email>
            <uri>https://shemol.tech</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[two-dark-clouds-over-agent-ja]]></title>
        <id>https://shemol.tech/two-dark-clouds-over-agent-ja</id>
        <link href="https://shemol.tech/two-dark-clouds-over-agent-ja"/>
        <updated>2025-10-19T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[エージェントを覆う二つの「暗雲」：環境とのリアルタイム対話と、経験からの学習。]]></summary>
        <content type="html"><![CDATA[<h1>エージェントを覆う二つの暗雲：環境とのリアルタイム対話と、経験からの学習</h1>
<a href="https://01.me/files/agent-learn-from-experience/dist/1">https://01.me/files/agent-learn-from-experience/dist/1</a>  
<p>Pine AI 共同創業者兼チーフサイエンティスト</p>
<h2>リアルタイム対話の課題</h2>
<li>音声対話の遅延が大きい（数十秒スケールに達しうる）  </li>
<li>GUI 操作は人間よりおおよそ 3〜5 倍遅い  </li>
<li>従来の ReAct ループの直列ボトルネック  </li>
<strong>ありうる技術的突破</strong>
<li><strong>SEAL アーキテクチャ</strong>（Streaming, Event-driven Agent Loop）  </li>
<p>  - 知覚層：音声信号のストリーミング処理  </p>
<p>  - 思考層：非同期の「観察—思考—行動」を伴う対話型 ReAct  </p>
<p>  - 実行層：VLA／TTS などのフィードバック閉ループ  </p>
<h2>経験から学ぶことの課題</h2>
<strong>中核の難しさ</strong>
<li>タスクごとにほぼゼロから始まる  </li>
<li>ドメイン知識が蓄積しにくい  </li>
<li>「熟練度」が時間とともに上がりにくい  </li>
<strong>三つの典型パラダイム</strong>
<p>1. <strong>Post-training</strong>：RL などでパラメータを更新  </p>
<p>2. <strong>In-context Learning</strong>：注意機構による「ソフト更新」  </p>
<p>3. <strong>外部化された学習</strong>  </p>
<p>   - RAG：経験の永続ストア  </p>
<p>   - Tool Generation：エージェントの自己進化  </p>
<p>姚順雨（Yao Shunyu）は二点を指摘している。一つは、エージェントがタスク遂行時に人間との十分な対話を欠くこと。もう一つは、経験から学ぶ仕組みが不足していること。（なのでそのブログを読みに行った。）</p>
<h2>後半戦 — 姚順雨</h2>
<a href="https://ysymyth.github.io/The-Second-Half/">https://ysymyth.github.io/The-Second-Half/</a>
<p>前半では、新しい学習法やモデルを次々に出し、各種ベンチマークでスコアを伸ばし、さらに難しいデータセットを設計してまた伸ばす、という循環を続けてきた。最終的に汎化に効く一本の道として <strong>強化学習</strong> が見つかった。この「レシピ」はおおむね標準化され、ループを回し続ければ性能はまだ伸びる。だから<strong>評価の仕方そのものを根本から見直す必要がある</strong>。</p>
<p>問題は、AI がチェスや囲碁で世界チャンピオンに勝ち、SAT や司法試験で多数の人を上回り、競技で金メダル級でも、<strong>現実世界（少なくとも経済／GDP の視点）ではそれに見合う変化が起きていない</strong>、という点だ。著者はこれを <strong>utility（便益）問題</strong> と呼ぶ。</p>
<p>過去のベンチマークと本番運用にはずれが多く、例を二つ挙げる。</p>
<li>ベンチはしばしば <strong>完全自動</strong> を仮定する：エージェントがタスクを受け取り自律実行し、報酬を得る。現実では <strong>人と何度も行き来しながら</strong> 進むことが多い——カスタマーサポートに長文を一本投げて、十分待てば一発で全部片づく、とは期待できない。  </li>
<li>ベンチはしばしば <strong>i.i.d.</strong> を仮定する：500 問を独立に解いて集計。現実のタスクは <strong>直列</strong> であることが多い——Google のエンジニアはコードベースに慣れるほど Google3 の問題が楽になるが、ソフトウェア工学エージェントが同一リポジトリで大量の問題を処理しても、同様に「慣れ」が現れないとは限らない。長期記憶が必要になるのは明らかで（<a href="https://yitaoliu17.com/assets/pdf/ICLR_2025_CER.pdf">先行研究</a>が一部実現）、だが学界にはその必要性を検証するベンチも、機械学習の根拠である i.i.d. 仮定に挑む胆力も、まだ不足している。</li>
<p>AI 開発の前半では、これらの仮定でベンチを回すのに足りた。「汎用手法」がその仮定のもとで安定して効くようになった今、<strong>後半戦の鍵</strong>は次のようなことだ。</p>
<li>実運用に即した新しい評価設定やタスクを設計する  </li>
<li>定めた方針で問題を解くか、新要素で手法を改善し、再び循環させる  </li>
<p>前半は漸進的な手法とモデルの積み重ねだった。後半では、ある程度それらがふるいにかけられる——常識外れの新しい前提を置けない限り、汎用解法が漸進的小改修を完全に凌駕し、そのとき初めて真に破壊的な研究の余地が開く。</p>
<p>文中の一節が特に秀逸だと感じた。</p>
<blockquote>思考（推論）は<strong>奇妙な</strong>行為である——外部世界を直接は変えないが、推論空間は開いて組合せ爆発する：一語、一文、一節、あるいは一万個のランダムな英単語を考えても、周囲はすぐには変わらない。古典的 RL にとってこれは極めて悪い取引で、意思決定をほぼ不可能にする。100 万入りの箱と空箱を二つ選べと言われたとき期待値は 50 万だが、空箱を無限に足すと期待値は 0 になる。ところが推論を任意の RL 環境の行動空間に入れると、言語の事前学習による事前分布で汎化し、異なる決定に柔軟なテスト時計算を割り当てられる。これは<strong>不思議</strong>なことで、ここでは説明しきれない；別稿かもしれない。<a href="https://arxiv.org/abs/2210.03629">ReAct</a> を読むと推論エージェントの原話がわかる。直観的には：空箱を無限に足しても、人生のいろいろなゲームでそれらを「見た」経験が、具体的なゲームで金の入った箱を選ぶのを助ける。抽象化すれば：<strong>言語はエージェント内の推論を通じて汎化する</strong>。</blockquote>
<h1>第1節：エージェントと環境のリアルタイム対話</h1>
<h2>音声エージェントのリアルタイム難題</h2>
<h3>根本矛盾：直列処理 vs リアルタイム要件</h3>
<li>待つ必要がある：聞き終わる → 考える → 考え終わって初めて話せる。  </li>
<li><strong>ブロッキング待ち</strong>：各段がボトルネックになりうる  </li>
<p>  - ユーザー発話終了（VAD）→ 音声認識（ASR）→ 完全な文  </p>
<p>  - 完全な文 → LLM 思考 → 思考完了まで出力なし  </p>
<p>  - 思考完了 → 文分割 → 音声合成（TTS）→ 音声応答  </p>
<li><strong>遅延の累積</strong>が人間の許容を大きく超える。  </li>
<h3>速さと遅さのジレンマ</h3>
<p>返すのが速いと誤りやすく、遅いとユーザーの忍耐を削る。聞きながら先読みし、聞きながら熟考するのも難しい。</p>
<h3>技術的ボトルネック</h3>
<strong>知覚段階</strong>
<li>音声：一文が終わるまで待つと遅延が大きい；断片をそのまま ASR に入れると認識率が落ちる。  </li>
<li>視覚：2K トークン級スクリーンショットの prefill が重い。  </li>
<strong>思考段階</strong>
<li>完全な入力がないと思考を始めにくい。  </li>
<li>ユーザ意図の先読みが難しい。  </li>
<li>Test-time scaling が遅延をさらに増幅する。  </li>
<strong>実行段階</strong>
<li>「考え終わる」まで行動できない。  </li>
<li>GUI 操作はしばしば毎ステップスクリーンショットを取り直して再判断する。  </li>
<h1>アーキテクチャの革新：SEAL（Streaming, Event-driven Agent Loop）</h1>
<p>核心は、対話を<strong>非同期イベント流</strong>として抽象化し、低遅延で割り込み可能なリアルタイム対話を実現すること。</p>
<p>1. <strong>知覚層</strong>：連続した現実信号（音声、GUI 動画）を離散イベント列にする。  </p>
<p>2. <strong>思考層</strong>：非同期イベント処理——聞きながら考え、考えながら話し、「思考と行動」が交錯する。  </p>
<p>3. <strong>実行層</strong>：離散の行動指令を連続信号（TTS 波形、マウス軌跡など）に戻す。  </p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/Two+dark+clouds+over+Agent_+re_1770869824890.png" alt="" />
<h2>第1層：知覚層</h2>
<strong>入力</strong>：順序信号——音声ストリーム、GUI 動画ストリーム。  
<strong>出力</strong>：<code>speech_start</code>、<code>interrupt</code>、笑い声、音声断片、<code>ui_change</code> など。  
<p>従来の VAD+ASR の代わりに<strong>ストリーミング音声知覚モデル</strong>。オープンな自回帰 LLM ベースのストリーミング音声知覚：</p>
<li>Whisper 型 ASR とは異なり、自回帰は認識遅延の低減に寄与する。  </li>
<p>  - 入力音声トークンをストリーム処理  </p>
<p>  - テキストと音響イベントをストリーム出力  </p>
<li>オープン LLM の後学習  </li>
<p>  - 対話コンテキストを保持し in-context learning 可能で、ユーザー固有情報やドメイン語の認識が大きく改善しうる。  </p>
<p>  - 世界知識・常識により、ブランド名や金額などの認識率が上がりうる。  </p>
<p>出力はテキストだけでなく音響イベントも含む。  </p>
<p>リアルタイムの書き起こし断片；音響イベント用の特殊トークン例：</p>
<li><code><speak_start></code>  </li>
<li><code><speak_end></code>  </li>
<li><code><interrupt></code>  </li>
<li><code><emotion:happy></code>  </li>
<li><code><laugh></code> <code><sigh></code>  </li>
<li><code><music></code>  </li>
<h2>第2層：思考層</h2>
<strong>イベント駆動ループ</strong>に基づく：割り込み可能で非同期——聞きながら考え、考えながら話す。
<strong>入力</strong>：イベントキューからの離散イベント流。  
<strong>出力</strong>：交錯する思考と行動指令。  
<h2>中核の革新：対話型 ReAct</h2>
<p>従来の ReAct：</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/Two+dark+clouds+over+Agent_+re_1770869826813.png" alt="" />
<p>対話型 ReAct：</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/Two+dark+clouds+over+Agent_+re_1770869827574.png" alt="" />
<h3>対話型 ReAct：聞きながら考える</h3>
<p>従来の ReAct：いったん割り込まれると、それまでの思考はすべて無効になり最初から。  </p>
<p>対話型 ReAct：<strong>割り込み前の推論を保持</strong>し、新しいユーザー入力を足したうえで、元のコンテキスト上で思考を続ける。</p>
<h3>対話型 ReAct：考えながら話す</h3>
<p>「前置き」などで深い思考の時間を稼ぎ、初発話までの遅延を下げる。</p>
<h2>第3層：実行層</h2>
<p>離散の行動指令を連続した現実信号に写像する。  </p>
<strong>入力</strong>：<code>speak(…)</code>、<code>click(…)</code> など。  
<strong>出力</strong>：順序信号（音声波形、マウス軌跡など）。  
<h2>GUI 操作の「ラストワンマイル」</h2>
<p>エージェントが座標を直接出すのは難しい。ロボット分野の VLA を参考に、RL 後学習でモデルが行動を直接出す。</p>
<li>案1：主モデルがマウスクリック座標を直接出力。  </li>
<li>案2：人間のマウス軌跡を模した VLA を別途学習し、「移動—微調整—クリック」の閉ループ。  </li>
<p>音声合成も人間らしく：まず注釈付きテキストを生成し、それを TTS で音にする。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/Two+dark+clouds+over+Agent_+re_1770869828406.png" alt="" />
<h1>エージェントが経験から学ぶ</h1>
<strong>パラダイム1：Post-Training</strong>  
<p>方法：パラメータ更新（後学習）</p>
<li>勾配で重みを更新  </li>
<li>大量のアノテーション付きデータが必要  </li>
<li>学習後はモデルが固定  </li>
<li>学習は遅く高コスト  </li>
<strong>パラダイム2：In-context Learning</strong>  
<p>方法：コンテキスト内学習</p>
<li>注意機構による暗黙の学習  </li>
<li>長いコンテキストを「一時記憶」として使う  </li>
<li>効果は多くの場合セッション内にとどまり、永続化しにくい  </li>
<strong>パラダイム3：外部化された学習</strong>  
<p>方法：知識と手順を外に出す</p>
<li>RAG：効率的で信頼性が高く、幻覚を抑える知識注入  </li>
<li>Tool-generation：手順をコード化し自己進化  </li>
<li>パラメトリック記憶の限界を突破  </li>
<strong>実践</strong>：Contextual Embeddings + Contextual BM25 + Reranking + Top-20 chunks  
<strong>Fine-tuning vs RAG</strong>（経験則）  
<p>論文 <em>Fine-Tuning or Retrieval? Comparing Knowledge Injection in LLMs</em>  </p>
<a href="https://aclanthology.org/2024.emnlp-main.15.pdf">https://aclanthology.org/2024.emnlp-main.15.pdf</a>  
<p>要点：RAG の方がしばしば有効で、微調整に伴う忘却や幻覚リスクを避けられる。</p>
<strong>Tool Generation — エージェントの自己進化を支える</strong>  
<a href="https://arxiv.org/abs/2505.20286">https://arxiv.org/abs/2505.20286</a>  
<strong>最小プリセット原則</strong>
<li>極小アーキテクチャ：少数の中核能力（例：Web プロキシ）だけ残す  </li>
<li>過剰設計を避ける：複雑なツールチェーンやワークフローを事前に仮定しない  </li>
<li>汎用を優先：ドメインのハードコードを減らす  </li>
<strong>最大自己進化メカニズム</strong>  
<p>中核能力：</p>
<p>1. ツールの自作：タスクに応じて新ツールを生成  </p>
<p>2. 能力強化：既存ツールを反復改善  </p>
<p>3. 経験の再利用：成功パターンを再利用可能な部品に固定  </p>
<strong>MCP-Zero と能動的ツール発見</strong>  
<p>従来のジレンマ：</p>
<li>全量注入：ツール一式がトークンを圧迫 → コンテキスト爆発  </li>
<li>静的検索：初期クエリだけでツールを選び、タスクの変化（例：デバッグでファイルシステム＋コード分析＋コマンド実行が必要）を予測できない  </li>
<strong>MCP-Zero：受動から能動へ</strong>  
<p>核心：エージェントに<strong>能力の欠落を能動的に見つけ、必要に応じてツールを申請</strong>させる</p>
<p>1. 能動的ツール要求：エージェントが構造化されたニーズを生成  </p>
<p>2. 階層的セマンティックルーティング：まずサーバを絞り、次にツールをマッチ  </p>
<p>3. 反復的な能力拡張：実行中に動的に発見しツールチェーンを組み立てる  </p>
<p>学習を外に出し、注意ウィンドウの制約を超えるのは必然の流れ。</p>
<p>七十年の AI 研究史で最大の教訓のひとつは、<strong>計算をうまく使う汎用手法が最終的にいちばん効き、しかも差が大きい</strong>、ということかもしれない。</p>]]></content>
        <author>
            <name>Shemol</name>
            <email>shemol106@gmail.com</email>
            <uri>https://shemol.tech</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[2025年 バークシャー株主総会 メモ]]></title>
        <id>https://shemol.tech/2025-buffet-ja</id>
        <link href="https://shemol.tech/2025-buffet-ja"/>
        <updated>2025-05-05T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[2025年バークシャー・ハサウェイ年次株主総会での質疑応答メモ。忍耐、後継者、若者へのアドバイス、低潮、執念、テック大手、教育など。]]></summary>
        <content type="html"><![CDATA[<h1>2025年 バークシャー株主総会 メモ</h1>
<strong>ニュージャージーから来ました。今日ここで質問の機会をいただけて本当に良かったです。投資の原則や忍耐の大切さをよくお話しされますが、もう一度ヒントをいただけますか。</strong>
<blockquote>チャンスは瞬く間に過ぎるので、ときには即断が必要だ。1966年、ある電話があった——詳細は控えるが——ある女性が夫の会社を 600 万ドルで売りたいと言ってきた。200 万ドルの資産と 900 件以上の事業が含まれ、税引前利益は年 200 万ドルになる見込みだった。かなり魅力的な価格に聞こえた。</blockquote>
<blockquote>チャーリーとすぐ話し合った。チャーリーはその女性を知らなかったが、彼女の共同経営者 Ben Rosser については知っていた。売り手は裕福な未亡人か、あるいは夫が何らかの理由で急いで手放したいのかもしれないと推測した。12 月 31 日まで帳簿を調べ、なぜ売るのか理解しようとしていた。</blockquote>
<blockquote>翌朝、Will Phillips から電話があった。東海岸の人は中西部の人に偏見を持つことがある。もしその女性がアイオワ出身なら、東部の人とは振る舞いが違うかもしれない。年率 33% も見込める取引の前では、忍耐を保つのは本当に難しい。</blockquote>
<blockquote>それで分かったのは、本当に良い機会が来たら、待つ必要はないということだ。合理的で利益の見込めるチャンスが現れたら、断固として動け。忍耐も大切だが、それ以上に<strong>機会を見抜く鋭さと実行する決断</strong>が重要だ。もちろん、市場のチャンスは永遠に誰かを待ってはくれない。</blockquote>
<blockquote>忍耐は確かに大切な資質だ。でもそれ以上に、チャンスが来たときに<strong>即断できるか</strong>だ。突然目の前に現れることがある——5 秒の電話かもしれない——その場で取るに値するかすぐ判断しなければならない。ビジネス判断でいちばん避けるべきは<strong>自分を疑いすぎること</strong>だ。多くの場合、ためらうからチャンスを逃す。だから商売はこんなに面白く、僕にとって最大の喜びなのだ。</blockquote>
<blockquote>90 代に入り、富も普通の人をはるかに超えているが、それでも毎朝オフィスに来るのが待ち遠しい。単なる仕事ではなく、人の役に立ち、価値を生む喜びの源泉だ。その熱意は受け継がせたい。子どもたちにもその喜びを味わってほしい。</blockquote>
<blockquote>チャーリーと 60 年以上築いてきたパートナーシップのように、いつも志を同じくする人と働いてきた。その協力の形は一度も裏切らなかったし、新しい仲間を探す基準にもなっている。だから今日ここにいる取締役やチームがこんなに息が合う。チャンスが本当に良いと確信したら、もうためらうな——すぐ動け。</blockquote>
<strong>カリフォルニアから来ました。こんな総会を準備してくださりありがとうございます。ジョブズ以外にあのような会社を創れる人はいないとおっしゃいましたが、ティム・クックは素晴らしい仕事をしています。ウォーレン、あなたはバークシャーを創り上げた方で、グレッグ・アーベルはその中でも意外な人材だとおっしゃいました。でもご本人はごく普通の方に見えます——普通と言うのは褒め言葉として——なぜ今後数十年、グレッグ・アーベルが最高の後継者だと思われるのか教えてください。</strong>
<blockquote>とても重要な質問だ。僕たちの業界で優れた投資チームを組むのは容易ではない。アメリカのような広い市場では、資本運用にふさわしい土壌を育てるには長い時間がかかる。特に資本配分ではそうだ。時間の蓄積が要るし、互いに信頼し合える志を同じくする仲間を見つける必要がある。何年も、投資判断には慎重に向き合い、リスクを丁寧に評価してきた。</blockquote>
<blockquote>昨日、会社の展示ブースを見に行った。情熱あふれる社員の印象が強く残った。見返りを求めず、ただ仕事を愛している。その姿勢は尊敬に値する。自分が愛する仕事を選ぶことは極めて重要だ。キャリアで五人のボスに会ったが、それぞれから大きく学んだ。最終的に起業を選んだのは、好きなことをするのが最高の働き方だからだ。</blockquote>
<blockquote>誰も僕のように恵まれているわけではない。七、八歳で生涯の情熱を見つけられるとは限らない。有名な指揮者グレン・ミラーの話のように、楽団は最初は無名で、1941年になって独特のスタイルで一気に名を上げた。若いうちに本当に愛せる仕事を見つけられたなら、初任給の高低はあまり気にしなくていい。ただし正しい会社とボスを選べ。やるに値しない仕事もある。</blockquote>
<blockquote>僕たちは偉大な国に、最高の時代に生きている。だからバトンをグレッグ・アーベルに渡す決断をした。ただしバークシャーのような企業を築くのは一朝一夕ではない。財務の世界にはこういう言葉がある。「一度豊かになれば十分。不必要なリスクを冒す必要はない。」市場には常に、借金やレバレッジで儲け、最後に誰かが引き受けてくれることを当てにする人がいる。覚えておいてほしい——そうした投機はいつか代償を払う。</blockquote>
<blockquote>人生をやり直せるわけではないが、やり直せるなら、それでも好きなことを選ぶだろう。今に至るまで、僕にとって実に素晴らしい旅だった。</blockquote>
<blockquote>先ほどの質問への補足だが、適切なチャンスにまだ出会えなくても、過度に焦る必要はない。人生には適切なタイミングがあり、本当に合う人にも出会える。生涯の伴侶を探すようなもので、一目惚れすることもある。一人を逃しても、もう適切な人に会えないわけではない。待つ価値のある人や出来事は、最もふさわしい瞬間に現れることが多い。</blockquote>
<strong>メリーランドから来ました。今日お時間をいただきありがとうございます。若者で投資に興味があります。あなたが若い頃に学んだ教訓は何でしょうか。自分なりの投資哲学を育てたいのですが、アドバイスをお願いします。</strong>
<blockquote>とても良い質問だ。若い頃に誰かこんなアドバイスをくれればよかった。本質は<strong>どんな人と組むか</strong>だ。毎回完璧な決断を期待してはいけない。人生が特定の方向に進むなら、尊敬できる人を仲間にしろ。ここ数年組んできた友人たちのように——規模はバークシャーほどではないが——志を同じくする者と進むのが賢明だ。残念ながらこうした道理は、人生の後半になってようやく腹に落ちることが多い。</blockquote>
<blockquote>富豪の成功パターンを盲信するより、心から尊敬できる賢者を探せ。僕自身もそうしてきた。優れた人から学び、実践の中で成長する。すでに意味のある仕事を見つけ、切迫した金銭的プレッシャーがないなら、チャーリー・マンガーのように賢者と時間を過ごせ。僕の言う人たちは職分を超えて価値を生み出している。そうした仲間を見つけ成功を分かち合えるのは大きな幸運だ。すぐに見つからなくても諦めるな。努力を続ければ、いつか志を同じくする人に出会える。</blockquote>
<blockquote>GEICO に就職面接に行ったとき、閉ざされたドアの向こうに誰がいるかまったく分からなかった。でも十分後、人生を変えてくれる人に出会った。<strong>自分を助けてくれた人を忘れるな。行動で報いろ。</strong>もちろん、うまくいかないこともある。恵まれた環境にいるなら、それを大切にしろ。アメリカで生まれただけで世界の大多数より恵まれている——世界 80 億人のうちアメリカ人は 3 億強だ。それ自体がアドバンテージだ。ただし、<strong>自分の原則に背いて他人に迎合してはいけない</strong>。</blockquote>
<blockquote>投資は僕にとって楽しみに満ちている。多くの人は金を稼いだら業界を去るが、本当に探すべきは<strong>生涯愛せる仕事</strong>だ。トム・マーフィーのように人の可能性を見抜く人は稀だ——彼は 98 歳まで、人の潜在能力を見抜く鋭さを保っていた。より良い自分になるには、そうしたメンターを探せ。バークシャーの成功もそこにある。1963年から組んできた Sandy Gottesman、30 年以上の Walter Scott、そして 25 年になるグレッグ・アーベル……こうした人たちと進む道は、いつも正しい。</blockquote>
<blockquote>面白いことに、そうすると長生きもするらしい。僕も仲間たちも妙に長寿だ——コーラをよく飲むから（笑）というより、みんな愛することをしているからかもしれない。幸せな人は長生きしやすい。それが僕の実感だ。</blockquote>
<strong>バークシャーの皆様、アジット・ジェイン、グレッグ・アーベル、ピーター・チェンと申します。上海から来ました。バークシャーの株主総会は初めてです。人生にも上り坂と下り坂があると思いますが、あなたにも最低点はありましたか。最低点をどう乗り越え、難関を突破しましたか。</strong>
<blockquote>誰の人生にも高揚と低落があり、それは普通のことだ。質問ありがとう。僕にとっては取るに足りない話かもしれない。チャーリーを例に取っても、彼も多くの苦しい瞬間を経験した。それが人生の一部だ——誰も永遠に順風満帆ではいられない。</blockquote>
<blockquote>最高の助言をするとは言わないが、<strong>低潮は一生のうち何度も訪れる</strong>。あなたにとって特に重く感じる谷もあるだろう。でも挫折が世界の終わりではないと信じてほしい。保証するが、谷を経験してもあなたは倒れない。困境に直面すると軽んじられたり笑われたりすることもあるが、本当に偉大な人は運が一時的に悪くても、好転が来ると信じ続ける。だから運は単なる運ではないと考えるな。</blockquote>
<blockquote>健康の問題のような低谷にいるなら、言葉にしにくい。でも覚えておいてほしい。僕たちは素晴らしい時代に生きている。百年前、五百年前、もっと荒れた時代に生まれていたら、運命はまったく違っていたかもしれない。比べれば僕たちの世代はすでに恵まれている。二十数世代の努力で、文明はかつてない高みに来ている。二十年前は個人の手の届かないことが多かったが、今日では挑戦により賢く対応できる。</blockquote>
<blockquote>人生の中の<strong>良いもの</strong>に意識を向けることを勧める。悪いことは必ず起きる。避けられない。それでも困難な時期に、良い人生は依然として築ける。それが僕の考えだ。</blockquote>
<blockquote>個人的には 94 年の人生で、本当にひどい目にあったことはない。多くの友人もそうだ。コーラが飲みたければ飲む。やりたいことをやる。少なくとも今のところ、なんとかうまくいっている。</blockquote>
<blockquote>例をもう一つ。プロアメフト選手のピークは 30 代か 40 代までかもしれないが、彼らはそのライフサイクルに慣れている。同様に、ある業界を選ぶなら、最初からその法則を理解しておけ。野球選手も同じで、ポジションごとに特有の難しさがある。</blockquote>
<blockquote>チャーリーとよく話すが、人体には過度の運動は必要ない。健康には気を遣うが、自分を消耗しすぎない。アスリートの例は、<strong>前向きな側面に目を向けることのほうが大事</strong>だと伝えたかった。寿命を延ばしたいなら、十分恵まれている（あなたのように遠路はるばる来て、こんなに多くの賢く面白い人と学べるエネルギーがあるなら、過去数百年・数千年の大多数より恵まれている）。それが伝えたかったことだ。</blockquote>
<strong>バークシャーのウォーレン・バフェット様、ポーランド出身でシカゴに住むアリサと申します。74 年前の寒い 1 月のあなたの話にずっと励まされてきました——1951年のある土曜、保険を学ぶためにニューヨークから 8 時間かけてワシントンに列車で向かい、着いてみればコートのオフィスは閉まっていた、という話です。その執念が僕を導いてくれました。2011年、15 歳のとき同じ決意で手紙を出し、面会をお願いしました。お返事では残り時間は 3000 日ほどだと。今は 5000 日以上経ち、1951年から変わらぬ情熱に励まされ続けています。今日、もう一度お願いします。四分の一の時間でも——オフィスで一時間でも。スケジュールはお忙しいでしょう。ポーランドで苦難を生き延びた身として、交友は慎重ですが誠実です。お断りしないでください——今この場に四万人が僕の後ろに立ち、この敬意は正々堂々としたものです。重ねてお願いします。人生の一時間でも分けていただけますか。貴重なお時間に感謝します。</strong>
<blockquote>素晴らしい！少し待って——実は僕の経歴を詳しく説明しなくて大丈夫だ。自分の物語はよく知っている。この四万人の前でこんな面白い質問をしてくれてありがとう。若い頃の経験を一つ話そう。</blockquote>
<blockquote>創業初期は、よく一人で州をまたいで各社を訪ねた。まだ若く、IR 部門もない時代で、だいたい CEO が直接応対してくれた。門前払いを恐れたが、やがて方法を見つけた。面会を頼むとき<strong>「10 分だけで十分」</strong>とはっきり言う——相手が延長を求めない限り。その時間の主導権は自分が握る。</blockquote>
<blockquote>70 年前の石炭業界の名問いを思い出す。「無人島に十年閉じ込められるなら、どの競合の株を持つ？」経営者は競合の話に熱を上げる。子どもがおもちゃを比べるように。でも僕は会話の焦点を導くことを学んだ——競合ばかりでなく、<strong>自社の核となる強み</strong>を語ってもらうことだ。</blockquote>
<blockquote>今は企業構造が複雑で、部門は独立したパズルのようだ。IR は株を買うメリットを強調し、その機能は肥大化している。大切なのは、<strong>自分の頭で企業を理解すること</strong>だ。バークシャーにも独特の経営哲学がある。研究用の資料は十分出すが、四万人全員の面接要請にはとても応じられない。</blockquote>
<blockquote>あなたの執念には心から敬意を表するが、はっきり言わせてほしい。ここが僕たちにできる限界だ。努力は称賛に値するが、ルールは全員に公平でなければならない。</blockquote>
<p>ドキュメンタリー『キャサリン・グラハムになる』の視聴をおすすめします。</p>
<strong>2017年のバークシャー株主総会で、大型テック企業の投資価値について議論しました。今ではマイクロソフト、アップル、アマゾンなどは外部資金を必要としない段階に達し、潤沢な自己資金を持ち、大量のリソースを人工知能の発展に投じています。過去と比べ、こうした巨大テック企業の貸借対照表の構造や資産配分戦略について見方は変わりましたか。特に現在の豊富な現金保有と、AI への大規模投資シフトを踏まえて。</strong>
<blockquote>その通りだ。こうした企業が豊かな利益を上げられるのは、大量の資本を投下しているからだ。どんな商売にも資本投入は要る。コカ・コーラを例に取れば、瓶詰め事業は設備に巨額の先行投資が必要だが、稼働に乗れば追加資本は比較的少なく、高いリターンが得られる。販売チャネルに要する資本はさらに限定的だ。このモデルは優れており、長く続く。</blockquote>
<blockquote>資本運用の観点では保険は特殊だ。損害保険には十分な担保資金が要るが、保険料を投資に回せる。こうした資本集約型の事業は、管理が良ければ極めて高いリターンをもたらす。アップルは別の典型——ほとんど追加調達を必要とせず、継続的に自社株買いができる。株価は波があるが、ビジネスモデルは堅牢だ。</blockquote>
<blockquote>投資の世界では、資本管理で巨万の富を築いた人が多い。秘訣は他人の資金を巧みに使い、管理報酬を取ることだ。成績が悪くてもかなりの収入があり、優秀ならさらに資金が集まる。資本市場の仕組みだ。過度に非難する必要はない。</blockquote>
<blockquote>チャーリーと僕は長年考え、最終的にこのモデルを選んだ。投資家の資金でリターンを生み、リスクも分かち合う。理想的なビジネスモデルの一つだ。もちろん濫用の余地もあり、米国やカナダでも例は見てきた。</blockquote>
<strong>フロリダから来ました。13 歳で、兄は 15 歳です。父と一緒に来ました。総会を開いてくださりありがとうございます。初めて参加します。高校の授業のうち、将来すごい投資家になるのに効く科目は何でしょうか。もう少し詳しく教えてください。</strong>
<blockquote>人生で出会う先生が、いちばん深い影響を与えることが多い。僕は幸運だった。学校だけでなく、雇い主や先輩からも多くを学んだ。父が最初の投資のメンターだった——投資業にいたので、毎週土曜にどう商売をするか観察できた。他の子がほとんど触れない投資の本も大量に読んだ。</blockquote>
<blockquote>オマハ公共図書館で偶然 19 世紀の投資書を見つけ、ニューヨークではさらに貴重な本にも当たった。読書は好きだが、チャーリー・マンガーほどではない。「誰とランチしたいか」と聞かれたら、答えはいつもチャーリーだ。歩く図書館のようで、本から真知を引き出してくる。好奇心を保ち、志を同じくする先生を見つけることが大切だ。</blockquote>
<blockquote>三つの学校に通い、最後はワシントン大学に進んだ。どこでも二、三人、深く学べる先生に出会った。知識だけでなく、特別な配慮と指導をしてくれた。ベンジャミン・グラハム教授は父のように教えてくれた。『The Great Bridge』は重要な人生の示唆をくれた。</blockquote>
<blockquote>父はよく言っていた。一人ひとり違う。今は迷っていても、やがて自分に合う道が見つかる。学校では、話し方も教え方も心地よい先生に出会う——コロンビア大学ではグラハム教授が父のような温かさをくれた。</blockquote>
<blockquote>振り返れば、少なくとも十人のメンターが人生を変えた。共通点は、若者のために<strong>余分な時間を惜しまなかった</strong>ことだ。質の高い学びは、学校そのものより、こうした個人的な師弟関係から来る。これは当初話したかったことの範囲を超えてしまった。</blockquote>
<h2>他言語</h2>
<li><a href="/2025-buffet">中文</a></li>
<li><a href="/2025-buffet-en">English</a></li>]]></content>
        <author>
            <name>Shemol</name>
            <email>shemol106@gmail.com</email>
            <uri>https://shemol.tech</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[zwy-ja]]></title>
        <id>https://shemol.tech/zwy-ja</id>
        <link href="https://shemol.tech/zwy-ja"/>
        <updated>2025-04-13T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[夜が暗いうちに出発すれば、歩けば歩くほど空が明るくなり、誰もが自信を持てる。]]></summary>
        <content type="html"><![CDATA[<h1>張維迎：夜が暗いうちに出発すれば、歩けば歩くほど空が明るくなり、誰もが自信を持てる（転載）</h1>
<p>WeChat のお気に入りを眺めていたらこの記事が出てきた。開いてみるとすでに公式アカウント側で削除されていたので、原文を探して自分のブログに転載した。学習用であり、自分用である。</p>
<p>初出：WeChat 公式アカウント「WSJ 中文」</p>
<p>長いあいだ、張維迎は少し孤独を感じていた。時代の思潮が激しく変わるなか、何十年も守ってきた考えに応える声は少なく、彼は揺らがなかったが、残念だと感じていた。ここ数年、若者の立場が少しずつ自分に近づき始めたのを微かに感じ取り、「とてもうれしい」と語る。</p>
<p>北京大学光華管理学院の院長を退いてからすでに十三年。周囲の論争はずいぶん減った。かえって考えを整理する時間が増え、これまでの見解を改訂し、さらに深めてきた。</p>
<p>「企業家精神」と「市場経済」が張維迎の思想を理解するキーワードだ。今の彼から見れば、市場経済は人を一気に大富豪にするものではなく、普通の人が「それなりに暮らせる可能性」を持てる仕組みにすぎない。それでもそれは貴重だと彼は言う。</p>
<p>張維迎はますます、市場経済の本当の意味は、最も創造力があり野心ある人々に「人類のための良いことしかできず、悪いことができない」ようにすることだと感じている。経済学の根本である合理人仮説の対極に立ち、「私は人間性に失望している」と言う。市場経済は、彼にとって人間性を抑えるメカニズムだ。「自分では自分を律することができないなら、市場経済に任せよう。」</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%BC%A0%E7%BB%B4%E8%BF%8E%EF%BC%9A%E5%A6%82%E6%9E%9C%E5%A4%A9%E9%BB%91%E5%B0%B1%E5%87%BA%E5%8F%91%EF%BC%8C%E8%B6%8A%E8%B5%B0%E5%A4%A9%E8%B6%8A%E4%BA%AE%EF%BC%8C%E8%B0%81%E9%83%BD%E4%BC%9A%E6%9C%89%E4%BF%A1%E5%BF%83%EF%BC%88%E8%BD%AC%E8%BD%BD%EF%BC%89_1770869795643.jpg" alt="" />
<p>市場経済の論理では、適切な人が最もふさわしい位置に置かれるべきであり、その過程で企業家が最も重要な役割を果たす。「今の成果は誰の目にも明らかに改革開放から来ており、そのなかで企業家は重要な役割を果たした」と張維迎は言う。新しいデジタル産業、インターネット、電子商取引から製造業まで、民営企業の功績は大きい。「国際市場にあれほど多くの低コスト製品を輸出できたのは、実はすべて企業家の努力の結果だ。」</p>
<p>市場経済は空気のようなものだと彼は考える。普通は誰も意識しない。「いつもそこにある」。誰もが当たり前で、大したことないと思う。しかしなくなった瞬間に、その大切さ、なくてはならないこと、生命体が生き延びるにはそれが要る、と気づく。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%BC%A0%E7%BB%B4%E8%BF%8E%EF%BC%9A%E5%A6%82%E6%9E%9C%E5%A4%A9%E9%BB%91%E5%B0%B1%E5%87%BA%E5%8F%91%EF%BC%8C%E8%B6%8A%E8%B5%B0%E5%A4%A9%E8%B6%8A%E4%BA%AE%EF%BC%8C%E8%B0%81%E9%83%BD%E4%BC%9A%E6%9C%89%E4%BF%A1%E5%BF%83%EF%BC%88%E8%BD%AC%E8%BD%BD%EF%BC%89_1770869796404.jpg" alt="" />
<p>張維迎は中国北西部の山あい——陝西省呉堡県辛荘村に生まれた。それを恥じたことはなく、劣等感もなかった。むしろ財産だとさえ思っている。本当の農民の息子であり、例を挙げるときは『平凡の世界』か、家庭連産請負責任制の話になる。「『平凡の世界』はうちの地方の話で、舞台は実家から近い。」</p>
<p>この例を挙げたのは、作品の一場面からだ。双水村の書記田福堂の仕事は、毎朝鈴を鳴らして村人を労働に呼ぶことだった。ある日もいつものように鈴を鳴らしたが、動きがない。鈴の音だけが野に響いている。よく見ると、みんなはとっくに出かけていた。田福堂は腑に落ちない。なぜもう人を呼ばなくてもよくなったのか。昔はそれが容易ではなかった。物語は家庭連産請負責任制の導入後で、農民は労働から自分の取り分を得られ、大鍋飯ではなく多労多得となり、村人の積極性が明らかに高まった。</p>
<p>張維迎はこの例で、体制・メカニズムの革新が経済の運行にいかに重要かを示した。「家庭連産請負責任制がなければ、田福堂が鈴を振るだけでは、本当の積極性を持てる人は少ない。」これが経済の道理だと彼は言う。多くの人の理解では、需要は通貨で創出でき、信頼は形作れ、経済は刺激できる。張維迎にとって経済発展は本来自然な過程だ。「夜が暗いうちに出発すれば、歩けば歩くほど空が明るくなり、誰もが自信を持てる。」</p>
<p>張維迎は経済学者のなかでは異色で、公衆に向けて発言することに熱心であり、学術研究も怠らない。どちらも同じくらい大切で、どちらも捨てたくない。近著に『企業家精神を再理解する』と『回望』がある。前者には近年の企業家精神についての思考をまとめ、後者では筆致を変え、成長のなかで大切だった人々——父母、教師、幼なじみ——について書き、感情がすらすらと流れ出る。</p>
<p>文字と同様、話していても中国の著名経済学者という感じはしない。飾り気がなく、いつも笑顔で、声も穏やか。画面の向こうでは定刻どおり笑顔で現れ、ダウンベストを着ていた。北京に住み働いて久しいが、訛りは変わらず、北西部の出身だとすぐ分かる。</p>
<p>前回、公の場に不意に立ったのは『信天游』の一曲がきっかけだった。運命を変えてくれた恩師何煉成が他界し、パンデミック下で都市間移動が難しく、最後の見送りができなかった。彼は『何先生、もう一曲信天游を聴いてください』という文章を書き、歌詞を添えた。</p>
<p>歌詞にはこうある。「初めて会った日、そっと頭を撫でてくれた。最後に会った日、微笑んで口を開かなかった。喜んでくれたことも、憂いてくれたこともあった。私の信天游を褒めてくれたこともあった。」</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%BC%A0%E7%BB%B4%E8%BF%8E%EF%BC%9A%E5%A6%82%E6%9E%9C%E5%A4%A9%E9%BB%91%E5%B0%B1%E5%87%BA%E5%8F%91%EF%BC%8C%E8%B6%8A%E8%B5%B0%E5%A4%A9%E8%B6%8A%E4%BA%AE%EF%BC%8C%E8%B0%81%E9%83%BD%E4%BC%9A%E6%9C%89%E4%BF%A1%E5%BF%83%EF%BC%88%E8%BD%AC%E8%BD%BD%EF%BC%89_1770869797232.jpg" alt="" />
<p>1977 年に高考が再開される前、高校卒業後の張維迎は村に戻り、団支部書記と民兵連副指導員を務め、文科と理科の区別さえ知らなかった。最終的に西北大学の新設政治経済学专业に合格した。何煉成がその専攻の責任者で、その年専攻は新設、定員 50 人。張維迎は増員枠で大学の門をくぐり、運命を変えた。家を離れ大学へ行く日、村人全員が見送り、家族は皆に米糕と煮込みを振る舞った。</p>
<p>「何先生が増員を主導してくれなければ、大学にも行けなかったかもしれない。」張維迎は何煉成の助力を今も忘れない。当時西北大学は何の主導で経済学専攻を新設し、50 人の受験生が運命を変える機会を得た。</p>
<p>後に北京大学の教師になっても、何煉成とは連絡を保ち、機会があれば会いに行った。「経済学の基礎は何先生が築いてくれた。」1951 年卒業後すぐ西北大学にいた何煉成は、さまざまな事情で長くまともに学生を持てず、高考再開後、張維迎らが初めての教え子となり、何にとっては我が子同然だった。</p>
<p>張維迎が北大光華管理学院院長に就任後、一連の改革を試み、多くの抵抗に遭った。何煉成は聞きつけ、当時の北大学長に手紙を書いた。学長とは面識がなかったが、湖南同郷だから試してみたいと思ったという。</p>
<p>何先生の死後、張維迎は歌詞の一節を書き、同郷ですでに名を馳せていた信天游の歌手丁文軍に曲と歌唱を依頼した。録り終えて丁文軍から送られてきた音源に、満足なら本格的なスタジオで録ろうと言われ、画像を何枚か添えて動画にし、後輩の公式アカウントで公開した。思いがけず話題になり、張維迎本人が歌ったと思われた。彼は慌てて否定し、動画末尾に歌っているのは自分ではないと書いてあるが、みんな見ていないのだと言った。</p>
<p>『回望』は創作というより、恩義のある人や記憶に深い人について書いたもので、せいぜい感情の吐露だ。「書こうと思って書いたのではなく、書かなければならないものがあった。頭に入りきらなくなった。」</p>
<p>張維迎は頑なで、やや型破りだ。多くの学者と違い、派閥を作らず、誰にも依存せず、言うことはすべて自分が信じることだ。何煉成の影響は大きいが、学派を組むことはせず、そうしたくない。今は世間の目もあまり気にせず、自分自身が自分の行いに満足できるかだけを気にしている。</p>
<p>60 歳を過ぎ、50 歳のとき突然「天命を知った」感覚があり、自分が何十年何をしてきて、これから何をすべきかはっきりした、と自分に答えを出した。彼がこの年月やってきたことは、公衆の観念を変えようとすることだ。実際大量の仕事をしてきたが、「以前は自覚がなかった」。今は少なくとも飾らないこと、すでに功成名遂げたからといって取り繕う姿勢は要らない、と考えている。</p>
<p>そう見ると、張維迎はますます自由になっている。</p>
<p>以下、張維迎との対話である。</p>
<strong>『WSJ.』：</strong>「合理人」は古典経済学の前提だが、いわゆる合理人仮説をどう見るか。
<strong>張維迎：</strong> 理性にはいろいろな選び方があり、一つだけではない。また、どこまで先を見るかも違う。泥棒も合理的だし、企業家になるのも合理的だ。まったく別物だ。私は個人を批判しない。何をするも本人の選択だ。気になるのは、なぜそうするのか、その背後の理由だ。
<p>本来、北大や清華の優秀な卒業生がみな商界へ向かうのは国の幸運だ。みな体制内へ頭を削って入ろうとするなら、国の不幸だ。</p>
<p>経済が活気に満ち、ますます多くの人が創造や起業を望むとき、機会は多い。今後数年、卒業生のことも心配だ。光華管理学院長のときから、学生の教育・進路・機会に注目していた。複数のオファーや仕事から選べるなら、うれしかった。</p>
<p>今は二人で一つのオファーも取れないと聞く。そういう状況が心配だ。企業家精神を持つ人が自由に起業できるときだけ、より多くの雇用が生まれる。市場は人により多くの選択肢を与え、気分がすっきりする。要因が多すぎると気分が悪くなり、気分が悪いと創造力は発揮しにくい。創造力は気分が晴れやかなときに最も高い。</p>
<strong>『WSJ.』：</strong> 中国はすでに「ケーキを分ける」段階に入ったのか。ケーキは十分大きいのか。
<strong>張維迎：</strong> 経済学から言えば、本当に「分ける段階だけ」に達した国はない。私には分ける段階など存在しない。社会が常にケーキを大きくしようとするなら、ケーキを作ること自体が分ける過程だ。配分が不合理なら、ケーキは大きくならない。ケーキが大きくなるのは、比較的合理的に分かれているからだ。
<p>改革開放の過程で私たちはみな恩恵を受けた。本当に解くべきは不合理で不公平な要因をどう取り除くかだ。例えば商いのなかで、本当に能力と努力で契約を取るのであって、コネで取るのであってはならない。コネ頼みなら配分は不合理だ。解くべきはそういう問題で、誰の手のケーキが大きいからといって奪うのではない。そうすれば将来のケーキはなくなる。今は取れても、未来は取れない。</p>
<p>また、富は変化することに注意が要る。大都市の高層ビルを見て大きな資産だと思うかもしれない。三年後には一銭にもならないかもしれない。デトロイトを見てほしい。大量の不動産が要らなくなり、ただでも誰も取らない。富は物質ではない。面積や重量ではなく、市場がその資産でいくら価値を創出できるかだ。価値を創出しなければ、意味がない。</p>
<p>今、ボーイング 747 をタダでくれるが離陸も機内レストランも禁止されるとしたら、その資産はプラスかマイナスか。明らかにマイナスだ。手入れしなければすぐ傷み、整備費もかかる。富は動きのなかで価値を創出するときに初めて富だ。創出できなければ富ではない。</p>
<p>高層ビルを見て「なぜ自分に分け前がない」と言う人がいる。分け与えられても、手元では富ではない。あなたの手では価値がないからだ。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%BC%A0%E7%BB%B4%E8%BF%8E%EF%BC%9A%E5%A6%82%E6%9E%9C%E5%A4%A9%E9%BB%91%E5%B0%B1%E5%87%BA%E5%8F%91%EF%BC%8C%E8%B6%8A%E8%B5%B0%E5%A4%A9%E8%B6%8A%E4%BA%AE%EF%BC%8C%E8%B0%81%E9%83%BD%E4%BC%9A%E6%9C%89%E4%BF%A1%E5%BF%83%EF%BC%88%E8%BD%AC%E8%BD%BD%EF%BC%89_1770869798040.jpg" alt="" />
<strong>『WSJ.』：</strong> パンデミック後の中国経済への見通しは。下向きの段階に入るのは運命づけられているか。
<strong>張維迎：</strong> 数十年の高成長のあと、速度は必ず下がる。公表データどおりの規模なら、安定して 3% を維持できればすでにすごい。これまでの発展モデルは、市場で検証済みの技術をベースにし、R&D 費をかけず投産すれば売れるから速かった。フロンティアに近づくほど自然に遅くなる。恥ずかしいことではない。悪いから遅いのではなく、うまくやっても遅くなる。
<p>問題は、その下降した速度を維持できるかだ。3% を維持できると思うが、挑戦は多い。努力しなければマイナスもありうる。例は多い。かつてのアルゼンチンは先進国・豊かな国だった。ブラジル、ベネズエラもそうだ。</p>
<strong>『WSJ.』：</strong> 経済刺激を繰り返し唱え、停滞への万能薬のようだが、この発想の問題は何か。
<strong>張維迎：</strong> 経済をどう刺激する？ 経済は内発的な衝動で発展する。金融政策で金利を下げ、補助を出すことはできる。しかし根本は解決しない。根本は衝動だ。経済発展は企業家に依るべきで、通貨を刷ることに依るべきではない。
<p>経済問題を議論するとき、思考を縛る理論がある。形は完璧に見え、実は害が大きい。残念だ。経済はコントロールできると思う人が多い。ここで試し、あそこで試す。経済発展をキーボード操作のように考えるが、実際は人の内発的衝動だ。</p>
<strong>『WSJ.』：</strong> 市場経済をどう理解するか。健全な社会でどんな役割を果たすべきか。資源配分という従来理解は更新すべきか。
<strong>張維迎：</strong> 私は人間性にそれほど楽観的ではない。だから人間性の悪い面を補い、人に誤りを正さざるを得ない体制が欲しい。今の市場経済観は多くの人とも、過去の自分とも違うかもしれない。昔は市場経済といえば資源配分と言ったが、それは間違いだ。本当の意味は、最も創造的で野心ある人が、人類のための善事しかできず、悪事ができないようにすることだ。
<p>例：市場経済のなかでイーロン・マスクは善事しかできない。悪事はできない。なぜか。悪事をすれば顧客も投資家も受け入れず、終わりだ。火星に人を送ると言って、死んだら誰も申し込まない。自分では自分を律できない。頼れるのは体制で、それが市場経済だ。</p>
<strong>『WSJ.』：</strong> 伝統的経済学者のレールから外れているように見える。なぜ公衆にもっと時間と精力を割くのか。
<strong>張維迎：</strong> どの学問も発達すれば分化し、専門化・技術化する。さまざまな方面に人がおり、みんなが興味を持つとは限らない。好みや性格、教育が違うのは当然だ。
<p>真心で真剣にやるなら、みな評価に値する。同僚の高度に定量化された研究も良い。すべての学者に、社会へ即効の影響を求めるべきではない。</p>
<p>しかし誠実であれ。自分が信じることを言え。人の聞きたいことだけを言い、誰かに媚びるのは無責任だ。責任ある人間とは、そうだと思えばそう言い、そうでなければ言わない、ということだ。</p>
<p>知識人には、自分が正しいと思い、聞いてもらえないと権力で従わせたがる傾向がある。反対の見解は別の見解で示せるが、権力で押し通してはならない。力を使えば自由主義経済学の初衷から外れる。説得だけだ。聞かなくても仕方ないが、力には訴えない。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%BC%A0%E7%BB%B4%E8%BF%8E%EF%BC%9A%E5%A6%82%E6%9E%9C%E5%A4%A9%E9%BB%91%E5%B0%B1%E5%87%BA%E5%8F%91%EF%BC%8C%E8%B6%8A%E8%B5%B0%E5%A4%A9%E8%B6%8A%E4%BA%AE%EF%BC%8C%E8%B0%81%E9%83%BD%E4%BC%9A%E6%9C%89%E4%BF%A1%E5%BF%83%EF%BC%88%E8%BD%AC%E8%BD%BD%EF%BC%89_1770869798799.jpg" alt="" />
<strong>『WSJ.』：</strong> 資本批判はいったん退潮したように見える。世界経済の下振れのなかで 996 が一部の人に「福報」と言われるような転換をどう見るか。
<strong>張維迎：</strong> 繰り返し言っているが、社長は社員より苦労するのが普通で、勤務時間も長い。
<p>以前のインタビューで、自ら工場を作った個人経営者が工場を売却し、新オーナーに工場管理を続けさせられた。記者が「売却後最大の感想は」と聞くと、「以前は月末になると給料の金を工面するのに悩んだ。今は月末が近づくほどうれしい。給料を受け取りに行く」と答えた。</p>
<p>雇用者と被雇用者の責任は非対称だが、多くの人は気づかない。以前は社長が搾取していると思う人もいた。近年は世論も少し変わり、倒産が増え、社員は仕事を失った。だから社長に辞めてほしくない、社長が辞めたら飯が食えない、となった。</p>
<strong>『WSJ.』：</strong>「信心は金より大切」という。世界で経済が下振れ気味だが、信心や期待は今の経済発展で何を意味するか。信心はどう取り戻すか。
<strong>張維迎：</strong> 人は目の前の障害より、長い先の期待を心配することがある。例：100 里を歩く人が午後 5 時に出発すると、歩けば歩くほど怖くなる。なぜか。暗くなるから。
<p>朝 5 時に出発すれば、まだ暗くても怖くない。明るくなるから。午後 5 時から歩けば暗くなり、信心は失われる。人は長い道を見る。目の前に障害があるかだけではない。</p>
<p>ミクロでは、信心に最も大事なのは何か。自律性だ。運命は自分が握る。方向を選び、選ぶ権利があり、結果は自分の行いで決まるなら、信心がある。</p>
<p>高考の例：良い大学に行けなくても誰のせいにもしない。それは運命を自分が握るに近い。試験後、大学に行けるかどうか、どこに行くかが点数ではなく、他人の好みやくじ引きで決まるなら、信心も努力も失われる。</p>
<p>真の信心は、運命を自分が握れるという感覚から来る。概率的に少なくとも「自分は変えられる」と思えなければ、努力の意味も信心もない。</p>
<p>企業家も同じ。冒険精神があり、必ず儲かると思う人はいない。儲かることへの信心はある。しかし成功や儲けが努力と大きく関係するなら、必死になる。</p>
<p>運命を握れるなら信心がある。失敗しないわけではない。失敗しても納得できる。失敗後も再挑戦する。最終的な成否が努力とほぼ無関係で他人に操られるなら、もう来ない。</p>
<strong>『WSJ.』：</strong> 北大にいて光華院長を務めた。エリートの揺りかごだが、あなたからは強い「下層への配慮」を感じる。多くの学者にない。両方をどう両立したか。
<strong>張維迎：</strong> 人はそれぞれだ。性格と選択が違う。故意に「下層への配慮」をしようとしたわけではない。素朴な天性かもしれない。自分は自分だ。成長の各段階で出会う人は影響するが、最大は父母だ。
<p>農村出身を恥じたことはない。むしろ感動だ。こんなに多くのことを経験した。書くことは真情だ。歴史と出自は自分の一部だ。大切にすべきだ。</p>
<p>人は素っ気ない方がよい。飾らない。自分は自分だ。飾ればバレる。みんな馬鹿ではない。人文配慮など、わざとではなく、もともとそうだ。</p>
<strong>『WSJ.』：</strong> 若者は生活圧力が大きい。就職も一線都市の生活も。階層の上昇も難しい。北大のような学校は貧しい家庭の子には届かない。「寒門から貴子は出にくい」が現実のように見える。どう観察するか。
<strong>張維迎：</strong> まず、改革開放の数十年で階層移動は非常に大きかったと認識すべきだ。
<p>一方、近年の固化は感じる。本当なら注視が要る。ただ私個人はそれほど悲観していない。</p>
<p>2021 年の通選授業で、三百人ほどのクラスに清華・人大の学生もいた。調査したところ、九割超が都市出身、一割未満が農村出身。深刻に聞こえるが、父母の出自を聞くと、八割超の学生の父母は農村出身だった。そこで『北大学生はどこから？農門の二段跳び』という文章を書いた。第一段階は農村から都市へ、第二段階は次世代が北大へ。数十年の中国の流動は大きい。農村から直接北大は依然難しい。農村の教育水準は都市に及ばない。しかし都市に入った農村出身の親は子の学習を厳しく見るので、次世代が北大清華に入る可能性は高い。調査はある程度代表的だが、この問題を軽視してはならない。</p>
<p>高考には問題が多いが、今の中国で最も公平なのは依然高考だ。私は北大に行けなかった。高考のとき北大は無理だと分かったが、後に北大の教師になれた。それも良かった。</p>
<p>有名企業家や富豪の多くは貧しい出自だ。調べたところ、馬化騰、馬雲もごく普通の出自で、多くの富豪は農民で大学の機会もなかった。</p>
<p>だから私は市場経済を支持する。真の市場経済は垂直流動をもたらし、創造力と企業家精神で市場で戦える。シュンペーターの言葉が好きだ。市場経済の富人クラブは高級ホテルのようにいつも満室だが、宿泊者の名前は変わり続ける。誰かが去り誰かが入る。この流動が社会の健康の重要な指標だ。</p>
<p>私の観察ではまだそれほど悲観していない。なぜ市場化改革を大切にするか。市場経済でなければ普通人は頭角を現す希望がない。市場経済でなければ希望がない。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/%E5%BC%A0%E7%BB%B4%E8%BF%8E%EF%BC%9A%E5%A6%82%E6%9E%9C%E5%A4%A9%E9%BB%91%E5%B0%B1%E5%87%BA%E5%8F%91%EF%BC%8C%E8%B6%8A%E8%B5%B0%E5%A4%A9%E8%B6%8A%E4%BA%AE%EF%BC%8C%E8%B0%81%E9%83%BD%E4%BC%9A%E6%9C%89%E4%BF%A1%E5%BF%83%EF%BC%88%E8%BD%AC%E8%BD%BD%EF%BC%89_1770869799547.jpg" alt="" />
<strong>『WSJ.』：</strong> 企業家精神はある意味輸入品か。中国に自発的企業家精神の素地はあるか。
<strong>張維迎：</strong> 我々のなかには常に落ち着かない人がいる。何かしたい。他人がしたがらないかできないことをしたい。衝動があり、リスクを取り、失敗も受け入れる。古今にいた。人類はアフリカから出たが、誰が出たか。企業家精神のある人だ。
<p>一般に言う企業家精神は商業面が多い。商業は挑戦があり、素質要求も高い。中国の伝統では、そういう人は科挙制度に「矯正」されてきた。科挙はすべての誘惑を官場に集め、古代の企業家精神の人は官場へ行った。政府には優秀な人が集まった。能力ある人たちだ。</p>
<p>社会にとっては損失だ。政府は富を分配する機構で、創造する場所ではない。真に企業家精神がある人は企業でこそ価値を発揮する。これが古代中国と近代西洋の違いの一つだ。</p>
<p>英国の例：才能ある人々が国教会を認めず、清教徒などもいたが、商業衝動があり企業家になった。工商業に従事すると創造力がより発揮された。</p>
<p>中国二千年史の大きな変化は改革開放後、優秀な人が企業を始めたことだ。しかし文化はまだ脆弱で、完全には変わっていない。</p>
<p>1980 年代から私がやりたかったのは観念の変化だ。公衆の商業観、企業家観、私が言う観念の近代化——「十大観念転換」。</p>
<p>光華院長のとき、公務員試験を受ける学生は少なかった。今は争って公務員を目指す。優秀な人が富を分配するより、富を創造すべきだ。</p>]]></content>
        <author>
            <name>Shemol</name>
            <email>shemol106@gmail.com</email>
            <uri>https://shemol.tech</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Eino-learning-notes-1-ChatModel-ja]]></title>
        <id>https://shemol.tech/Eino-learning-notes-1-ChatModel-ja</id>
        <link href="https://shemol.tech/Eino-learning-notes-1-ChatModel-ja"/>
        <updated>2025-04-11T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Eino の学習メモ。更新し続けるかもしれないし、しないかもしれない。]]></summary>
        <content type="html"><![CDATA[<h1>Eino 学習メモ 1：ChatModel</h1>
<p>ChatModel は、Eino フレームワークにおける対話型大規模言語モデルの抽象であり、OpenAI や Ollama など異なるモデルサービスとやり取りするための統一インターフェースを提供する。</p>
<p>このコンポーネントは次のような場面で効いてくる：</p>
<li>自然言語対話</li>
<li>テキスト生成・補完</li>
<li>ツール呼び出し用パラメータの生成</li>
<li>マルチモーダル（テキスト・画像・音声など）</li>
<h1>コンポーネント定義</h1>
<h2>インターフェース定義</h2>
<blockquote>コード位置：eino/components/model/interface.go</blockquote>
<p>``<code>go</p>
<p>type ChatModel interface {</p>
<p>    Generate(ctx context.Context, input []<em>schema.Message, opts ...Option) (</em>schema.Message, error)</p>
<p>    Stream(ctx context.Context, input []<em>schema.Message, opts ...Option) (</em>schema.StreamReader[*schema.Message], error)</p>
<p>    BindTools(tools []*schema.ToolInfo) error</p>
<p>}</p>
</code>`<code>
<p>Generate メソッド</p>
<li>機能：モデル応答を一括生成</li>
<li>パラメータ：</li>
<p>  - ctx：リクエスト単位の情報や Callback Manager を運ぶコンテキスト</p>
<p>  - input：入力メッセージのスライス</p>
<p>  - opts：モデル挙動を調整する任意オプション</p>
<li>戻り値：</li>
<p>  - </code>*schema.Message<code>：生成された応答メッセージ</p>
<p>  - error：生成中のエラー</p>
<p>Stream メソッド</p>
<li>機能：ストリーミングでモデル応答を生成</li>
<li>パラメータ：Generate と同じ</li>
<li>戻り値：</li>
<p>  - </code><em>schema.StreamReader[</em>schema.Message]<code>：応答ストリームのリーダー</p>
<p>  - error：生成中のエラー</p>
<p>BindTools メソッド</p>
<li>機能：モデルに利用可能なツールをバインド</li>
<li>パラメータ：</li>
<p>  - tools：ツール情報のスライス</p>
<li>戻り値：</li>
<p>  - error：バインド時のエラー</p>
<p>中核の位置づけ 対話モデルの中核抽象レイヤーで、二つの呼び出しモードをサポートする：</p>
<li>Generate：同期で全文応答（通常の対話向け）</li>
<li>Stream：ストリーム応答（長文生成・リアルタイム対話向け）</li>
<p>アーキテクチャ上の特徴</p>
</code>`<code>go
<p>type ChatModel interface {</p>
<p>    // 同步生成（典型AI对话模式）</p>
<p>    Generate(ctx context.Context, input []<em>schema.Message, opts ...Option) (</em>schema.Message, error)</p>
<p>    // 流式处理（适合逐段输出场景）</p>
<p>    Stream(ctx context.Context, input []*schema.Message, opts ...Option) (</p>
<p>        <em>schema.StreamReader[</em>schema.Message], error)</p>
<p>    // 工具绑定机制（支持功能扩展）</p>
<p>    BindTools(tools []*schema.ToolInfo) error</p>
<p>}</p>
</code>`<code>
<p>設計上の要点：</p>
<li>マルチモデル対応：インターフェース抽象で OpenAI／MAAS など異なるエンジンを吸収</li>
<li>コンテキスト対応：</code>context.Context<code> でタイムアウトやトレースなど</li>
<li>拡張可能なパラメータ：</code>...Option<code> で実装ごとの設定を足せる</li>
<li>ツールの動的バインド：</code>BindTools<code> で実行時に機能拡張（Function Calling 等を想定）</li>
<p>実務上の示唆：</p>
<p>   </code>//go:generate<code> で </code>ChatModelMock<code> を自動生成していることから：</p>
<li>インターフェース優先の設計</li>
<li>ユニットテストを書きやすい</li>
<li>依存注入しやすい（環境ごとのテスト）</li>
<p>注意点：</p>
<li>並行性：コメントで </code>BindTools<code> と </code>Generate<code> がアトミックでない旨が示されており、同期制御が必要になり得る</li>
<li>メッセージプロトコル：</code>schema.Message<code> に依存（プロトコル詳細は別途）</li>
<li>ストリームのライフサイクル：</code>StreamReader<code> は Close で資源解放</li>
<h2>Message 構造体</h2>
<blockquote>コード位置：eino/schema/message.go</blockquote>
</code>`<code>go
<p>type Message struct {   </p>
<p>    // Role 表示消息的角色（system/user/assistant/tool）</p>
<p>    Role RoleType</p>
<p>    // Content 是消息的文本内容</p>
<p>    Content string</p>
<p>    // MultiContent 是多模态内容，支持文本、图片、音频等</p>
<p>    MultiContent []ChatMessagePart</p>
<p>    // Name 是消息的发送者名称</p>
<p>    Name string</p>
<p>    // ToolCalls 是 assistant 消息中的工具调用信息</p>
<p>    ToolCalls []ToolCall</p>
<p>    // ToolCallID 是 tool 消息的工具调用 ID</p>
<p>    ToolCallID string</p>
<p>    // ResponseMeta 包含响应的元信息</p>
<p>    ResponseMeta *ResponseMeta</p>
<p>    // Extra 用于存储额外信息</p>
<p>    Extra map[string]any</p>
<p>}</p>
</code>`<code>
<p>Message はモデル対話の基本単位で、次をサポートする：</p>
<li>複数ロール：system（システム）、user（ユーザー）、assistant（AI）、tool（ツール）</li>
<li>マルチモーダル：テキスト・画像・音声・動画・ファイル</li>
<li>ツール呼び出し：外部ツール／関数</li>
<li>メタ情報：終了理由、トークン使用量など</li>
<h2>共通 Option</h2>
<p>Model コンポーネントはモデル挙動を設定する共通 Option を提供する。</p>
<blockquote>コード位置：eino/components/model/option.go</blockquote>
</code>`<code>go
<p>type Options struct {</p>
<p>    // Temperature 控制输出的随机性</p>
<p>    Temperature *float32</p>
<p>    // MaxTokens 控制生成的最大 token 数量</p>
<p>    MaxTokens *int</p>
<p>    // Model 指定使用的模型名称</p>
<p>    Model *string</p>
<p>    // TopP 控制输出的多样性</p>
<p>    TopP *float32</p>
<p>    // Stop 指定停止生成的条件</p>
<p>    Stop []string</p>
<p>}</p>
</code>`<code>
<p>Option の設定例：</p>
</code>`<code>go
<p>// 设置温度</p>
<p>WithTemperature(temperature float32) Option</p>
<p>// 设置最大 token 数</p>
<p>WithMaxTokens(maxTokens int) Option</p>
<p>// 设置模型名称</p>
<p>WithModel(name string) Option</p>
<p>// 设置 top_p 值</p>
<p>WithTopP(topP float32) Option</p>
<p>// 设置停止词</p>
<p>WithStop(stop []string) Option</p>
</code>`<code>
<h1>使い方</h1>
<h2>単体利用</h2>
</code>`<code>go
<p>import (</p>
<p>    "context"</p>
<p>    "fmt"</p>
<p>    "io"</p>
<p>    "github.com/cloudwego/eino-ext/components/model/openai"</p>
<p>    "github.com/cloudwego/eino/components/model"</p>
<p>    "github.com/cloudwego/eino/schema"</p>
<p>)</p>
<p>// 初始化模型 (以openai为例)</p>
<p>cm, err := openai.NewChatModel(ctx, &openai.ChatModelConfig{</p>
<p>    // 配置参数</p>
<p>})</p>
<p>// 准备输入消息</p>
<p>messages := []*schema.Message{</p>
<p>    {</p>
<p>       Role:    schema.System,</p>
<p>       Content: "你是一个有帮助的助手。",</p>
<p>    },</p>
<p>    {</p>
<p>       Role:    schema.User,</p>
<p>       Content: "你好！",</p>
<p>    },</p>
<p>}</p>
<p>// 生成响应</p>
<p>response, err := cm.Generate(ctx, messages, model.WithTemperature(0.8))</p>
<p>// 响应处理</p>
<p>fmt.Print(response.Content)</p>
<p>// 流式生成</p>
<p>streamResult, err := cm.Stream(ctx, messages)</p>
<p>defer streamResult.Close()</p>
<p>for {</p>
<p>    chunk, err := streamResult.Recv()</p>
<p>    if err == io.EOF {</p>
<p>       break</p>
<p>    }</p>
<p>    if err != nil {</p>
<p>       // 错误处理</p>
<p>    }</p>
<p>    // 响应片段处理</p>
<p>    fmt.Print(chunk.Content)</p>
<p>}</p>
</code>`<code>
<h2>オーケストレーションでの利用</h2>
</code>`<code>go
<p>import (</p>
<p>    "github.com/cloudwego/eino/schema"</p>
<p>    "github.com/cloudwego/eino/compose"</p>
<p>)</p>
<p>/<em>*</em> 初始化ChatModel</p>
<p>* cm, err := xxx</p>
<p>*/</p>
<p>// 在 Chain 中使用</p>
<p>c := compose.NewChain[[]<em>schema.Message, </em>schema.Message]()</p>
<p>c.AppendChatModel(cm)</p>
<p>// 在 Graph 中使用</p>
<p>g := compose.NewGraph[[]<em>schema.Message, </em>schema.Message]()</p>
<p>g.AddChatModelNode("model_node", cm)</p>
</code>`<code>
<h1>Option と Callback</h1>
<h2>Option の例</h2>
</code>`<code>go
<p>import "github.com/cloudwego/eino/components/model"</p>
<p>// 使用 Option</p>
<p>response, err := cm.Generate(ctx, messages,</p>
<p>    model.WithTemperature(0.7),</p>
<p>    model.WithMaxTokens(2000),</p>
<p>    model.WithModel("gpt-4"),</p>
<p>)</p>
</code>`<code>
<h2>Callback の例</h2>
</code>`<code>go
<p>import (</p>
<p>    "context"</p>
<p>    "fmt"</p>
<p>    "github.com/cloudwego/eino/callbacks"</p>
<p>    "github.com/cloudwego/eino/components/model"</p>
<p>    "github.com/cloudwego/eino/compose"</p>
<p>    "github.com/cloudwego/eino/schema"</p>
<p>    callbacksHelper "github.com/cloudwego/eino/utils/callbacks"</p>
<p>)</p>
<p>// 创建 callback handler</p>
<p>handler := &callbacksHelper.ModelCallbackHandler{</p>
<p>    OnStart: func(ctx context.Context, info <em>callbacks.RunInfo, input </em>model.CallbackInput) context.Context {</p>
<p>       fmt.Printf("开始生成，输入消息数量: %d\n", len(input.Messages))</p>
<p>       return ctx</p>
<p>    },</p>
<p>    OnEnd: func(ctx context.Context, info <em>callbacks.RunInfo, output </em>model.CallbackOutput) context.Context {</p>
<p>       fmt.Printf("生成完成，Token 使用情况: %+v\n", output.TokenUsage)</p>
<p>       return ctx</p>
<p>    },</p>
<p>    OnEndWithStreamOutput: func(ctx context.Context, info <em>callbacks.RunInfo, output </em>schema.StreamReader[*model.CallbackOutput]) context.Context {</p>
<p>       fmt.Println("开始接收流式输出")</p>
<p>       defer output.Close()</p>
<p>       return ctx</p>
<p>    },</p>
<p>}</p>
<p>// 使用 callback handler</p>
<p>helper := callbacksHelper.NewHandlerHelper().</p>
<p>    ChatModel(handler).</p>
<p>    Handler()</p>
<p>/<em>*</em> compose a chain</p>
<p>* chain := NewChain</p>
<p>* chain.appendxxx().</p>
<p>*       appendxxx().</p>
<p>*       ...</p>
<p>*/</p>
<p>// 在运行时使用</p>
<p>runnable, err := chain.Compile()</p>
<p>if err != nil {</p>
<p>    return err</p>
<p>}</p>
<p>result, err := runnable.Invoke(ctx, messages, compose.WithCallbacks(helper))</p>
</code>`<code>
<h1>既存実装</h1>
<p>1. OpenAI ChatModel：GPT 系 <a href="https://www.cloudwego.io/zh/docs/eino/ecosystem_integration/chat_model/chat_model_openai">ChatModel - OpenAI</a></p>
<p>2. Ollama ChatModel：ローカルモデル <a href="https://www.cloudwego.io/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ollama">ChatModel - Ollama</a></p>
<p>3. ARK ChatModel：ARK プラットフォーム <a href="https://www.cloudwego.io/zh/docs/eino/ecosystem_integration/chat_model/chat_model_ark">ChatModel - ARK</a></p>
<h1>自作実装の参考</h1>
<p>カスタム ChatModel を実装するときの注意：</p>
<p>1. 共通 option を実装する</p>
<p>2. callback 機構を実装する</p>
<p>3. ストリーム出力終了時に writer を close する</p>
<h2>Option 機構</h2>
<p>共通 Option 以外が必要なら、コンポーネント抽象のヘルパーで独自 Option を定義できる。例：</p>
</code>`<code>go
<p>import (</p>
<p>    "time"</p>
<p>    "github.com/cloudwego/eino/components/model"</p>
<p>)</p>
<p>// 定义 Option 结构体</p>
<p>type MyChatModelOptions struct {</p>
<p>    Options    *model.Options</p>
<p>    RetryCount int</p>
<p>    Timeout    time.Duration</p>
<p>}</p>
<p>// 定义 Option 函数</p>
<p>func WithRetryCount(count int) model.Option {</p>
<p>    return model.WrapImplSpecificOptFn(func(o *MyChatModelOptions) {</p>
<p>       o.RetryCount = count</p>
<p>    })</p>
<p>}</p>
<p>func WithTimeout(timeout time.Duration) model.Option {</p>
<p>    return model.WrapImplSpecificOptFn(func(o *MyChatModelOptions) {</p>
<p>       o.Timeout = timeout</p>
<p>    })</p>
<p>}</p>
</code>`<code>
<h2>Callback 処理</h2>
<p>ChatModel 実装は適切なタイミングでコールバックを発火する。ChatModel コンポーネントが定義する入出力：</p>
</code>`<code>go
<p>import (</p>
<p>    "github.com/cloudwego/eino/schema"</p>
<p>)</p>
<p>// 定义回调输入输出</p>
<p>type CallbackInput struct {</p>
<p>    Messages    []*schema.Message</p>
<p>    Model       string</p>
<p>    Temperature *float32</p>
<p>    MaxTokens   *int</p>
<p>    Extra       map[string]any</p>
<p>}</p>
<p>type CallbackOutput struct {</p>
<p>    Message    *schema.Message</p>
<p>    TokenUsage *schema.TokenUsage</p>
<p>    Extra      map[string]any</p>
<p>}</p>
</code>`<code>
<h1>実装サンプル（全体像）</h1>
</code>`<code>go
<p>import (</p>
<p>    "context"</p>
<p>    "errors"</p>
<p>    "net/http"</p>
<p>    "time"</p>
<p>    "github.com/cloudwego/eino/callbacks"</p>
<p>    "github.com/cloudwego/eino/components/model"</p>
<p>    "github.com/cloudwego/eino/schema"</p>
<p>)</p>
<p>type MyChatModel struct {</p>
<p>    client     *http.Client</p>
<p>    apiKey     string</p>
<p>    baseURL    string</p>
<p>    model      string</p>
<p>    timeout    time.Duration</p>
<p>    retryCount int</p>
<p>}</p>
<p>type MyChatModelConfig struct {</p>
<p>    APIKey string</p>
<p>}</p>
<p>func NewMyChatModel(config <em>MyChatModelConfig) (</em>MyChatModel, error) {</p>
<p>    if config.APIKey == "" {</p>
<p>       return nil, errors.New("api key is required")</p>
<p>    }</p>
<p>    return &MyChatModel{</p>
<p>       client: &http.Client{},</p>
<p>       apiKey: config.APIKey,</p>
<p>    }, nil</p>
<p>}</p>
<p>func (m <em>MyChatModel) Generate(ctx context.Context, messages []</em>schema.Message, opts ...model.Option) (*schema.Message, error) {</p>
<p>    // 1. 处理选项</p>
<p>    options := &MyChatModelOptions{</p>
<p>       Options: &model.Options{</p>
<p>          Model: &m.model,</p>
<p>       },</p>
<p>       RetryCount: m.retryCount,</p>
<p>       Timeout:    m.timeout,</p>
<p>    }</p>
<p>    options.Options = model.GetCommonOptions(options.Options, opts...)</p>
<p>    options = model.GetImplSpecificOptions(options, opts...)</p>
<p>    // 2. 开始生成前的回调</p>
<p>    ctx = callbacks.OnStart(ctx, &model.CallbackInput{</p>
<p>       Messages: messages,</p>
<p>       Config: &model.Config{</p>
<p>          Model: *options.Options.Model,</p>
<p>       },</p>
<p>    })</p>
<p>    // 3. 执行生成逻辑</p>
<p>    response, err := m.doGenerate(ctx, messages, options)</p>
<p>    // 4. 处理错误和完成回调</p>
<p>    if err != nil {</p>
<p>       ctx = callbacks.OnError(ctx, err)</p>
<p>       return nil, err</p>
<p>    }</p>
<p>    ctx = callbacks.OnEnd(ctx, &model.CallbackOutput{</p>
<p>       Message: response,</p>
<p>    })</p>
<p>    return response, nil</p>
<p>}</p>
<p>func (m <em>MyChatModel) Stream(ctx context.Context, messages []</em>schema.Message, opts ...model.Option) (<em>schema.StreamReader[</em>schema.Message], error) {</p>
<p>    // 1. 处理选项</p>
<p>    options := &MyChatModelOptions{</p>
<p>       Options: &model.Options{</p>
<p>          Model: &m.model,</p>
<p>       },</p>
<p>       RetryCount: m.retryCount,</p>
<p>       Timeout:    m.timeout,</p>
<p>    }</p>
<p>    options.Options = model.GetCommonOptions(options.Options, opts...)</p>
<p>    options = model.GetImplSpecificOptions(options, opts...)</p>
<p>    // 2. 开始流式生成前的回调</p>
<p>    ctx = callbacks.OnStart(ctx, &model.CallbackInput{</p>
<p>       Messages: messages,</p>
<p>       Config: &model.Config{</p>
<p>          Model: *options.Options.Model,</p>
<p>       },</p>
<p>    })</p>
<p>    // 3. 创建流式响应</p>
<p>    // Pipe产生一个StreamReader和一个StreamWrite，向StreamWrite中写入可以从StreamReader中读到，二者并发安全。</p>
<p>    // 实现中异步向StreamWrite中写入生成内容，返回StreamReader作为返回值</p>
<p>    // <em>*</em>StreamReader是一个数据流，仅可读一次，组件自行实现Callback时，既需要通过OnEndWithCallbackOutput向callback传递数据流，也需要向返回一个数据流，需要对数据流进行一次拷贝</p>
<p>    // 考虑到此种情形总是需要拷贝数据流，OnEndWithCallbackOutput函数会在内部拷贝并返回一个未被读取的流</p>
<p>    // 以下代码演示了一种流处理方式，处理方式不唯一</p>
<p>    sr, sw := schema.Pipe<a href="1">*model.CallbackOutput</a></p>
<p>    // 4. 启动异步生成</p>
<p>    go func() {</p>
<p>       defer sw.Close()</p>
<p>       // 流式写入</p>
<p>       m.doStream(ctx, messages, options, sw)</p>
<p>    }()</p>
<p>    // 5. 完成回调</p>
<p>    _, nsr := callbacks.OnEndWithStreamOutput(ctx, sr)</p>
<p>    return schema.StreamReaderWithConvert(nsr, func(t <em>model.CallbackOutput) (</em>schema.Message, error) {</p>
<p>       return t.Message, nil</p>
<p>    }), nil</p>
<p>}</p>
<p>func (m <em>MyChatModel) BindTools(tools []</em>schema.ToolInfo) error {</p>
<p>    // 实现工具绑定逻辑</p>
<p>    return nil</p>
<p>}</p>
<p>func (m <em>MyChatModel) doGenerate(ctx context.Context, messages []</em>schema.Message, opts <em>MyChatModelOptions) (</em>schema.Message, error) {</p>
<p>    // 实现生成逻辑</p>
<p>    return nil, nil</p>
<p>}</p>
<p>func (m <em>MyChatModel) doStream(ctx context.Context, messages []</em>schema.Message, opts <em>MyChatModelOptions, sr </em>schema.StreamWriter[*model.CallbackOutput]) {</p>
<p>    // 流式生成文本写入sr中</p>
<p>    return</p>
<p>}</p>
</code>``
<h1>参考資料</h1>]]></content>
        <author>
            <name>Shemol</name>
            <email>shemol106@gmail.com</email>
            <uri>https://shemol.tech</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Eino 学習ノート 2]]></title>
        <id>https://shemol.tech/eino-learning-notes-2-ja</id>
        <link href="https://shemol.tech/eino-learning-notes-2-ja"/>
        <updated>2025-04-11T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Eino のコンポーネント抽象と Chain / Graph オーケストレーションの要点。ハッカソン参加も視野に学習を続けるメモ。]]></summary>
        <content type="html"><![CDATA[<h1>Eino 学習ノート 2</h1>
<h2>Components</h2>
<p>大規模言語モデルアプリ開発には、大きく次の 3 つのアプリモードがある。</p>
<p>1. <strong>直接対話モード</strong>：ユーザー入力を処理し、応答を生成する  </p>
<p>2. <strong>知識処理モード</strong>：文書を意味的に処理し、保存・検索する  </p>
<p>3. <strong>ツール呼び出しモード</strong>：文脈に基づいて判断し、適切なツールを呼ぶ  </p>
<p>Eino はよく使う能力を<strong>再利用可能なコンポーネント（Components）</strong>として抽象化する。  </p>
<p>コンポーネント抽象とモードの対応はおおむね次のとおり。</p>
<strong>対話処理系</strong>
<p>1. 大モデルとの対話パラメータをモジュール化する抽象：<code>ChatTemplate</code>  </p>
<p>2. 大モデルと直接やりとりする抽象：<code>ChatModel</code>  </p>
<strong>テキスト意味処理系</strong>
<p>1. 文書の取得・処理：<code>Document.Loader</code>、<code>Document.Transformer</code>  </p>
<p>2. 文書の意味的処理：<code>Embedding</code>  </p>
<p>3. Embedding 後のインデックス保存：<code>Indexer</code>  </p>
<p>4. 意味的に関連する文書のインデックス化と取得：<code>Retriever</code>  </p>
<strong>意思決定・実行系</strong>
<li>モデルが判断してツールを呼ぶ抽象：<code>ToolsNode</code>  </li>
<strong>カスタム</strong>
<li>ユーザー定義ロジック：<code>Lambda</code>  </li>
<p>Eino のコンポーネント設計の原則：</p>
<p>1. <strong>モジュール化と標準化</strong>：同種の能力を統一モジュールにまとめ、役割と境界を明確にし、柔軟な組み合わせを可能にする。  </p>
<p>2. <strong>拡張性</strong>：インターフェースの制約をできるだけ小さくし、カスタムコンポーネントの実装を容易にする。  </p>
<p>3. <strong>再利用性</strong>：よく使う能力と実装をパッケージ化し、すぐ使える形で提供する。  </p>
<h2>Chain & Graph のオーケストレーション</h2>
<strong>オーケストレーション</strong>：Components の原子能力を組み合わせ、つなぐこと。
<li>ビジネスロジックをオーケストレーション層に混ぜない。  </li>
<li>大規模言語モデルアプリの中心は「原子能力を提供するコンポーネント」の組み合わせであり、コンポーネントがオーケストレーションの<strong>第一級市民</strong>。  </li>
<li>抽象としてのオーケストレーション：ネットワークを構築し、データが流れる。各ノードは流れてくるデータの形式・内容に要求を持つ。スムーズに流れるには「<strong>前後のノード間でデータ形式が揃っているか</strong>」が鍵。  </li>
<li>シーンの複雑さはオーケストレーション成果物の複雑さに表れる。<strong>横断的なガバナンス</strong>がなければ複雑さは制御不能になりやすい。  </li>
<li>モデルもアプリも急速に進化する。<strong>拡張できるアプリ</strong>だけが長く生き残れる。  </li>
<p>Eino は Graph モデル（辺＋ノード）に基づき、<strong>コンポーネントを原子ノードとし</strong>、<strong>前後の型の整合を土台にした</strong>オーケストレーションを提供する。</p>
<li>コンポーネントを中心に、機能のカプセル化の仕方を規定する。  </li>
<li>ビジネスロジックの複雑さはコンポーネント内部に閉じ、オーケストレーション層はより全体を見渡せる。  </li>
<li><strong>横断的関心（アスペクト）</strong>の仕組みがあり、コールバックでノード単位の統一的なガバナンスが可能（アスペクト能力とは何か、は別途）。  </li>
<li><strong>call option</strong> 機があり、高速イテレーションするシステムにとって拡張性は最低限の要求。  </li>
<li>「型の整合」を強化した開発スタイルで認知負荷を下げ、Go の型安全を活かす。  </li>
<li><strong>ストリームの自動変換</strong>により、ストリームがオーケストレーション複雑性の主因リストから外れる（<strong>Eino のストリーム指向プログラミング</strong>）。  </li>
<hr />
<p>Graph の欠点：「点」「辺」モデルでは、開発者が <code>graph.AddXXXNode()</code> と <code>graph.AddEdge()</code> の 2 つでデータ経路を作る必要があり、強力だがやや煩雑。</p>
<p>Eino はより使いやすい <strong><code>Chain</code></strong> でラップしている。Chain は Graph のラッパーで、「環」を除けば Graph のほぼすべての能力をさらけ出す。</p>
<h2>他言語</h2>
<li><a href="/eino-learning-notes-2">中文</a></li>
<li><a href="/eino-learning-notes-2-en">English</a></li>]]></content>
        <author>
            <name>Shemol</name>
            <email>shemol106@gmail.com</email>
            <uri>https://shemol.tech</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[学び方について——倪爽（転載）]]></title>
        <id>https://shemol.tech/learning-ways-from-nishuang-ja</id>
        <link href="https://shemol.tech/learning-ways-from-nishuang-ja"/>
        <updated>2025-04-11T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[デザイナー倪爽氏の学び方。]]></summary>
        <content type="html"><![CDATA[<h1>学び方について——倪爽（転載）</h1>
<p>元ポスト：<a href="https://x.com/nishuang/status/1787939646129008771">https://x.com/nishuang/status/1787939646129008771</a></p>
<p>私の学習法も一つ共有します。略称は「子どもを水に突き落とすと、最初は溺れなければそのうち勝手に泳げるようになり、しかも『泳ぎって習うもの？』と逆に聞いてくる……実用学習法」です。</p>
<a href="https://x.com/hashtag/%E6%B4%BB%E5%88%B0%E6%AD%BB%E5%AD%A6%E5%88%B0%E6%AD%BB?src=hashtag_click">#活到死学到死</a>
<p>デザインを学ぶ過程でやったことは、みんなと同じで、模倣、練習、研究、原理の学習、方法やテクニック……など。違うのは三つあります。</p>
<li>学校式の練習問題（単語帳と同じで虚構の用事）が特に嫌いで、自分・会社・クライアントの本当の案件を練習台にしている</li>
<p>その実案件があるからデザインに集中でき、多くのデザイナーが陥る自己陶酔や、自分を騙す偽りの自信を避けられる</p>
<li>まずデザインの仕事を引き受けてから、デザインの考え方や方法を学ぶ</li>
<p>一見プレッシャーが大きいが、難易度はコントロールでき、本当の自信が少しずつ積み上がる。会議で堂々と発言し、二、三言で「そう！」と自分に暗示をかけるのは、まだ浅い模倣であって本物の自信ではない</p>
<li>まずデザインし、学び、研究し、模倣し、実戦で経験を積み、落ち着いてから必要なところを深く学び直す。最後には戦略・経験・方法論などあらゆるレイヤーで高人を真似でき、表面だけの模倣にとどまらない</li>
<p>従来の教育の順序は、美術工員やコード職人のような技術労働者を育てるには向いているが、デザイナーのような創造仕事には、教育の効率は学習の効率に及ばない</p>
<p>変に聞こえますか？</p>
<p>実は多くの人が似たような方法で学び、成長しています。</p>
<p>この「水に突き落とす」学習法の利点は、強い正の報酬に完全に基づいていることです。好奇心が忍耐より強く、意志を知性で代用する性格の私には、一見難しく見えて実は自走するこの方法が合っています。</p>
<p>今日に至るまで、毎日デザインを学び、毎日自分を水に突き落としています。</p>]]></content>
        <author>
            <name>Shemol</name>
            <email>shemol106@gmail.com</email>
            <uri>https://shemol.tech</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[関税と株価暴落をどう見るか——ウォーレン・バフェットの発言（転載）]]></title>
        <id>https://shemol.tech/buffett-on-tariffs-stock-market-ja</id>
        <link href="https://shemol.tech/buffett-on-tariffs-stock-market-ja"/>
        <updated>2025-04-09T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[米中関税と株安をどう考えるか。バフェットの過去の発言をまとめた記事の要約転載。]]></summary>
        <content type="html"><![CDATA[<h1>関税と株価暴落をどう見るか——ウォーレン・バフェットの発言（転載）</h1>
<p>米国が中国に 104% の関税を課したのを見て、ある先輩が転載していた記事がある。ブログに転載して学習用に残す。自己学習目的のみ。</p>
<p>原文リンク：<a href="https://ec.ltn.com.tw/article/breakingnews/5005890">如何看待關稅和股市大跌？股神巴菲特這樣說</a></p>
<p>〔財経チャンネル／総合報道〕米国のトランプ大統領による相互関税が世界市場の揺れ動きを招いている。史上最も有名な投資家の一人、「株式の神」とも呼ばれるウォーレン・バフェットの考え方は常に注目され、海外メディアはバフェットの過去の発言を整理し、長年にわたり「関税」と「株価下落」の二つのテーマについても語ってきたと指摘している。バフェットがこれらをどう見ているかを押さえることは、現在の不安定な市場の筋を投資家が把握する助けになるかもしれない、と報じている。</p>
<p>CNBC の報道によれば、バフェットが関税について最近公に語ったのは 3 月初旬、米 CBS のノーラ・オドネル・アンカーへのインタビューで、関税は一般に物価上昇を招き、「時間が経つにつれ、関税は最終的には消費税のようなものに変わる」と述べたという。さらに冗談めかして、「歯の妖精がそれを払ってはくれない」とも言った、とされる。</p>
<p>報道は、バフェットはこの先何が起きるかすでに見通していた可能性があると指摘する。第一に「インフレ」——2018 年、トランプ政権の初めの比較的穏やかな関税について問われた際、バフェットはアルミニウムや鉄鋼などの関税が傘下の一部子会社のコストを押し上げたと述べた。トランプ政権が外国製品に関税をかける前から米国にはインフレの兆しがあったが、バフェットは「関税の状況はインフレ問題をさらに悪化させるだろう」と語った、という。</p>
<p>バフェットが懸念するもう一つの影響は「貿易戦争」——米国と貿易相手が関税を交互に引き上げて報復し合い、世界経済の成長を損なう可能性がある、という点だ。3 月のインタビューでは、バフェットは関税はある意味で戦争行為だとも述べた、とされる。</p>
<p>2019 年、米中の貿易緊張が高まるなか、バフェットの言い方はさらに率直だった。CNBC のインタビューで「本当に貿易戦争を始めれば、世界全体にとって良くない。世界経済は相互に連動しているからだ」と語った、という。</p>
<p>トランプ政権が最新の関税を発表したあと、S&P 500 は下落したが、まだ正式な「弱気相場」（直近の高値から 20% 以上下落）には入っていない。アナリストは、弱気相場に入るとすれば、投資家が貿易戦争が世界景気後退を招くのではないかと懸念しているためである可能性が高い、と指摘する。</p>
<p>そしてバフェットが世界規模の景気後退に初めて直面したわけではない。2008 年、世界金融危機が弱気相場を招いたとき、バフェットはニューヨーク・タイムズに寄稿し、「世界金融システムは混乱に陥っている。米国でも他地域でもそうだ。さらに悪いことに、これらの問題は実体経済にじわじわ染み込み、今や堤防が決壊したように制御不能だ」「短期的には失業率は上がり、商取引は停滞し、見出しはますます扇情的になるだろう」と書いた。</p>
<p>バフェットは続けて、「だから……私は米国株の買いを始めた」と述べた。</p>
<p>バフェットは、市場が次にどう動くかは予測できないと認めている。実際、2008 年 10 月にこの文章を発表したあと、S&P 500 は底を打って反発するまであと 5 か月下落した。</p>
<p>しかしバフェットが一貫して強調してきたように、企業全体は革新を続け、長期的には収益力を高め、それが株価の長期上昇につながる。2008 年、バフェットは多くの投資家がリスクに金を晒すことを嫌がっていると指摘した。</p>
<p>それでもバフェットは、こうした堅実な企業の長期的繁栄を疑うのは意味がないと考えている。「企業は時どき利益の波にさらされるが、これまでと同じだ。5 年、10 年、20 年後には、大多数の大企業はまた利益の新高値を記録しているだろう」と書いた。</p>
<p>バフェットは株価が相対的に安いときに買うことを好み、そうすれば長期リターンは高くなる。2008 年の文章では「簡単に言えば、悪い知らせは投資家にとって最高の友人だ。割引価格で米国の未来の一部を買えるからだ」と綴っている。</p>
<h2>他言語版</h2>
<li><a href="/buffett-on-tariffs-stock-market">中文</a></li>
<li><a href="/buffett-on-tariffs-stock-market-en">English</a></li>]]></content>
        <author>
            <name>Shemol</name>
            <email>shemol106@gmail.com</email>
            <uri>https://shemol.tech</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[RPC-learning-notes-ja]]></title>
        <id>https://shemol.tech/RPC-learning-notes-ja</id>
        <link href="https://shemol.tech/RPC-learning-notes-ja"/>
        <updated>2025-04-08T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[RPC（Remote Procedure Call）に関する学習メモ。]]></summary>
        <content type="html"><![CDATA[<h1>RPC 学習メモ</h1>
<p>RPC — Remote Procedure Call — リモートプロシージャコール。  </p>
<p>分散システムの通信問題を解くための仕組みで、ローカル呼び出しのようにリモート呼び出しを行えるのが特徴。RPC はマイクロサービスやクラウドネイティブ専用の用語ではなく、ネットワーク通信があれば使われうる。</p>
<p>例を二つ：</p>
<li>大規模な分散アプリは、メッセージキュー、分散キャッシュ、分散 DB、統合設定センターなどに依存することが多く、アプリとこれらミドルウェアの間も RPC で通信できる。例：etcd は統合設定サービスとして、クライアントが gRPC でサーバと通信する。</li>
<li>Kubernetes 自体が分散システムであり、<code>kube-apiserver</code> とクラスタ各コンポーネントの通信は gRPC で行われる。</li>
<p>RPC が扱う要素：</p>
<li><strong>シリアライズ</strong>：オブジェクトを転送可能なバイト列にし、逆に復元する。ネットワーク越し・言語越しのデータ交換に使う。</li>
<li><strong>圧縮アルゴリズム</strong>：転送量と帯域・遅延を抑える。</li>
<li><strong>プロトコル</strong>：クライアントとサーバのルール（転送形式・対話モード）。HTTP/2、TCP、UDP など。</li>
<li><strong>動的プロキシ</strong>：リモート呼び出しの複雑さを隠し、ローカルメソッドのように見せる。JDK 動的プロキシ、バイトコード強化など。</li>
<li><strong>サービス登録・発見</strong>：インスタンスの可用性を動的管理し、LB やフェイルオーバを支える。ZooKeeper、Consul、ETCD などがアドレスとメタデータを保持。</li>
<li><strong>暗号化</strong>：機密性・完全性。中間者攻撃や改ざんの防止。</li>
<li><strong>ネットワーク通信</strong>：IO モデル、接続管理、送受信など。一見単純だが、対端探索、接続確立、エンコード／デコード、接続管理など含め非常に複雑。RPC はその全体をパッケージ化し、分散構築時の通信実装を簡単にし、安全・信頼性も高める。</li>
<p>RPC クラスタでよく出る話題：</p>
<li>監視</li>
<li>サーキットブレーカ・レート制限</li>
<li>グレースフルな起動・停止</li>
<li>マルチプロトコル</li>
<li>分散トレーシング</li>
<p>RPC フレームワークが強いところ：</p>
<li>接続管理</li>
<li>ヘルスチェック</li>
<li>ロードバランス</li>
<li>グレースフル起停</li>
<li>異常時リトライ</li>
<li>ビジネスグルーピング</li>
<li>サーキットブレーカ・流量制御</li>
<p>RPC がなければ別サーバの API をどう呼ぶ？  </p>
<p>RPC はネットワークの細部を隠し、「リモートでも同じプロジェクト内のメソッドを呼ぶ」体験にする。リモートだからといって業務と無関係なコードを大量に書かなくてよい。</p>
<p>RPC の役割は主に二つ：</p>
<li>リモートとローカルの違いを隠し、プロジェクト内メソッド呼び出しに感じさせる。</li>
<li>下層ネットワークの複雑さを隠し、業務ロジックに集中できるようにする。</li>
<h1>シリアライズ</h1>
<p>ネットワーク上のデータはバイナリでなければならないが、呼び出し側の入出力はオブジェクト。事前に可逆なアルゴリズムでバイナリに変換する必要がある。</p>
<p>一般にヘッダは識別用（プロトコル ID、データサイズ、リクエスト種別、シリアライズ方式など）。ボディは業務パラメータと拡張属性など。</p>
<h1>デシリアライズ</h1>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/RPC%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0_1770869763125.png" alt="" />
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/RPC%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0_1770869764196.png" alt="" />
<p>RPC は通信だけでなく、MQ・分散キャッシュ・DB への発行にも使える。</p>
<p>RPC と HTTP はどちらもアプリケーション層プロトコル。</p>
<p>RPC リクエストはネットワークに出す前にメソッド呼び出し引数をバイナリ化し、ローカルソケットに書き、NIC 経由でネットワーク機器へ送られる。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/RPC%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0_1770869765390.png" alt="" />
<p>拡張性と後方互換のあるプロトコル設計では、ヘッダとペイロードの拡張フィールドを活用し、拡張フィールドで後方互換を取る。</p>
<p>場面に応じてシリアライズ方式を選ぶ。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/RPC%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0_1770869766673.png" alt="" />
<p>よく使うシリアライズ：</p>
<li>JDK ネイティブシリアライズ</li>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/RPC%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0_1770869768004.png" alt="" />
<p>いずれのフレームワークも、本質はシリアライズプロトコルの設計にある。</p>
<li><strong>JSON</strong>：典型的な Key-Value で型情報が弱く、テキスト型。  </li>
<p>  問題点：  </p>
<p>  - オーバーヘッドが大きく、大量データではメモリ・ディスク負荷が大きい。  </p>
<p>  - JSON に型がないため、Java のような強型言語では反射で統一処理になり性能が落ちる。  </p>
<p>  JSON を選ぶ RPC では、やり取りデータ量は小さめに抑えるべき。</p>
<li><strong>Hessian</strong>：動的型・バイナリ・コンパクトで多言語移植可能。JDK や JSON よりコンパクトで高速、バイト数も少ない。  </li>
<p>  - ただし公式版は Java の一部型をサポートしない。  </p>
<p>    - Linked 系（LinkedHashMap など）は <code>CollectionDeserializer</code> 拡張で対応可能。  </p>
<p>    - <code>Locale</code> は <code>ContextSerializerFactory</code> 拡張で対応。  </p>
<p>    - Byte/Short が Integer になる 等。</p>
<li><strong>Protobuf</strong>：Google の構造化データ標準。IDL を書き各言語のコンパイラでコード生成。  </li>
<p>  - シリアライズ後のサイズが JSON/Hessian より小さい。  </p>
<p>  - IDL で意味が明確になり型が落ちにくい。  </p>
<p>  - 反射なしで高速なシリアライズ／デシリアライズ。  </p>
<p>  - メッセージ形式の更新と互換性が取りやすい。  </p>
<p>  （文中の「IDL なしで Java オブジェクトを…」の記述は原文どおり Protobuf 周辺の別実装の話として読める。）</p>
<p>  ほかに MessagePack、kryo など。選定要因：</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/RPC%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0_1770869769098.png" alt="" />
<p>第一候補は Hessian と Protobuf が多い。性能・時間・空間・汎用性・互換・安全性のバランスが良い。Hessian は使いやすくオブジェクト互換に強い。Protobuf はより高速で汎用性に優れる。</p>
<p>RPC 利用時の注意：</p>
<li>オブジェクト構造が過度に複雑（属性が多く多層ネスト）。</li>
<li>オブジェクトが巨大すぎる。</li>
<li>フレームワークがサポートしない型を引数に使う。</li>
<li>継承関係が複雑。</li>
<h1>ネットワーク IO モデル</h1>
<p>RPC フレームワークはどの IO モデルを好むか。</p>
<p>よくある分類：</p>
<li>同期ブロッキング IO（BIO）</li>
<li>同期非ブロッキング IO（NIO）</li>
<li>IO 多路複用</li>
<li>非同期非ブロッキング IO（AIO）  </li>
<p>非同期 IO と言えるのは AIO だけで、他は同期 IO。</p>
<strong>ブロッキング IO</strong> は最も単純で一般的。Linux ではデフォルトでソケットはブロッキング。アプリが IO システムコールを出すとブロックしてカーネルへ。カーネルはデータ待ち、その後ユーザメモリへコピーして返る。待ちとコピーの両段階でスレッドはブロックし続ける。Java マルチスレッドなら IO 一つにつきスレッドが IO 完了まで占有される。
<strong>IO 多路複用</strong> は高並列で広く使われる。Java NIO、Redis、Nginx の下層、古典的 Reactor もこの系。複数接続の IO を select などのマルチプレクサに登録し、ユーザが select を呼ぶとプロセスはブロックするが、カーネルが担当ソケットを監視し、どれかがレディになると select が返る。その後 read でカーネルからユーザへコピーする。流れはブロッキング IO より複雑に見えるが、<strong>一つのスレッドで複数ソケットの IO を扱える</strong>のが最大の利点。同期ブロッキングだけでは同じことをマルチスレッドでしかできない。
<p>なぜブロッキングと多路複用が多いか：カーネルがブロッキング・非ブロッキング・多路複用を大抵サポートするが、シグナル駆動 IO や真の非同期 IO は高めの Linux でないと弱い。言語面では C++/Java の高性能フレームワークの多くが Reactor＝多路複用ベース（Java なら Netty が代表）。低並列なら同期ブロッキングがよくある。</p>
<p>RPC は高並列が多い。カーネル・言語・モデル特性から、実装では <strong>IO 多路複用</strong> を選ぶことが多い。言語フレームワークは <strong>Reactor</strong> 実装が最適（Java なら Netty が代表）。Linux では epoll を有効にする（Windows はカーネルが epoll をサポートしない）。</p>
<blockquote>Reactor ベースのネットワーク IO とは？  </blockquote>
<blockquote><strong>イベント駆動の高性能ネットワークモデル</strong>で、IO イベントの待受・ディスパッチと業務処理を分離し、高並列接続をまとめて扱う。<strong>多路複用</strong>（select/epoll/kqueue）で複数接続を監視し、イベント種別ごとにハンドラへ渡すことで、ブロッキング IO のスレッド浪費を避ける。  </blockquote>
<blockquote>主要コンポーネント：  </blockquote>
<blockquote>- <strong>Reactor</strong>：全 IO を監視し Event Loop でレディイベントを配る。多くは単独スレッド。Selector で Channel の接続・読み書きを検知。  </blockquote>
<blockquote>- <strong>Acceptor</strong>：接続確立を処理し、新しい <code>SocketChannel</code> を Reactor に登録。  </blockquote>
<blockquote>- <strong>Handler</strong>：読み取り・デコード、書き戻し、重い処理はスレッドプールへ、など。</blockquote>
<h1>ゼロコピー（zero copy）</h1>
<p>カーネルの IO は「データ待ち」と「データコピー」の二段階。待ちは NIC 受信後カーネルへ、コピーはカーネルからユーザ空間へ。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/RPC%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0_1770869770162.png" alt="" />
<p>典型的な書き込み：ユーザバッファ → CPU でカーネルバッファ → DMA で NIC。往復でコピーは二度、読み込みも対称的に二度。各コピーでユーザ／カーネル間のコンテキスト切替が発生する。</p>
<p>ゼロコピーはユーザとカーネル間の余分なコピーを減らし、DMA でカーネルと NIC の間を直接扱う考え方。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/RPC%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0_1770869771154.png" alt="" />
<p>代表案：</p>
<li><strong>mmap + write</strong>：仮想メモリでカーネル読みバッファをユーザ空間にマップし共有。</li>
<li><strong>sendfile</strong>：read/write を一つのシステムコールにまとめカーネル内で転送。SG-DMA があれば CPU を介さない経路も可能。</li>
<p>（詳細な mmap/sendfile の段数・長所短所は原文のブロッククォートと同趣旨：前処理が要るなら mmap+write、単純高速転送なら sendfile。）</p>
<strong>Netty のゼロコピー</strong>は主に JVM 上のデータ操作最適化：
<li><code>CompositeByteBuf</code> で複数 <code>ByteBuf</code> を論理一体にしコピー回避。</li>
<li><code>slice</code> で同一ストレージを共有する複数ビュー。</li>
<li><code>wrap</code> で <code>byte[]</code> 等をラップしてコピー回避。</li>
<li><code>FileRegion</code> + <code>FileChannel.transferTo()</code> は Linux の sendfile と同系。</li>
<h1>動的プロキシ・プロトコル・サービス発見</h1>
<p>動的プロキシ：インタフェース指向で RPC の処理を隠す（コードは未精読なら詳細は割愛）。  </p>
<p>ネットワークは <strong>信頼できる転送</strong> を心に留める。</p>
<p>RPC はインタフェースに対しプロキシを生成する。DI でインタフェースを注入すると実行時はプロキシが束縛され、メソッド呼び出しをインターセプトしてリモート処理を差し込める。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/RPC%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0_1770869772582.png" alt="" />
<li>実行時生成の速度・生成バイトコードサイズが性能に効く。小さいほどリソース節約。  </li>
<li>毎回のメソッド呼び出しでプロキシが走るため実行効率が重要。  </li>
<li>API の分かりやすさ、コミュニティ、依存の重さも選定要素。</li>
<strong>gRPC</strong>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/RPC%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0_1770869773779.png" alt="" />
<strong>プロトコルカプセル化</strong>  
<p>呼び出し引数のバイナリのあとに「区切り」記号を入れリクエストを分離し、区切りの間がペイロード。これがプロトコルカプセル化。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/RPC%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0_1770869775929.png" alt="" />
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/RPC%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0_1770869777676.png" alt="" />
<strong>サービス発見：CP か AP か</strong>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/RPC%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0_1770869779051.png" alt="" />
<p>1. <strong>サービス登録</strong>：プロバイダ起動時にレジストリへインタフェースを登録し、IP と情報を保存。  </p>
<p>2. <strong>サービス購読</strong>：コンシューマ起動時にレジストリでプロバイダ IP を取得しローカルキャッシュ、以降のリモート呼び出しに利用。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/RPC%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0_1770869780595.png" alt="" />
<strong>DNS でのサービス発見</strong>  
<p>同一ドメインに全プロバイダを載せ、DNS でランダム IP を取って長接続、一見よさそうだが：</p>
<li>IP/ポートが落ちたとき即座にコンシューマが外せるか。  </li>
<li>スケールアウトで新ノードにすぐ流量が乗るか。  </li>
<p>多くの場合 <strong>いいえ</strong>。DNS は多段キャッシュで TTL が長めになりがち。</p>
<strong>ZooKeeper ベース</strong>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/RPC%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0_1770869781800.png" alt="" />
<p>1. 管理端が ZK にサービス根パス（例：<code>/service/com.demo.xxService</code>）を作り、その下に <code>provider</code> / <code>consumer</code>。  </p>
<p>2. プロバイダ登録時に provider 側にエフェメラルノード。  </p>
<p>3. コンシューマは consumer 側にエフェメラルノードを作り、provider ディレクトリを watch。  </p>
<p>4. provider 配下が変わると ZK がコンシューマへ通知。</p>
<strong>メッセージバスによる最終一貫性レジストリ</strong>  
<p>ZK は強い一貫性のため更新のたびにクラスタ全体が同期し、性能に影響する。RPC のサービス発見では、新ノードが数秒遅れで見えても許容できる場面が多い。<strong>CP を捨て AP（最終一貫性）</strong> に振るとレジストリ集群の性能・安定性とトレードオフできる。全レジストリがメモリに全データを持ち、メッセージバスで同期する方式が取れる。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/RPC%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0_1770869783000.png" alt="" />
<h1>あとがき</h1>
<p>このあと gRPC や Kitex のコードリーディング、バイトダンス云原生の記事なども追う予定。本質的には OSS を離れて、より基礎に立ち返ることの方が大事。</p>]]></content>
        <author>
            <name>Shemol</name>
            <email>shemol106@gmail.com</email>
            <uri>https://shemol.tech</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[投資対象としての金について]]></title>
        <id>https://shemol.tech/gold-as-investment-ja</id>
        <link href="https://shemol.tech/gold-as-investment-ja"/>
        <updated>2025-04-05T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[金は良い投資対象ではない。]]></summary>
        <content type="html"><![CDATA[<h1>投資対象としての金について</h1>
<p>目論見は、読んだことを消化して自分の言葉でまとめる練習なので、学習用のメモとしての位置づけだ。</p>
<p>まずバフェットの言葉を引用する。</p>
<blockquote>私は持株を金に換えるなど考えたことはない。優れた企業に賭け、その内在価値が着実に伸びると信じる方がいい。そうした企業は優秀な経営者が動かし、人々が今も未来も愛する製品を売っている。南アフリカの地下から金属を掘り出し、輸送や保険などの手続きを経て、ノックス堡の金庫にしまうより、人々は汗水で稼いだ給料で See's のピーナッツバターやコーラのようなものを買いたいと思うだろう。</blockquote>
<p>></p>
<blockquote>父は金本位に傾倒していたが、私は金に興奮したことはない。実物を持ったことはないが、金を尊ぶ家庭で育ち、チャンスは十分与えた。それでも金の内在価値が何なのかずっと理解できない。ボーシャム（波仙）の宝石店では金製品も扱う。だが私は持株を売って金を買うことはしない。<strong>生産的資産</strong>を<strong>非生産的資産</strong>と交換する考えは、私には非常に遠い。</blockquote>
<p>></p>
<blockquote>バフェットは株主への手紙でも金に触れている。</blockquote>
<p>></p>
<blockquote>現金に近い資産のほかに、持つに適さない資産がある。自分からキャッシュフローを生まず、将来誰かがより高く買ってくれることだけを期待する資産——金、美術品、骨董などだ。彼はこれを無生産資産と呼び、対照的にキャッシュフローを生むのが有生産資産だ。</blockquote>
<p>></p>
<blockquote>バフェットは金を例に二種の資産の違いを説明する。<strong>世界の金保有量はおおよそ十七万トンで、溶かし直せば一辺二十一メートルの立方体になる。その立方体は地球のどこかから掘り出され、精錬され、また穴を掘って埋め戻され、周りを人が固めて見張っている。そこから何も生み出されることはない。</strong> 人々は買うのは、将来もっと多くの人がもっと高く買ってくれると期待するからだ。</blockquote>
<p>金はキャッシュフローを生まず、将来他人がより高く買うことだけを期待する資産で、非生産的資産に分類される。</p>
<p>次に陳嘉禾氏の見方。</p>
<p>金は価値を生まない。良い株は利益を積み上げ、安い価格ならその利益を安く買える。金はそうはならない。きちんと保管しても金が増えるわけではない。</p>
<p>毎年資産が増え、良い資産は日々一歩ずつ積み上がり、複利でリターンが膨らむ。金の「成長ゼロ」という性質は、長い目で見ても投資物として優れない。バフェットと同じ筋だ。</p>
<p>金では「取引」で優位を積めない。株を持っていると、企業価値の成長を楽しみながら売買でポートフォリオのファンダメンタルを厚くできる。ほかに割安な株があれば乗り換え、ファンダメンタルがさらに伸びる。不動産やコレクターズアイテムも同様だ。だが金はすべて同質で、単純すぎて誰も価格を大きく誤定価しない。だから保有者は取引でファンダメンタルを積み増しにくい。その積み増しはバリュー投資にとって重要な武器だ。</p>
<p>金は避難にも役に立たない。多くの人は「いざというときに使える」と買うが、社会が金を頼るほど乱れたら、同じ金額で今日買える食料（缶詰）、武器、医薬品の方がはるかに価値がある。</p>
<p>以前飒飒（ササ）さんとも話したが、コロナの頃に金を少し買おうと思っていた——その考えは誤りだった。間違った考えを一つ手放せてよかった。</p>
<p>唐二僧氏の答えはさらにストレートで、金を買うのは内心の安心感、つまり情緒的価値を買っている、という。</p>
<p>性質が安定しているから貴重、希少だから貴重、コンセンサスがあるから貴重、のいずれ単独では理由にならない。</p>
<p>（でも三つそろえば貴重になるのでは、暗号資産も同じでは、と一瞬思う。よくわからない。素人すぎる！）</p>
<p>金は情緒価値を買う行為だと割り切り、強制貯金と捉えればいい。</p>
<p>参考資料：</p>
<a href="https://mp.weixin.qq.com/s/9cwjIGgqYPG-6DNk8Z-PAQ">投资闲谈：巴菲特谈黄金</a>
<a href="https://mp.weixin.qq.com/s/fffqAMS3jYIVyyNxpuAREw">猫猫看市：为啥我不爱黄金</a>
<a href="https://mp.weixin.qq.com/s/fffqAMS3jYIVyyNxpuAREw">我们买黄金到底是在买什么？</a>]]></content>
        <author>
            <name>Shemol</name>
            <email>shemol106@gmail.com</email>
            <uri>https://shemol.tech</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[kubeedge-sedna-sourcecode-analysis-ja]]></title>
        <id>https://shemol.tech/kubeedge-sedna-sourcecode-analysis-ja</id>
        <link href="https://shemol.tech/kubeedge-sedna-sourcecode-analysis-ja"/>
        <updated>2025-01-09T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[転載：Sedna ソースコード読み（学習・参照用）]]></summary>
        <content type="html"><![CDATA[<h1>KubeEdge-Sedna ソースコード解析（転載）</h1>
<p>原著者：<a href="https://github.com/jaypume">jaypume</a></p>
<p>公開講義動画：<a href="https://www.bilibili.com/video/BV1hg4y1b78L">https://www.bilibili.com/video/BV1hg4y1b78L</a></p>
<p>原著 README：<a href="https://github.com/jaypume/article/blob/main/sedna/%E8%BE%B9%E4%BA%91%E5%8D%8F%E5%90%8CAI%E6%A1%86%E6%9E%B6Sedna%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/README.MD">https://github.com/jaypume/article/blob/main/sedna/边云协同AI框架Sedna源码解析/README.MD</a></p>
<p>学習・参照のため転載。</p>
<h1>KubeEdge-Sedna 概要</h1>
<p>Sedna は KubeEdge SIG AI で育てられてきたクラウド–エッジ協調 AI プロジェクトである。KubeEdge の協調能力により、連合推論・増分学習・連邦学習・ライフロング学習など、クラウドとエッジにまたがる学習・推論が可能になる。TensorFlow／PyTorch／MindSpore など主要フレームワークを想定しており、既存アプリを比較的スムーズに載せ替え、コスト・モデル性能・データプライバシー面での利点を狙える。</p>
<p>プロジェクト：</p>
<p>https://github.com/kubeedge/sedna</p>
<p>ドキュメント：</p>
<p>https://sedna.readthedocs.io</p>
<h2>全体アーキテクチャ</h2>
<p>Sedna の協調は KubeEdge の次の能力の上に乗る。</p>
<p>* クラウド–エッジ横断のアプリ統合オーケストレーション</p>
<p>* Router：管理面の高信頼メッセージ経路</p>
<p>* EdgeMesh：データ面のサービス発見とトラフィック制御</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/KubeEdge-Sedna%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%EF%BC%88%E8%BD%AC%E8%BD%BD%EF%BC%89_1770869745557.png" alt="" />
<strong>主なコンポーネント</strong>：
<li><strong>GlobalManager</strong></li>
<p>  - 協調 AI ジョブの一元管理</p>
<p>  - クラウド–エッジ間の調整</p>
<p>  - 中央設定</p>
<li><strong>LocalController</strong></li>
<p>  - エッジ側のローカル制御</p>
<p>  - モデル・データセット・状態同期などのローカル管理</p>
<li><strong>Lib</strong></li>
<p>  - AI／アプリ開発者向けに協調機能を API として提供</p>
<li><strong>Worker</strong></li>
<p>  - 学習・推論の実行（既存フレームワーク上のプログラム）</p>
<p>  - 機能ごとにワーカー群があり、エッジ／クラウドに配置して協調</p>
<h2>リポジトリ構成</h2>
<p>| ディレクトリ | 内容 |</p>
<p>| --- | --- |</p>
<p>| .github | GitHub CI/CD 設定 |</p>
<p>| LICENSES | Sedna および vendor のライセンス |</p>
<p>| build | GM／LC 等の Dockerfile、生成 CRD YAML、サンプル CR |</p>
<p>| cmd | GM／LC のエントリ |</p>
<p>| components | 監視・可視化など |</p>
<p>| docs | proposal とインストール手順 |</p>
<p>| examples | 連合推論・増分・ライフロング・連邦学習の例 |</p>
<p>| hack | コード生成ツールや開発用スクリプト |</p>
<p>| lib | Python ライブラリ（協調 AI アプリ開発用） |</p>
<p>| pkg | API 定義、client-go 生成コード、GM／LC の中核 |</p>
<p>| scripts | 利用者向けインストールスクリプト |</p>
<p>| test | E2E とテストユーティリティ |</p>
<p>| vendor | サードパーティソース |</p>
<h1>Sedna 管理面ソース（Go）</h1>
<h2>GM: Global Manager</h2>
<h3>GM は一種の Kubernetes Operator</h3>
<strong>Operator とは？</strong>
<blockquote>An Operator is an application-specific controller that extends the Kubernetes API to create, configure and manage instances of complex stateful applications on behalf of a Kubernetes user. It builds upon the basic Kubernetes resource and controller concepts, but also includes domain or application-specific knowledge to automate common tasks better managed by computers. 1</blockquote>
<p>Sedna の GM は、協調 AI アプリにおけるワーカーのデプロイ・起動パラメータ、協調の仕方、データの流れなどを司る。<strong>「クラウド–エッジ協調 AI アプリ」というドメイン特化のコントローラ</strong>と言える。</p>
<blockquote>The following components form the three main parts of an operator:</blockquote>
<blockquote>- <em>API</em>: The data that describes the operand’s configuration. The API includes:</blockquote>
<blockquote>  - <strong><em>Custom resource definition (CRD)</em></strong>, which defines a schema of settings available for configuring the operand.</blockquote>
<blockquote>  - <strong><em>Programmatic API</em></strong>, which defines the same data schema as the CRD and is implemented using the operator’s programming language, such as <a href="https://developers.redhat.com/blog/category/go/"><em>Go</em></a><em>.</em></blockquote>
<blockquote>  - <strong><em>Custom resource (CR)</em></strong>, which specifies values for the settings defined by the CRD; these values describe the configuration of an operand.</blockquote>
<blockquote>- <strong><em>Controller</em></strong>: The brains of the operator. The controller creates managed resources based on the description in the custom resource; controllers are implemented using the operator’s programming language, such as Go. <a href="about:blank#fn2">2</a></blockquote>
<p>Red Hat の定義によれば、Kubernetes Operator を構成する主な概念は CRD、API、CR、Controller である。</p>
<p>Sedna GM を Operator として見たときの模式図：</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/KubeEdge-Sedna%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%EF%BC%88%E8%BD%AC%E8%BD%BD%EF%BC%89_1770869746491.jpg" alt="" />
<p>以降は CR、CRD、API、Controller の順に触れる。制御の本体は Controller。</p>
<h3>CR</h3>
<p>Sedna は連合推論・増分学習・ライフロング学習・連邦学習をサポートする。コード読みやすさのため本稿はライフロング学習の特性とサンプルに寄せて説明する。他三つも実装パターンは共通部分が多い。</p>
<strong>CR サンプル</strong>
<p>ライフロング学習の <a href="https://github.com/kubeedge/sedna/blob/main/build/crd-samples/sedna/lifelonglearningjobv1alpha1.yaml">CR サンプル</a> を引用する。これを <code>kubectl</code> で適用してリソースを作れる。手順の詳細は <a href="https://github.com/kubeedge/sedna/tree/main/examples/lifelong_learning/atcii">examples/lifelong_learning/atcii</a>。主要フィールド：</p>
<li>dataset：データセット CR の名前（データセットも CR）。</li>
<li>trainSpec：学習ワーカーの Pod テンプレート（イメージ・環境変数など）。</li>
<li>trigger：学習ワーカーを起こす条件。</li>
<li>evalSpec：評価ワーカーのテンプレート。</li>
<li>deploySpec：推論ワーカーのテンプレート。</li>
<li>outputDir：学習で出力するモデルパス。</li>
<code>build/crd-samples/sedna/lifelonglearningjobv1alpha1.yaml</code>
<p>``<code>yaml</p>
<p>apiVersion: sedna.io/v1alpha1</p>
<p>kind: LifelongLearningJob</p>
<p>metadata:</p>
<p>  name: atcii-classifier-demo</p>
<p>spec:</p>
<p>  dataset:</p>
<p>    name: "lifelong-dataset"</p>
<p>    trainProb: 0.8</p>
<p>  trainSpec:</p>
<p>    template:</p>
<p>      spec:</p>
<p>        nodeName:  "edge-node"</p>
<p>        containers:</p>
<p>          - image: kubeedge/sedna-example-lifelong-learning-atcii-classifier:v0.3.0</p>
<p>            name:  train-worker</p>
<p>            imagePullPolicy: IfNotPresent</p>
<p>            args: ["train.py"]</p>
<p>            env:</p>
<p>              - name: "early_stopping_rounds"</p>
<p>                value: "100"</p>
<p>              - name: "metric_name"</p>
<p>                value: "mlogloss"</p>
<p>    trigger:</p>
<p>      checkPeriodSeconds: 60</p>
<p>      timer:</p>
<p>        start: 02:00</p>
<p>        end: 24:00</p>
<p>      condition:</p>
<p>        operator: ">"</p>
<p>        threshold: 500</p>
<p>        metric: num_of_samples</p>
<p>  evalSpec:</p>
<p>    template:</p>
<p>      spec:</p>
<p>        nodeName:  "edge-node"</p>
<p>        containers:</p>
<p>          - image: kubeedge/sedna-example-lifelong-learning-atcii-classifier:v0.3.0</p>
<p>            name:  eval-worker</p>
<p>            imagePullPolicy: IfNotPresent</p>
<p>            args: ["eval.py"]</p>
<p>            env:</p>
<p>              - name: "metrics"</p>
<p>                value: "precision_score"</p>
<p>              - name: "metric_param"</p>
<p>                value: "{'average': 'micro'}"</p>
<p>              - name: "model_threshold"</p>
<p>                value: "0.5"</p>
<p>  deploySpec:</p>
<p>    template:</p>
<p>      spec:</p>
<p>        nodeName:  "edge-node"</p>
<p>        containers:</p>
<p>        - image: kubeedge/sedna-example-lifelong-learning-atcii-classifier:v0.3.0</p>
<p>          name:  infer-worker</p>
<p>          imagePullPolicy: IfNotPresent</p>
<p>          args: ["inference.py"]</p>
<p>          env:</p>
<p>          - name: "UT_SAVED_URL"</p>
<p>            value: "/ut_saved_url"</p>
<p>          - name: "infer_dataset_url"</p>
<p>            value: "/data/testData.csv"</p>
<p>          volumeMounts:</p>
<p>          - name: utdir</p>
<p>            mountPath: /ut_saved_url</p>
<p>          - name: inferdata</p>
<p>            mountPath: /data/</p>
<p>          resources:</p>
<p>            limits:</p>
<p>              memory: 2Gi</p>
<p>        volumes:</p>
<p>          - name: utdir</p>
<p>            hostPath:</p>
<p>              path: /lifelong/unseen_task/</p>
<p>              type: DirectoryOrCreate</p>
<p>          - name: inferdata</p>
<p>            hostPath:</p>
<p>              path:  /data/</p>
<p>              type: DirectoryOrCreate</p>
<p>  outputDir: "/output"</p>
</code>`<code>
<h3>CRD</h3>
<p>CRD は CR の雛形である。CR を作る前にクラスタへ CRD を登録する必要がある。YAML は手書きでもよいが、複雑ならツール生成が無難。Sedna は kubebuilder の <a href="https://book.kubebuilder.io/reference/controller-gen.html#controller-gen-cli">controller-gen</a> で CRD を生成・更新し、</code>make crds<code> で </code>build/crds/<code> を更新する（</code>Makefile<code> の </code>crds: controller-gen<code> を参照）。</p>
<p>CRD では group・version・kind（GVK）が肝になる。CR インスタンスは Resource と呼ばれ、OO で言えば Kind がクラス、Resource がオブジェクトに近い。ライフロング学習の GVR／GVK は次表。</p>
<p>|  | Group | Version | Resource | Kind |</p>
<p>| --- | --- | --- | --- | --- |</p>
<p>| CRD | apiextensions.k8s.io | v1 | lifelonglearningjobs.sedna.io | CustomResourceDefinition |</p>
<p>| CR | sedna.io | v1alpha1 | lifelonglearningjob | LifelongLearningJob |</p>
<p>クラスタ内のリソースは REST URI で表される。パスの組み立ては次図のとおり。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/KubeEdge-Sedna%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%EF%BC%88%E8%BD%AC%E8%BD%BD%EF%BC%89_1770869747439.jpg" alt="" />
<p>規則が分かれば、管理対象の REST URL を手で組み立てられる。公式 client がない言語でも HTTP で叩ける。例：</p>
<p>ライフロング学習 CRD の取得：</p>
</code>`<code>plain text
<p>curl -k --cert ./client.crt --key ./client.key https://127.0.0.1:5443/apis/apiextensions.k8s.io/v1beta1/customresourcedefinitions/lifelonglearningjobs.sedna.io</p>
</code>`<code>
<p>ライフロング学習 CR 一覧：</p>
</code>`<code>plain text
<p>curl -k --cert ./client.crt --key ./client.key https://127.0.0.1:5443/apis/sedna.io/v1alpha1/lifelonglearningjobs</p>
</code>`<code>
<p>Sedna のライフロング学習 CRD で注目すべきフィールド：</p>
<li></code>apiVersion: apiextensions.k8s.io/v1<code> — 現行 CRD はこの API グループ。</li>
<li></code>kind: CustomResourceDefinition<code></li>
<li></code>spec.group: sedna.io<code></li>
<li></code>spec.names.kind: LifelongLearningJob<code></li>
<li></code>spec.names.shortNames: - ll<code> — </code>kubectl get ll<code> などの短縮名。</li>
</code>build/crds/sedna.io_lifelonglearningjobs.yaml<code>
</code>`<code>yaml
<p>apiVersion: apiextensions.k8s.io/v1</p>
<p>kind: CustomResourceDefinition</p>
<p>metadata:</p>
<p>  annotations:</p>
<p>    controller-gen.kubebuilder.io/version: v0.4.1</p>
<p>  creationTimestamp: null</p>
<p>  name: lifelonglearningjobs.sedna.io</p>
<p>spec:</p>
<p>  group: sedna.io</p>
<p>  names:</p>
<p>    kind: LifelongLearningJob</p>
<p>    listKind: LifelongLearningJobList</p>
<p>    plural: lifelonglearningjobs</p>
<p>    shortNames:</p>
<p>    - ll</p>
<p>    singular: lifelonglearningjob</p>
<p>  scope: Namespaced</p>
<p>  versions:</p>
<p>  - name: v1alpha1</p>
<p>	...</p>
<p>status:</p>
<p>  acceptedNames:</p>
<p>    kind: ""</p>
<p>    plural: ""</p>
<p>  conditions: []</p>
<p>  storedVersions: []</p>
</code>`<code>
<h3>API</h3>
<p>CRD を自動生成するための型定義は次にある。</p>
</code>pkg/apis/sedna/v1alpha1/lifelonglearningjob_types.go<code>
</code>`<code>go
<p>package v1alpha1</p>
<p>import (</p>
<p>	v1 "k8s.io/api/core/v1"</p>
<p>	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"</p>
<p>)</p>
<p>// 这里展示了</p>
<p>// +genclient</p>
<p>// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object</p>
<p>// +kubebuilder:resource:shortName=ll</p>
<p>// +kubebuilder:subresource:status</p>
<p>// 整体的LifelongLearningJob的API定义，主要包含Spec和Status定义，分别代表期望状态和实际状态。</p>
<p>type LifelongLearningJob struct {</p>
<p>	metav1.TypeMeta   </code>json:",inline"<code></p>
<p>	metav1.ObjectMeta </code>json:"metadata"<code></p>
<p>	Spec              LLJobSpec   </code>json:"spec"<code></p>
<p>	Status            LLJobStatus </code>json:"status,omitempty"<code></p>
<p>}</p>
<p>// 在创建LifelongLearningJob时候需要配置的参数；如果需要扩展终身学习字段的接口，可以在这里修改。</p>
<p>type LLJobSpec struct {</p>
<p>	Dataset    LLDataset    </code>json:"dataset"<code></p>
<p>	TrainSpec  LLTrainSpec  </code>json:"trainSpec"<code></p>
<p>	EvalSpec   LLEvalSpec   </code>json:"evalSpec"<code></p>
<p>	DeploySpec LLDeploySpec </code>json:"deploySpec"<code></p>
<p>	// the credential referer for OutputDir</p>
<p>	CredentialName string </code>json:"credentialName,omitempty"<code></p>
<p>	OutputDir      string </code>json:"outputDir"<code></p>
<p>}</p>
<p>type LLDataset struct {</p>
<p>	Name      string  </code>json:"name"<code></p>
<p>	TrainProb float64 </code>json:"trainProb"<code></p>
<p>}</p>
<p>// 剩下还有一些结构体定义省略了。</p>
</code>`<code>
<p>補足：</p>
<li></code>// +kubebuilder...<code> はコード生成ツール向けのディレクティブ。</li>
<li></code>LifelongLearningJob<code> が Spec（望ましい状態）と Status（実状態）を持つ API の核。</li>
<li></code>LLJobSpec<code> は CR 作成時にユーザーが埋めるフィールド。拡張もここから。</li>
<p>連合推論・増分・連邦学習の型は同じく </code>pkg/apis/sedna/v1alpha1/<code> 以下。</p>
<strong>client-go の再生成</strong>
</code>*_types.go<code> を変えたら次を実行：
</code>`<code>plain text
<p>bash hack/update-codegen.sh</p>
</code>`<code>
<p>生成物は </code>pkg/client<code>：</p>
</code>`<code>plain text
<p>➜  pkg tree client -L 2</p>
<p>client</p>
<p>├── clientset</p>
<p>│   └── versioned</p>
<p>├── informers</p>
<p>│   └── externalversions</p>
<p>└── listers</p>
<p>    └── sedna</p>
</code>`<code>
<p>後述の Controller から参照される。</p>
<strong>CRD YAML の更新</strong>
<p>同様に型を変えたら：</p>
</code>`<code>plain text
<p>make crds</p>
</code>`<code>
<p>出力先は </code>build/crds<code>。クラスタ側も </code>kubectl apply<code> で CRD を更新する。</p>
<h3>Controller</h3>
<p>ライフロング学習の中核は </code>pkg/globalmanager/controllers/lifelonglearning/lifelonglearningjob.go<code>（学習／評価ワーカーの起動タイミング、エッジへのパラメータ同期など）。</p>
<p>全体の呼び出しは次の疑似コードで把握できる：</p>
</code>`<code>go
<p>cmd/sedna-gm/sedna-gm.go/main() 【1】</p>
<p>pkg/globalmanager/controllers/manager.go/New() 【2】GM 設定読み込み</p>
<p>pkg/globalmanager/controllers/manager.go/Start() 【3】GM プロセス起動</p>
<p>    - clientset.NewForConfig()：【4】client-go で Sedna CRD 用 clientset 生成</p>
<p>    - NewUpstreamController()：【5】UpstreamController 生成（GM プロセスあたり 1 つ）</p>
<p>    - uc.Run(stopCh)：for ループの goroutine で</p>
<p>        - pkg/globalmanager/controllers/upstream.go/syncEdgeUpdate() </p>
<p>    - NewRegistry()：【6】全 controller 登録</p>
<p>        - f.SetDownstreamSendFunc()【7】</p>
<p>            -> pkg/globalmanager/controllers/lifelonglearning/downstream.go</p>
<p>        - f.SetUpstreamHandler()【8】</p>
<p>            -> pkg/globalmanager/controllers/lifelonglearning/upstream.go/updateFromEdge()</p>
<p>        - f.Run()【9】</p>
<p>    - ws.ListenAndServe() 【10】</p>
</code>`<code>
<p>LifelongLearningJob Controller の説明も、上記【1】〜【10】に沿う。</p>
<h3>【1】</code>main<code> エントリ</h3>
</code>sedna-gm.go<code> は GM の入口。ログ初期化、</code>app.NewControllerCommand()<code> でフラグ解析と controller 起動。
</code>cmd/sedna-gm/sedna-gm.go<code>
</code>`<code>go
<p>func main() {</p>
<p>   rand.Seed(time.Now().UnixNano())</p>
<p>   command := app.NewControllerCommand()</p>
<p>   logs.InitLogs()</p>
<p>   defer logs.FlushLogs()</p>
<p>   if err := command.Execute(); err != nil {</p>
<p>      os.Exit(1)</p>
<p>   }</p>
<p>}</p>
</code>`<code>
<h3>【2】GM 設定の読み込み</h3>
<p>k8s 接続情報、WebSocket の待受アドレス／ポート、ナレッジベース（KB）URL などを読み込む。</p>
</code>pkg/globalmanager/controllers/manager.go<code>
</code>`<code>go
<p>// New creates the controller manager</p>
<p>func New(cc <em>config.ControllerConfig) </em>Manager {</p>
<p>   config.InitConfigure(cc)</p>
<p>   return &Manager{</p>
<p>      Config: cc,</p>
<p>   }</p>
<p>}</p>
</code>`<code>
</code>pkg/globalmanager/config/config.go<code>
</code>`<code>go
<p>// ControllerConfig indicates the config of controller</p>
<p>type ControllerConfig struct {</p>
<p>   // KubeAPIConfig indicates the kubernetes cluster info which controller will connected</p>
<p>   KubeConfig string </code>json:"kubeConfig,omitempty"<code></p>
<p>   // Master indicates the address of the Kubernetes API server. Overrides any value in KubeConfig.</p>
<p>   // such as https://127.0.0.1:8443</p>
<p>   // default ""</p>
<p>   Master string </code>json:"master"<code></p>
<p>   // Namespace indicates which namespace the controller listening to.</p>
<p>   // default ""</p>
<p>   Namespace string </code>json:"namespace,omitempty"<code></p>
<p>   // websocket server config</p>
<p>   // Since the current limit of kubeedge(1.5), GM needs to build the websocket channel for communicating between GM and LCs.</p>
<p>   WebSocket WebSocket </code>json:"websocket,omitempty"<code></p>
<p>   // lc config to info the worker</p>
<p>   LC LCConfig </code>json:"localController,omitempty"<code></p>
<p>   // kb config to info the worker</p>
<p>   KB KBConfig </code>json:"knowledgeBaseServer,omitempty"<code></p>
<p>   // period config min resync period</p>
<p>   // default 30s</p>
<p>   MinResyncPeriodSeconds int64 </code>json:"minResyncPeriodSeconds,omitempty"<code></p>
<p>}</p>
</code>`<code>
<h3>【3】GM の全体初期化</h3>
<p>Sedna CRD 用クライアント作成、クラウド–エッジメッセージ処理のバインド、機能別 controller の起動、WebSocket 待受開始、までをまとめて行う。</p>
</code>pkg/globalmanager/controllers/manager.go<code>
</code>`<code>go
<p>// Start starts the controllers it has managed</p>
<p>func (m *Manager) Start() error {</p>
<p>   ...</p>
<p>   // 初始化Sedna CRD client，Controller会监听Sedna CR 增删改查的变化，并执行对应的处理逻辑。</p>
<p>   sednaClient, err := clientset.NewForConfig(kubecfg)</p>
<p>   ...</p>
<p>   sednaInformerFactory := sednainformers.NewSharedInformerFactoryWithOptions(sednaClient, genResyncPeriod(minResyncPeriod), sednainformers.WithNamespace(namespace))</p>
<p>   // 初始化UpstreamController，用于处理边缘LC上传的消息</p>
<p>   uc, _ := NewUpstreamController(context)</p>
<p>   downstreamSendFunc := messagelayer.NewContextMessageLayer().SendResourceObject</p>
<p>   stopCh := make(chan struct{})</p>
<p>   go uc.Run(stopCh)</p>
<p>   // 针对每个特性（协同推理、终身学习等），绑定对应的消息处理函数</p>
<p>   for name, factory := range NewRegistry() {</p>
<p>      ...</p>
<p>      f.SetDownstreamSendFunc(downstreamSendFunc)</p>
<p>      f.SetUpstreamHandler(uc.Add)</p>
<p>      ...</p>
<p>      // 启动各个特性对应controller</p>
<p>      go f.Run(stopCh)</p>
<p>   }</p>
<p>   ...</p>
<p>   // 启动整体GM的websocket，默认监听在0.0.0.0:9000这个端口地址</p>
<p>   ws := websocket.NewServer(addr)</p>
<p>   ...</p>
<p>}</p>
</code>`<code>
<h3>【4】CRD クライアント初期化</h3>
</code>clientset.NewForConfig()<code> の実体は </code>pkg/client/clientset/versioned/clientset.go<code>。codegen 済みの clientset で CR の CRUD を行う。
</code>LifelongLearningJob<code> Controller の </code>New<code> は主に次を行う：
<li></code>LifelongLearningJob<code> の Informer 取得（API Server 負荷軽減のローカルキャッシュ）</li>
<li>kube／sedna クライアントや GM 共通設定の注入</li>
<li>Job CR の Add／Update／Delete にコールバックを登録</li>
</code>pkg/globalmanager/controllers/lifelonglearning/lifelonglearningjob.go<code>
</code>`<code>go
<p>// New creates a new LifelongLearningJob controller that keeps the relevant pods</p>
<p>// in sync with their corresponding LifelongLearningJob objects.</p>
<p>func New(cc *runtime.ControllerContext) (runtime.FeatureControllerI, error) {</p>
<p>   cfg := cc.Config</p>
<p>   podInformer := cc.KubeInformerFactory.Core().V1().Pods()</p>
<p>   // 获取LifelongLearningJob的Informer</p>
<p>   jobInformer := cc.SednaInformerFactory.Sedna().V1alpha1().LifelongLearningJobs()</p>
<p>   eventBroadcaster := record.NewBroadcaster()</p>
<p>   eventBroadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: cc.KubeClient.CoreV1().Events("")})</p>
<p>   // 配置LifelongLearningJob Controller的参数</p>
<p>   jc := &Controller{</p>
<p>      kubeClient: cc.KubeClient,</p>
<p>      client:     cc.SednaClient.SednaV1alpha1(),</p>
<p>      queue:      workqueue.NewNamedRateLimitingQueue(workqueue.NewItemExponentialFailureRateLimiter(runtime.DefaultBackOff, runtime.MaxBackOff), Name),</p>
<p>      cfg:        cfg,</p>
<p>   }</p>
<p>   // 绑定LifelongLearningJob CRD资源的Add、Update、Delete对应事件的回调函数。</p>
<p>   jobInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{</p>
<p>      AddFunc: func(obj interface{}) {</p>
<p>         jc.enqueueController(obj, true)</p>
<p>         jc.syncToEdge(watch.Added, obj)</p>
<p>      },</p>
<p>      UpdateFunc: func(old, cur interface{}) {</p>
<p>         jc.enqueueController(cur, true)</p>
<p>         jc.syncToEdge(watch.Added, cur)</p>
<p>      },</p>
<p>      DeleteFunc: func(obj interface{}) {</p>
<p>         jc.enqueueController(obj, true)</p>
<p>         jc.syncToEdge(watch.Deleted, obj)</p>
<p>      },</p>
<p>   })</p>
<p>   jc.jobLister = jobInformer.Lister()</p>
<p>   jc.jobStoreSynced = jobInformer.Informer().HasSynced</p>
<p>   // 绑定Pod对应的增删改对应事件的回调函数。</p>
<p>   podInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{</p>
<p>      AddFunc:    jc.addPod,</p>
<p>      UpdateFunc: jc.updatePod,</p>
<p>      DeleteFunc: jc.deletePod,</p>
<p>   })</p>
<p>   jc.podStore = podInformer.Lister()</p>
<p>   jc.podStoreSynced = podInformer.Informer().HasSynced</p>
<p>   return jc, nil</p>
<p>}</p>
</code>`<code>
<p>図は他モジュールから Sedna clientset を参照している例。</p>
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/KubeEdge-Sedna%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%EF%BC%88%E8%BD%AC%E8%BD%BD%EF%BC%89_1770869748199.png" alt="" />
<h3>【5】アップストリーム（エッジ→クラウド）の初期化</h3>
</code>uc.Run()<code> が </code>UpstreamController<code> を回し、エッジ LC からの更新をまとめて処理する。
<p>ループでメッセージレイヤから受け取り、</code>uc.updateHandlers[kind]<code> で連合推論・増分・連邦・ライフロングなど種別ごとのハンドラに振る。</p>
</code>pkg/globalmanager/controllers/upstream.go<code>
</code>`<code>go
<p>// syncEdgeUpdate receives the updates from edge and syncs these to k8s.</p>
<p>func (uc *UpstreamController) syncEdgeUpdate() {</p>
<p>   for {</p>
<p>      select {</p>
<p>      case <-uc.messageLayer.Done():</p>
<p>         klog.Info("Stop sedna upstream loop")</p>
<p>         return</p>
<p>      default:</p>
<p>      }</p>
<p>      update, err := uc.messageLayer.ReceiveResourceUpdate()</p>
<p>	  ...</p>
<p>      handler, ok := uc.updateHandlers[kind]</p>
<p>      if ok {</p>
<p>         err := handler(name, namespace, operation, update.Content)</p>
<p>         ...</p>
<p>      }</p>
<p>   }</p>
<p>}</p>
</code>`<code>
</code>ReceiveFromEdge<code> は LC からのメッセージをブロッキングで受ける経路（実装はレイヤ内で </code>nodeMessage<code> 等として扱う）。
</code>pkg/globalmanager/messagelayer/ws/context.go<code>
</code>`<code>go
<p>// ReceiveResourceUpdate receives and handles the update</p>
<p>func (cml <em>ContextMessageLayer) ReceiveResourceUpdate() (</em>ResourceUpdateSpec, error) {</p>
<p>   nodeName, msg, err := wsContext.ReceiveFromEdge()</p>
<p>   ...</p>
<p>}</p>
</code>`<code>
<h3>【6】Controller の登録</h3>
</code>NewRegistry()<code> が機能ごとの </code>New<code> を束ねる。新しい協調機能を足すならここにエントリを追加する。
</code>pkg/globalmanager/controllers/registry.go<code>
</code>`<code>go
<p>func NewRegistry() Registry {</p>
<p>   return Registry{</p>
<p>      ji.Name:      ji.New,</p>
<p>      fe.Name:      fe.New,</p>
<p>      fl.Name:      fl.New,</p>
<p>      il.Name:      il.New,</p>
<p>      ll.Name:      ll.New,</p>
<p>      reid.Name:    reid.New,</p>
<p>      va.Name:      va.New,</p>
<p>      dataset.Name: dataset.New,</p>
<p>      objs.Name:    objs.New,</p>
<p>   }</p>
<p>}</p>
</code>`<code>
<h3>【7】クラウド→エッジの同期</h3>
</code>f.SetDownstreamSendFunc()<code> が各機能の </code>syncToEdge<code> を下流送信に結び付ける。
<p>ライフロング学習では概ね次の順：</p>
<li>Dataset CR からデータ所在ノードを取得</li>
<li>Annotation から学習／評価／デプロイ各フェーズのノード名を取得</li>
<li>現在フェーズに応じてメッセージを送る先を切り替える</li>
</code>pkg/globalmanager/controllers/lifelonglearning/downstream.go<code>
</code>`<code>go
<p>func (c *Controller) syncToEdge(eventType watch.EventType, obj interface{}) error {</p>
<p>   // 获取到对应的数据集指定的节点（Dataset CRD对象中有一个字段记录了Node名称）</p>
<p>   ds, err := c.client.Datasets(job.Namespace).Get(context.TODO(), dataName, metav1.GetOptions{})</p>
<p>   </p>
<p>   // 获取到训练、评估、部署对应的节点名称</p>
<p>   getAnnotationsNodeName := func(nodeName sednav1.LLJobStage) string {</p>
<p>      return runtime.AnnotationsKeyPrefix + string(nodeName)</p>
<p>   }</p>
<p>   ann := job.GetAnnotations()</p>
<p>   if ann != nil {</p>
<p>      trainNodeName = ann[getAnnotationsNodeName(sednav1.LLJobTrain)]</p>
<p>      evalNodeName = ann[getAnnotationsNodeName(sednav1.LLJobEval)]</p>
<p>      deployNodeName = ann[getAnnotationsNodeName(sednav1.LLJobDeploy)]</p>
<p>   }</p>
<p>   </p>
<p>   ...</p>
<p>   // 根据LifelongLearningJob所处阶段不同，发送消息到不同的节点上</p>
<p>   switch jobStage {</p>
<p>   case sednav1.LLJobTrain:</p>
<p>      doJobStageEvent(trainNodeName)</p>
<p>   case sednav1.LLJobEval:</p>
<p>      doJobStageEvent(evalNodeName)</p>
<p>   case sednav1.LLJobDeploy:</p>
<p>      doJobStageEvent(deployNodeName)</p>
<p>   }</p>
<p>   return nil</p>
<p>}</p>
</code>`<code>
<h3>【8】エッジ→クラウドの同期</h3>
</code>f.SetUpstreamHandler()<code> が </code>updateFromEdge<code> を登録する。
<p>ライフロング学習では：</p>
<li>LC からの報告に応じてジョブ全体の状態を更新</li>
<li></code>LifelongLearningJob<code> の </code>Status<code> を k8s に書き戻す</li>
<li>JSON 本文をパースする。GM が受け取る例：</li>
</code>`<code>json
<p>{</p>
<p>    "phase": "train",</p>
<p>    "status": "completed",</p>
<p>    "output": {</p>
<p>        "models": [{</p>
<p>            "classes":  ["road", "fence"],</p>
<p>            "current_metric": null,</p>
<p>            "format": "pkl",</p>
<p>            "metrics": null,</p>
<p>            "url": "/output/train/1/index.pkl"</p>
<p>        }],</p>
<p>        "ownerInfo": null</p>
<p>    }</p>
<p>}</p>
</code>`<code>
</code>pkg/globalmanager/controllers/lifelonglearning/upstream.go<code>
</code>`<code>go
<p>// updateFromEdge syncs the edge updates to k8s</p>
<p>func (c *Controller) updateFromEdge(name, namespace, operation string, content []byte) error {</p>
<p>   var jobStatus struct {</p>
<p>      Phase  string </code>json:"phase"<code></p>
<p>      Status string </code>json:"status"<code></p>
<p>   }</p>
<p>   </p>
<p>   // 把边缘消息结构体进行解析。</p>
<p>   err := json.Unmarshal(content, &jobStatus)</p>
<p>   ...</p>
<p>   cond := sednav1.LLJobCondition{</p>
<p>      Status:             v1.ConditionTrue,</p>
<p>      LastHeartbeatTime:  metav1.Now(),</p>
<p>      LastTransitionTime: metav1.Now(),</p>
<p>      Data:               string(condDataBytes),</p>
<p>      Message:            "reported by lc",</p>
<p>   }</p>
<p>   // 根据不同的边缘节点任务状态实现，变更当前LifelongLearningJob的整体状态</p>
<p>   switch strings.ToLower(jobStatus.Status) {</p>
<p>   case "ready":</p>
<p>      cond.Type = sednav1.LLJobStageCondReady</p>
<p>   case "completed":</p>
<p>      cond.Type = sednav1.LLJobStageCondCompleted</p>
<p>   case "failed":</p>
<p>      cond.Type = sednav1.LLJobStageCondFailed</p>
<p>   case "waiting":</p>
<p>      cond.Type = sednav1.LLJobStageCondWaiting</p>
<p>   default:</p>
<p>      return fmt.Errorf("invalid condition type: %v", jobStatus.Status)</p>
<p>   }</p>
<p>   // 将当前LifelongLearningJob的整体状态写回k8s，也就是LifelongLearningJob这个CR的Status字段。</p>
<p>   err = c.appendStatusCondition(name, namespace, cond)</p>
<p>   ...</p>
<p>}</p>
</code>`<code>
<h3>【9】Controller のメイン処理</h3>
<p>各機能の </code>Run<code> がワーカーを起動する。ライフロングでは </code>WaitForNamedCacheSync<code> で Pod／Job の Informer 同期を待ってから </code>worker<code> を回す。</p>
</code>pkg/globalmanager/controllers/lifelonglearning/lifelonglearningjob.go<code>
</code>`<code>go
<p>// Run starts the main goroutine responsible for watching and syncing jobs.</p>
<p>func (c *Controller) Run(stopCh <-chan struct{}) {</p>
<p>   workers := 1</p>
<p>   defer utilruntime.HandleCrash()</p>
<p>   defer c.queue.ShutDown()</p>
<p>   klog.Infof("Starting %s controller", Name)</p>
<p>   defer klog.Infof("Shutting down %s controller", Name)</p>
<p>   if !cache.WaitForNamedCacheSync(Name, stopCh, c.podStoreSynced, c.jobStoreSynced) {</p>
<p>      klog.Errorf("failed to wait for %s caches to sync", Name)</p>
<p>      return</p>
<p>   }</p>
<p>   klog.Infof("Starting %s workers", Name)</p>
<p>   for i := 0; i < workers; i++ {</p>
<p>      go wait.Until(c.worker, time.Second, stopCh)</p>
<p>   }</p>
<p>   <-stopCh</p>
<p>}</p>
</code>`<code>
</code>worker<code> は </code>processNextWorkItem<code> を回し、キューから key を取り出して </code>sync<code> に渡す。
</code>pkg/globalmanager/controllers/lifelonglearning/lifelonglearningjob.go<code>
</code>`<code>go
<p>// worker runs a worker thread that just dequeues items, processes them, and marks them done.</p>
<p>// It enforces that the syncHandler is never invoked concurrently with the same key.</p>
<p>func (c *Controller) worker() {</p>
<p>   for c.processNextWorkItem() {</p>
<p>   }</p>
<p>}</p>
</code>`<code>
<p>続けて </code>sync<code> の本体。</p>
</code>pkg/globalmanager/controllers/lifelonglearning/lifelonglearningjob.go<code>
</code>`<code>go
<p>func (c *Controller) sync(key string) (bool, error) {</p>
<p>   //省略了部分代码</p>
<p>   ns, name, err := cache.SplitMetaNamespaceKey(key)</p>
<p>   sharedJob, err := c.jobLister.LifelongLearningJobs(ns).Get(name)</p>
<p>   // if job was finished previously, we don't want to redo the termination</p>
<p>   if IsJobFinished(&job) {</p>
<p>      return true, nil</p>
<p>   }</p>
<p>   // transit this job's state machine</p>
<p>   needUpdated, err = c.transitJobState(&job)</p>
<p>   if needUpdated {</p>
<p>      if err := c.updateJobStatus(&job); err != nil {</p>
<p>         return forget, err</p>
<p>      }</p>
<p>      if jobFailed && !IsJobFinished(&job) {</p>
<p>         // returning an error will re-enqueue LifelongLearningJob after the backoff period</p>
<p>         return forget, fmt.Errorf("failed pod(s) detected for lifelonglearningjob key %q", key)</p>
<p>      }</p>
<p>      forget = true</p>
<p>   }</p>
<p>   return forget, err</p>
<p>}</p>
</code>`<code>
</code>sync<code> は個別ジョブの状態機械を進める：
<li>key を namespace／name に分割</li>
<li>lister で </code>LifelongLearningJob<code> を取得</li>
<li></code>transitJobState<code> で学習→評価→デプロイなどへ遷移すべきか判断</li>
<li>変化があれば </code>updateJobStatus<code> で API に反映（</code>kubectl<code> で見える最新フェーズやモデルパスなど）</li>
<li>失敗時の再キュー／エラー処理</li>
</code>`<code>go
<p>// transit this job's state machine</p>
<p>needUpdated, err = c.transitJobState(&job)</p>
</code>`<code>
</code>transitJobState()<code> がフェーズ遷移の中核。詳細は次の状態図と照らすとよい。
<img src="https://pub-187e5a9ad2b040c7aa8e0208d1291b32.r2.dev/KubeEdge-Sedna%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%EF%BC%88%E8%BD%AC%E8%BD%BD%EF%BC%89_1770869748995.png" alt="" />
<h3>【10】WebSocket サーバ起動</h3>
<p>【8】の LC からのメッセージを受ける WebSocket。デフォルトは </code>0.0.0.0:9000<code>。</p>
</code>pkg/globalmanager/controllers/manager.go<code>
</code>`<code>go
<p>addr := fmt.Sprintf("%s:%d", m.Config.WebSocket.Address, m.Config.WebSocket.Port)</p>
<p>ws := websocket.NewServer(addr)</p>
<p>err = ws.ListenAndServe()</p>
</code>`<code>
<h2>LC: Local Controller</h2>
<p>LC はエッジノード上で動き、ローカルジョブ管理とメッセージ中継を担う。エントリは </code>cmd/sedna-lc/sedna-lc.go<code>（GM 章と同様の読み方）。ローカルマネージャ登録部分：</p>
</code>cmd/sedna-lc/app/server.go<code>
</code>`<code>go
<p>// runServer runs server</p>
<p>func runServer() {</p>
<p>   c := gmclient.NewWebSocketClient(Options)</p>
<p>   if err := c.Start(); err != nil {</p>
<p>      return</p>
<p>   }</p>
<p>   dm := dataset.New(c, Options)</p>
<p>   mm := model.New(c)</p>
<p>   jm := jointinference.New(c)</p>
<p>   fm := federatedlearning.New(c)</p>
<p>   im := incrementallearning.New(c, dm, mm, Options)</p>
<p>   lm := lifelonglearning.New(c, dm, Options)</p>
<p>   s := server.New(Options)</p>
<p>   for _, m := range []managers.FeatureManager{</p>
<p>      dm, mm, jm, fm, im, lm,</p>
<p>   } {</p>
<p>      s.AddFeatureManager(m)</p>
<p>      c.Subscribe(m)</p>
<p>      err := m.Start()</p>
<p>      if err != nil {</p>
<p>         klog.Errorf("failed to start manager %s: %v",</p>
<p>            m.GetName(), err)</p>
<p>         return</p>
<p>      }</p>
<p>      klog.Infof("manager %s is started", m.GetName())</p>
<p>   }</p>
<p>   s.ListenAndServe()</p>
<p>}</p>
</code>`<code>
<h3>ローカルジョブ管理</h3>
<p>エッジ側のジョブを束ねる </code>Manager<code> の構造体：</p>
</code>pkg/localcontroller/managers/lifelonglearning/lifelonglearningjob.go<code>
</code>`<code>go
<p>// LifelongLearningJobManager defines lifelong-learning-job Manager</p>
<p>type Manager struct {</p>
<p>   Client                 clienttypes.ClientI</p>
<p>   WorkerMessageChannel   chan workertypes.MessageContent</p>
<p>   DatasetManager         *dataset.Manager</p>
<p>   LifelongLearningJobMap map[string]*Job</p>
<p>   VolumeMountPrefix      string</p>
<p>}</p>
</code>`<code>
</code>startJob()<code> の流れの要点：
<li>エッジに同期された Dataset を監視し、サンプル数しきい値などで学習トリガーを判断</li>
<li>フェーズに応じて学習／評価／デプロイ処理を呼ぶ。実際の Pod 起動は GM 側オーケストレーションに任せ、LC は状態を GM に報告する</li>
</code>pkg/localcontroller/managers/lifelonglearning/lifelonglearningjob.go<code>
</code>`<code>go
<p>// startJob starts a job</p>
<p>func (lm *Manager) startJob(name string) {</p>
<p>   ...</p>
<p>    </p>
<p>   // 监控并处理同步到边缘的Dataset对象。 </p>
<p>   go lm.handleData(job)</p>
<p>   tick := time.NewTicker(JobIterationIntervalSeconds * time.Second)</p>
<p>   for {</p>
<p>      // 根据当前任务不同阶段，触发不同阶段的训练、评估、部署任务。</p>
<p>      select {</p>
<p>      case <-job.JobConfig.Done:</p>
<p>         return</p>
<p>      case <-tick.C:</p>
<p>         cond := lm.getLatestCondition(job)</p>
<p>         jobStage := cond.Stage</p>
<p>         switch jobStage {</p>
<p>         case sednav1.LLJobTrain:</p>
<p>            err = lm.trainTask(job)</p>
<p>         case sednav1.LLJobEval:</p>
<p>            err = lm.evalTask(job)</p>
<p>         case sednav1.LLJobDeploy:</p>
<p>            err = lm.deployTask(job)</p>
<p>         default:</p>
<p>            klog.Errorf("invalid phase: %s", jobStage)</p>
<p>            continue</p>
<p>         }</p>
<p>		 ...</p>
<p>      }</p>
<p>   }</p>
<p>}</p>
</code>`<code>
<p>ほかにもデータセット監視、モデル取得、ローカル DB バックアップなどがある。</p>
<h3>メッセージプロキシ</h3>
<p>状態変化はクラウドへ送るほか、ローカル </code>0.0.0.0:9100<code> で HTTP サーバを立て、Lib からのワーカー報告を集約して GM へ転送する。REST ルート例：</p>
</code>pkg/localcontroller/server/server.go<code>
</code>`<code>go
<p>// register registers api</p>
<p>func (s <em>Server) register(container </em>restful.Container) {</p>
<p>	ws := new(restful.WebService)</p>
<p>	ws.Path(fmt.Sprintf("/%s", constants.ServerRootPath)).</p>
<p>		Consumes(restful.MIME_XML, restful.MIME_JSON).</p>
<p>		Produces(restful.MIME_JSON, restful.MIME_XML)</p>
<p>	ws.Route(ws.POST("/workers/{worker-name}/info").</p>
<p>		To(s.messageHandler).</p>
<p>		Doc("receive worker message"))</p>
<p>	container.Add(ws)</p>
<p>}</p>
</code>`<code>
</code>pkg/localcontroller/server/server.go<code>
</code>`<code>go
<p>// messageHandler handles message from the worker</p>
<p>func (s <em>Server) messageHandler(request </em>restful.Request, response *restful.Response) {</p>
<p>   var err error</p>
<p>   workerName := request.PathParameter("worker-name")</p>
<p>   workerMessage := workertypes.MessageContent{}</p>
<p>   err = request.ReadEntity(&workerMessage)</p>
<p>   if workerMessage.Name != workerName || err != nil {</p>
<p>      var msg string</p>
<p>      if workerMessage.Name != workerName {</p>
<p>         msg = fmt.Sprintf("worker name(name=%s) in the api is different from that(name=%s) in the message body",</p>
<p>            workerName, workerMessage.Name)</p>
<p>      } else {</p>
<p>         msg = fmt.Sprintf("read worker(name=%s) message body failed, error: %v", workerName, err)</p>
<p>      }</p>
<p>      klog.Errorf(msg)</p>
<p>      err = s.reply(response, http.StatusBadRequest, msg)</p>
<p>      if err != nil {</p>
<p>         klog.Errorf("reply messge to worker(name=%s) failed, error: %v", workerName, err)</p>
<p>      }</p>
<p>   }</p>
<p>   if m, ok := s.fmm[workerMessage.OwnerKind]; ok {</p>
<p>      m.AddWorkerMessage(workerMessage)</p>
<p>   }</p>
<p>   err = s.reply(response, http.StatusOK, "OK")</p>
<p>   if err != nil {</p>
<p>      klog.Errorf("reply message to worker(name=%s) failed, error: %v", workerName, err)</p>
<p>      return</p>
<p>   }</p>
<p>}</p>
</code>`<code>
<h1>Sedna Lib（Python）</h1>
<p>Lib は AI／アプリ開発者向けの Python パッケージで、既存コードを協調型に載せ替えやすくする。</p>
<p>ディレクトリ構成：</p>
</code>`<code>plain text
<p>➜  sedna tree lib -L 2</p>
<p>lib</p>
<p>├── __init__.py</p>
<p>├── MANIFEST.in</p>
<p>├── OWNERS</p>
<p>├── requirements.dev.txt</p>
<p>├── requirements.txt    // Sedna Pythonの依存</p>
<p>├── sedna</p>
<p>│   ├── algorithms  // 協調向けアルゴリズム</p>
<p>│   ├── backend     // tensorflow/pytorch 等のバックエンド</p>
<p>│   ├── common</p>
<p>│   ├── core        // 各機能の中核ロジック</p>
<p>│   ├── datasources // txt/csv 等のデータソース</p>
<p>│   ├── __init__.py</p>
<p>│   ├── README.md</p>
<p>│   ├── service     // KB などサーバが要るコンポーネント</p>
<p>│   ├── VERSION</p>
<p>│   └── __version__.py</p>
<p>└── setup.py</p>
</code>`<code>
<p>代表的なコード片を領域ごとに見る。</p>
<h3>core</h3>
<p>ユーザーの </code>train<code> などをラップし、TensorFlow／PyTorch／MindSpore 実装を呼び出す。</p>
<li>後処理コールバックの設定</li>
<li>クラウド KB を介した学習・推論</li>
<li>KB の更新（ライフロングではモデルとサンプルが蓄積される）</li>
<li>進捗・メトリクスを LC へ報告</li>
</code>lib/sedna/core/lifelong_learning/lifelong_learning.py<code>
</code>`<code>python
<p>def train(self, train_data,</p>
<p>          valid_data=None,</p>
<p>          post_process=None,</p>
<p>          **kwargs):</p>
<p>    is_completed_initilization = \</p>
<p>        str(Context.get_parameters("HAS_COMPLETED_INITIAL_TRAINING",</p>
<p>                                   "false")).lower()</p>
<p>    if is_completed_initilization == "true":</p>
<p>        return self.update(train_data,</p>
<p>                           valid_data=valid_data,</p>
<p>                           post_process=post_process,</p>
<p>                           **kwargs)</p>
<p>    # 配置后处理函数</p>
<p>    callback_func = None</p>
<p>    if post_process is not None:</p>
<p>        callback_func = ClassFactory.get_cls(</p>
<p>            ClassType.CALLBACK, post_process)</p>
<p>    res, seen_task_index = \</p>
<p>        self.cloud_knowledge_management.seen_estimator.train(</p>
<p>            train_data=train_data,</p>
<p>            valid_data=valid_data,</p>
<p>            **kwargs</p>
<p>        ) </p>
<p>    # 调用云端知识库进行训练、或推理</p>
<p>    unseen_res, unseen_task_index = \</p>
<p>        self.cloud_knowledge_management.unseen_estimator.train()</p>
<p>    # 更新云端知识库</p>
<p>    task_index = dict(</p>
<p>        seen_task=seen_task_index,</p>
<p>        unseen_task=unseen_task_index)</p>
<p>    task_index_url = FileOps.dump(</p>
<p>        task_index, self.cloud_knowledge_management.local_task_index_url)</p>
<p>    task_index = self.cloud_knowledge_management.update_kb(task_index_url)</p>
<p>    res.update(unseen_res)</p>
<p>    ...</p>
<p>    </p>
<p>    # 将当前训练任务执行的情况发送给LC，比如训练任务是否完成、训练后的指标是多少</p>
<p>    self.report_task_info(</p>
<p>            None, K8sResourceKindStatus.COMPLETED.value, task_info_res)</p>
<p>        self.log.info(f"Lifelong learning Train task Finished, "</p>
<p>                      f"KB index save in {task_index}")</p>
<p>        return callback_func(self.estimator, res) if callback_func else res</p>
<p>    </p>
<p>    ...</p>
</code>`<code>
<h3>backend</h3>
</code>MSBackend<code> は MindSpore バックエンドの一例。フレームワークごとに </code>train<code>／</code>predict<code>／</code>evaluate<code> を揃えれば、Lib から協調実行へ載せられる。
</code>lib/sedna/backend/mindspore/__init__.py<code>
</code>`<code>python
<p>class MSBackend(BackendBase):</p>
<p>    def __init__(self, estimator, fine_tune=True, **kwargs):</p>
<p>        super(MSBackend, self).__init__(estimator=estimator,</p>
<p>                                        fine_tune=fine_tune,</p>
<p>                                        **kwargs)</p>
<p>        self.framework = "mindspore"</p>
<p>        if self.use_npu:</p>
<p>            context.set_context(mode=context.GRAPH_MODE,</p>
<p>                                device_target="Ascend")</p>
<p>        elif self.use_cuda:</p>
<p>            context.set_context(mode=context.GRAPH_MODE,</p>
<p>                                device_target="GPU")</p>
<p>        else:</p>
<p>            context.set_context(mode=context.GRAPH_MODE,</p>
<p>                                device_target="CPU")</p>
<p>        if callable(self.estimator):</p>
<p>            self.estimator = self.estimator()</p>
<p>    def train(self, train_data, valid_data=None, **kwargs):</p>
<p>        if callable(self.estimator):</p>
<p>            self.estimator = self.estimator()</p>
<p>        if self.fine_tune and FileOps.exists(self.model_save_path):</p>
<p>            self.finetune()</p>
<p>        self.has_load = True</p>
<p>        varkw = self.parse_kwargs(self.estimator.train, **kwargs)</p>
<p>        return self.estimator.train(train_data=train_data,</p>
<p>                                    valid_data=valid_data,</p>
<p>                                    **varkw)</p>
<p>    def predict(self, data, **kwargs):</p>
<p>        if not self.has_load:</p>
<p>            self.load()</p>
<p>        varkw = self.parse_kwargs(self.estimator.predict, **kwargs)</p>
<p>        return self.estimator.predict(data=data, **varkw)</p>
<p>    def evaluate(self, data, **kwargs):</p>
<p>        if not self.has_load:</p>
<p>            self.load()</p>
<p>        varkw = self.parse_kwargs(self.estimator.evaluate, **kwargs)</p>
<p>        return self.estimator.evaluate(data, **varkw)</p>
</code>`<code>
<h3>datasource</h3>
<p>CSV などよく使う形式のパースをまとめている。</p>
</code>lib/sedna/datasources/__init__.py<code>
</code>`<code>python
<p>class CSVDataParse(BaseDataSource, ABC):</p>
<p>    """</p>
<p>    csv file which contain Structured Data parser</p>
<p>    """</p>
<p>    # 提供了方便的数据集解析函数，</p>
<p>    def parse(self, <em>args, </em>*kwargs):</p>
<p>        x_data = []</p>
<p>        y_data = []</p>
<p>        label = kwargs.pop("label") if "label" in kwargs else ""</p>
<p>        usecols = kwargs.get("usecols", "")</p>
<p>        if usecols and isinstance(usecols, str):</p>
<p>            usecols = usecols.split(",")</p>
<p>        if len(usecols):</p>
<p>            if label and label not in usecols:</p>
<p>                usecols.append(label)</p>
<p>            kwargs["usecols"] = usecols</p>
<p>        for f in args:</p>
<p>            if isinstance(f, (dict, list)):</p>
<p>                res = self.parse_json(f, **kwargs)</p>
<p>            else:</p>
<p>                if not (f and FileOps.exists(f)):</p>
<p>                    continue</p>
<p>                res = pd.read_csv(f, **kwargs)</p>
<p>            if self.process_func and callable(self.process_func):</p>
<p>                res = self.process_func(res)</p>
<p>            if label:</p>
<p>                if label not in res.columns:</p>
<p>                    continue</p>
<p>                y = res[label]</p>
<p>                y_data.append(y)</p>
<p>                res.drop(label, axis=1, inplace=True)</p>
<p>            x_data.append(res)</p>
<p>        if not x_data:</p>
<p>            return</p>
<p>        self.x = pd.concat(x_data)</p>
<p>        self.y = pd.concat(y_data)</p>
</code>`<code>
<h3>algorithms</h3>
<p>協調 AI 向けに、難例マイニング（HEM）などを同梱する。例として交差エントロピーしきい値で「モデルが自信がない」サンプルを拾う。</p>
<p>Lib はこれらの基礎実装にとどまらず、協調フレームワーク上でアルゴリズムを差し替え・拡張して全体の学習／推論品質を上げることを狙う。</p>
</code>lib/sedna/algorithms/hard_example_mining/hard_example_mining.py<code>
</code>`<code>python
<p>@ClassFactory.register(ClassType.HEM, alias="CrossEntropy")</p>
<p>class CrossEntropyFilter(BaseFilter, abc.ABC):</p>
<p>    """</p>
<p>    <strong>Object detection</strong> Hard samples discovery methods named </code>CrossEntropy<code></p>
<p>    Parameters</p>
<p>    ----------</p>
<p>    threshold_cross_entropy: float</p>
<p>        hard coefficient threshold score to filter img, default to 0.5.</p>
<p>    """</p>
<p>    def __init__(self, threshold_cross_entropy=0.5, **kwargs):</p>
<p>        self.threshold_cross_entropy = float(threshold_cross_entropy)</p>
<p>    def __call__(self, infer_result=None) -> bool:</p>
<p>        """judge the img is hard sample or not.</p>
<p>        Parameters</p>
<p>        ----------</p>
<p>        infer_result: array_like</p>
<p>            prediction classes list, such as</p>
<p>            [class1-score, class2-score, class2-score,....],</p>
<p>            where class-score is the score corresponding to the class,</p>
<p>            class-score value is in [0,1], who will be ignored if its</p>
<p>            value not in [0,1].</p>
<p>        Returns</p>
<p>        -------</p>
<p>        is hard sample: bool</p>
<p>            </code>True<code> means hard sample, </code>False<code> means not.</p>
<p>        """</p>
<p>        if not infer_result:</p>
<p>            # if invalid input, return False</p>
<p>            return False</p>
<p>        log_sum = 0.0</p>
<p>        data_check_list = [class_probability for class_probability</p>
<p>                           in infer_result</p>
<p>                           if self.data_check(class_probability)]</p>
<p>        if len(data_check_list) != len(infer_result):</p>
<p>            return False</p>
<p>        for class_data in data_check_list:</p>
<p>            log_sum += class_data * math.log(class_data)</p>
<p>        confidence_score = 1 + 1.0 * log_sum / math.log(</p>
<p>            len(infer_result))</p>
<p>        return confidence_score < self.threshold_cross_entropy</p>
</code>``
<hr />
<p>1. <a href="https://www.redhat.com/en/topics/containers/what-is-a-kubernetes-operator">https://www.redhat.com/en/topics/containers/what-is-a-kubernetes-operator</a></p>
<p>2. <a href="https://developers.redhat.com/articles/2021/06/22/kubernetes-operators-101-part-2-how-operators-work">https://developers.redhat.com/articles/2021/06/22/kubernetes-operators-101-part-2-how-operators-work</a></p>]]></content>
        <author>
            <name>Shemol</name>
            <email>shemol106@gmail.com</email>
            <uri>https://shemol.tech</uri>
        </author>
    </entry>
</feed>