AIを使った自動生成を徹底的に行うためクラス図からコード生成をやってみた。
PlantUML形式のクラス図
クラス図これ。
@startuml entity Prompt { contents string get_next() void } interface AIClient { ask(promt Prompt) string } class ChatGPTClient { ask(promt Prompt) string } interface CodeRepository { load(key string) void save(key string, contents string) void } class FileIORepository { load(key string) void save(key string, contents string) void } class CodeOperator { generate(order string, key string) string request(promt Prompt) string } FileIORepository --|> CodeRepository ChatGPTClient --|> AIClient Prompt --- AIClient : ask CodeOperator ..> AIClient CodeOperator ..> CodeRepository CodeOperator --- Prompt @enduml
Generator.py
ChatGPTにプロンプトを投げるpythonスクリプトがこれ
import os from openai import OpenAI client = OpenAI( # This is the default and can be omitted api_key=os.environ.get("OPENAI_API_KEY"), ) def call(content: str) -> str: print ("Call GPT-4 with: ```" + content + "```") completion = client.chat.completions.create( messages=[ { "role": "user", "content": content, } ], model="gpt-4", ) return completion.choices[0].message.content def load_file(file_name: str): with open(file_name, "r", encoding="utf-8") as f: return f.read() def save_file(file_name: str, content: str): with open(file_name, "w", encoding="utf-8") as f: f.write(content) class_diagram = load_file("my-class-diagrams.puml") msg = '''\ Generate code in Golang from class diagrams using PlantUML. - Separate the code for each class, entity, and interface. - Output the generated code in the following format. ``` filename:{class name lowercase} {code} ``` The class diagrams is below. ''' result = call(msg + class_diagram) save_file("generated.go", result) print ("Finished!")
出来たファイル
出来たファイルがこれ
filename:prompt.go
```golang
package main
type Prompt struct {
Contents string
}
func (p *Prompt) get_next() {
}
```
filename:aiclient.go
```golang
package main
type AIClient interface {
Ask(promt Prompt) string
}
```
filename:chatgptclient.go
```golang
package main
type ChatGPTClient struct {
}
func (c *ChatGPTClient) Ask(promt Prompt) string {
return ""
}
```
filename:coderepository.go
```golang
package main
type CodeRepository interface {
Load(key string)
Save(key string, contents string)
}
```
filename:fileiorepository.go
```golang
package main
type FileIORepository struct {
}
func (f *FileIORepository) Load(key string) {
}
func (f *FileIORepository) Save(key string, contents string) {
}
```
filename:codeoperator.go
```golang
package main
type CodeOperator struct {
}
func (co *CodeOperator) Generate(order string, key string) string {
return ""
}
func (co *CodeOperator) Request(promt Prompt) string {
return ""
}
```
separator.py
手動で分割するのが嫌だったので分割するスクリプトを書いた。これ。
import re import os import asyncio with open("generated.go", 'r') as file: lines = file.readlines() code_block = False for line in lines: # ファイル名の検出 if re.match(r'filename:(.+\.go)', line): file_name_match = re.match(r'filename:(.+\.go)', line) new_file_name = file_name_match.group(1).strip() code_block = True code = '' continue if line.strip() == '```golang': continue # コードブロックの終了を検出 if line.strip() == '```' and code_block: code_block = False with open(new_file_name, 'w') as new_file: new_file.write(code) continue # コードブロックの中のテキストを追加 if code_block: code += line
書いたというか大半ChatGPTとCopilotに書かせて自分は軽く手直ししただけなのだが。
結果
こんな感じのファイルが生成された。
package main type CodeOperator struct { } func (co *CodeOperator) Generate(order string, key string) string { return "" } func (co *CodeOperator) Request(promt Prompt) string { return "" }
所感
悪くないんだがこれだけだと所詮足場にすぎないし、あまりに不徹底。この程度で終わりでは話にならない。結局クラス図は処理の情報がないから処理部分が書けない。
しかし知見は得られた。