내장 모듈 변형들

내장 모듈 변형들 (Built-in module variants)

Predict, ChainOfThought, ReAct 는 대부분의 프로그램을 커버하지만, DSPy는 한 번의 LM 호출로는 부족하거나, 추론이 Python 런타임을 필요로 하거나, 예시들에 걸쳐 팬아웃(fan out)하고 싶거나, 모듈의 구조 자체를 손으로 쓰기보다 배우는 게 가치있는 상황을 위해 몇 가지 다른 모듈도 제공합니다. 이 페이지는 그 모듈들을 모아 무엇을 위한 것인지로 묶고, 언제 어떤 것을 집어 들어야 하는지 알 수 있는 선택 가이드를 제시해요.

출처: 문서

본문

평범한 Predict 나 ChainOfThought 로는 목적지에 닿지 못하고, 더 샘플링할지, 초안들을 비교할지, 코드를 실행할지, 같은 모듈을 병렬로 돌릴지, 아니면 최적화기가 구조를 발견하게 할지 사이에서 결정을 내리고 있을 때 이 문서를 읽으세요.

설계 결정

1. 이 변형들은 중심을 감싼다(replace 아님)

BestOfN 과 Refine 은 모듈을 받아 샘플링합니다. MultiChainComparison 은 바깥 Predict 가 만든 완성을 소비합니다. ProgramOfThought 와 CodeAct 는 내부 ChainOfThought predictor들을 담습니다. 변형들은 중심 위에 지어진 레시피이지, 중심의 병렬 구현이 아니에요. 하나를 집어 들기 전에, "나는 Predict가 이미 하는 것의 더 많은 것이 필요한가" 아니면 "나는 Predict가 전혀 할 수 없는 무언가가 필요한가"라고 스스로 물어보세요. 답은 보통 전자이고, 그 경우가 바로 이것들이 처리하는 경우입니다.

2. 샘플링·집계 모듈은 단 한 가지, 롤아웃 ID를 바꾼다

BestOfN 과 Refine 은 시도마다 감싼 모듈을 deep-copy하고, rollout_id = start + i 이고 temperature=1.0 인 LM 사본을 교체해 넣습니다. 롤아웃 ID가 바로 같은 입력에도 각 샘플이 다른 출력을 내게 만드는 것입니다. DSPy가 그것을 LM 캐시 키에 실어, 모델이 캐시된 응답을 재생하는 대신 다시 샘플링하게 합니다. 샘플링이 핵심이므로 LM의 기본값과 무관하게 temperature는 1.0으로 강제됩니다.

3. 보상 함수는 추론 시점에 채점되는 메트릭이다

reward_fn(args, pred) -> float 시그니처는 추론 시점 메트릭의 모양을 미러링하지만, gold 예시가 필요 없습니다 — 호출의 입력에 대해 예측을 채점해요. 같은 함수 모양을 훈련 메트릭으로 재사용할 수 있지만, 보상(reward)은 지금 채점되어 어떤 샘플을 유지할지를 결정하고, 메트릭(metric)은 나중에 채점되어 어떤 프로그램을 유지할지를 결정합니다. 두 역할은 모양에서 겹치고 목적에서 갈라집니다.

4. MultiChainComparison 은 미리 생성된 완성을 입력으로 기대한다

forward(completions, **kwargs) 는 완성을 만들어 내지 않고 위치 인자로 받습니다. 호출자가 별도의 Predict(보통 n=M으로)를 통해 M개의 샘플을 만들고 그것을 넘겨줘요. 샘플링과 판정을 분리하면 두 관심사가 독립적으로 유지됩니다. 완성이 다른 LM, 캐시, 또는 이전 시그니처에서 왔을 수 있는데, 그중 어느 것도 MultiChainComparison 이 알 필요가 없습니다.

5. ProgramOfThought 와 CodeAct 는 Python 인터프리터를 내장한다

둘 다 Deno의 WASM 런타임을 통해 샌드박스에서 LM 생성 코드를 실행하는 PythonInterpreter 에 의존합니다. Deno 격리가 그 이유예요. LM의 코드가 기본적으로 파일시스템·네트워크 접근이 없는 프로세스에서 실행되므로, 신뢰하지 않는 출력을 실행하는 것이 경계를 갖습니다. 각 호출은 새 인터프리터를 얻고, 그 호출 안의 재시도·반복은 그 상태를 공유합니다.

6. CodeAct 는 ReAct에 코드 샌드박스를 더한 것이다

그 클래스는 말 그대로 둘 다에서 상속합니다: class CodeAct(ReAct, ProgramOfThought). 그 조합이 중요한 이유는 어떤 작업은 ReAct의 반복 루프(생각 → 행동 → 관찰)와 Python 작성의 표현력(루프, 리스트 컴프리헨션, 라이브러리 호출)을 모두 필요로 하기 때문이에요. 도구는 평범한 def 함수로 전달되고 inspect.getsource 를 통해 샌드박스에 들어가므로, LM은 그것들을 JSON 형태의 도구 호출이 아니라 일반 Python으로서 호출합니다.

7. Parallel 은 Module 이 아니라 실행자(runner)다

forward(exec_pairs) 메서드를 노출하지만 Module 서브클래스가 아니에요. predictor를 담지 않고, 직렬화되지 않으며, named_predictors 에 나타나지 않습니다. 작업은 "당신을 대신해 내가 하는 LM 호출"이 아니라 "당신이 조립한 LM 호출들의 배치를 병렬로 실행"하는 것입니다. Module 트리 밖에 두면 최적화기와 dump_state 가 그것을 무시하는데, 실행자에게는 그것이 원하는 바입니다.

8. dspy.Parallel 과 Module.batch 는 의도적으로 겹친다

둘 다 아래의 같은 ParallelExecutor 로 모듈을 병렬 실행합니다. 차이는 받아들이는 것이에요. Module.batch(examples) 는 하나의 모듈과 많은 예시를, dspy.Parallel()(exec_pairs) 는 많은 (module, example) 쌍을 받습니다. 흔한 경우 — 데이터셋에서 하나의 프로그램 평가 — 에는 Module.batch 를 쓰고, 예시마다 모듈이 다르거나 어느 쌍이 어디서 돌지 정밀하게 제어하고 싶으면 Parallel 을 쓰세요.

9. majority 는 LM 없는 집계자다

평범한 함수입니다 — LM 호출도, 시그니처도, dspy.Module 도 없어요. 완성을 집계해 가장 흔한 정규화된 값을 반환합니다(default_normalize 가 대소문자와 공백을 접습니다). 집계는 모델을 호출하는 단계처럼 보이면 안 되므로, API는 모듈 생성자가 아니라 함수 호출입니다. 작업에 이산적인 답이 있을 때 다중 샘플 Predict 나 BestOfN 의 pred.completions 와 짝지어 쓰세요.

10. RLM 이 실험적 표시를 받는 데는 이유가 있다

클래스는 @experimental 로 장식되어 있고 인터페이스는 여전히 변동 중입니다. 코드 샌드박스를 내장된 llm_query / llm_query_batched 도구와 조합하는데, 이 도구들은 생성된 코드가 실행 중간에 별도의 서브-LM을 호출하게 합니다. 정신적 모델은 LM이 구동하는 Python REPL이고, 그 안에 또 다른 LM이 호출 가능한 것으로 있는 것입니다. 유용하지만, 경계 조건 — 최대 호출 수, 샌드박스 수명, 오류 복구 — 은 여전히 다듬어지는 중입니다.

11. Flex 는 모듈의 코드를 최적화 탐색 공간에 넣는다

다른 모든 DSPy 모듈은 구조를 구성 시점에 고정합니다. Flex 는 그렇게 하지 않아요. 구현을 소스 코드(module_src)로 보관하고 그 코드를 최적화 가능한 파라미터로 표시해서, dspy.GEPA 가 지시문만 튜닝하는 대신 당신의 메트릭에 맞춰 전체 구현을 다시 쓰도록 합니다 — predictor가 몇 개인지, 어떤 기본 요소를 쓰는지, 무엇이 LM 대신 Python에서 도는지 같은 것들이요. 다른 모듈처럼 시그니처로부터 구성하고(한 번 호출하는 Predict 기준선으로 시작하고, 도구가 주어지면 RLM 으로 시작), 최적화가 분해를 발견하게 하세요. 이것도 실험적입니다. 자체 심층 탐구가 있어요: Flex: 최적화 가능한 모듈 코드.

API 살펴보기

하려는 일에 따라 묶었어요.

샘플링과 집계

한 번의 LM 호출이 변동(variance)이 너무 클 때를 위한 것입니다. 여러 개를 샘플링한 뒤 고르거나 결합하세요.

dspy.BestOfN(module, N, reward_fn, threshold, fail_count=None) forward(**kwargs) 에서 BestOfN 은 시도마다 감싼 모듈을 deep-copy하고 rollout_id = start + i 이고 temperature=1.0 인 새 LM을 교체해 넣으며, 시도별 트레이스가 격리되도록 dspy.context(trace=[]) 블록 안에서 호출을 실행합니다. reward_fn(kwargs, pred) 로 채점하고, 지금까지의 최고를 유지하며, 보상이 threshold 를 충족하면 단락(short-circuit) 합니다. 루프 후 승리한 시도의 트레이스가 부모 dspy.settings.trace 로 병합됩니다 — 그래서 트레이스를 검사하는 호출자는 N개 모두가 아니라 승리 경로만 봅니다. 실패는 fail_count(기본값 N)를 줄이고, 소진되면 다시 던집니다.

dspy.Refine(module, N, reward_fn, threshold, fail_count=None) BestOfN 과 같은 모양인데, 시도 사이에 피드백 생성이 있습니다. 실패한 시도 후 Refine 은 스냅샷 — 모듈의 소스 코드, predictor별 시그니처, 트레이스에서의 predictor별 I/O, 보상 함수의 소스 — 을 만들고 내부 dspy.Predict(OfferFeedback) 에 넣습니다. 그 호출은 모듈 이름을 키로 한 조언을 반환합니다. 다음 시도에서 Refine 은 활성 어댑터를 감싸서 각 서브-predictor의 시그니처가 조언의 조각을 담은 hint_ 입력 필드를 얻게 합니다. 감싸기는 dspy.context(adapter=...) 로 범위가 정해져, 감싼 어댑터는 그 시도 동안만 존재하고 힌트가 최종 트레이스로 누출되지 않습니다.

**dspy.majority(prediction_or_completions, normalize=default_normalize, field=None)`` 독립 함수입니다. Prediction(prediction.completions를 사용),Completions객체, 또는 평범한 리스트를 받아요.field는 기본적으로 시그니처의 *마지막* 출력 필드입니다 — **마지막 필드가 답**이라는 관례 때문이에요. 값은normalize(기본적으로 소문자+공백)를 통과하고 가장 흔한 정규화 값을 이깁니다. 동률이면 더 이른 완성이 이깁니다. 승자를 감싼 단일 완성 Prediction` 을 반환합니다.

미리 생성된 초안 비교

dspy.MultiChainComparison(signature, M=3, temperature=0.7, **config) 생성자는 입력 시그니처를 바꿉니다. 하나의 출력 필드(합성된 rationale)를 앞에 추가(prepend)하고, reasoning_attempt_1 부터 reasoning_attempt_M 까지의 M개 입력 필드를 뒤에 추가(append)합니다. 그다음 수정된 시그니처 위에 내부 Predict 를 만듭니다. 호출 시점에 forward(completions, **kwargs) 는 각 완성의 rationale (또는 reasoning)와 마지막 출력 필드를 읽어 한 줄씩 시도 문자열로 포맷하고 새 입력으로 공급합니다. LM은 시도들을 전체적으로 추론하여 원래 출력 필드에 하나의 합성 답을 만들도록 요청받습니다. 공급된 완성의 수는 M 과 같아야 하며, assertion이 이를 강제합니다.

코드 생성과 실행

답을 서술하는(narrated) 것보다 계산하는 것이 더 나은 작업을 위한 것입니다.

각 모듈은 호출당 한 번 호출되는 interpreter_factory 를 받습니다. DSPy는 호출이 예외를 던져도 반환된 인터프리터를 종료시킵니다. PythonInterpreter 가 공개 기본값이고, dspy.configure(interpreter_factory=...) 가 각 호출에서 그것을 교체합니다. 모듈 자신의 팩토리가 이기는데, 단 PythonInterpreter 일 때는 제외합니다. program(interpreter, **inputs) 처럼 모듈을 호출할 때 첫 번째 위치 인자로 인터프리터를 넘기면, 종료하지 않고 호출자 소유 인스턴스를 사용합니다. 호출자 소유 재사용은 순차적이며, 동시 호출에는 팩토리 경로를 사용하세요. PythonInterpreter 오버라이드도 처음 사용된 스레드에 머물러야 합니다.

dspy.ProgramOfThought(signature, max_iters=3, interpreter_factory=PythonInterpreter) 세 개의 내부 ChainOfThought predictor를 담습니다. code_generate 는 Python을 만들고, code_regenerate 는 회복 가능한 실행 오류 후 그것을 다시 쓰며, generate_output 은 실행 결과에서 선언된 출력 필드를 추출합니다. forward 루프는 code_generate 에게 코드를 요청하고, PythonInterpreter 로 실행하며, CodeExecutionError 나 SyntaxError 를 최대 max_iters 라운드 동안 code_regenerate 에 되먹입니다. 말단(terminal) CodeInterpreterError 는 즉시 전파됩니다. 실행이 성공하면 generate_output 이 시그니처의 출력 필드를 만듭니다. max_iters 가 소진되면 모듈은 예외를 던집니다.

dspy.CodeAct(signature, tools, max_iters=5, interpreter_factory=PythonInterpreter) ReAct 와 ProgramOfThought 에서의 다중 상속입니다. 도구는 호출 가능한 객체가 아니라 평범한 def 함수여야 합니다. 모듈은 inspect.getsource(tool.func) 를 읽고 매 forward 시작에 각 정의를 샌드박스에 주입합니다. 각 반복: 내부 codeact predictor가 Python과 finished 불리언을 만들고, 인터프리터가 코드를 실행하며, trajectory dict에 generated_code_i 와 code_output_i(파싱이나 회복 가능한 실행 오류 시 observation_i)를 추가합니다. 말단 인터프리터 실패는 전파됩니다. 루프는 LM이 finished=True 를 설정하거나 max_iters 에 도달하면 종료됩니다. 그다음 ChainOfThought 추출기가 trajectory를 읽고 선언된 출력을 만듭니다.

dspy.RLM(signature, max_iters=20, max_llm_calls=50, max_output_chars=10_000, verbose=False, tools=None, sub_lm=None, interpreter_factory=PythonInterpreter) 실험적입니다. 두 내장 도구 — llm_query 와 llm_query_batched — 를 노출하는 REPL 스타일 코드 에이전트로, 생성된 코드가 실행 중간에 별도의 sub_lm 을 호출하게 합니다. 반복들에 걸친 공유 카운터가 max_llm_calls 를 강제하고, 도구 이름은 Python 식별자로 검증되며, SandboxSerializable 입력은 샌드박스에 인코딩되어 큰 컨텍스트를 매 턴 다시 마샬할 필요가 없게 합니다. 루프가 명시적 제출 없이 끝나면, 추출기 패스가 trajectory로부터 최종 출력을 만듭니다.

모듈 병렬 실행

dspy.Parallel(num_threads=None, max_errors=None, access_examples=True, return_failed_examples=False, provide_traceback=None, disable_progress_bar=False, timeout=120, straggler_limit=3) ParallelExecutor 를 감싸고 각 (module, example) 쌍을 스레드 풀에 제출합니다. 예시는 dspy.Example(access_examples=True 일 때 .inputs() 로 언팩), dict(kwargs로 언팩), 튜플(위치적으로 언팩), 또는 리스트(모듈 자체가 Parallel 일 때 그대로 전달)일 수 있어요. 실행자는 부모의 thread_local_overrides 스냅샷을 찍어 각 워커 안에서 다시 적용하므로, 둘러싼 dspy.context(...) 가 존중됩니다. 입력 순서대로 예측을 반환하고, return_failed_examples=True 이면 (results, failed_examples, exceptions) 튜플을 반환합니다.

다른 곳에서 다루는 관련 모듈

dspy.KNN 은 검색 도우미이지 생성 모듈이 아니에요. Retrievers 참조 페이지를 보세요.

dspy.ReAct 는 정식 도구 사용 루프이고 자체 페이지가 있습니다: ReAct와 ReActV2. 공유되는 도구 감싸기 기계장치는 CodeAct 와 RLM 도 씁니다.

크로스링크

더 알아보기 (Learn more)