Approval Gate with a Skill — Email Drafter¶
This notebook shows how to combine the framework's built-in approval gate
with a skill run using run_with_skill(with_approval=True).
The email-drafter skill drafts a reply to an email. Before that draft is
accepted as the task result, the framework pauses and asks the operator to
approve or reject it — the same request_approval mechanic from ch08.ipynb
Example 2, now applied at the skill level.
Setting the backbone LLM of your agent¶
These notebooks run on Ollama by default, the setup the book teaches.
If you do nothing, nothing changes: make_llm() starts a local Ollama
service when one isn't already running.
To use OpenAI or Anthropic instead:
- Install the extra:
uv sync --extra openaioruv sync --extra anthropic. - Export
OPENAI_API_KEYorANTHROPIC_API_KEYbefore launching Jupyter. - Set
LLM_PROVIDER=openai(oranthropic), or passprovider="openai"tomake_llm(). A key on its own never switches providers, so one exported for unrelated work cannot reroute you off the Ollama path.
The switch applies wherever a notebook builds its LLM with make_llm().
A notebook that constructs OllamaLLM directly stays on Ollama regardless
of these settings.
If you opted in but forgot to export the key, you will be prompted for it.
That is the safe path on hosted kernels, and it keeps the key out of the
saved notebook. Setting OLLAMA_API_KEY alone routes Ollama to Ollama
Cloud.
Caveat: the examples are tuned for qwen3. Output on gpt-5 or Claude will differ from what is printed in the book, and prompt-sensitive examples (the ch09 evaluator pattern, ch08 supervised trajectories) may behave noticeably differently.
# Uncomment the line below to install `llm-agents-from-scratch` from PyPI
# !pip install llm-agents-from-scratch
Running an Ollama service¶
To execute the code provided in this notebook, you'll need to have Ollama
installed on your local machine and have its LLM hosting service running.
To download Ollama, follow the instructions found on this page:
https://ollama.com/download. After downloading and installing Ollama, you
can start a service by opening a terminal and running ollama serve.
from llm_agents_from_scratch.notebook_utils import make_llm
import logging
from llm_agents_from_scratch import LLMAgent
from llm_agents_from_scratch.logger import enable_console_logging
enable_console_logging(logging.INFO)
llm = make_llm()
agent = LLMAgent(llm=llm)
✓ Using Ollama Cloud (kimi-k2.7-code:cloud)
EMAIL = """\
From: sarah.chen@acmecorp.com
To: alex@mycompany.com
Subject: Re: Q3 Partnership Proposal
Hi Alex,
Thanks for sending over the Q3 partnership proposal last week. We reviewed it
with the team and we're excited about the direction.
A few things we'd like to clarify before signing:
1. Revenue share — the proposal mentions 20% for the first year, but our
standard agreement starts at 15%. Is there flexibility here?
2. The exclusivity clause covers our North America region, but we'd need it
limited to the retail vertical only.
3. Start date — we're targeting September 1. Does that work on your end?
If these points can be addressed, we'd like to move quickly. Can we get on a
call this week to align?
Best,
Sarah
Sarah Chen | Head of Partnerships | Acme Corp
"""
Running the Skill¶
Passing with_approval=True to run_with_skill() adds the end-of-loop
approval gate to the skill run. Once the agent has drafted the reply, the
framework will pause and ask you to approve or reject before the
result is finalised.
result = await agent.run_with_skill(
"email-drafter",
prompt=f"Draft a reply to the following email:\n\n{EMAIL}",
with_approval=True,
)
print(result.content)
INFO (llm_agents_fs.LLMAgent) : 🚀 Starting task: This is a user-explicit skill activation. Call the from_scratch__use_skill tool with name='email-drafter'. Use exactly this name — it...[TRUNCATED] INFO (llm_agents_fs.TaskHandler) : ⚙️ Processing Step: This is a user-explicit skill activation. Call the from_scratch__use_skill tool with name='email-drafter'. Use exactly this name —...[TRUNCATED] INFO (llm_agents_fs.TaskHandler) : 🛠️ Executing Tool Call: from_scratch__use_skill INFO (llm_agents_fs.TaskHandler) : ✅ Successful Tool Call: <skill_content name="email-drafter"> # Email Drafter Draft a clear, professional reply to the email provided by the user, add...[TRUNCATED] INFO (llm_agents_fs.TaskHandler) : ✅ Step Result: Following the email-drafter skill, here is the draft reply addressing all of Sarah's points: --- **To:** sarah.chen@acmecorp.com **...[TRUNCATED] INFO (llm_agents_fs.TaskHandler) : No new step required.
╭───────────────────────────────────────────── Proposed Task Result ──────────────────────────────────────────────╮ │ Following the email-drafter skill, here is the draft reply addressing all of Sarah's points: │ │ │ │ --- │ │ │ │ **To:** sarah.chen@acmecorp.com │ │ **From:** alex@mycompany.com │ │ **Subject:** Re: Q3 Partnership Proposal │ │ │ │ Hi Sarah, │ │ │ │ Thanks for the thoughtful feedback — we're glad the team is excited about the direction. │ │ │ │ Quick thoughts on your questions: │ │ │ │ 1. **Revenue share:** We're open to discussing this. 20% reflects the first-year investment we're making in │ │ onboarding and enablement, but I understand 15% aligns with your standard agreement. Let's put it on the agenda │ │ for our call and see if we can land somewhere that works for both sides. │ │ │ │ 2. **Exclusivity:** Limiting the exclusivity to the retail vertical within North America sounds reasonable. │ │ I'll flag this with our legal team so we can revise the clause accordingly. │ │ │ │ 3. **Start date:** September 1 works on our end. │ │ │ │ I'm glad you want to move quickly — so do we. How does **Thursday at 2pm ET** work for a call? If not, send │ │ over a couple of slots and we'll lock it in. │ │ │ │ Looking forward to aligning. │ │ │ │ Best, │ │ Alex │ │ │ │ --- │ │ │ │ This draft is ready for your review before sending. │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Approve this result? [y/n]:
[scripted human] > y INFO (llm_agents_fs.LLMAgent) : 🏁 Task completed: Following the email-drafter skill, here is the draft reply addressing all of Sarah's points: --- **To:** sarah.chen@acmecorp.com ...[TRUNCATED] Following the email-drafter skill, here is the draft reply addressing all of Sarah's points: --- **To:** sarah.chen@acmecorp.com **From:** alex@mycompany.com **Subject:** Re: Q3 Partnership Proposal Hi Sarah, Thanks for the thoughtful feedback — we're glad the team is excited about the direction. Quick thoughts on your questions: 1. **Revenue share:** We're open to discussing this. 20% reflects the first-year investment we're making in onboarding and enablement, but I understand 15% aligns with your standard agreement. Let's put it on the agenda for our call and see if we can land somewhere that works for both sides. 2. **Exclusivity:** Limiting the exclusivity to the retail vertical within North America sounds reasonable. I'll flag this with our legal team so we can revise the clause accordingly. 3. **Start date:** September 1 works on our end. I'm glad you want to move quickly — so do we. How does **Thursday at 2pm ET** work for a call? If not, send over a couple of slots and we'll lock it in. Looking forward to aligning. Best, Alex --- This draft is ready for your review before sending.