Strands harness: de um import a um agente em produção
Um harness é tudo o que cerca o modelo: ferramentas, contexto, memória, permissões. Este curso faz você rodar o do Strands, entender cada default e decidir quais mudar antes de colocá-lo para trabalhar.
- módulos
- 8
- exercícios
- 16
- projeto final
- 1
- Teoria: Curta, com um insight e um antipadrão por módulo.
- Exercícios: Dois por módulo. Rodam no seu terminal e têm um resultado verificável.
- Entregável: Uma checklist por módulo. Seu progresso fica salvo neste navegador.
- Projeto final: Um agente com sessão, memória, gate de aprovação e estado durável, com critérios mensuráveis.
Baseado na documentação oficial do Strands harness (strandsagents.com/docs/user-guide/harness). Cobre overview, quickstart, subagentes, sessões e memória, contexto e caching, interventions, produção e a referência de configuração. Skills, MCP, tarefas em segundo plano e ferramentas integradas ficam nos próximos passos. Nomes de modelos e versões vêm da doc em outubro de 2026: confirme antes de usar.
Módulo 1 · Fundamentos
O que é um harness e o que vem por padrão
Um agente é um modelo com ferramentas dentro de um loop. O harness é todo o resto: o prompt afinado, o gerenciamento de contexto, a memória, as ferramentas base. O Strands entrega isso montado em um único import.
Ao terminar: Rodar create_harness() sem argumentos, listar de memória seus defaults e localizar cada um na referência de configuração.
Um import, um agente pronto
create_harness() (createHarness() em TypeScript) devolve um Agent padrão do Strands. Não há wrapper nem abstração escondida: o que muda são os defaults com que ele vem montado.
O que vem por padrão
- Um modelo com raciocínio ativo. Amazon Bedrock é o provedor padrão.
- Um prompt afinado: explorar antes de agir, confirmar antes do irreversível, verificar antes de dar algo como concluído.
- Ferramentas de shell e arquivos (read, write, edit) e acesso web.
- Gerenciamento da janela de contexto e prompt caching.
- Memória de longo prazo e sessões que se retomam com um id.
- Um subagente generalist e um checklist de tarefas (todos).
- Agent Skills se existirem e chamada programática de ferramentas.
Opinativo, não restritivo
Cada default pode ser restringido, trocado ou desligado. Quando seu caso fica específico, você sobrescreve o que importa e deixa o resto. Se quiser construir seu próprio harness do zero, o Strands Harness SDK aceita a mesma configuração.
Referência de configuração
| Opção (Python) | TypeScript | Default | O que faz |
|---|---|---|---|
model | model | bedrock/global.anthropic.claude-opus-5 | Uma string provider/name, um id do Bedrock ou uma instância de Model. |
effort | effort | "auto" | Esforço de raciocínio: auto, low, medium, high ou off. Ignorado se você passar uma instância de Model. |
instructions | instructions | nenhum | Bloco de domínio acrescentado depois do contrato do harness. Ignorado se você passar um system prompt completo. |
tools | tools | nenhum | Suas ferramentas, somadas às integradas. |
plugins | plugins | nenhum | Plugins do SDK, somados aos plugins integrados. |
mcp_servers | mcpServers | nenhum | Servidores MCP para conectar: caminho de um JSON ou um mapping. |
builtin_tools | builtinTools | shell, read, write, edit, web_fetch, web_search, programmatic_tool_caller, subagent | Ferramentas integradas a habilitar, ou [] para nenhuma. |
builtin_tools={"web_fetch": {"model": ...}} | builtinTools: { web_fetch: { model } } | o do provedor | Modelo resumidor em que o web_fetch roda. |
caching | caching | "auto" (ativo) | Prompt caching onde o provedor suporta. Desligar desativa o que o harness configura. |
context_manager | contextManager | "auto" | auto, agentic ou off. Ativa o gerenciamento de contexto e o offloading. |
session={"id": ...} | session: { id } | nenhum | Persiste e retoma esta conversa por id. |
session={"dir": ...} | session: { dir } | ./.agent/sessions | Onde ficam o estado de sessão e os artefatos descarregados do contexto. |
skills | skills | ./.agent/skills | Diretório (ou lista) escaneado em busca de Agent Skills. Off desativa. |
builtin_plugins | builtinPlugins | ["todos", "environment"] | Plugins integrados a habilitar, ou [] para nenhum. |
memory | memory | ativa | Memória de longo prazo baseada em arquivos. Off desativa. |
memory={"dir": ...} | memory: { dir } | ./.agent/memory | Onde ficam os arquivos do store de memória padrão. |
memory={"stores": [...]} | memory: { stores } | nenhum | Troca o backend de memória e mantém a política do harness. |
interventions | interventions | nenhum | Coloca as chamadas de ferramentas atrás de aprovação ou de uma política. |
background_tasks | backgroundTasks | { agentic: ['*'] } | Política de tarefas em segundo plano. |
Como o resultado é um Agent comum, tudo o que você sabe do SDK (hooks, streaming, observabilidade) continua valendo. O harness é uma fábrica de configuração, não um framework à parte.
Tratá-lo como caixa-preta. Os defaults escrevem em disco (./.agent), podem executar shell, editar arquivos e usam Claude Opus 5 no Bedrock. Leia-os antes de colocá-lo diante de dados reais.
Primeiro run sem configurar nada
- Crie um ambiente com Python 3.10 ou superior e instale strands-harness.
- Dê credenciais do Bedrock (
AWS_BEARER_TOKEN_BEDROCKou credenciais AWS) e habilite o modelo no console do Bedrock. - Rode o código e espere terminar.
- Veja o que apareceu no diretório de trabalho.
# pip install strands-harness
from strands_harness import create_harness
agent = create_harness()
agent("Research the top three vector databases, compare pricing and limits, and write it up in comparison.md")import { createHarness } from '@strands-agents/harness'
const agent = await createHarness()
await agent.invoke("Research the top three vector databases, compare pricing and limits, and write it up in comparison.md") Como saber que deu certo: Existe comparison.md escrito pelo agente e uma pasta ./.agent/sessions. A pasta ./.agent/memory aparece quando a extração de memória roda (a cada poucos turnos). Se não tem Bedrock, use model="anthropic/claude-sonnet-5" e siga para o módulo 2.
Mapa de defaults contra a referência
- Abra a tabela de referência de configuração deste módulo.
- Em um arquivo defaults.md, escreva para cada linha de "o que vem por padrão" qual opção a controla e como se desliga.
- Confira sua tabela montando um agente com tudo o que dá para desligar desligado.
- Peça para criar um arquivo e observe o que responde.
from strands_harness import create_harness
agent = create_harness(
builtin_tools=[], # no shell, files, web, subagent
builtin_plugins=[], # no todos, no environment
memory=False,
session=False,
context_manager=False,
caching=False,
)
agent("Create a file named hello.txt with the word hi.")import { createHarness } from '@strands-agents/harness'
const agent = await createHarness({
builtinTools: [],
builtinPlugins: [],
memory: false,
contextManager: false,
caching: false,
// session off: see the "Persist sessions" page for your version
})
await agent.invoke('Create a file named hello.txt with the word hi.') Como saber que deu certo: Sem ferramentas integradas o agente não consegue criar hello.txt: só conversa. Depois religue uma opção por vez e veja qual habilita o quê.
Revisão
Responda em voz alta antes de abrir a resposta.
O que create_harness() devolve?
Um Agent padrão do Strands, sem wrapper nem abstração oculta.
Cite quatro coisas que o agente tem sem você pedir.
Por exemplo: shell e ferramentas de arquivos, acesso web, memória de longo prazo, gerenciamento de contexto, o subagente generalist e o checklist todos.
Entregável do módulo
Vai para o projeto final: Guarde o defaults.md. No projeto final você decide quais defaults mantém e quais muda, e justifica no README.
Módulo 2 · Fundamentos
Quickstart: CLI, biblioteca e escolha de modelo
Há três formas de começar: pedir ao seu assistente de código que te guie, montar o agente na CLI ou usá-lo como biblioteca. As três terminam no mesmo agente.
Ao terminar: Rodar o mesmo agente pela CLI e por código, trocar de provedor com uma linha e retomar uma conversa por id.
Três caminhos
- Com seu agente de código: cole o prompt da doc no Codex, Claude Code ou Kiro e ele te guia passo a passo.
- CLI:
npm install -g @strands-agents/cli, execute strands e escolha Quickstart. A única coisa que você configura é o provedor de modelo. - Biblioteca:
pip install strands-harness(Python 3.10 ou superior) ou o pacote@strands-agents/harnessem TypeScript.
Escolher modelo
O modelo é passado como provider/name. Amazon Bedrock é o padrão. A doc lista Anthropic, OpenAI, Google e Ollama para rodar localmente. Na CLI, strands --model anthropic/claude-sonnet-5 troca o modelo só para essa execução.
Da CLI ao código
Quando quiser embutir o agente, /export dentro do chat da CLI escreve um projeto Python ou TypeScript com suas escolhas colocadas em create_harness(...). A CLI é uma rampa: você constrói de forma interativa e depois passa para código.
Sessões desde o primeiro dia
As sessões vêm ativas: cada conversa é salva em ./.agent/sessions com um id gerado. Se você escolher o id, uma execução posterior retoma a mesma conversa. Na CLI: strands --session-id api-design.
Com o Ollama o modelo roda na sua máquina e você não precisa de credenciais de nuvem: é a forma mais barata de iterar a configuração. Um modelo pequeno pode falhar mais ao usar ferramentas, então teste com sua tarefa real antes de confiar nele.
Colar uma API key dentro do código. A CLI guarda uma key colada só para a sessão atual; para reutilizá-la, coloque no perfil do seu shell. No código, leia do ambiente.
Mesmo agente, três provedores
- Coloque a key de cada provedor no ambiente e rode o Ollama com ollama pull llama3.1.
- Rode o script: usa a mesma tarefa com três modelos e salva um arquivo por modelo.
- Anote em uma linha qual usou melhor as ferramentas e qual serve para iterar barato.
from strands_harness import create_harness
MODELS = ["anthropic/claude-sonnet-5", "openai/gpt-5.4", "ollama/llama3.1"]
for model in MODELS:
name = model.split("/")[0]
try:
agent = create_harness(model=model, session=False)
agent(
"Research the three most common strategies for versioning a REST API, "
f"compare their tradeoffs, and write a recommendation to api-versioning-{name}.md"
)
except Exception as err:
print(f"{model} failed: {err}")import { createHarness } from '@strands-agents/harness'
const models = ['anthropic/claude-sonnet-5', 'openai/gpt-5.4', 'ollama/llama3.1']
for (const model of models) {
const name = model.split('/')[0]
try {
const agent = await createHarness({ model })
await agent.invoke('Research the three most common strategies for versioning a REST API, compare their tradeoffs, and write a recommendation to api-versioning-' + name + '.md')
} catch (err) {
console.error(model + ' failed:', err)
}
} Como saber que deu certo: Você tem um arquivo api-versioning-*.md para cada provedor que funcionou. Se um falhou, a mensagem diz qual credencial falta.
CLI, export e sessão retomável
- Instale a CLI e execute strands. Escolha Quickstart, um provedor e um modelo, e Save and Launch.
- Saia e entre de novo com um id escolhido por você. Peça uma tarefa curta.
- Feche, reabra com o mesmo id e pergunte "o que eu te pedi antes?".
- Dentro do chat execute
/exporte escolha Python. Abra o projeto e procurecreate_harness(...).
npm install -g @strands-agents/cli
strands # setup, then chat
strands --model anthropic/claude-sonnet-5
strands --session-id curso-m2 # resume by id Como saber que deu certo: Ao reabrir com o mesmo id o agente lembra da conversa. O projeto exportado importa create_harness e traz suas opções.
Revisão
Responda em voz alta antes de abrir a resposta.
Qual é o formato do argumento model e qual é o padrão?
provider/name, por exemplo anthropic/claude-sonnet-5. O padrão é bedrock/global.anthropic.claude-opus-5.
O que /export faz na CLI?
Escreve um projeto Python ou TypeScript com suas escolhas em create_harness(...), com um agent pronto para importar.
Entregável do módulo
Vai para o projeto final: Escolha o modelo e o provedor do projeto final e anote no README junto com a variável de ambiente necessária.
Módulo 3 · Configurar o agente
Instruções, ferramentas e subagentes
O harness traz um prompt afinado e ferramentas base. Seu trabalho é adicionar o contexto do seu domínio, suas próprias ferramentas e, se preciso, especialistas para delegar.
Ao terminar: Adicionar instructions, somar um especialista com as_tool() e usar o subagente generalist para não inundar o contexto principal.
instructions não substitui o prompt
O parâmetro instructions adiciona um bloco de domínio depois do contrato do harness (explorar antes de agir, confirmar antes do irreversível, verificar antes de terminar). Se você passar um system prompt completo, instructions é ignorado e você perde esse contrato.
Suas ferramentas convivem com as integradas
tools soma as suas a shell, read, write, edit, web_fetch, web_search, programmatic_tool_caller e subagent. builtin_tools escolhe quais integradas ficam: uma lista vazia não deixa nenhuma.
Subagentes: delegar para proteger o contexto
Um subagente é um agente que o principal chama como uma ferramenta. Seu trabalho intermediário fica fora da conversa principal: só volta a resposta final.
- Seus especialistas: um Agent com name, description e
system_prompt, passado comas_tool(). Cada chamada começa do zero, sem estado acumulado, e o nome deve ser único entre as ferramentas. - generalist: vem ativo. Herda modelo, raciocínio, caching, contexto, ferramentas, plugins, subagentes, interventions e sandbox, mas usa um prompt de papel genérico em vez das suas instructions. Começa em branco: a chamada precisa levar tudo o que ele precisa.
Quando delegar
Quando uma subtarefa inundaria o contexto: buscar em muitos arquivos, uma mudança de vários passos ou uma exploração aberta em que só importa a conclusão. O generalist roda em segundo plano, então o agente principal pode continuar trabalhando.
Um subagente herda interventions e sandbox. Por design ele não pode ser uma porta dos fundos para pular a aprovação que você colocou no agente principal.
Um especialista com descrição vaga. O modelo decide quando chamar uma ferramenta pelo nome e pela descrição: "helper" não diz nada. Escreva o que faz, o que recebe e o que devolve.
Um especialista com nome e descrição
- Defina um Agent researcher com name, description e
system_prompt. - Passe com
as_tool()e some um bloco instructions que diga quando delegar. - Peça uma tarefa que exija pesquisar e redigir e veja a chamada a researcher na saída.
- Experimento: troque a description por "helper" e repita.
from strands import Agent
from strands_harness import create_harness
researcher = Agent(
name="researcher",
description="Researches a topic and returns a concise, sourced summary.",
system_prompt="Research the given topic and return a concise, sourced summary.",
)
agent = create_harness(
instructions=(
"You write short technical briefs. "
"Delegate research to the researcher tool, then write the brief to brief.md."
),
tools=[researcher.as_tool()],
)
agent("Brief me on prompt caching in LLM APIs, in under 300 words.")import { Agent } from '@strands-agents/sdk'
import { createHarness } from '@strands-agents/harness'
const researcher = new Agent({
name: 'researcher',
description: 'Researches a topic and returns a concise, sourced summary.',
systemPrompt: 'Research the given topic and return a concise, sourced summary.',
})
const agent = await createHarness({
instructions: 'You write short technical briefs. Delegate research to the researcher tool, then write the brief to brief.md.',
tools: [researcher.asTool()],
})
await agent.invoke('Brief me on prompt caching in LLM APIs, in under 300 words.') Como saber que deu certo: brief.md foi criado e você viu a chamada à ferramenta researcher. Com a descrição vaga, anote se o agente deixa de delegar ou delega pior.
Generalist: delegar para não inundar o contexto
- Coloque uma pasta
./reposcom vários README.md (podem ser repos seus ou cópias). - Rode o script: a mesma tarefa com o generalist ativo e com
builtin_tools={"subagent": False}. Cada rodada usa seu próprio diretório de sessão. - Compare o tamanho de cada diretório de sessão e anote o que viu na saída.
from strands_harness import create_harness
TASK = "Read every README.md under ./repos, then write overview.md with one line per project."
with_delegate = create_harness(
session={"id": "m3-with", "dir": "./.s-with"},
)
without_delegate = create_harness(
session={"id": "m3-without", "dir": "./.s-without"},
builtin_tools={"subagent": False},
)
with_delegate(TASK)
without_delegate(TASK)
# then, in the terminal: du -sh ./.s-with ./.s-without Como saber que deu certo: Existem dois overview.md e dois diretórios de sessão. É esperável que a rodada sem delegado deixe mais conteúdo na conversa principal; se não vir, anote por quê (por exemplo, o offloader moveu resultados volumosos).
Revisão
Responda em voz alta antes de abrir a resposta.
O que acontece com instructions se você passar system_prompt?
É ignorado: system_prompt substitui o prompt que o harness monta e você perde o contrato dele.
O que o generalist herda e o que não herda?
Herda modelo, raciocínio, caching, contexto, ferramentas, plugins, subagentes, interventions e sandbox. Não herda suas instructions (usa um prompt de papel genérico) nem a conversa (começa em branco).
Entregável do módulo
Vai para o projeto final: O projeto final leva um especialista próprio (por exemplo um verificador de fontes) e um bloco instructions com as regras do seu domínio.
Módulo 4 · Configurar o agente
Estado: sessões, checkpoints e memória
Um agente pode lembrar de duas maneiras diferentes e elas não são a mesma coisa. Uma sessão retoma uma conversa exata. A memória leva fatos duráveis entre conversas. Confundi-las produz agentes que esquecem o que não deviam ou arrastam o que não cabe.
Ao terminar: Decidir entre sessão, memória ou ambas, e demonstrar com uma rodada que sobrevive a um reinício e outra que não deixa rastro.
Duas perguntas distintas
Uma sessão (checkpoint) persiste uma conversa para retomar aquela tarefa exata depois de um reinício. A memória de longo prazo destila fatos duráveis e os recupera em qualquer conversa, com ou sem sessão. São ortogonais: você pode rodar com nenhuma, uma ou ambas. Por padrão as duas estão ativas, mas você só retoma uma conversa se der um id de sessão.
- Retomar uma tarefa depois de um reinício: uma sessão, com
session={"id": ...}. - Levar fatos, preferências ou decisões entre rodadas não relacionadas: memória, que já vem ativa.
- As duas coisas: id de sessão e memória ligada.
- Uma tarefa única sem rastro: session=False e memory=False.
Como a memória funciona
O harness destila fatos duráveis em arquivos sob ./.agent/memory, busca antes de cada turno e soma os melhores resultados ao contexto. O agente também recebe uma ferramenta search_memory para lembrar sob demanda. A extração roda em segundo plano a cada poucos turnos com um modelo pequeno, então mantê-la custa pouco.
Backends: o que vem e o que não
LocalFileStorage(padrão, escritas atômicas),S3Storage(produção e multi-instância) eInMemoryStorage(testes).- Um backend próprio implementa quatro métodos async: write, read, delete e list.
- SQLite e PostgreSQL não são first-party: você escreve contra Storage ou
MemoryStore. Redis ou Valkey só existem via o pacote comunitáriostrands-valkey-session-manager(Python).
Concorrência, isolamento e exclusão
- Um único escritor por conversa. Os managers não tomam um lock distribuído: duas invocações no mesmo id se sobrescrevem e nenhuma falha (vence a última escrita). Se abrir em paralelo, adicione seu lock ou roteie cada id para um único worker.
- Isolamento por namespace e por store com escopo: a memória admite um store por tenant em vez de um compartilhado.
- Exclusão: apagar uma sessão remove seu diretório raiz (ou o prefixo no S3, que precisa de
s3:DeleteObject). A memória são arquivos simples: apagar a de um tenant é apagar seu diretório ou store. - O diretório de sessão é um store confiável: restrinja suas permissões ao processo do agente. O SDK não bloqueia symlinks dentro.
A sessão serve para continuar uma tarefa; a memória, para que o conhecimento sobreviva à tarefa. Se você se pergunta "quero isso amanhã em outra conversa?", a resposta decide qual usar.
Dois workers sobre o mesmo id de sessão. Não há erro: o segundo sobrescreve os turnos do primeiro e você descobre quando a conversa já está quebrada.
Retomar após um reinício
- Rode o primeiro bloco com o id m4-api e deixe terminar.
- Encerre o processo por completo. Esse é o reinício.
- Em um processo novo rode o segundo bloco com o mesmo id.
- Veja
./.agent/sessionspara ver onde ficou o estado.
# run 1
from strands_harness import create_harness
agent = create_harness(session={"id": "m4-api"})
agent("List three strategies for versioning a REST API.")
# run 2, in a brand new process
agent = create_harness(session={"id": "m4-api"})
agent("Which of those would you pick for an API with external customers, and why?")// run 1
import { createHarness } from '@strands-agents/harness'
const agent = await createHarness({ session: { id: 'm4-api' } })
await agent.invoke('List three strategies for versioning a REST API.')
// run 2, in a brand new process
const again = await createHarness({ session: { id: 'm4-api' } })
await again.invoke('Which of those would you pick for an API with external customers, and why?') Como saber que deu certo: A segunda resposta fala das três estratégias sem que você as repita.
Memória entre conversas não relacionadas
- Na conversa A, peça que lembre de um dado e faça mais três ou quatro turnos para dar tempo à extração.
- Na conversa B (outro id de sessão) pergunte por esse dado.
- Na conversa C, com memory=False, faça a mesma pergunta.
- Veja os arquivos de
./.agent/memory.
from strands_harness import create_harness
a = create_harness(session={"id": "m4-a"})
for turn in [
"Remember: my stack is FastAPI and Postgres, and I prefer answers as short tables.",
"Give me one tip about queues.",
"Give me one tip about caching.",
"Give me one tip about retries.",
]:
a(turn)
b = create_harness(session={"id": "m4-b"}) # new conversation
b("What stack do I use?")
c = create_harness(session={"id": "m4-c"}, memory=False) # memory off
c("What stack do I use?") Como saber que deu certo: B responde com o stack e C não. Se B não sabe, pode ser que a extração em segundo plano ainda não tenha rodado: some turnos em A e veja ./.agent/memory.
Revisão
Responda em voz alta antes de abrir a resposta.
Qual a diferença entre uma sessão e a memória?
A sessão persiste uma conversa para retomá-la por id. A memória destila fatos duráveis e os recupera em qualquer conversa.
O que acontece se dois processos escrevem na mesma sessão?
Sobrescrevem-se: não há lock distribuído e vence a última escrita, sem erro. Use um escritor por id.
Entregável do módulo
Vai para o projeto final: O projeto final usa um id de sessão por tarefa e deixa a memória ligada com seu diretório em armazenamento durável. Anote no README quem escreve cada id.
Módulo 5 · Configurar o agente
Contexto e caching
Um modelo só lê um trecho limitado de texto por vez. O harness mantém a conversa dentro desse limite e reaproveita o que não muda entre turnos, para que cada passo seja mais barato e rápido.
Ao terminar: Entender o que context_manager e caching fazem, mudar seu modo e saber onde terminam os resultados volumosos.
Gerenciamento de contexto
Com context_manager ativo, o harness resume os turnos antigos conforme a conversa cresce e adiciona um offloader: os resultados volumosos de ferramentas vão para armazenamento e são trocados por uma prévia curta e uma referência que o agente pode seguir para recuperar o conteúdo completo quando realmente precisar.
- auto (padrão) e agentic escolhem a estratégia de contexto do SDK. Ambos deixam o offloader ativo.
- Desligar (False ou null, ou off na CLI) desliga também o offloading: o histórico completo fica na janela e o tamanho é com você.
- Com uma sessão ativa, os artefatos descarregados persistem sob o diretório de sessão. Sem sessão vão para um diretório temporário que não sobrevive ao processo.
Prompt caching
Reaproveita o que não muda entre turnos (system prompt, definições de ferramentas e conversa anterior), assim o prefixo estável de uma conversa longa é processado mais barato e rápido. Está ativo por padrão.
- No Bedrock e no Anthropic direto, o harness configura pontos de cache e definições de ferramentas em cache.
- Na OpenAI, Google e bedrock-mantle o caching é automático no lado do servidor: não há nada para configurar.
- Desligar caching não tem efeito onde já é automático. Habilitá-lo explicitamente em uma instância de Model já construída é ignorado com um aviso: configure na própria instância.
O offloader muda o que o modelo vê, não o que se perde: o conteúdo completo continua guardado e o agente pode recuperá-lo. Por isso convém ter uma sessão ativa se quiser auditar esses artefatos depois.
Desligar context_manager "para ver tudo" em uma tarefa longa. Sem ele o histórico completo fica na janela e o limite é com você: a tarefa pode bater nesse limite.
O que o modelo vê e o que fica guardado
- Rode uma tarefa que gere resultados volumosos (páginas longas), uma vez com o contexto gerenciado e outra com
context_manager=False. Cada rodada usa seu próprio diretório de sessão. - Procure em cada diretório um subdiretório de contexto: a doc indica que o stash do context manager vive sob context/.
- Anote o que contém e quanto pesa cada diretório.
from strands_harness import create_harness
TASK = "Fetch three long documentation pages about HTTP caching and write a comparison to caching.md"
managed = create_harness(session={"id": "m5-on", "dir": "./.s-on"})
unmanaged = create_harness(session={"id": "m5-off", "dir": "./.s-off"}, context_manager=False)
managed(TASK)
unmanaged(TASK)
# terminal: find ./.s-on ./.s-off -type d -name 'context*' ; du -sh ./.s-on ./.s-off Como saber que deu certo: Em ./.s-on deveria haver um diretório de contexto com artefatos descarregados e em ./.s-off não. Se sua tarefa não produziu resultados grandes o bastante, não haverá offloading: tente com páginas mais longas.
Caching: o que o harness configura e o que não
- Para cada provedor que testou no módulo 2, anote se o caching é configurado pelo harness (Bedrock, Anthropic) ou é automático (OpenAI, Google, bedrock-mantle).
- Rode a mesma conversa de vários turnos com caching padrão e com caching=False e compare o tempo total.
- Se seu provedor reporta tokens em cache, anote esse número.
import time
from strands_harness import create_harness
TURNS = [
"Explain HTTP caching in 3 bullets.",
"Now ETag versus Last-Modified.",
"Now the Cache-Control directives.",
"Summarize our whole chat in 2 lines.",
]
for label, extra in [("caching auto", {}), ("caching off", {"caching": False})]:
agent = create_harness(session=False, **extra)
start = time.time()
for turn in TURNS:
agent(turn)
print(label, round(time.time() - start, 1), "s") Como saber que deu certo: Você tem dois tempos e uma nota por provedor. Não espere uma diferença fixa: depende do provedor e do tamanho do prefixo. O que importa é saber quem configura o caching no seu caso.
Revisão
Responda em voz alta antes de abrir a resposta.
O que o offloader faz?
Move resultados volumosos de ferramentas para armazenamento e os troca por uma prévia curta com uma referência para recuperá-los.
O que acontece com context_manager=False?
O offloading também é desligado: o histórico completo fica na janela e você gerencia o tamanho.
Entregável do módulo
Vai para o projeto final: O projeto final deixa context_manager em auto, a sessão com diretório durável (os artefatos vivem lá) e documenta no README se o caching é configurado pelo harness ou pelo provedor.
Módulo 6 · Controle e produção
Interventions: colocar um gate nas ferramentas
Por padrão toda chamada de ferramenta é executada. Uma intervention decide se uma chamada roda, e é a forma documentada de colocar aprovação humana ou uma política na frente do shell e da edição de arquivos.
Ao terminar: Controlar chamadas com os presets ask e smart, com uma regra em linguagem natural e com uma política Cedar, e comprovar que o subagente não pula o gate.
Quatro formas de controlar
"ask": pede aprovação em cada chamada."smart": usa o classificador de risco do SDK e só barra as chamadas arriscadas.- Uma string que não é preset nem termina em .cedar é uma política de risco em linguagem natural: vira o prompt do classificador, como smart com sua própria régua.
- Uma string que termina em .cedar carrega uma política Cedar (requer
strands-agents[cedar]em Python ou@cedar-policy/cedar-wasmem TypeScript). O texto Cedar inline não é autodetectado: passe uma instância deCedarAuthorization.
Camadas e handlers próprios
Para o que os presets não cobrem, construa o handler do SDK e passe: uma instância passa sem alteração. Também pode passar uma lista para combinar, por exemplo uma política Cedar com uma comporta de aprovação humana. Um agente registra no máximo um handler por nome, então combine tipos diferentes, não duplicados.
Pausar e retomar
Quando um gate pede aprovação, a execução é interrompida. Com o Agent do SDK o padrão é: se result.stop_reason for "interrupt", você responde com interruptResponse e o id da interrupção para retomar. Como o harness devolve um Agent padrão, confirme na sua versão como isso é exposto antes de construir uma interface em cima.
O generalist herda a política
O subagente integrado herda o que você colocar em interventions, então não pode pular o gate do agente principal.
Uma regra em linguagem natural é flexível, mas quem avalia é um modelo: serve para barrar o duvidoso, não para garantir o impossível. O que nunca deve acontecer vai para uma política Cedar ou uma sandbox, que são determinísticas.
Deixar interventions sem configurar em um agente com shell e edição de arquivos que recebe entrada de terceiros. O padrão não aplica nenhuma: toda chamada prossegue.
Presets e retomada
- Crie um agente com interventions=
"ask"e peça para criar e apagar um arquivo. - Quando interromper, aprove a primeira chamada e negue a de exclusão.
- Repita com
"smart"e anote quais chamadas barrou e quais deixou passar.
from strands_harness import create_harness
agent = create_harness(interventions="ask") # then try "smart"
result = agent("Create temp.txt with the word hi, then delete it.")
# pattern from the SDK human-in-the-loop docs: verify it on your version
while result.stop_reason == "interrupt":
interrupt = result.interrupts[0]
print("Approval needed:", interrupt)
answer = input("approve? (yes/no) ")
result = agent([{"interruptResponse": {"interruptId": interrupt.id, "response": answer}}]) Como saber que deu certo: Com ask, cada ferramenta pede aprovação. Se responder "no" à exclusão, temp.txt continua existindo. Com smart anote o que observa: o classificador é um modelo e pode decidir diferente a cada rodada.
Regra natural e gate herdado
- Crie um agente com a regra "Ask before deleting files or making any network request."
- Peça algo que exija a web e verifique se
result.stop_reasoné"interrupt". - Peça uma tarefa que delegue ao generalist (ler muitos arquivos e depois apagar um) e verifique se a exclusão também é barrada.
- Escreva uma ação que você gostaria de bloquear sempre e por que merece Cedar ou uma sandbox em vez de linguagem natural.
from strands_harness import create_harness
agent = create_harness(
interventions="Ask before deleting files or making any network request.",
)
result = agent("Search the web for the latest Python release and write it to python.txt")
print(result.stop_reason) # expect "interrupt" when a network call is gatedimport { createHarness } from '@strands-agents/harness'
const agent = await createHarness({
interventions: 'Ask before deleting files or making any network request.',
})
const result = await agent.invoke('Search the web for the latest Python release and write it to python.txt')
console.log(result.stopReason) // expect "interrupt" when a network call is gated Como saber que deu certo: stop_reason é "interrupt" quando se tenta uma chamada de rede. No ponto 3 a exclusão dentro da delegação também pede aprovação, porque o generalist herda a política.
Revisão
Responda em voz alta antes de abrir a resposta.
Qual é o padrão de interventions?
Nenhum: toda chamada de ferramenta é executada.
Quando uma regra em linguagem natural e quando Cedar?
A regra em linguagem natural é avaliada por um modelo: serve para escalar o duvidoso. Cedar é uma política programática: vai para o que nunca deve acontecer.
Entregável do módulo
Vai para o projeto final: O projeto final leva um gate: uma regra em linguagem natural para o duvidoso e, se seu ambiente permitir, um .cedar para o proibido. O critério mensurável é que uma ação destrutiva negada não seja executada.
Módulo 7 · Controle e produção
Produção: o que escreve, o que executa e onde vive o estado
O harness devolve um Agent normal, então implantá-lo, observá-lo e protegê-lo segue os guias do SDK. O específico do harness são seus defaults: escrevem em disco e podem executar código.
Ao terminar: Fazer o inventário de riscos dos defaults, mover o estado para armazenamento durável e empacotar o agente em um contêiner.
Implantar: igual a qualquer Agent
Todos os destinos dos guias de implantação do SDK (Lambda, Fargate, EKS, Bedrock AgentCore, Docker e mais) funcionam sem mudanças: onde o guia constrói Agent(), você constrói create_harness().
Estado sobre armazenamento efêmero
Sessões e memória escrevem por padrão em diretórios sob ./.agent. Em um contêiner ou em serverless, aponte session={"dir": ...} e memory={"dir": ...} para armazenamento durável (um volume montado) ou para um backend próprio, via memory={"stores": [...]} ou um session manager, para que o estado sobreviva a uma única instância.
Observar
É um Agent padrão: a telemetria do SDK (traces, métricas e logs) funciona sem mudanças e não há nada do harness para conectar. Para medir qualidade, o Evals SDK testa e pontua rodadas do harness como as de qualquer agente.
Dois defaults que pedem uma decisão de segurança
programmatic_tool_callerexecuta código escrito pelo modelo no Monty: isola o código, mas não as ferramentas que esse código chama. Se o agente lida com entrada não confiável, rode-o dentro de uma sandbox do SDK ou remova a ferramenta.- O agente padrão pode executar comandos e editar arquivos: controle o que faz com interventions e limite o que alcança com uma sandbox.
- Os guias de segurança do SDK (guardrails, redação de PII, histórico de mensagens confiável) aplicam-se diretamente.
Uma lista de permitidos é mais fácil de auditar que uma lista de bloqueio: em vez de se perguntar o que desligar, você declara com builtin_tools exatamente quais ferramentas existem.
Implantar em um contêiner sem montar os diretórios de sessão e memória. Tudo funciona até o primeiro redeploy: aí o agente perde a conversa e o que aprendeu.
Inventário de superfície de risco
- Para cada ferramenta integrada (shell, read, write, edit,
web_fetch,web_search,programmatic_tool_caller, subagent) escreva que dano poderia fazer com entrada não confiável. - Decida quais ficam e monte o agente com essa lista em
builtin_tools. - Peça algo fora da lista e verifique que não consegue fazer.
from strands_harness import create_harness
# example allowlist for a research agent that must not touch the filesystem
agent = create_harness(
builtin_tools=["web_search", "web_fetch"],
builtin_plugins=["todos"],
interventions="smart",
)
agent("Write a file named x.txt with the word hi.") # it should be unable to Como saber que deu certo: O agente responde que não pode escrever arquivos. Sua tabela de riscos explica por que cada ferramenta que você deixou é necessária.
Contêiner com estado durável
- Crie os três arquivos: agent.py, Dockerfile e compose.yaml. O estado vive no volume /data.
- Rode uma tarefa com um
SESSION_IDfixo. - Destrua o contêiner com
docker compose down. O volume é conservado. - Rode outra tarefa com o mesmo
SESSION_IDe verifique que retoma.
# agent.py
import os
import sys
from strands_harness import create_harness
agent = create_harness(
model=os.environ.get("HARNESS_MODEL", "anthropic/claude-sonnet-5"),
session={"id": os.environ["SESSION_ID"], "dir": "/data/sessions"},
memory={"dir": "/data/memory"},
)
agent(sys.argv[1])FROM python:3.12-slim
WORKDIR /app
RUN pip install --no-cache-dir strands-harness
COPY agent.py .
ENTRYPOINT ["python", "agent.py"]services:
agent:
build: .
environment:
- SESSION_ID=${SESSION_ID:-demo}
- HARNESS_MODEL=${HARNESS_MODEL:-anthropic/claude-sonnet-5}
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} # use the variable your provider expects
volumes:
- agent-data:/data
volumes:
agent-data:docker compose run --rm agent "List three API versioning strategies."
docker compose down # the volume stays
docker compose run --rm agent "Which of those would you pick, and why?"
# to prove the volume matters: docker compose down -v and repeat Como saber que deu certo: A segunda rodada retoma a conversa mesmo que o contêiner original não exista mais. Com docker compose down -v (que apaga o volume) começa do zero: essa é a prova de que o estado vive no volume.
Revisão
Responda em voz alta antes de abrir a resposta.
Por que mover session e memory para fora de ./.agent em um contêiner?
Porque por padrão escrevem em ./.agent, e em um contêiner ou em serverless isso é armazenamento efêmero: o estado se perde com a instância.
O que o Monty isola e o que não?
Isola o código escrito pelo modelo, mas não as ferramentas que esse código chama.
Entregável do módulo
Vai para o projeto final: O projeto final é entregue como compose.yaml com um volume para sessão e memória, uma lista de permitidos de ferramentas e o README com os riscos que você aceitou.
Módulo 8 · Além dos defaults
Sair dos defaults: descer ao SDK
O harness é uma fábrica com opiniões. Quando uma opção não basta, qualquer argumento que a fábrica não nomeia passa para o construtor do Agent, e você pode descer ao SDK sem perder a configuração que já tem.
Ao terminar: Saber o que é sobrescrito com um argumento passado ao Agent, quais peças o harness exporta e quando convém construir seu próprio harness com o SDK.
Passthrough ao Agent
Todo argumento que a fábrica não nomeia é repassado ao construtor de Agent. Um valor explícito passado assim vence o default correspondente: passar system_prompt substitui o prompt que o harness monta a partir de instructions, e passar memory_manager substitui memory. Em TypeScript, HarnessAgentOptions estende AgentConfig do SDK (menos os campos que o harness gerencia), então outros campos como retryStrategy passam como estão.
Peças exportadas
Além da fábrica, o harness exporta o que compõe: HARNESS_CONTRACT, build_system_prompt (buildSystemPrompt em TypeScript), resolve_memory e resolve_interventions (só Python). Servem para inspecionar ou recompor em vez de reescrever do zero.
Que nível de override usar
- Muda um parâmetro: um argumento de
create_harness. - Muda o prompt completo:
system_prompt, sabendo que você perde o contrato e que instructions é ignorado. - Muda o backend de memória:
memory={"stores": [...]}mantém a política do harness;memory_managera substitui. - Muda o backend de sessão: passe seu próprio session manager, que vence o que o harness monta a partir de session.
- Muda tudo: o Strands Harness SDK direto, com a mesma configuração que você já conhece.
Escolha o nível mais baixo que resolva seu problema. memory={"stores": [...]} muda onde se guarda e mantém a política do harness; memory_manager substitui a política inteira.
Passar system_prompt para "adicionar uma regra". Para somar contexto de domínio existe instructions: com system_prompt você substitui tudo e perde o contrato de explorar, confirmar e verificar.
instructions contra system_prompt
- Crie um arquivo notes.txt na pasta de trabalho.
- Monte dois agentes com a mesma regra: um com instructions e outro com
system_prompt. - Peça a cada um que apague notes.txt (restaure o arquivo entre as rodadas) e anote se o agente confirma antes de agir.
from strands_harness import create_harness
RULE = "Always answer in exactly two sentences."
a = create_harness(instructions=RULE, session=False) # keeps the harness contract
b = create_harness(system_prompt=RULE, session=False) # replaces it entirely
for agent in (a, b):
agent("Delete notes.txt from the current folder.") Como saber que deu certo: A segue o contrato do harness (confirmar antes do irreversível); B não o tem mais. Se ambos se comportarem igual, anote: o modelo pode confirmar por conta própria, mas com system_prompt já não há garantia de contrato.
Ler o contrato e trocar um store
- Importe
HARNESS_CONTRACTe imprima. Sublinhe as regras que reconhece do seu defaults.md. - Mude só o diretório de memória com
memory={"dir": ...}. - Depois de alguns turnos, verifique que os arquivos de memória aparecem no diretório novo e não em
./.agent/memory. - Escreva em que caso usaria stores em vez de dir.
# if the import path differs in your version, see "Compose with the Strands Harness SDK"
from strands_harness import create_harness, HARNESS_CONTRACT
print(HARNESS_CONTRACT)
agent = create_harness(memory={"dir": "./my-memory"}, session={"id": "m8"})
for turn in [
"Remember that my deploy target is a single VPS with Docker Compose.",
"Give me one tip about volumes.",
"Give me one tip about backups.",
]:
agent(turn)
# terminal: ls ./my-memory Como saber que deu certo: Conseguiu ler o contrato que o harness adiciona e viu que ./my-memory se enche no lugar de ./.agent/memory (pode demorar alguns turnos pela extração em segundo plano).
Revisão
Responda em voz alta antes de abrir a resposta.
O que vence: um argumento passado ao Agent ou o default do harness?
O argumento explícito. Passar system_prompt substitui o prompt montado a partir de instructions, e passar memory_manager substitui memory.
Quando convém descer ao SDK?
Quando várias peças estruturais mudam ou você quer seu próprio harness do zero. Se muda uma só peça, o passthrough basta.
Entregável do módulo
Vai para o projeto final: No README do projeto final, uma seção "Overrides" lista cada parâmetro que você mudou em relação ao default e o motivo.
Projeto final
Um agente de pesquisa com guarda-corpos
Você monta um agente que pesquisa, escreve um relatório e faz isso de forma auditável: retoma sua conversa, lembra o que importa, pede permissão para o destrutivo e conserva seu estado quando o contêiner morre.
O que você entrega
- Um repositório com compose.yaml, Dockerfile e agent.py, executável com um único comando docker compose run.
create_harnesscom: modelo explícito, instructions de domínio, um especialista comas_tool(),builtin_toolscomo lista de permitidos, interventions, session com um id por tarefa e memory, ambos com diretórios no volume.- Um README que reúna o escrito em cada módulo: defaults mantidos e mudados (1), modelo e variável de ambiente (2), especialista e regras (3), quem escreve cada id (4), quem configura o caching (5), o que vai para Cedar ou sandbox (6), riscos aceitos (7) e overrides (8).
Ordem de trabalho sugerida
- Esqueleto com o modelo escolhido e uma tarefa de pesquisa que rode de ponta a ponta (módulos 1 e 2).
- Instructions de domínio e o especialista (módulo 3).
- Estado no volume: sessão por tarefa e memória (módulos 4, 5 e 7).
- Gate de aprovação e lista de permitidos de ferramentas (módulos 6 e 7).
- README com as decisões e os overrides (módulos 1 e 8).
- Verificação dos seis critérios abaixo a partir de um clone limpo.
Critérios de aprovação
| Critério | Como se mede | Módulo |
|---|---|---|
Você roda uma tarefa, executa docker compose down, repete com o mesmo SESSION_ID e a resposta usa o contexto anterior. | 4, 7 | |
Um dado dito na tarefa A aparece na resposta da tarefa B, que usa outro SESSION_ID. | 4 | |
| Uma ação destrutiva que você nega não é executada: o arquivo continua existindo. | 6 | |
| Uma ação destrutiva pedida por meio do especialista ou do generalist também é barrada. | 3, 6 | |
builtin_tools é uma lista explícita e cada ferramenta tem uma linha de justificativa no README. | 1, 7 | |
| A partir de um clone limpo, com a key do provedor, o README basta para executar os cinco pontos anteriores. | 8 |
Seu progresso fica salvo neste navegador.
Próximos passos
Estas páginas da doc ficaram fora do curso. Cada uma completa uma peça que você já viu por fora.
- Load agent skills: O diretório
./.agent/skillsque o harness escaneia por padrão. - Connect MCP servers: A opção
mcp_servers: um caminho para um JSON ou um mapping. - Run work in the background: A política
background_taskse como o generalist roda em segundo plano. - Shell and file tools: As ferramentas shell, read, write e edit em detalhe.
- Web access:
web_fetch,web_searche o modelo resumidor. - Programmatic tool calling: A ferramenta que roda código do modelo no Monty.
- Task tracking and environment: Os plugins integrados todos e environment.
- Compose with the Strands Harness SDK: Como o harness se apoia no SDK e como ir além.
- Versioning & Support: Política de versões: leia antes de fixar dependências.