diff --git a/skills/managed-agents/SKILL.md b/skills/managed-agents/SKILL.md new file mode 100644 index 0000000..3eb2640 --- /dev/null +++ b/skills/managed-agents/SKILL.md @@ -0,0 +1,103 @@ +--- +name: managed-agents +description: | + This skill should be used when the user asks about "managed agents", + "Anthropic API agents", "cloud-hosted agents", "agent SDK", + "deploying agents to the cloud", "serverless agents", + "API-based agent deployment", "/v1/agents endpoint", + "remote agent hosting", "agent as a service" +version: 0.1.0 +--- + +## What are managed agents + +Managed agents are Anthropic-hosted agent runtimes accessed via the Agent SDK +(`@anthropic-ai/sdk` for TypeScript, `anthropic` for Python). Instead of running +Claude Code locally, the agent runs on Anthropic's infrastructure with persistent +sessions, tool access, and automatic scaling. + +Key difference from local agents: managed agents don't have local filesystem +access by default. They work through tools you define in code, not through +Claude Code's built-in Read/Write/Bash tools. + +## When to use managed agents vs local deployment + +| Dimension | Managed Agents (API) | Local (Claude Code CLI) | +|-----------|---------------------|------------------------| +| Infrastructure | Anthropic-hosted | Your machine/server | +| Filesystem | Via tools you define | Full local access | +| MCP servers | Not available | Full MCP support | +| Scaling | Automatic | Manual | +| Cost model | Per-token API billing | Subscription or API key | +| Best for | SaaS products, API integrations | Personal pipelines, file-heavy work | +| Session persistence | Via API sessions | Via `--resume` / `--name` | + +**Decision rule:** If your agents need local filesystem access, MCP servers, or +run as part of a personal workflow → use local deployment (cron/launchd/systemd/Docker). +If your agents are part of a product, need to scale, or don't need local files → +use managed agents. + +**Important limitation:** Managed agents cannot use MCP servers. If your agent +system relies on MCP servers for Slack, GitHub, databases, or other integrations, +use local deployment with Docker for isolation instead. + +## SDK patterns + +For concrete code patterns, see: +`${CLAUDE_PLUGIN_ROOT}/skills/managed-agents/references/api-patterns.md` + +## Session management + +Managed agents support persistent sessions via the API: + +```typescript +// Create a new session +const session = await client.agents.sessions.create({ + agent_id: "ag_...", + system_prompt: "You are a research agent..." +}); + +// Resume an existing session +const response = await client.agents.sessions.messages.create({ + agent_id: "ag_...", + session_id: session.id, + messages: [{ role: "user", content: "Continue the analysis" }] +}); +``` + +Sessions maintain conversation history and tool state across multiple +interactions, similar to `claude --resume` for local agents. + +## Budget and cost considerations + +Managed agents bill per token at standard API rates. For cost control: + +1. **Set max_tokens** on each request to cap output length +2. **Use prompt caching** — cached input tokens cost 90% less +3. **Batch non-urgent work** — batch API gives 50% discount +4. **Monitor with Admin API** — if you have org access, use + `/v1/organizations/usage_report/messages` with an Admin API key + (`sk-ant-admin...`) for detailed cost breakdowns +5. **Use `--max-budget-usd`** flag for local headless runs as a budget cap + +Note: The Usage & Cost API requires an Admin API key and organization account. +Individual accounts should estimate costs from token counts. + +## Migration path: local → managed + +1. Extract your agent's system prompt from `.claude/agents/[name].md` +2. Convert tool access to SDK tool definitions +3. Replace file-based memory with session persistence or external storage +4. Replace MCP server integrations with direct API calls in tool handlers +5. Test with the SDK before removing local deployment + +This is a significant architectural change. Only migrate if you need API-based +access or auto-scaling. Local deployment is simpler and cheaper for personal use. + +## Getting started + +For a guided setup: run `/agent-factory:build` and choose "Managed Agents" +as the deployment target in Phase 6. + +For manual setup: see the API patterns reference at +`${CLAUDE_PLUGIN_ROOT}/skills/managed-agents/references/api-patterns.md` diff --git a/skills/managed-agents/references/api-patterns.md b/skills/managed-agents/references/api-patterns.md new file mode 100644 index 0000000..8825879 --- /dev/null +++ b/skills/managed-agents/references/api-patterns.md @@ -0,0 +1,183 @@ +# Managed Agents API Patterns + +Code patterns for creating and managing agents via the Anthropic SDK. +All examples use `@anthropic-ai/sdk` (TypeScript) with Python equivalents noted. + +--- + +## Basic agent creation + +```typescript +import Anthropic from "@anthropic-ai/sdk"; + +const client = new Anthropic(); + +// Create an agent with tools +const response = await client.messages.create({ + model: "claude-sonnet-4-6", + max_tokens: 4096, + system: "You are a research agent. Produce structured briefs.", + tools: [ + { + name: "web_search", + description: "Search the web for information", + input_schema: { + type: "object", + properties: { + query: { type: "string", description: "Search query" } + }, + required: ["query"] + } + } + ], + messages: [ + { role: "user", content: "Research the latest Claude Code features" } + ] +}); +``` + +## Agent with persistent sessions + +```typescript +// Create a session-based agent +const session = await client.agents.sessions.create({ + agent_id: "ag_your_agent_id", + system_prompt: `You are a data analyst. You have access to the + company database via the query tool. Always verify your findings + before reporting.`, + tools: [/* your tool definitions */] +}); + +// First interaction +const result1 = await client.agents.sessions.messages.create({ + agent_id: "ag_your_agent_id", + session_id: session.id, + messages: [{ role: "user", content: "Analyze Q1 revenue trends" }] +}); + +// Continue the conversation (session remembers context) +const result2 = await client.agents.sessions.messages.create({ + agent_id: "ag_your_agent_id", + session_id: session.id, + messages: [{ role: "user", content: "Now compare with Q4 of last year" }] +}); +``` + +## Tool handling pattern + +```typescript +async function runAgentLoop( + client: Anthropic, + messages: Anthropic.MessageParam[], + tools: Anthropic.Tool[] +) { + let response = await client.messages.create({ + model: "claude-sonnet-4-6", + max_tokens: 4096, + tools, + messages + }); + + while (response.stop_reason === "tool_use") { + const toolUseBlocks = response.content.filter( + (b): b is Anthropic.ToolUseBlock => b.type === "tool_use" + ); + + const toolResults: Anthropic.ToolResultBlockParam[] = []; + for (const toolUse of toolUseBlocks) { + const result = await executeToolCall(toolUse.name, toolUse.input); + toolResults.push({ + type: "tool_result", + tool_use_id: toolUse.id, + content: JSON.stringify(result) + }); + } + + messages.push({ role: "assistant", content: response.content }); + messages.push({ role: "user", content: toolResults }); + + response = await client.messages.create({ + model: "claude-sonnet-4-6", + max_tokens: 4096, + tools, + messages + }); + } + + return response; +} +``` + +## Cost optimization with prompt caching + +```typescript +const response = await client.messages.create({ + model: "claude-sonnet-4-6", + max_tokens: 1024, + system: [ + { + type: "text", + text: longSystemPrompt, // cached — 90% cheaper on reuse + cache_control: { type: "ephemeral" } + } + ], + messages: [{ role: "user", content: userQuery }] +}); +``` + +## Error handling + +```typescript +try { + const response = await client.messages.create(/* ... */); +} catch (error) { + if (error instanceof Anthropic.RateLimitError) { + // Retry with exponential backoff + await sleep(retryDelay); + retryDelay *= 2; + } else if (error instanceof Anthropic.APIError) { + console.error(`API error ${error.status}: ${error.message}`); + // Log for debugging, don't retry on 4xx + } +} +``` + +## Python equivalent + +```python +import anthropic + +client = anthropic.Anthropic() + +response = client.messages.create( + model="claude-sonnet-4-6", + max_tokens=4096, + system="You are a research agent.", + messages=[{"role": "user", "content": "Research topic X"}] +) +``` + +## Deployment pattern: scheduled managed agent + +```typescript +// Run as a scheduled job (e.g., via cron or cloud scheduler) +async function dailyReport() { + const client = new Anthropic(); + + const response = await runAgentLoop( + client, + [{ role: "user", content: "Generate the daily status report" }], + reportTools + ); + + // Extract and save the report + const text = response.content + .filter((b): b is Anthropic.TextBlock => b.type === "text") + .map((b) => b.text) + .join("\n"); + + await saveReport(text); +} + +dailyReport().catch(console.error); +```