第 1 課:起動と設定:一行の設定がエージェント全体を変える
一言でいうと:DSH では「設定はコンポジション」——ひとつの
cordis.ymlが、エージェント全体がどのプラグインをロードし、どのモデルを使い、どんなツールを持つかを決定します。一行の設定を変えるだけで、モデルの交換、ツールの追加、能力の組み合わせの変更ができ、コードは一行も触る必要がありません。
1. ユーザーストーリー:なぜ「設定を変える」だけで済むのか?
小 D は昨日、dsh --profile headless "このリポジトリを要約して" でタスクを実行し、agent は deepseek-v4-flash モデルを使っていました。今日、彼がやりたいことは 3 つあります:
- agent を別のモデルに切り替える;
- agent に「Web を検索する」ツールを追加する;
- 「読み取り専用監査版」の agent を作る——ファイルの読み取りだけ許可し、コマンドは実行させない。
従来のフレームワークでは、この 3 つへの答えはほぼ同じです:ソースコードを改修する。モデル名はフレームワークにハードコードされ、ツールはメインループに登録する必要があり、能力の組み合わせは異なる fork ブランチを維持することで管理します。フレームワークをアップグレードする際には、自分が変更したコードを苦労してマージする必要もあります。
DSH では答えがまったく異なります:モデル、ツール、ポリシー、さらには agent のメインループそのものまで、すべてプラグインです。そして「どのプラグインをロードし、パラメータは何か」は、すべて設定ファイルが決めます。つまり:
| やりたいこと | 従来のフレームワーク | DSH |
|---|---|---|
| モデルを交換 | ソースを fork し、モデル名を書き換える | cordis.yml 内のモデル関連プラグインエントリの設定を変更する |
| ツールを追加 | フレームワークのソースを改修し、ツールをメインループに登録する | 設定にプラグインエントリをひとつ追加する |
| 能力の組み合わせを変更 | 複数の fork を維持し、それぞれコードを改修する | profile のレイヤーで、同じ土台の上に複数の組み合わせを作る |
つまり DSH における「設定」とは「プログラムにいくつかのパラメータを渡す」ことではなく、コンポジション(合成)——どのブロックがこのエージェントに組み込まれるかを決めることです。これこそが本課のタイトルの意味するところです:一行の設定が、エージェント全体を変えるのです。
2. 設定はコンポジション:ひとつの cordis.yml が agent 全体を決める
DSH は cordis.yml を使って、エージェントがどのプラグインをロードし、各プラグインがどんなパラメータを持つかを記述します。公式ドキュメントは明言しています:「設定ファイルは能力の合成を担う」(出典:docs/user/develop/basic/config.zh.md)。
最小限の設定とは、プラグインエントリの集合です(出典:docs/user/develop/basic/config.zh.md):
- id: llm-deepseek
name: '@deepseek-ai/dsh-llm-deepseek'
- id: bash
name: '@deepseek-ai/dsh-bash-local'
- id: agent-loop
name: '@deepseek-ai/dsh-agent-loop'
config:
agents:
- id: main
provider: deepseek-official
model: deepseek-v4-flash
このファイルを読み解けば、DSH の半分を理解したも同然です:
name:どの npm パッケージ(またはcordis.ymlからの相対パスのローカルモジュール)をロードするかを指定します;id:このプラグインインスタンスに安定した識別子を与え、他の設定レイヤーが id でこの行を見つけてパッチできるようにします;config:プラグイン自身に渡すパラメータ——たとえばagent-loopのconfig.agentsは「mainという名前の agent を起動し、deepseek-officialプロバイダーとdeepseek-v4-flashモデルを使う」と宣言しています。
つまり:モデルの交換 = model: の行の値を変える(または別のプロバイダープラグインに切り替える);ツールの追加 = ツールプラグインのエントリを追加する;ある能力を切り捨てる = エントリを削除するか、disabled: true を付けて一時的にスキップする。
現実の世界はこの最小例よりはるかに豊かです:DSH は「すべての profile で共通する」能力を dsh-base バンドルとしてパッケージ化しており、その cordis.patch.yml は長大なプラグインリストです——モデルアダプター、セッション永続化、サンドボックス、ファイルツール、サブエージェント、ワークフロー、テレメトリ……(出典:packages/bundle/base/cordis.patch.yml)。たとえば、そこで定義されているデフォルトのモデルルーティングは次の通りです:
- id: agent-default-model
name: '@deepseek-ai/dsh-agent-default-model'
config:
provider: deepseek-official
model: deepseek-v4-flash
🎁 たとえ話:
cordis.ymlはエージェントの「組立指示書」のようなものです——同じ生産ラインでも、組立指示書を差し替えれば、出来上がるのは異なる能力を持つ機械です。モデル、ツール、ループは、すべて組立指示書に記載された部品なのです。
3. profile のレイヤー:公式デフォルト層 → profile プラグイン層 → ユーザーオーバーライド層
「設定はコンポジション」にはもうひとつ重要な仕組みがあります:レイヤー(layer)です。完全な設定をゼロから書く必要はなく、他人の設定の上にオーバーライドして自分のバージョンを作ります。
CLI ドキュメントは dsh コマンドを「profiles(設定プロファイル)のプロダクトランチャー:順序付けられたプラグインバンドルのパッチレイヤーのスタックで、その下にユーザー自身のオーバーライドレイヤーがある」と定義しています(出典:apps/cli/README.md)。dsh web も dsh --profile headless も、実は異なる profile の組み合わせにすぎません:
dsh --profile headless "タスク"はheadlessprofile を起動します:dsh-baseとdsh-headlessが空のルートの上で合成され、その後 runner が core の Agent サービスと Session サービスを直接駆動します(出典:docs/user/guide/index.zh.md);dsh webは--profile webのエイリアスです:dsh-baseとdsh-web-appが合成され、ブラウザホスト、HTTP、クライアントプラグインが追加されます。
完全な設定の最終形は複数のレイヤーが重なってできており、後のレイヤーが前のレイヤーを上書きします——同じ行は、より後のレイヤーが優先されます。レイヤーの順序の原文(出典:apps/cli/README.md 20 行目):
コンポジションツリーは空のルートの上に重なります:まず
dsh.profile.bundlesリストの順序で各バンドルのパッチレイヤーを適用し、次に profile 自身のcordis.patch.yml、次に home レベルの$DSH_HOME/cordis.patch.yml、次に各--patch <path>オーバーレイ、最後に CLI フラグのパッチです。
配置即组合:一个 cordis.yml / profile 决定整个智能体的能力组合
3 層の直感に対応させると:
| レイヤー | どこにあるか | 誰のものか |
|---|---|---|
| 公式デフォルト層 | バンドルに内蔵されたパッチ(例:@deepseek-ai/dsh-base) | プラットフォーム側が提供 |
| profile プラグイン層 | profile ディレクトリ内のプラグインと cordis.patch.yml(dsh plugin --profile <name> ... で管理) | あなたが作った「組立プラン」 |
| ユーザーオーバーライド層 | $DSH_HOME/profiles/<name>/cordis.patch.yml など | あなた個人の好み |
⚠️ ハマりやすい落とし穴:パッチは
configを行ごと置き換えるのであって、個々のキーをディープマージするわけではありません(出典:docs/user/develop/basic/config.zh.md)。llm-deepseekの行をパッチするのにconfig: { thinking: disabled }だけを書くと、その行に元からあったapiKeyEnvやbaseURLがすべて消えてしまいます——オーバーライドする際は、保持したいキーをすべて書き直してください。💡 レイヤー適用の結果を確認したい?
--dump-default-configと--dump-configを使えば、実際に起動せずに合成後の完全な設定ツリーを直接表示できます(出典:apps/cli/README.md)。
4. 起動フロー:boot の組立 → スコープ付き ctx の準備完了 → agent の公開
設定は書けました。dsh はそれをどうやって「生きている agent」に変えるのでしょうか? おおむね 4 つのステップです:
① boot の組立。 app-boot は各 app の bin が共有する起動用の接着レイヤーです:.env のロード、明確にエラーを報告する Loader 保護機構、スナップショットを認識する設定解析、そしてツリー全体が安定するのを待つ起動シーケンス(出典:packages/boot/README.zh.md)。これが前述のレイヤーを最終的なひとつの設定に解決し、すべてのプラグインパッケージをロードします。
② プラグインは必要に応じてアクティベート。 Cordis は「サービスの可用性」によってアクティベーションを駆動します:プラグインは inject で自分が必要とするサービスを宣言し、依存がすべて揃ってから起動します——つまり設定ファイル内の記述順はロード順を決めません(出典:docs/user/develop/basic/config.zh.md)。
③ スコープ付き ctx の準備完了。 dsh-scope パッケージはスコープ付きの登録プリミティブを提供します:createScope(ctx, key) はラベル付きの Cordis コンテキストを作成し、それを通じて行われるすべての登録は、スコープの可視性を持ち、スコープのライフサイクルに従います(出典:packages/core/scope/README.zh.md)。agent loop はライブな agent ごとにスコープを作成します——各 agent は自分専用の独立した ctx を受け取り、それぞれが登録したツールやサービスは互いに干渉しません。これは第 2 章で扱った「コンテキストの型」と一貫しています:コンテキストそのものを、実行時に操作可能な第一級の実体として具現化するということです(直感の参照:cordis.txt 300〜304 行目)。
④ agent の公開。 スコープの準備が整って初めて、agent は正式に「公開」されます:dsh --profile headless は core の Agent サービスと Session サービスを直接駆動してタスクをひとつ完了させると終了します;dsh web はブラウザレイヤーが接続してくるのを待ってからセッションを作成します。
ここで、本課で最も重要な一文が見えてきます:すべての能力はプラグインであり、プラグインは登録を通じて ctx にアタッチされる——ロードすれば即座に有効になり、アンロードすれば元に戻る。これこそが第 2 章の「時空間コンポーザビリティ」の実地の姿です:インストールされたプラグインはツール、サービス、ポリシーをコンテキストに登録し;アンロード時には Cordis がスコープのライフサイクルに従って登録をまとめて取り消し、システムは組立前の姿に戻ります。つまり「一行の設定を変える」ことは乱暴な置き換えではなく、クリーンな再組立なのです。
要点の振り返り
- 設定はコンポジション:
cordis.ymlのプラグインエントリ(name+id+config)がエージェント全体の能力の組み合わせを決定します;モデルの交換、ツールの追加、ポリシーの変更はすべて設定の変更であり、コードを変える必要はありません。 - 能力はすべてプラグイン:モデルアダプター、ツール、サンドボックス、さらには agent loop そのものまでプラグインです;ctx に登録すれば即座に有効になり、アンロードすれば元に戻ります(第 2 章に呼応)。
- profile のレイヤー:公式デフォルト層 → profile プラグイン層 → ユーザーオーバーライド層で、後のレイヤーが前のレイヤーを上書きします;パッチは
configの行ごとの置き換えであり、ディープマージではありません。 - 起動の 4 ステップ:boot の組立(レイヤーの解決)→ プラグインはサービスの可用性に応じてアクティベート → agent loop がライブな agent ごとにスコープ付きの ctx を作成 → agent の公開。
🚀 次課では「組立が終わった後」の世界に踏み込みます:第 2 課「agent loop とセッション」——エージェントはどうやってループし、セッションはどうやって永続化されるのか。
セルフチェック · 起動と設定
回答を終えたら「解答を送信」をクリックすると、正誤と解説を確認できます。
