프롬프트 엔지니어링
목표
기존에 간단한 파일 관리 서버를 만들었던 것이 있었는데, 대충 만들다 보니 인증을 그냥 웹서버에서 simple auth로 구현하고 그 안에서는 뭐든지 허용하는 식으로 만들었다. 이번에 이걸 고쳐볼 생각이다.
Setting up S3 compatible service with min.io
I’m looking for a blob storage service which allows authenticated users to download/upload blob files. Is there any existing services offering the functionality?
이렇게 물어보니 S3을 시작으로 이것저것 추천해준다.
I’m interested in self-serviced project which is open source. Any alternatives?
이렇게 하면 아 오픈소스 S3 대체재를 찾는구나, min.io는 어때? 라고 추천해준다.
Generate minio.nix, which configures minio server using NixOS
이렇게 하면 대충 다음과 같이 생성해준다.
{ config, lib, pkgs, ... }:
{
services.minio = {
enable = true;
region = "us-west-1";
# Configure the data directory where MinIO will store objects
dataDir = ["/var/lib/minio/data"];
# Configure the root credentials
# Note: For production, consider using a more secure way to manage credentials
rootCredentialsFile = "/var/lib/minio/credentials";
};
# Create the credentials file with proper permissions
system.activationScripts.minio-credentials = ''
if [ ! -f /var/lib/minio/credentials ]; then
mkdir -p /var/lib/minio
echo 'MINIO_ROOT_USER=minioadmin' > /var/lib/minio/credentials
echo 'MINIO_ROOT_PASSWORD=minioadmin' >> /var/lib/minio/credentials
chmod 600 /var/lib/minio/credentials
chown minio:minio /var/lib/minio/credentials
fi
'';
# Open the MinIO API and Console ports
networking.firewall.allowedTCPPorts = [ 9000 9001 ];
# Add MinIO to your configuration.nix imports
# (This is just a reminder, you'll need to add it manually)
}
호오… 그럴듯한데? 다만 http 프로토콜로 돌아가는 9000, 9001 포트를 공개적으로 오픈하는 것은 좀 꺼려지니 https 웹 서버를 통해 접근하도록 할 생각으로 127.0.0.1에서만 접속을 받도록 변경했다. caddy.nix파일에서 minio 서비스로 연결하는 reverse proxy를 추가해 보자.
I need to create a reverse-proxy for minio server I created under minio.nix. Create minio.blmarket.net for API endpoint, and minio-console.blmarket.net for console
"minio.blmarket.net" = {
extraConfig = ''
reverse_proxy localhost:9000
'';
};
"minio-console.blmarket.net" = {
extraConfig = ''
reverse_proxy localhost:9001
'';
};
이제 이정도는 놀랍지도 않다.
Create a sample code playing with minio
I created minio.blmarket.net which provides a S3 compatible API. I’d like to see an example code which lists all the files in a bucket, written in Rust language.
뭔가 new와 list_objects를 제공하는 client wrapper를 만들긴 했는데, 이럴거면 뭐하러 wrapper를 만드나 싶긴 하다.
Given a S3 compatible API endpoint minio.blmarket.net, I’d like to see an example code which lists all the files in a bucket, written in Rust language. Please define a trait providing the required functionality first.
원래 블로그 글 쓰기 시작할땐 이정도는 쉽게ㅎ 만들 수 있을 줄 알았는데, 막상
만들어놓은 걸 돌려보니 InternalError
가 마구 발생한다. 조금 알아보니 기본 s3
client는 test-bucket.minio.blmarket.net 이라는 주소가 존재하는 것으로 가정하고
요청을 날리는데 내가 reverse proxy를 그렇게 설정하지 않았기 때문에 접속이 아예
실패하는 것으로 보인다. (실은 RUST_LOG=debug를 설정해서 확인했다) s3 client의
설정에서 force_path_style를 true로 설정해주니 잘 돌아간다.
Reverse engineering from the sample code
내가 원하는 것은 LLM에게 적당히 입력할 프롬프트를 만들어내는 것이기 때문에, 이 예제 코드를 가지고 LLM에게 입력할 프롬프트를 만들어달라고 한 다음에, 거기에 내가 원하는 기능을 적절히 추가해서 LLM에게 다시 코드를 작성해달라고 할 생각이다.
Generate formal specification from reverse engineering mod.rs
Add that information to the README.md
이렇게 하면 아주 길다란 spec을 생성해서 README.md에 넣어준다.
이제 이걸 적당히 간략하게 만들어보자.
Refined module specification
불필요하게 추가된 것으로 보이는 부분들을 제거해봤다.
## Common code specification
* Every public function should be defined as a trait first and struct to implement the trait. There should be no
exposed function without a trait.
* Each trait has well written docstring for trait and its member functions.
* At the end of the module, there will be tests submodule testing its basic behavior.
## Blobs
The `blobs` module provides an interface to provide APIs to generate pre-signed URLs to upload/download blobs.
### `BlobStorage` Trait
This trait provides the following functionality:
1. `create_get_object`: create a pre-signed url which allows downloading the file. The URL should expire in 86400 seconds.
2. `create_put_object`: create a pre-signed url to allow url holder to upload a file. The URL should expire in 86400 seconds.
### `S3BlobStorage` implementation
Implementing `BlobStorage` using `aws_sdk_s3` crate. Implement all trait members, and especially use `force_path_style(true)` for its configuration.
### Dependencies
- `anyhow` - Error handling
- `aws-sdk-s3` - AWS S3 SDK
- `aws-credential-types` - AWS authentication
Forward engineering 1
이제 기존의 챗 내용을 삭제하고,
Generate mod.rs from the specification in README.md
이렇게 하면서 정리된 spec을 고쳐나가보자. 중간 생성 결과를 매번 붙여넣기 하면 블로그 글이 너무 길어지니, 간단하게 요약하자면 다음과 같다:
- 첫 생성물에선
BlobStorage
의 함수들이 bucket을 매개변수로 받았다. 나는BlobStorage
가 S3 이외의 구현체를 만들 수 있도록 하고 싶었기 때문에, spec을 수정해서 bucket은 S3BlobStorage에만 적용하는 것으로 변경했다. - 두번째 생성물을 보니 trait은 sync trait으로 가정했는데, 구현체는 async API를 사용하니 컴파일이 안되는 문제가 생겼다. 모든 구현이 network call을 통해 외부 API를 호출하는 것으로 가정해서, Trait의 모든 멤버는 async 함수이고 future를 반환한다는 것으로 spec을 수정했다.
- 짜잘한 실수(이를테면 스펙에 behavior version을 2024-03-28으로 설정했는데, 생성 코드에선 latest_version으로 생성됨)가 있긴 했지만, 어쨌든 적당한 스펙에 맞는 적당한 코드가 생성되었다. 다만 100% as-is로 쓸 순 없었고, 약간 수정을 하긴 해야 했다.
결론
AI가 생성한 코드를 디버깅하는 속터지는 일이 아주 피곤하지만, 이제 코드를 직접 작성하는 것보다 AI가 생성하는 코드를 감독하면서 스펙을 통해 코드를 작성하는 것도 꽤나 경쟁력 있는 방법이 아닐까 싶다.