AI との組み合わせ: 上級開発者のプラグイン構築の旅

著者は、ChatGPT を使用して Go を学習し、Kolide API を探索し、複雑な Steampipe プラグインを構築した経験を共有します。

『Pairing With AI: A Senior Developer's Journey Building a Plugin』 (著者 Jon Udell)より翻訳。

開発者向けドキュメントの改善は常に役に立ちますが、多くの人 (私も含めて) は実践して学ぶことを好みます。これは私の7 つの指導原則の 7 番目で最も重要です。タスク指向で教えやすい瞬間に知識を獲得するため、学習は積極的ではなく即時的で具体的なものになります。

経験豊富な開発者が LLM を使用すると、そのマシン インテリジェンスがユーザーのインテリジェンスをサポートし、強化します。

そのメリットは私にとって明らかです。 LLM 時代にSteampipe 用の ODBC プラグインを作成することは、そのような助けを借りずにプラグインを作成した私の経験よりもはるかに簡単でした。しかし、それは明らかに主観的な評価であるため、他のプラグイン開発者とメモを比較する機会を探していたとき、James Ramirez がコミュニティ Slack に現れ、Kolide APIの新しいプラグインを発表しました。私がそれを構築した経験について話してほしいと彼に誘ったところ、彼は親切にも私を ChatGPT との長い会話に導いてくれました。そこで彼は、Kolide API、Go 言語、Steampipe プラグインという、彼にとって初めての 3 つの技術分野について詳しくなりました。建築。

さらなる課題として、プラグイン開発者は通常、プラグインのターゲットとなる API に適した Go SDK を見つけますが、ここでは当てはまりません。したがって、Kolide API の Go ラッパーを作成し、それをプラグインに統合する必要があります。

ChatGPT の Go 機能をテストする

ジェームズは準備運動から始めます。まず、ChatGPT の Go 機能をテストするために、関連する API_/devices//devices/ID_を呼び出すために作成した 1 対の Go 関数を提供し、それらの間の共有ロジックの慣用的なリファクタリングを必要としました。

次に、彼は、より複雑な関数オプション パターンではなく、単純な可変引数を使用して関数のオプションの引数を検討し、_Search_ 構造体のスライスを使用して、Kolide のクエリ パラメーター/ 演算子/値スタイルのフィールドをカプセル化するという単純なアプローチに落ち着きました。十分。彼は、検索構造のスライスを REST URL にシリアル化する関数を要求し、次に ChatGPT によって提案されたバージョンを最適化して最終的なSerializeSearchesを作成しました。これにより、フレンドリ名のパラメータへのマッピングと文字列ビルダーの使用のサポートが追加されました。

AI は細かい指摘を処理し、提出可能な提案を提供することがよくあります。

文字列ビルダーの使用を含むこれらの最適化の一部は、役立つコード レビューを提供するAI 搭載ボットであるCodeRabbitによって提案されました。これは、あなたとあなたのチームが全体像に集中するのに役立つフィードバックであり、細かい点を処理し、多くの場合 (常にではありませんが) 提出可能な提案を提供してくれるためです、と彼は言います。また、プル リクエストを要約し、クローズされた PR が関連する問題に記載されている目標に対処しているかどうかを評価するには、より広い視点が必要です。

マッピング演算子

彼は、Steampipe オペレーター (_QualOperatorEqual_ など) を Kolide オペレーター (_Equals_ など) にマップする方法を模索し続けています。同様に、ChatGPT によって提案されたアプローチは 1 回限りのソリューションとなり、クリーンでシンプルなソリューションへと移行しています。しかし、James がインタビューで認めたように、いずれにしても 1 回限りのバージョンで反復処理を行うことになるため、面倒な手作業でコーディングするよりも賢明な反復を生成できると便利です。その過程で、彼は基本的な囲碁のイディオムを学んでいます。

ジェームス:

Go には do-while ループはありますか?

チャットGPT

いや、でも...

ジェームス:

Go に三項演算子はありますか?

チャットGPT

いや、でも...

ジェームス:

_map[string]string_ に追加するにはどうすればよいですか?

チャットGPT

このような……

リフレクション拡張訪問者パターンの使用

基本を理解し、Kolide API 用の Go クライアントを開発した後、James はプラグイン開発の実際の作業に取り組む準備が整いました。API ラッパーから返された Go タイプを、それらのテーブルに対する SQL クエリを制御する Steampipe スキーマにマップするテーブルを定義するというものです。 。

すべてのプラグイン開発者と同様に、彼はリソースのコレクションをリストするテーブルから始めて、それをフィルタリングとページネーションで強化します。 2 番目のテーブルを追加したら、一般的なパターンと動作​​を抽象化する方法を考えます。最終的には Visitor パターンのエレガントな実装が得られます。以下は、テーブルkolide_deviceおよびkolide_issueに対応する Steampipe_List_ 関数です。

func listDevices(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
	var visitor ListPredicate = func(client *kolide.Client, cursor string, limit int32, searches ...kolide.Search) (interface{}, error) {
		return client.GetDevices(cursor, limit, searches...)
	}

	return listAnything(ctx, d, h, "kolide_device.listDevices", visitor, "Devices")
}


func listAdminUsers(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
	var visitor ListPredicate = func(client *kolide.Client, cursor string, limit int32, searches ...kolide.Search) (interface{}, error) {
		return client.GetAdminUsers(cursor, limit, searches...)
	}

	return listAnything(ctx, d, h, "kolide_admin_user.listAdminUsers", visitor, "AdminUsers")
}

以下は、すべてのプラグイン テーブルに共通のリスト関数です。

func listAnything(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData, callee string, visitor ListPredicate, target string) (interface{}, error) {
	// Create a slice to hold search queries
	searches, err := query(ctx, d)
	if err != nil {
		plugin.Logger(ctx).Error(callee, "qualifier_operator_error", err)
		return nil, err
	}

	// Establish connection to Kolide client
	client, err := connect(ctx, d)
	if err != nil {
		plugin.Logger(ctx).Error(callee, "connection_error", err)
		return nil, err
	}

	// Iterate through pagination cursors, with smallest number of pages
	var maxLimit int32 = kolide.MaxPaging
	if d.QueryContext.Limit != nil {
		limit := int32(*d.QueryContext.Limit)
		if limit < maxLimit {
			maxLimit = limit
		}
	}

	cursor := ""

	for {
		// Respect rate limiting
		d.WaitForListRateLimit(ctx)

		res, err := visitor(client, cursor, maxLimit, searches...)
		if err != nil {
			plugin.Logger(ctx).Error(callee, err)
			return nil, err
		}

		// Stream retrieved results
		collection := reflect.ValueOf(res).Elem().FieldByName(target)
		if collection.IsValid() {
			for i := 0; i < collection.Len(); i++ {
				d.StreamListItem(ctx, collection.Index(i).Interface())

				// Context can be cancelled due to manual cancellation or the limit has been hit
				if d.RowsRemaining(ctx) == 0 {
					return nil, nil
				}
			}
		}

		next := reflect.ValueOf(res).Elem().FieldByName("Pagination").FieldByName("NextCursor")
		if next.IsValid() {
			cursor = next.Interface().(string)
		}

		if cursor == "" {
			break
		}
	}

	return nil, nil
}

この設定では、プラグインへの新しいテーブルの追加はほぼ完全に宣言的です。必要なのは、スキーマと KeyColumns、および SQL クエリの where (または join) 句と API レベルのフィルター シンボルの間の相関演算を定義することだけです。 。次に、アクセサーを定義する小さな List 関数を作成し、その関数を共通の listAnything 関数に渡します。この関数は、クエリ パラメーターのマーシャリングをカプセル化し、API クライアントに接続し、API を呼び出し、応答をコレクションに展開するだけでなく、反復する機能も備えています。データ項目を Steampipe の外部データ ラッパーに転送するためのコレクション。

James は ChatGPT を使用して、Go での訪問者パターンの慣用的な実装を可能にしています。これには、ビジター関数の型を定義する方法を学習し、その型を満たす関数を宣言することが含まれます。各テーブル訪問者は API クライアントへの呼び出しをカプセル化し、インターフェイスを返します。これらはすべてかなり一般的なものですが、訪問者の応答はラップされた API 応答の Go タイプに固有のものであるため、テーブルごとに異なる List 関数を記述する必要があります。この状況を回避するにはどうすればよいでしょうか? James は、「res 変数のフィールド参照は、実行時に指定される可変型である必要があります。これを行う方法を提案してもらえますか?」と尋ねました。

ChatGPTの提案(彼はこれに従った)は、listAnything(listAnything(ctx, d, h, "kolide_device.listDevices", visit, "Devices")のような)への呼び出しに名前("Devices")を渡すことができるようにリフレクションを使用することでした。 make listAnything タイプに依存しない方法で応答構造のフィールド (ここでは Devices フィールドなど) にアクセスする機能。

    type DeviceListResponse struct {
      Devices    []Device   `json:"data"`
      Pagination Pagination `json:"pagination"`
    }

このため、listAnything は最終的にその名前にふさわしい、一般的な Steampipe List 関数になります。このソリューションはリフレクションをほとんど使用せず、API 層と Steampipe 層の両方で Go の強力な型チェックを維持します。

LLM 支援とは実際には何を意味しますか?

それは確かに、LLM が次のプロンプトに基づいて複雑な設計パターンを具体化するプラグインを作成したという意味ではありません: 「Kolide API 用の Steampipe プラグインが必要です。それを作成してください。私にとって、そして James にとって、それはもっと意味があります。」興味深い: 「Kolide API のプラグインを作成するプロセスについて話し合いましょう。これは、要件と戦略について大声で考えるためにゴム製のアヒルに話しかけるようなものです。」しかし、LLM はしゃべるゴム製のアヒルです。回答が直接当てはまる場合もあれば、当てはまらない場合もありますが、いずれにせよ、通常は明確にするのに役立ちます。

経験豊富なソフトウェア エンジニアとして、James はそれを理解できたかもしれませんが、それにはもっと時間がかかったでしょう。

「会話では、自分が尋ねる質問を非常に具体的にする必要がありました」と James 氏は言います。彼は Go をゼロから始めましたが、豊富な経験をもたらし、質問すべき適切な質問を素早く特定することができました。 。経験豊富なソフトウェア エンジニアとして、James はこれらすべてを自分で解決できたはずです。しかし、それには時間がかかり、実践的に学ぶのではなく、事前に記事や文書を読むことに多くの時間を費やすことになります。そして時間がないかもしれません!他の多くの人から聞いたように、LLM が提供する高速化は、アイデアを思いつくか、それを実行できるかに違いをもたらすことがよくあります。

James は、私が考慮していなかったオープンソースの観点についても言及しました。 LLM が登場する前は、彼はこれを完全に公の場で行うことはなかったでしょう。 「もっと自信が持てるまでは秘密にしていますが、最初から存在していて、そのおかげでターボットチームとの早期接触が可能になったことをうれしく思っています。」と彼は語った。

これは自動化の話ではなく、拡張の話です。 James Ramirez のような経験豊富な開発者が LLM を使用すると、そのマシン インテリジェンスが彼のインテリジェンスをサポートし、増幅させます。この 2 つは連携して作業します。コードを書くだけでなく、より重要なことに、アーキテクチャとデザインについても考えます。

この記事はYunyunzhongsheng ( https://yylives.cc/ ) で最初に公開されたもので、どなたでもご覧いただけます。

RustDesk、不正行為横行のため国内サービスを停止 Apple、M4チップを発売 タオバオ(taobao.com)、Webバージョンの最適化作業を再開 高校生が成人への贈り物として独自のオープンソースプログラミング言語を作成 - ネチズンの批判的なコメント:防衛 Yunfeng 氏は Alibaba を退職し、将来的には Windows プラットフォーム上で 独立したゲーム プログラマー向けの。 Visual Studio Code 1.89 は Java 17 をリリースします。これは、最も一般的に使用されている Java LTS バージョンです。Windows 10 の市場シェアは 70 です。 %、Windows 11 は減少し続ける。Google はオープンソースの Rabbit R1 を支持する。Haier Electric はオープン プラットフォームを閉鎖する。
{{名前}}
{{名前}}

おすすめ

転載: my.oschina.net/u/6919515/blog/11105744