Skip to content

Commit d20adec

Browse files
committed
docs(gepa): replace custom proposer example with reference to ReActModuleProposer
Address PR comment #6 by simplifying the custom proposer documentation. Changes: - Replace long inline implementation example with clickable GitHub link - Point to ReActModuleProposer as reference implementation - Add bulleted list of what the reference shows (parsing, dynamic signatures, etc.) - Keep essential JSON structure and interface documentation - Remove 100+ lines of redundant code example Benefits: - Less overwhelming for users (no duplicate code) - Single source of truth (reference implementation) - Clickable link to actual working code on GitHub - Users can copy/modify real implementation instead of example Addresses PR comment from @LakshyAAAgrawal about using reference instead of full implementation example.
1 parent 781cca5 commit d20adec

File tree

1 file changed

+30
-114
lines changed

1 file changed

+30
-114
lines changed

docs/docs/api/optimizers/GEPA/GEPA_Advanced.md

Lines changed: 30 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -754,119 +754,35 @@ for tool_name, tool in optimized_agent.tools.items():
754754

755755
#### Implementing a Custom Proposer for ReAct
756756

757-
If you need custom logic, you must handle ReAct components yourself. ReAct components are stored as JSON strings containing all 4 parts:
758-
759-
```python
760-
import json
761-
762-
# Define signature for improving ReAct components
763-
class ImproveReActInstruction(dspy.Signature):
764-
"""Analyze agent execution failures and improve the instruction.
765-
766-
Focus on common ReAct failure patterns:
767-
- Tool selection errors (wrong tool chosen)
768-
- Missing tool calls (agent gave up without trying)
769-
- Incorrect tool arguments
770-
- Extraction failures (couldn't extract answer from trajectory)
771-
"""
772-
current_instruction = dspy.InputField(desc="The current instruction being optimized")
773-
component_type = dspy.InputField(desc="Type: 'react' (reasoning), 'extract' (extraction), or 'tool' (tool description)")
774-
examples_with_feedback = dspy.InputField(desc="Examples showing what went wrong: inputs, outputs, and feedback")
775-
improved_instruction = dspy.OutputField(desc="Improved instruction addressing the observed failures")
776-
777-
778-
class CustomProposer:
779-
def __call__(self, candidate, reflective_dataset, components_to_update):
780-
"""
781-
When you provide a custom proposer, it receives ALL components (regular + ReAct).
782-
783-
Args:
784-
candidate: dict[str, str] - All component instructions to update
785-
- Regular: "predict" -> "Your instruction..."
786-
- ReAct: "react_module" -> JSON string: {"react": "...", "extract": "...", "tools": {...}}
787-
reflective_dataset: dict[str, list[ReflectiveExample]]
788-
- Component name -> list of examples with Inputs, Generated_Outputs, Feedback
789-
components_to_update: list[str] - All components to update this round
790-
791-
Returns:
792-
dict[str, str] - Updated instructions for all components
793-
"""
794-
propose_instruction = dspy.Predict(ImproveReActInstruction)
795-
results = {}
796-
797-
for component in components_to_update:
798-
if not component.startswith("react_module"):
799-
continue # Skip non-ReAct components (handle them separately if needed)
800-
801-
# Parse the JSON config
802-
config = json.loads(candidate[component])
803-
# config contains: {"react": "...", "extract": "...", "tools": {...}}
804-
805-
component_reflective_data = reflective_dataset[component]
806-
807-
# Format examples (limit to first 3 for efficiency)
808-
formatted_examples = self._format_examples(component_reflective_data[:3])
809-
810-
# Improve react instruction (reasoning and tool selection)
811-
improved_react = propose_instruction(
812-
current_instruction=config["react"],
813-
component_type="react",
814-
examples_with_feedback=formatted_examples
815-
).improved_instruction
816-
817-
# Improve extract instruction (answer extraction from trajectory)
818-
improved_extract = config.get("extract", "")
819-
if improved_extract:
820-
improved_extract = propose_instruction(
821-
current_instruction=improved_extract,
822-
component_type="extract",
823-
examples_with_feedback=formatted_examples
824-
).improved_instruction
825-
826-
# Improve tool descriptions (what each tool does and when to use it)
827-
improved_tools = {}
828-
for tool_name, tool_info in config.get("tools", {}).items():
829-
improved_desc = propose_instruction(
830-
current_instruction=tool_info["desc"],
831-
component_type="tool",
832-
examples_with_feedback=formatted_examples
833-
).improved_instruction
834-
835-
improved_tools[tool_name] = {
836-
"desc": improved_desc,
837-
"args": tool_info["args"], # Keep args schema unchanged
838-
"arg_desc": tool_info.get("arg_desc", {}) # Can also improve these
839-
}
840-
841-
# Return as JSON string
842-
results[component] = json.dumps({
843-
"react": improved_react,
844-
"extract": improved_extract,
845-
"tools": improved_tools
846-
})
847-
848-
return results
849-
850-
def _format_examples(self, reflective_data: list) -> str:
851-
"""Format reflective examples into markdown for the LM."""
852-
formatted_parts = []
853-
for i, example in enumerate(reflective_data):
854-
s = f"# Example {i + 1}\n"
855-
for key, val in example.items():
856-
s += f"## {key}\n{str(val).strip()}\n\n"
857-
formatted_parts.append(s)
858-
return "\n\n".join(formatted_parts)
859-
860-
gepa = dspy.GEPA(
861-
metric=my_metric,
862-
reflection_lm=dspy.LM(model="gpt-5", temperature=1.0, max_tokens=32000),
863-
instruction_proposer=CustomProposer(), # Receives ALL components (regular + ReAct)
864-
optimize_react_components=True, # Must be True to discover ReAct modules
865-
auto="medium"
866-
)
757+
If you need custom logic, you can start with the existing implementation at [`ReActModuleProposer`](https://github.com/stanfordnlp/dspy/blob/main/dspy/teleprompt/gepa/instruction_proposal.py). This reference implementation shows how to:
758+
759+
- Parse ReAct JSON configurations with `json.loads()`
760+
- Build dynamic signatures for tools and parameters
761+
- Call the reflection LM to optimize all components jointly
762+
- Handle optional improvements (reflection LM returns `None` to keep originals)
763+
- Serialize improved components back to JSON with `json.dumps()`
764+
765+
**Key concepts for custom proposers:**
766+
767+
ReAct components are JSON strings containing 4 parts:
768+
```json
769+
{
770+
"react": "instruction for reasoning and tool selection",
771+
"extract": "instruction for answer extraction",
772+
"tools": {
773+
"tool_name": {
774+
"desc": "what the tool does",
775+
"args": {"param": {"type": "string"}},
776+
"arg_desc": {"param": "description of param"}
777+
}
778+
}
779+
}
867780
```
868781

869-
**Key points:**
870-
- ReAct components are JSON strings - use `json.loads()` to parse, `json.dumps()` to return
871-
- 4 parts to improve: `react` instruction, `extract` instruction, tool `desc`, tool `arg_desc`
872-
- Tools structure: `{"tool_name": {"desc": "...", "args": {...}, "arg_desc": {...}}}`
782+
Your proposer receives:
783+
- `candidate: dict[str, str]` - Component names to instructions (ReAct values are JSON strings)
784+
- `reflective_dataset: dict[str, list[ReflectiveExample]]` - Execution traces with feedback
785+
- `components_to_update: list[str]` - Which components to optimize this round
786+
787+
Your proposer returns:
788+
- `dict[str, str]` - Same keys with improved instructions (ReAct as JSON strings)

0 commit comments

Comments
 (0)