feat(skills): add managed-agents knowledge skill

This commit is contained in:
Kjell Tore Guttormsen 2026-04-12 06:44:39 +02:00
commit f3a3dcd997
2 changed files with 286 additions and 0 deletions

View file

@ -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`

View file

@ -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);
```