Loading the notebook…
Loading the notebook…
Notebooks · Skills · 27
Full tour: catalog browse, tool bundles, memory, streaming, chat sessions, and real-world project building with skills.
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 IPython.display import Markdown, display
from shipit_agent import Agent, DeepAgent, DEFAULT_SKILLS_PATH, FileSkillRegistry
from shipit_agent.builtins import get_builtin_tool_map
from shipit_agent.stores import InMemoryMemoryStore
from shipit_agent.skills.tool_bundles import (
SKILL_TOOL_BUNDLES,
validate_tool_bundles,
)
registry = FileSkillRegistry(DEFAULT_SKILLS_PATH)
print(f"Catalog path: {DEFAULT_SKILLS_PATH}")
print(f"Total skills: {len(registry.list())}")Catalog path: ~/Documents/MYWORK/ai_developer/others/shipit_agent/shipit_agent/skills/skills.json Total skills: 37
all_skills = registry.list()
print(f"{'ID':35} {'Category':20} Name")
print("-" * 85)
for skill in all_skills:
print(
f"{skill.id:35} {(skill.category or 'uncategorized'):20} {skill.display_name or skill.name}"
)ID Category Name ------------------------------------------------------------------------------------- web-scraper-pro Web Scraping Web Scraper Pro portfolio-website-builder utilities Portfolio Website Builder google-maps-lead-finder Leads & Sales Google Maps Lead Finder amazon-affiliate-marketing-agent Marketing Amazon Affiliate Marketing Agent gmail-and-calendar-agent Productivity Gmail and Calendar Agent ugc-video-ad-agent Video UGC Video Ad Agent video-clone-agent Video Video Clone Agent ai-music-generator Audio AI Music Generator startup-idea-scout Research Startup Idea Scout lead-enrichment Sales & CRM Lead Enrichment competitor-intelligence-tracker Data & APIs Competitor Intelligence Tracker expense-tracker Finance & Accounting Expense Tracker ai-video-prompt-builder Development AI Video Prompt Builder analytics-setup-assistant Development Analytics Setup Assistant api-test-assistant Testing API Test Assistant app-store-screenshots Design App Store Screenshots backup-manager Utilities Backup Manager brand-voice-guide Marketing Brand Voice Guide code-workflow-assistant Development Code Workflow Assistant database-architect Development Database Architect deal-strategist Sales Deal Strategist devops-automation DevOps DevOps Automation full-stack-developer Development Full Stack Developer marketing-advisor Marketing Marketing Advisor mcp-server-builder Development MCP Server Builder multi-agent-manager Automation Multi-Agent Manager notion-workspace-manager Productivity Notion Workspace Manager obsidian-vault-manager Productivity Obsidian Vault Manager product-development-guide Productivity Product Development Guide security-engineer Security Security Engineer seo-growth-guide Marketing SEO Growth Guide social-media-post-manager Marketing Social Media Post Manager technical-writer Development Technical Writer tiktok-app-marketing-agent Marketing TikTok App Marketing Agent ui-and-ux-design-guide Design UI and UX Design Guide video-downloader Utilities Video Downloader x-search-and-sentiment-tool Marketing X Search and Sentiment Tool
for query in ["database", "web scrape", "security", "marketing", "devops"]:
matches = registry.search(query)
print(f"\n'{query}' → {len(matches)} match(es)")
for skill in matches[:3]:
print(f" - {skill.id}: {skill.description[:80]}")'database' → 4 match(es) - database-architect: Design schemas, optimize queries, choose indexes, plan migrations, and improve d - notion-workspace-manager: Helps you create, update, search, and organize pages and databases in Notion. - mcp-server-builder: Helps you build MCP servers with custom tools, integrations, and resources for A 'web scrape' → 2 match(es) - web-scraper-pro: Scrapes modern websites with JavaScript support, anti-bot handling, login sessio - security-engineer: Protect your applications and infrastructure with expert security analysis. Find 'security' → 4 match(es) - security-engineer: Protect your applications and infrastructure with expert security analysis. Find - api-test-assistant: Checks your API for broken endpoints, bad responses, slow requests, and common s - devops-automation: Automate your deployment pipeline. Set up CI/CD, containers, and infrastructure 'marketing' → 7 match(es) - marketing-advisor: Get clear marketing advice, better messaging, and practical ideas to grow your b - amazon-affiliate-marketing-agent: Turns Amazon product links into social media posts with images, captions, and af - tiktok-app-marketing-agent: Helps you create, post, and improve TikTok content for app marketing. 'devops' → 3 match(es) - devops-automation: Automate your deployment pipeline. Set up CI/CD, containers, and infrastructure - backup-manager: Helps you set up backups, schedule them, and restore files when needed. - full-stack-developer: The complete full stack development system — React, Next.js, TypeScript, Postgre
print(f"Total skills with tool bundles: {len(SKILL_TOOL_BUNDLES)}\n")
for skill_id in [
"web-scraper-pro",
"code-workflow-assistant",
"full-stack-developer",
"security-engineer",
]:
tools = SKILL_TOOL_BUNDLES.get(skill_id, [])
print(f"{skill_id}:")
print(f" Tools ({len(tools)}): {', '.join(tools)}\n")Total skills with tool bundles: 37 web-scraper-pro: Tools (10): web_search, open_url, playwright_browse, bash, glob_files, grep_files, read_file, edit_file, write_file, run_code code-workflow-assistant: Tools (10): read_file, edit_file, write_file, glob_files, grep_files, bash, run_code, workspace_files, plan_task, verify_output full-stack-developer: Tools (13): read_file, edit_file, write_file, glob_files, grep_files, bash, run_code, workspace_files, web_search, open_url, playwright_browse, plan_task, verify_output security-engineer: Tools (9): bash, web_search, open_url, playwright_browse, read_file, grep_files, glob_files, run_code, verify_output
class StubLLM:
def complete(self, **kwargs):
pass
builtin_names = set(get_builtin_tool_map(llm=StubLLM()).keys())
print(f"Built-in tools: {len(builtin_names)}")
errors = validate_tool_bundles(builtin_names)
print(f"Validation: {'All valid' if not errors else errors}")Built-in tools: 32 Validation: All valid
Why memory matters: without memory_store, the agent may ask clarifying questions.
With InMemoryMemoryStore, the agent stores facts it discovers and uses them across turns.
from examples.run_multi_tool_agent import build_llm_from_env
llm = build_llm_from_env("bedrock")
memory = InMemoryMemoryStore()
agent = Agent.with_builtins(
llm=llm,
name="skills-memory-agent",
project_root="/tmp",
skills=["database-architect", "code-workflow-assistant"],
auto_use_skills=True,
skill_match_limit=2,
memory_store=memory,
max_iterations=12,
)
print("Skills:", [s.id for s in agent.skills])
print(f"max_iterations: {agent.max_iterations}")
print(f"memory: {type(memory).__name__}")Skills: ['database-architect', 'code-workflow-assistant'] max_iterations: 12 memory: InMemoryMemoryStore
result = agent.run(
"""
We are adding tenant-based billing alerts to an existing FastAPI service.
Project context:
- Backend: FastAPI + PostgreSQL 15
- Current endpoint: GET /api/billing/alerts
- Current performance: 4.8s p95
- Schema areas: alerts, tenants, invoices, users
- Constraint: no breaking API changes
- Goal: reduce latency below 500ms
Please:
1. Summarize the likely problem areas
2. Propose a concrete implementation plan
3. Suggest database/index/query improvements
4. Call out testing and rollout risks
"""
)
print(f"Used skills: {result.metadata['used_skills']}")
print(f"Skill tools: {result.metadata['used_skill_tools']}\n")
display(Markdown(result.output))Used skills: ['database-architect', 'code-workflow-assistant'] Skill tools: []
Quick TL;DR
| Area | Likely bottleneck | Quick win |
|---|---|---|
| SQL queries (joins on alerts ↔ tenants ↔ invoices ↔ users) | Missing/inefficient indexes, SELECT * and N+1 loading | Add covering indexes, limit columns, fetch‑only‑needed rows |
| ORM layer (SQLAlchemy/Pydantic) | Implicit lazy loads, default relationship loading, heavy schema validation | Switch to eager‑loaded, pre‑filtered sub‑queries; use joinedload/selectinload |
| Business‑logic filtering | Pulling all alerts then filtering per‑tenant in Python | Push tenant filter into the DB — WHERE alerts.tenant_id = :tid |
| Network/serialization | Returning full alert objects with nested user/invoice payloads | Return a lean DTO (only fields needed by the client) |
| Missing caching | Re‑computing the same alert list for a tenant on every request | Add per‑tenant cache (e.g. Redis) with TTL ≈ 1 min for “stable” alerts |
| I/O blocking** | Synchronous DB calls inside FastAPI async endpoints | Use async driver (asyncpg/SQLModel async) or run DB calls in a thread pool |
The goal of ≤ 500 ms p95 is realistic once the above are addressed.
Heavy multi‑table joins
alerts → tenants → invoices → users are likely joined with LEFT JOIN + SELECT *. Without proper indexes each join scans large tables.
N+1 queries / lazy loading
SQLAlchemy (or the ORM you use) often loads related rows lazily. A single alert list can trigger dozens of extra DB round‑trips (e.g. one per alert to fetch the tenant, invoice, or user).
Fetching more columns than needed
Pydantic models serialize every column, many of which are never used by the client (e.g., large JSON blobs, audit fields).
Tenant filtering done in‑application
If the endpoint first loads all alerts and then filters alert.tenant_id == current_tenant, the DB work is completely wasted.
Synchronous DB driver in an async endpoint
Blocking DB calls stall the event loop, causing apparent latency spikes under concurrency.
Missing caching / materialized view
Alert calculations (e.g., overdue‑payment detection) may be recomputed on each request.
| Step | Description | Tool / Action | Deliverable | … 113 more lines
Use agent.stream() to see events in real time — tool calls, completions, and the final output.
Skills work exactly the same in streaming mode.
stream_agent = Agent.with_builtins(
llm=llm,
project_root="/tmp",
skills=["devops-automation"],
auto_use_skills=False,
memory_store=InMemoryMemoryStore(),
max_iterations=10,
)
print("Streaming events:\n")
final_output = ""
for event in stream_agent.stream(
"""
Use the write_file tool to create these files in the project root:
1. Dockerfile — multi-stage Python build, non-root user, uvicorn CMD
2. docker-compose.yml — app + PostgreSQL + Redis services
You MUST use the write_file tool to create each file. Do NOT just show the code.
After creating the files, use glob_files to verify they exist.
"""
):
if event.type == "step_started":
iteration = event.payload.get("iteration", "?")
print(f" [step {iteration}] LLM thinking...")
elif event.type == "tool_called":
print(f" [tool] {event.message}")
elif event.type == "tool_completed":
output_preview = event.payload.get("output", "")[:80]
print(f" [done] {event.message} → {output_preview}...")
elif event.type == "tool_failed":
print(f" [FAIL] {event.message}")
elif event.type == "run_completed":
final_output = event.payload.get("output", "")
print(" [finished]")
print()
display(Markdown(final_output))DeepAgent streaming works the same way. With verify=True, you also get a
verification event at the end.
deep_stream = DeepAgent.with_builtins(
llm=llm,
project_root="/tmp/deep-stream",
skills=["full-stack-developer"],
auto_use_skills=False,
memory_store=InMemoryMemoryStore(),
verify=True,
max_iterations=10,
)
print("DeepAgent streaming with verify=True:\n")
final_output = ""
for event in deep_stream.stream(
"""
Use the write_file tool to create these files:
1. cli.py — a Python CLI tool that:
- Accepts a directory path as argument
- Counts files by extension
- Prints a summary table
- Handles errors gracefully
2. README.md — explains usage and examples.
You MUST use the write_file tool to create each file on disk. Do NOT just show code.
"""
):
if event.type == "step_started":
print(f" [step {event.payload.get('iteration', '?')}]")
elif event.type == "tool_called":
print(f" [tool] {event.message}")
elif event.type == "tool_completed":
print(f" [done] {event.message}")
elif event.type == "run_completed":
final_output = event.payload.get("output", "")
verification = event.payload.get("verification", None)
print(" [finished]")
if verification:
print(f" [verify] {verification}")
print()
display(Markdown(final_output))Use chat_session() for multi-turn conversations. The session keeps message
history so the agent never asks "what project are you working on?" again.
chat = agent.chat_session(session_id="billing-debug-session")
# Turn 1: Provide context
r1 = chat.send(
"""
I'm working on the billing-api service. The GET /api/billing/alerts endpoint
joins alerts, tenants, invoices, and users tables. It's currently at 4.8s p95.
PostgreSQL 15, FastAPI backend. Help me debug and fix this.
"""
)
print("=== Turn 1 ===")
display(Markdown(r1.output))# Turn 2: Follow-up — agent remembers the context.
r2 = chat.send(
"Show me the specific CREATE INDEX statements for the join path you identified."
)
print("=== Turn 2 (no re-asking) ===")
display(Markdown(r2.output))# Turn 3: Another follow-up.
r3 = chat.send("How do I deploy these index changes safely with zero downtime?")
print("=== Turn 3 (full context retained) ===")
display(Markdown(r3.output))Use chat.stream() instead of chat.send() to get events as they happen
during a multi-turn conversation.
stream_chat_agent = Agent.with_builtins(
llm=llm,
project_root="/tmp",
skills=["full-stack-developer"],
auto_use_skills=False,
memory_store=InMemoryMemoryStore(),
max_iterations=10,
)
chat = stream_chat_agent.chat_session(session_id="stream-chat-demo")
# Turn 1: Stream the response
print("=== Turn 1 (streaming) ===")
final_output = ""
for event in chat.stream(
"Use the write_file tool to create a FastAPI project with a User model at /tmp/stream-chat-project. Write app/main.py and app/models.py. You MUST use write_file to create the actual files."
):
if event.type == "tool_called":
print(f" [tool] {event.message}")
elif event.type == "tool_completed":
print(f" [done] {event.message}")
elif event.type == "run_completed":
final_output = event.payload.get("output", "")
display(
Markdown(final_output[:500] + "..." if len(final_output) > 500 else final_output)
)# Turn 2: Stream follow-up — agent remembers the project from turn 1.
print("=== Turn 2 (streaming follow-up) ===")
final_output = ""
for event in chat.stream(
"Now add a Task model with CRUD endpoints. Tasks belong to users."
):
if event.type == "tool_called":
print(f" [tool] {event.message}")
elif event.type == "tool_completed":
print(f" [done] {event.message}")
elif event.type == "run_completed":
final_output = event.payload.get("output", "")
display(
Markdown(final_output[:500] + "..." if len(final_output) > 500 else final_output)
)The full-stack-developer skill brings 13 tools: write_file, edit_file,
workspace_files, bash, run_code, plan_task, verify_output, and web tools.
The agent can scaffold an entire project from scratch.
builder = Agent.with_builtins(
llm=llm,
project_root="/tmp/fullstack-demo",
skills=["full-stack-developer"],
auto_use_skills=False,
memory_store=InMemoryMemoryStore(),
max_iterations=15,
)
result = builder.run(
"""
Create a FastAPI REST API project at /tmp/fullstack-demo with:
1. Project structure:
- app/main.py (FastAPI app with CORS)
- app/models.py (SQLAlchemy models: User, Task)
- app/routes/tasks.py (CRUD endpoints)
- app/database.py (SQLite connection)
- requirements.txt
- README.md
2. Task model: id, title, description, status, created_at, user_id
3. Endpoints: GET/POST /tasks, GET/PUT/DELETE /tasks/{id}
4. Proper error handling and status codes
IMPORTANT: You MUST use the write_file tool to create each file on disk.
Do NOT just show the code — actually write the files.
After creating all files, use glob_files to list what was created.
"""
)
print(f"Skills: {result.metadata['used_skills']}")
print(f"Tools injected: {result.metadata['used_skill_tools']}\n")
display(Markdown(result.output))The web-scraper-pro skill brings web_search, open_url, playwright_browse,
plus file tools to save scraped data.
scraper = Agent.with_builtins(
llm=llm,
project_root="/tmp/scrape-output",
skills=["web-scraper-pro"],
auto_use_skills=False,
memory_store=InMemoryMemoryStore(),
max_iterations=12,
)
result = scraper.run(
"""
Research the top 5 Python web frameworks in 2024.
For each framework, find: GitHub stars, latest version, key differentiator.
Save results to /tmp/scrape-output/frameworks.json as structured JSON,
then create a markdown summary in /tmp/scrape-output/summary.md.
"""
)
print(f"Skills: {result.metadata['used_skills']}")
print(f"Tools injected: {result.metadata['used_skill_tools']}\n")
display(Markdown(result.output))Use deep.chat() for persistent multi-turn conversations with DeepAgent.
Stream each turn for real-time feedback.
deep_chat = DeepAgent.with_builtins(
llm=llm,
project_root="/tmp/deep-chat-demo",
skills=["full-stack-developer", "database-architect"],
auto_use_skills=True,
memory_store=InMemoryMemoryStore(),
max_iterations=10,
)
chat = deep_chat.chat(session_id="deep-iterative-build")
# Turn 1: Scaffold (streaming)
print("=== Turn 1: Scaffold ===")
t1_output = ""
for event in chat.stream(
"Use the write_file tool to create a FastAPI app with a User model and auth endpoints at /tmp/deep-chat-demo. Write app/main.py, app/models.py, app/auth.py. You MUST use write_file to create the actual files on disk."
):
if event.type == "tool_called":
print(f" [tool] {event.message}")
elif event.type == "run_completed":
t1_output = event.payload.get("output", "")
display(Markdown(t1_output[:400] + "..."))# Turn 2: Add features — agent remembers the project structure.
print("=== Turn 2: Add Task model ===")
t2_output = ""
for event in chat.stream(
"Now add a Task model with CRUD endpoints. Tasks belong to users. Add indexes."
):
if event.type == "tool_called":
print(f" [tool] {event.message}")
elif event.type == "run_completed":
t2_output = event.payload.get("output", "")
display(Markdown(t2_output[:400] + "..."))# Turn 3: Deploy config — agent still has full context.
print("=== Turn 3: Deploy config ===")
t3_output = ""
for event in chat.stream(
"Create a Dockerfile and docker-compose.yml for this project."
):
if event.type == "tool_called":
print(f" [tool] {event.message}")
elif event.type == "run_completed":
t3_output = event.payload.get("output", "")
display(Markdown(t3_output[:400] + "..."))added = agent.add_skill("security-engineer")
print(f"Added: {added.id}")
print("All skills:", [s.id for s in agent.skills])
print("\nSearch:")
for skill in agent.search_skills("devops")[:3]:
print(f" - {skill.id}")all_skills = registry.list()
missing = [s.id for s in all_skills if s.id not in SKILL_TOOL_BUNDLES]
print(f"Skills with bundles: {len(all_skills) - len(missing)}/{len(all_skills)}")
print("Missing:" if missing else "All skills have tool bundles.", missing or "")| Feature | API |
|---|---|
| Attach skills | skills=["skill-id", ...] |
| Auto-match | auto_use_skills=True |
| Add at runtime | agent.add_skill("skill-id") |
| Search catalog | agent.search_skills("query") |
| Memory | memory_store=InMemoryMemoryStore() |
| Chat (Agent) | agent.chat_session(session_id="...") |
| Chat (DeepAgent) | deep.chat(session_id="...") |
| Chat send | chat.send(prompt) |
| Chat stream | for event in chat.stream(prompt): ... |
| Agent stream | for event in agent.stream(prompt): ... |
| Deep stream | for event in deep.stream(prompt): ... |
| Used skills | result.metadata["used_skills"] |
| Injected tools | result.metadata["used_skill_tools"] |
| Validate | validate_tool_bundles(builtin_names) |