langsmith hub로 살펴보는 prompt

미래의 코딩은 인간이 직접 작성하는 code 비중 보다 prompt의 비중이 더 커질 것은 당연한 사실이다. 또한 실제 코드 작성은 인간보다 AI가 작성하는 양이 더 많아 질 것이다. 현재 시점에서 나는 prompt가 곧 code라고 생각한다. 물론 가까운 시점에 이런 생각은 맞지 않겠지만 말이다. 예를들어 code로 결과를 OK나 DENY라는 문자열 중 하나만을 얻어야 할때 어떻게 코드를 작성할 것인가.. 라는 물음에 대한 코드를 대신하는 프롬프트는 ‘너는 결과를 반드시 “OK” 또는 “DENY” 둘 중에 하나로만 답해야 한다.’ 정도가 된다. 결국 LLM을 통해 좀더 정확한 결과를 얻기 위해서 프롬프트의 작성 방법을 이해하는 것은 개발자의 핵심 역량이다.

이처럼 중요한 프롬프트에 대한 좋은 예시를 통해 프롬프트에 대한 작성 방법을 학습할 수 있다. 좋은 프롬프트 예시는 어디서 구할 수 있을까? langchain 프레임워크의 소스코드를 살펴보면 코드 곳곳에 사용된 프롬프트가 존재하며 이들 역시 좋은 예시다. 추가로 langchain의 개발사는 langsmith hub를 통해 prompt를 등록할 수 있도록 해 두었는데, 여기에 등록된 프롬프트 중 사용 횟수가 많은 놈들을 분석해 보는 것을 강력히 추천한다. 그 중 3개 정도만 분석해 보자.

먼저 2천7백만번 사용된 rlm/rag-prompt이다. RAG를 통해 context를 제공하는 형태이다.

human

You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise.
Question: {question} 
Context: {context} 
Answer:

다음은 8백만번 사용된 프롬프트인 hwchase17/react 이다.

human

Answer the following questions as best you can. You have access to the following tools:

{tools}

Use the following format:

Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question

Begin!

Question: {input}
Thought:{agent_scratchpad}

마지막으로 5만번 정도 사용된 hardkothari/prompt-marker 의 프롬프트는 다음과 같다. 프롬프트를 좀더 개선하기 위한 용도이다.

system

You are an expert Prompt Writer for Large Language Models.

human

Your goal is to improve the prompt given below for {task} :
--------------------

Prompt: {lazy_prompt}
--------------------

Here are several tips on writing great prompts:
-------

Start the prompt by stating that it is an expert in the subject.
Put instructions at the beginning of the prompt and use ### or to separate the instruction and context 
Be specific, descriptive and as detailed as possible about the desired context, outcome, length, format, style, etc 
---------

Here's an example of a great prompt:
As a master YouTube content creator, develop an engaging script that revolves around the theme of "Exploring Ancient Ruins."
Your script should encompass exciting discoveries, historical insights, and a sense of adventure.
Include a mix of on-screen narration, engaging visuals, and possibly interactions with co-hosts or experts.
The script should ideally result in a video of around 10-15 minutes, providing viewers with a captivating journey through the secrets of the past.

Example:
"Welcome back, fellow history enthusiasts, to our channel! Today, we embark on a thrilling expedition..."
-----

Now, improve the prompt.

IMPROVED PROMPT:

언급된 프롬프트를 살펴보니, 이런 다소 어려운 프롬프트들을 잘도 이해하는 LLM이 대단스럽단 생각이 든다. 개인적으로 나는 프롬프트를 위처럼 작성하지 않고 마크다운 형식으로 작성한다.

TSL에서 2D 회전

어떤 2차원 좌표(st)를 정해진 중심점(mid)을 기준으로 원하는 각도(rotation)만큼 회전된 결과를 얻는 TSL 함수 정의는 다음과 같다.

const _rotate = Fn(([st, rotation, mid]) => {
  return vec2(
    cos(rotation).mul(st.x.sub(mid.x)).add(sin(rotation).mul(st.y.sub(mid.y))).add(mid.x),
    cos(rotation).mul(st.y.sub(mid.y)).sub(sin(rotation).mul(st.x.sub(mid.x))).add(mid.y),
  );
}, { st: 'vec2', rotation: 'float', mid: 'vec2', return: 'vec2'})

TSL에서 위의 함수처럼 2D 회전에 대한 내장 함수를 제공하는데 바로 rotateUV 노드 함수이다. rotateUV 노드 함수는 3차원 좌표를 회전하는 TSL 내장 함수인 rotate를 사용한 것에 지나지 않는다.

즉, 아래의 결과는 모두 같다.

material.fragmentNode = Fn(([]) => {
  const { x, y } = uv().toVar();

  const rotatedUv = _rotate(vec2(x, y), Math.PI * 0.25, vec2(0.5));
  // const rotatedUv = rotateUV(vec2(x, y), Math.PI * 0.25, vec2(0.5)); // 위와 같고
  // const rotatedUv = rotate(vec2(x, y).sub(vec2(0.5)), Math.PI * 0.25).add(vec2(0.5)) // 또 위와 같고..

  const l1 = float(0.03).div(length(rotatedUv.sub(vec2(.5, .5)).mul(vec2(.2, 1))));
  const l2 = float(0.03).div(length(rotatedUv.sub(vec2(.5, .5)).mul(vec2(1, .2))));

  return vec4(vec3(mul(l1, l2)), 2);
})();

아래 이미지는 위의 코드를 통해 45도 회전된 결과이다.

HDR 이미지로 캡쳐가 안되서 너무 구리게 보이는데….

Y축으로 회전 (TSL 관점)

Y축으로 회전하는 TSL 코드를 2가지 관점에서 정리하면, 먼저 Y축으로 회전하는 행렬을 직접 구성해 회전하고자 하는 정점에 적용하는 방식이다.

export const rotateY = /*@__PURE__*/ Fn( ( [ theta ] ) => {
	const c = cos( theta );
	const s = sin( theta );
    return mat3(
        c, 0, s,
        0, 1, 0,
        s.negate(), 0, c
    );

	// same as : return mat3( vec3( c, 0, s ), vec3( 0, 1, 0 ), vec3( s.negate(), 0, c ) );
}, { theta: 'float', return: 'mat3' } );

...

const p = vec3(x, y, z);
const rMat = rotateY(angle);
const rotatedP = rMat.mul(p).add(offset); // 회전하고 offsect 만큼 이동

두번째는 TSL에서 제공하는 범용적인 회전 노드 함수를 이용하는 것이다.

const rotatedP = rotate(p, vec3(0, angle, 0)).add(offset); // 회전하고 offsect 만큼 이동

uv로 파이썬 프로젝트 생성

먼저 터미널로 프로젝트를 구성할 폴더로 이동하고 ..

uv init

위의 명령이 실행되면 해당 폴더에 프로젝트 구성 파일이 생성된다. 그중 main.py 파일이 보이는데 이 파일을 다음처럼 실행하면 필요한 가상환경이 생성되면서 해당 py 파일이 실행된다.

uv run main.py

참고로 uv는 다음처럼 설치할 수 있다.

pip install uv

uv 통한 패키지 설치는 다음과 같다. (예: requests)

uv add requests

위의 방식을 권장하지만 다음으로도 가능하다.

uv pip install requests