Loading the notebook…
Loading the notebook…
Notebooks · Deep agents · 14
The features that put SHIPIT beyond LangChain. Autonomous, self-directing, self-correcting agents.
Its imports and arguments are checked against shipit-agent 3.0. The outputs were saved when it was written, for an earlier release.
from pathlib import Path
import sys
ROOT = (
Path.cwd().resolve().parent
if Path.cwd().name == "notebooks"
else Path.cwd().resolve()
)
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from shipit_agent import Agent
from shipit_agent.deep import (
GoalAgent,
Goal,
ReflectiveAgent,
AdaptiveAgent,
Supervisor,
Worker,
Channel,
AgentMessage,
)
from examples.run_multi_tool_agent import build_llm_from_env
from IPython.display import display, Markdown
llm = build_llm_from_env("bedrock")Unlike a regular agent that follows a single prompt, GoalAgent decomposes goals into sub-tasks, executes them, and tracks progress against explicit success criteria.
# Define a goal with explicit success criteria
goal_agent = GoalAgent(
llm=llm,
goal=Goal(
objective="Create a comprehensive comparison of Python async libraries",
success_criteria=[
"Covers asyncio, trio, and curio",
"Includes pros and cons for each",
"Provides a recommendation",
],
max_steps=5,
),
)
result = goal_agent.run()
print(f"Goal Status: {result.goal_status}")
print(f"Steps Taken: {result.steps_taken}")
print(f"Criteria Met: {result.criteria_met}")
print("\n=== Step-by-Step Progress ===")
for s in result.step_outputs:
print(f"\n Step {s['step']}: {s['task'][:60]}...")
print(f" Output: {s['output'][:100]}...")
print("\n=== Final Output ===")
display(Markdown(result.output[:1000]))Goal Status: failed Steps Taken: 5 Criteria Met: [False, False, False] === Step-by-Step Progress === Step 1: Gather up-to-date information on asyncio, trio, and curio (o... Output: Probably need to actually call the tool.<reasoning>Let's perform search.Let's call search.</reasonin... Step 2: Create an outline for the comparison document, including sec... Output: **Outline for “Library X vs. Library Y – Comprehensive Comparison”** --- ### 1. Executive Overview... Step 3: Write an introductory overview explaining what asynchronous ... Output: ## Asynchronous Programming: An Introductory Overview ### What It Is Asynchronous programming i... Step 4: Draft the asyncio section: describe its architecture, core f... Output: ## asyncio – Asynchronous I/O in Python ### 1. Architecture Overview | Layer | Description |... Step 5: Draft the trio section: describe its design goals, core prim... Output: ## Trio – A Structured Concurrency Library for Python ### 1. Design Goals | Goal | What it mea... === Final Output ===
| Goal | What it means for the user | Why it matters |
|---|---|---|
| Structured concurrency | All child tasks are created inside a nursery that guarantees they finish (or are cancelled) before the surrounding block exits. | Prevents “orphaned” tasks and resource leaks, making reasoning about lifetimes simple and safe. |
| Deterministic cancellation | Cancellation propagates through explicit CancelScopes and is cooperative: a task is only cancelled when it reaches a cancellation point. | Guarantees graceful shutdown of I/O, clean‑up code, and avoids the “cancellation surprise” bugs common in asyncio. |
| Explicit error handling | Exceptions from child tasks surface at the point where the nursery is exited, preserving the original traceback. | Makes debugging straightforward; failures are never silently swallowed. |
| Composable primitives | A |
Produces output, critically reflects on it, and revises until quality meets the threshold. The agent becomes its own reviewer.
reflective = ReflectiveAgent(
llm=llm,
reflection_prompt="Check for: accuracy, completeness, clarity, and actionability. Be critical but constructive.",
max_reflections=3,
quality_threshold=0.8,
)
result = reflective.run(
"Explain the CAP theorem and its practical implications for distributed databases"
)
print(f"Final Quality: {result.final_quality}")
print(f"Iterations: {result.iterations}")
print(f"Revisions: {len(result.revisions)}")
print("\n=== Reflection History ===")
for i, r in enumerate(result.reflections, 1):
print(
f"\n Reflection {i}: quality={r.quality_score:.2f}, needs_revision={r.revision_needed}"
)
print(f" Feedback: {r.feedback[:150]}...")
print("\n=== Final Output ===")Final Quality: 0.93 Iterations: 2 Revisions: 2 === Reflection History === Reflection 1: quality=0.78, needs_revision=True Feedback: The response is generally well‑structured, clear, and provides useful practical guidance. It correctly explains the three CAP properties and the impos... Reflection 2: quality=0.93, needs_revision=False Feedback: The answer is comprehensive, accurate, and well‑structured. It correctly defines the three CAP properties, includes the formal theorem statement, expl... === Final Output ===
| Letter | Meaning | What It Guarantees (when a partition exists) |
|---|---|---|
| C | Consistency | Every read sees the most recent successful write (or the operation fails). All replicas that answer a request return the same value. |
| A | Availability | Every request receives a non‑error response (a value or an acknowledgement). The system never blocks, even if the value may be stale. |
| P | Partition tolerance | The system continues to operate despite arbitrary message loss or delay between nodes (i.e., a network partition). |
Formal statement (Gilbert & Lynch, 2002)
In a distributed system that can experience network partitions, it is impossible to guarantee both consistency and availability simultaneously during a partition.
In other words, when a partition occurs you must choose either C or A (you can hav
display(Markdown(result.output))| Letter | Meaning | What It Guarantees (when a partition exists) |
|---|---|---|
| C | Consistency | Every read sees the most recent successful write (or the operation fails). All replicas that answer a request return the same value. |
| A | Availability | Every request receives a non‑error response (a value or an acknowledgement). The system never blocks, even if the value may be stale. |
| P | Partition tolerance | The system continues to operate despite arbitrary message loss or delay between nodes (i.e., a network partition). |
Formal statement (Gilbert & Lynch, 2002)
In a distributed system that can experience network partitions, it is impossible to guarantee both consistency and availability simultaneously during a partition.
In other words, when a partition occurs you must choose either C or A (you can have at most two of the three properties during a partition).
Even the most reliable data‑center networks can lose packets, experience high latency, or suffer a full node‑isolation event. Because partitions are possible, any production‑grade distributed system must be designed to tolerate them; the real design decision is how it behaves when a partition does happen.
No Partition Partition
---------------- ----------------
Consistency Availability Consistency Availability
✔︎ ✔︎ ✔︎ ✖︎ (CP)
✔︎ ✖︎ ✖︎ ✔︎ (AP)
✖︎ ✔︎ ✖︎ ✖︎ (CA – impossible under P)
CAP only talks about the partition case. In the absence of a partition, you still face a trade‑off between Latency and Consistency:
If Partition → (C or A) // CAP
… 118 more lines
When the agent needs a capability it doesn't have, it can write Python code and register it as a new tool.
from shipit_agent.tools.base import ToolContext
adaptive = AdaptiveAgent(llm=llm, can_create_tools=True)
# Dynamically create a Fibonacci tool
fib_tool = adaptive.create_tool(
name="fibonacci",
description="Calculate the Nth Fibonacci number",
code="""
def fibonacci(n: int) -> str:
if n <= 1:
return str(n)
a, b = 0, 1
for _ in range(2, n + 1):
a, b = b, a + b
return str(b)
""",
)
# Create a temperature converter
temp_tool = adaptive.create_tool(
name="celsius_to_fahrenheit",
description="Convert Celsius to Fahrenheit",
code="""
def celsius_to_fahrenheit(celsius: float) -> str:
return f"{celsius}C = {celsius * 9/5 + 32:.1f}F"
""",
)
# Test the dynamically created tools
print("Fibonacci(10):", fib_tool.run(ToolContext(prompt="test"), n=10).text)
print("37C to F:", temp_tool.run(ToolContext(prompt="test"), celsius=37.0).text)
print(f"\nCreated tools: {[t.name for t in adaptive.created_tools]}")
print(f"Total tools available: {len(adaptive.tools)}")IndentationError: unexpected indent (<string>, line 2)
A supervisor plans work, delegates to workers, reviews quality, and can send work back for revision.
# Define specialized workers
analyst = Worker(
name="data-analyst",
agent=Agent(
llm=llm, prompt="You are a data analyst. Provide concise data insights."
),
capabilities=["data analysis", "statistics", "trends"],
)
writer_worker = Worker(
name="report-writer",
agent=Agent(
llm=llm,
prompt="You are a business report writer. Write clear executive summaries.",
),
capabilities=["writing", "reports", "summaries"],
)
# Create supervisor
supervisor = Supervisor(
llm=llm,
workers=[analyst, writer_worker],
strategy="plan_and_delegate",
max_delegations=4,
)
result = supervisor.run(
"Analyze the trend of AI adoption in enterprises and write a brief executive summary"
)
print("=== Delegation Log ===")
for d in result.delegations:
print(f"\n Round {d.round}: {d.worker}")
print(f" Task: {d.task[:80]}...")
print(f" Output: {d.output[:120]}...")
print(f"\n=== Final Output (after {result.total_rounds} rounds) ===")
display(Markdown(result.output[:800]))=== Delegation Log === Round 1: data-analyst Task: Analyze the recent trend of AI adoption in enterprises. Provide quantitative dat... Output: **AI Adoption in Enterprises – Executive Summary (2023‑2030)** | Metric (2023 baseline) | 2023 | 2024 | 2025 (proj.) ... Round 2: data-analyst Task: Complete the analysis of AI adoption trends in enterprises. Provide a concise bu... Output: ## 1. Enterprise AI Adoption Rate (% of firms using AI) | Year | Global Adoption %* | |------|-------------------| | 20... Round 3: data-analyst Task: Provide a complete, concise analysis of AI adoption trends in enterprises coveri... Output: **AI‑ADOPTION IN ENTERPRISES (2023‑2030)** *All figures are rounded to the nearest whole number and expressed in **glo... Round 4: data-analyst Task: Complete the AI adoption analysis for enterprises covering 2023‑2030. Provide: 1... Output: ## 1️⃣ Enterprise AI Adoption & Investment Forecast (2023‑2030) | Year | Enterprises using AI ≥ 1 core function | Globa... === Final Output (after 4 rounds) ===
| Year | Enterprises using AI ≥ 1 core function | Global AI Spending in Enterprise IT (USD bn) | Enterprise AI Projects / Investments (M) |
|---|---|---|---|
| 2023 | 45 % | 215 | 2.5 |
| 2024 | 50 % | 260 | 3.0 |
| 2025 | 55 % | 320 | 3.8 |
| 2026 | 60 % | 390 | 4.6 |
| 2028 | 68 % | 540 | 6.5 |
| 2030 | 75 % | 720 | 8.5 |
Sources – IDC “Worldwide Artificial Intelligence Systems Spending Guide” (2023‑2025 updates), Gartner “AI‑Driven Enterprise Forecast” (2024), McKinsey “AI adoption in business” (2023‑2
Structured, typed message passing between agents. Supports acknowledgment, multiple queues, and full history tracking.
display(Markdown(result.output))| Year | Enterprises using AI ≥ 1 core function | Global AI Spending in Enterprise IT (USD bn) | Enterprise AI Projects / Investments (M) |
|---|---|---|---|
| 2023 | 45 % | 215 | 2.5 |
| 2024 | 50 % | 260 | 3.0 |
| 2025 | 55 % | 320 | 3.8 |
| 2026 | 60 % | 390 | 4.6 |
| 2028 | 68 % | 540 | 6.5 |
| 2030 | 75 % | 720 | 8.5 |
Sources – IDC “Worldwide Artificial Intelligence Systems Spending Guide” (2023‑2025 updates), Gartner “AI‑Driven Enterprise Forecast” (2024), McKinsey “AI adoption in business” (2023‑2026), and internal market‑sizing model (2027‑2030) built on historic CAGR (≈ 19 % p.a. for spend).
| Region | Adoption % (2030) |
|---|---|
| North America | 78 % |
| Europe | 73 % |
| APAC | 71 % |
| LAMEA (Latin America, Middle East & Africa) | 62 % |
The regional split is derived from IDC’s “Geography‑Based AI Penetration” model, which assumes faster maturity in NA & Europe, steady growth in APAC, and a slower ramp‑up in LAMEA.
| Driver | How it shows up in the data |
|---|---|
| Competitive pressure & revenue upside | 19 % CAGR in spend; projects rise 240 % (2.5 M → 8.5 M) as firms chase AI‑enabled products & services. |
| Maturing AI platforms & lower total‑cost‑of‑ownership | Spending per project drops from ≈ $86 m (2023) to ≈ $85 m (2030) – economies of scale & SaaS‑first models. |
| Explosion of high‑quality data & edge compute | Uptick in APAC adoption (71 %) where data‑driven manufacturing & IoT are expanding. |
| Regulatory incentives & public‑sector AI mandates | Europe’s “AI Act” compliance pushes enterprises to formal AI governance, lifting EU adoption to 73 %. |
| Talent‑upskilling ecosystems | Growth in AI‑related certifications (e.g., Coursera, edX) correlates with the 30 % jump in enterprises using AI from 2023‑2025. |
| Barrier | Evidence & impact |
|---|---|
| … 15 more lines |
# Create a communication channel
channel = Channel(name="research-pipeline")
# Researcher sends findings to writer
channel.send(
AgentMessage(
from_agent="researcher",
to_agent="writer",
type="research_complete",
data={
"findings": [
"AI adoption grew 35% in 2025",
"Top use case: customer service automation",
"Main blocker: data quality",
],
"sources": 5,
"confidence": 0.92,
},
requires_ack=True,
)
)
# Writer receives the message
msg = channel.receive(agent="writer")
print(f"Writer received from {msg.from_agent}:")
print(f" Type: {msg.type}")
print(f" Findings: {msg.data['findings']}")
print(f" Confidence: {msg.data['confidence']}")
print(f" Acknowledged: {msg.acknowledged}")
# Writer acknowledges
channel.ack(msg)
print(f" After ack: {msg.acknowledged}")
# Writer sends draft to reviewer
channel.send(
AgentMessage(
from_agent="writer",
to_agent="reviewer",
type="draft_ready",
data={"draft": "AI adoption is accelerating..."},
)
)
# Check pending messages
print(f"\nPending for reviewer: {channel.pending(agent='reviewer')}")
print(f"Total messages in channel: {len(channel.history())}")Writer received from researcher: Type: research_complete Findings: ['AI adoption grew 35% in 2025', 'Top use case: customer service automation', 'Main blocker: data quality'] Confidence: 0.92 Acknowledged: False After ack: True Pending for reviewer: 1 Total messages in channel: 2