0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Go未経験だけど何か作りたい

Posted at

レガシー案件を抜け出すためにGoで何か作ってみる。

というわけで、仕事ではJavaでレガシーシステムをぽちぽち保守してたりしますが、
さすがに今の時代AI駆動もできない案件ばかりだとつまらなくなってきたので、
モダン案件につきたいという願いをこめた個人開発の作業メモ。

やりたいこと

Go + LLM + GitHub ActionsでREADMEを自動更新するBot作成

  1. プルリクエスト作成
  2. GitHub ActionsからBOTがキックされる
  3. プルリクのdiffを読んでLLMで分析、READMEとCHANGELOGに書くべき内容を返してもらう
  4. READMEとCHANGELOG更新
  5. GitHub Actionsでpush

これを自動化する。

この記事でやること

  • Go で LLM クライアントを叩く最低限の実装例
  • Markdown(RULES.md)から特定セクションだけを抜き出す方法
  • カスタムルールを環境変数で差し替えつつ、デフォルトは go:embed で同梱する構成

環境

項目 内容
OS Windows11 pro
エディタ Visual Studio Code
言語 Go 1.24.0
ライブラリ go-github v79.0.0
go-openai v1.41.2
oauth2

VSCodeで書くときにCopilotは切ってます。

やったこと

RULES.mdの読み込み

実際に実装に入ったときに「なんもなしだったら別に既存のエージェントでいいな、、」となったので、
カスタムルールの読み込みを導入。
とりあえずmdファイル読んで、ファイルの内容をLLMに投げるところまで実装
RULES.mdはバイナリに同梱してembedで読み込めるようにしつつ、
環境変数 + ファイル配置でカスタムルールファイルを格納できるようにしてみた。

// rule_reader.go
package rules

import (
    _ "embed"
    "errors"
    "fmt"
    "os"
    "path/filepath"
    "strings"
)
//go:embed rules/RULES.md
var RulesMd string

func LoadRules(section string) (string, error) {
    var sectionStr string
    if path := os.Getenv("RULES_PATH"); path != "" {
        abs, err := filepath.Abs(path)
        if err != nil {
            sectionStr = readSection(RulesMd, section)
            return sectionStr, fmt.Errorf("your RULES_PATH is wrong %s, fallback to default rules", path)
        }
        
        file, err := os.ReadFile(abs)
        if err != nil {
            sectionStr = readSection(RulesMd, section)
            return sectionStr, errors.New("failed to load RULES_PATH file, fallback to default rules")
        }
    
        sectionStr = readSection(string(file), section)
        return sectionStr, nil
    }

    sectionStr = readSection(RulesMd, section)
    return sectionStr, nil
}

func readSection(rules string, section string) string {
    start := strings.Index(strings.ToLower(rules), fmt.Sprintf("## %s", section))
    if start == -1 {
        return "" // not found
    }
    // スタート位置から先だけ抽出
    rest := rules[start:] // restを行split
    lines := strings.Split(rest, "\n")

    var out []string
    for _, l := range lines[1:] { // 1行目は "## readme"
        if strings.HasPrefix(l, "## ") {
            break
        }
        out = append(out, l)
    }
    
    result := strings.Join(out, "\n")
    return result
}

何も考えずにカスタムルールをmdにしてしまったので、
今回はとりあえず「Markdown からセクションをインデックスで切り出す」という雑実装。
将来的にはルールのファイルをjsonとかyamlにしてstructで受けたいきもち。
ちなみに今のRULES.mdはこんな感じ

RULES.md の例

# README Bot Rules

## readme
- このセクションは README の更新方針
- 変更内容の概要を書いてもらう

## report

"## report"以降は将来的に追加したい機能のやつ

LLM呼び出しの最小実装

以下はopenai-goでOpenAI APIを叩く最小実装
外出ししたプロンプト に diff と rule を埋め込んで LLM に JSON を返してもらう

func CallLLM(diff, rule string) (response.Response, error) {
	apiKey := os.Getenv("OPENAI_API_KEY")
	if apiKey == "" {
		return response.Response{}, errors.New("missing OPENAI_API_KEY environment variable")
	}
	client := openai.NewClient(apiKey)
	resp, err := client.CreateChatCompletion(
		context.Background(),
		openai.ChatCompletionRequest{
			Model: openai.GPT4oMini,
			Messages: []openai.ChatCompletionMessage{
				{
					Role:    openai.ChatMessageRoleUser,
					Content: fmt.Sprintf(Template, diff, rule),
				},
			},
		},
	)

	if err != nil {
		return response.Response{}, fmt.Errorf("api call is failed %w", err)
	}

	var resJson response.Response
	if err := json.Unmarshal([]byte(resp.Choices[0].Message.Content), &resJson); err != nil {
		return response.Response{}, fmt.Errorf("failed to parse LLM JSON: %w", err)
	}
	return resJson, nil
}

たぶんChoicesがあるかチェック入れたりするべきなんだろうけどとりあえず無視
あと将来的には環境変数なんかで使用するモデルの切り替えとかも実装したい。

まだ修正は入るだろうけどとりあえずはこんな感じ。
rule_readerで読み込んだルールをプロンプトのテンプレートに埋め込んでLLMに投げる。

次にやること

次はGitHubでプルリク差分を取得してファイルに書き込むところまで実装して記事にまとめる。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?