Skip to content

Commit 32ca214

Browse files
author
AgentPatterns
committed
feat(examples/python): add self-critique-agent runnable example
1 parent 47b991c commit 32ca214

File tree

2 files changed

+61
-25
lines changed

2 files changed

+61
-25
lines changed

examples/agent-patterns/self-critique-agent/python/llm.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,24 @@ class LLMInvalid(Exception):
9898
- Do not output markdown or extra keys.
9999
""".strip()
100100

101+
REVISE_SYSTEM_PROMPT_STRICT = """
102+
You are an editor applying a constrained rewrite.
103+
Return exactly one JSON object:
104+
{
105+
"revised_answer": "updated answer"
106+
}
107+
108+
Rules:
109+
- Apply required_changes only.
110+
- Keep original scope and customer intent.
111+
- Do not add new facts or numbers.
112+
- Keep the answer concise and actionable.
113+
- You MUST satisfy each required_changes item exactly.
114+
- For ADD/MUST_INCLUDE: include the quoted phrase verbatim.
115+
- For REMOVE/MUST_REMOVE: ensure the quoted phrase does not appear.
116+
- Do not output markdown or extra keys.
117+
""".strip()
118+
101119

102120

103121
def _get_client() -> OpenAI:
@@ -196,14 +214,16 @@ def revise_once(
196214
incident_context: dict[str, Any],
197215
draft: str,
198216
required_changes: list[str],
217+
strict_mode: bool = False,
199218
) -> str:
200219
payload = {
201220
"goal": goal,
202221
"incident_context": incident_context,
203222
"draft": draft,
204223
"required_changes": required_changes,
205224
}
206-
data = _chat_json(system_prompt=REVISE_SYSTEM_PROMPT, payload=payload)
225+
system_prompt = REVISE_SYSTEM_PROMPT_STRICT if strict_mode else REVISE_SYSTEM_PROMPT
226+
data = _chat_json(system_prompt=system_prompt, payload=payload)
207227

208228
revised = data.get("revised_answer")
209229
if not isinstance(revised, str):

examples/agent-patterns/self-critique-agent/python/main.py

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -172,30 +172,44 @@ def stopped(stop_reason: str, *, phase: str, **extra: Any) -> dict[str, Any]:
172172
revised = False
173173

174174
if critique["decision"] == "revise":
175-
if (time.monotonic() - started) > BUDGET.max_seconds:
176-
return stopped("max_seconds", phase="revise")
177-
178-
try:
179-
revised_raw = revise_once(
180-
goal=goal,
181-
incident_context=incident_context,
182-
draft=draft,
183-
required_changes=critique["required_changes"],
184-
)
185-
revised_payload = gateway.validate_revision(
186-
original=draft,
187-
revised=revised_raw,
188-
context=incident_context,
189-
required_changes=critique["required_changes"],
190-
)
191-
except LLMTimeout:
192-
return stopped("llm_timeout", phase="revise")
193-
except LLMInvalid as exc:
194-
return stopped(exc.args[0], phase="revise")
195-
except LLMEmpty:
196-
return stopped("llm_empty", phase="revise")
197-
except StopRun as exc:
198-
return stopped(exc.reason, phase="revise")
175+
revise_attempts = 0
176+
revise_retried = False
177+
revised_payload: dict[str, Any] | None = None
178+
for attempt in range(1, 3):
179+
if (time.monotonic() - started) > BUDGET.max_seconds:
180+
return stopped("max_seconds", phase="revise")
181+
182+
revise_attempts = attempt
183+
strict_mode = attempt > 1
184+
try:
185+
revised_raw = revise_once(
186+
goal=goal,
187+
incident_context=incident_context,
188+
draft=draft,
189+
required_changes=critique["required_changes"],
190+
strict_mode=strict_mode,
191+
)
192+
revised_payload = gateway.validate_revision(
193+
original=draft,
194+
revised=revised_raw,
195+
context=incident_context,
196+
required_changes=critique["required_changes"],
197+
)
198+
break
199+
except LLMTimeout:
200+
return stopped("llm_timeout", phase="revise")
201+
except LLMInvalid as exc:
202+
return stopped(exc.args[0], phase="revise")
203+
except LLMEmpty:
204+
return stopped("llm_empty", phase="revise")
205+
except StopRun as exc:
206+
if exc.reason == "patch_violation:required_changes_not_applied" and attempt < 2:
207+
revise_retried = True
208+
continue
209+
return stopped(exc.reason, phase="revise")
210+
211+
if revised_payload is None:
212+
return stopped("patch_violation:required_changes_not_applied", phase="revise")
199213

200214
final_answer = revised_payload["answer"]
201215
revised = True
@@ -209,6 +223,8 @@ def stopped(stop_reason: str, *, phase: str, **extra: Any) -> dict[str, Any]:
209223
"required_changes_total": revised_payload["required_changes_total"],
210224
"required_changes_enforced": revised_payload["required_changes_enforced"],
211225
"required_changes_unenforced": revised_payload["required_changes_unenforced"],
226+
"attempts_used": revise_attempts,
227+
"retried": revise_retried,
212228
"revised_hash": text_hash(final_answer),
213229
"ok": True,
214230
}

0 commit comments

Comments
 (0)