Agentes de IA: Desvendamos Como Eles ‘Pensam’ e Dominam Ferramentas! 🤯
Olá, pessoal! Aqui é o Lucas Tech, e hoje a gente vai mergulhar em um universo fascinante: a mente dos Agentes de IA! Sabe quando a gente vê um assistente virtual fazendo coisas complexas e pensa "como ele consegue isso?" Pois é, chegou a hora de desvendar os bastidores. Prepare-se, porque vamos usar um dataset super especial para entender como esses agentes raciocinam, escolhem e usam ferramentas, e respondem em conversas complexas. É tipo um raio-X no cérebro digital! Vamos nessa?
Acesso ao Cérebro da IA: O Dataset lambda/hermes-agent-reasoning-traces
Pra começar nossa exploração, a gente vai usar um dataset incrível chamado lambda/hermes-agent-reasoning-traces. Pense nele como um diário de bordo superdetalhado onde os agentes de IA registram cada "pensamento" e ação. Nosso objetivo aqui é entender direitinho como os modelos baseados em agentes funcionam, desde o raciocínio interno até o uso de ferramentas e a geração de respostas em conversas que têm várias etapas.
Pra isso, o primeiro passo é carregar e inspecionar esse dataset. A gente dá uma olhada na sua estrutura, nas categorias de tarefas que ele cobre e no formato das conversas. É como arrumar a bancada do laboratório e ver todas as ferramentas que temos à disposição!
python
!pip -q install -U datasets pandas matplotlib seaborn transformers accelerate trl
import json, re, random, textwrap
from collections import Counter, defaultdict
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from datasets import load_dataset, concatenate_datasets
random.seed(0)
CONFIG = "kimi"
ds = load_dataset("lambda/hermes-agent-reasoning-traces", CONFIG, split="train")
print(ds)
print("Config:", CONFIG, "| Fields:", ds.column_names)
print("Categories:", sorted(set(ds["category"])))
Opcional: combinar diferentes configurações do dataset para análises mais amplas
COMPARE_BOTH = False
if COMPARE_BOTH:
ds_kimi = load_dataset("lambda/hermes-agent-reasoning-traces", "kimi", split="train")
ds_glm = load_dataset("lambda/hermes-agent-reasoning-traces", "glm-5.1", split="train")
ds_kimi = ds_kimi.add_column("source", ["kimi"] len(ds_kimi))
ds_glm = ds_glm.add_column("source", ["glm-5.1"] len(ds_glm))
ds = concatenate_datasets([ds_kimi, ds_glm]).shuffle(seed=0)
print("Combined:", ds, "→ counts:", Counter(ds["source"]))
sample = ds[0]
print("\n=== Exemplo 0 ===")
print("id :", sample["id"])
print("category :", sample["category"], "/", sample["subcategory"])
print("task :", sample["task"])
print("turns :", len(sample["conversations"]))
print("system[0] :", sample["conversations"][0]["value"][:220], "…\n")
Basicamente, a gente instala as bibliotecas necessárias, carrega o dataset e dá uma primeira olhada nos dados. É como abrir um livro e folhear as primeiras páginas para ter uma ideia do que vem por aí.
Dissecando a Conversa: Como Separamos o "Pensamento" da Ação
Agora que temos o dataset carregado, o desafio é extrair as partes mais importantes. Pense assim: o agente de IA tem "pensamentos" internos, faz "chamadas de ferramentas" e recebe "respostas das ferramentas". Tudo isso vem meio misturado no texto. Pra separar, a gente cria uns "parsers", que são como detetives especializados em encontrar esses padrões.
Usamos expressões regulares (regex) para "pescar" essas informações-chave: o que o agente pensou (entre as tags <think>), as chamadas de ferramentas (<tool_call>) e as respostas das ferramentas (<tool_response>). Isso nos permite separar o raciocínio interno das ações externas. Depois, a gente testa esses parsers em um exemplo pra ver se estão funcionando direitinho, extraindo as informações de forma estruturada.
python
THINK_RE = re.compile(r"
TOOL_CALL_RE = re.compile(r"
TOOL_RESP_RE = re.compile(r"
def parse_assistant(value: str) -> dict:
thoughts = [t.strip() for t in THINK_RE.findall(value)]
calls = []
for raw in TOOL_CALL_RE.findall(value):
try:
calls.append(json.loads(raw))
except json.JSONDecodeError:
calls.append({"name": "
final = TOOL_CALL_RE.sub("", THINK_RE.sub("", value)).strip()
return {"thoughts": thoughts, "tool_calls": calls, "final": final}
def parse_tool(value: str):
raw = TOOL_RESP_RE.search(value)
if not raw: return {"raw": value}
body = raw.group(1)
try: return json.loads(body)
except: return {"raw": body}
first_gpt = next(t for t in sample["conversations"] if t["from"] == "gpt")
p = parse_assistant(first_gpt["value"])
print("Prévia do pensamento:", (p["thoughts"][0][:160] + "…") if p["thoughts"] else "(nenhum)")
print("Chamadas de ferramenta:", [(c.get("name"), list(c.get("arguments", {}).keys())) for c in p["tool_calls"]])
Com esses parsers, a gente consegue olhar para uma resposta do assistente e ver exatamente o que ele pensou, quais ferramentas chamou e qual foi a resposta final. É um grande passo para a transparência!
Os Hábitos dos Agentes: Análises e Gráficos Reveladores
Agora que conseguimos "ler a mente" dos agentes, bora analisar os dados em larga escala! A gente pega uma parte do dataset e começa a medir várias coisas:
- Frequência de uso de ferramentas: Quais ferramentas são as "queridinhas" dos agentes?
- Comprimento das conversas: Quão longas são as interações até que o agente resolva a tarefa?
- Taxa de erros: Com que frequência as ferramentas dão problemas ou os agentes erram em suas ações?
Essas estatísticas nos ajudam a entender o comportamento geral do agente. E, claro, porque uma imagem vale mais que mil palavras, criamos gráficos pra visualizar essas tendências de forma intuitiva. Assim, a gente consegue ver rapidamente os padrões de uso de ferramentas, quantas chamadas paralelas um agente faz de uma vez e a distribuição das diferentes categorias de tarefas.
python
N = 3000
sub = ds.select(range(min(N, len(ds))))
tool_calls = Counter()
parallel_widths = Counter()
thoughts_per_turn = []
calls_per_traj = []
errors_per_traj = []
turns_per_traj = []
cat_counts = Counter()
for ex in sub:
cat_counts[ex["category"]] += 1
n_calls = n_err = 0
turns_per_traj.append(len(ex["conversations"]))
for t in ex["conversations"]:
if t["from"] == "gpt":
p = parse_assistant(t["value"])
thoughts_per_turn.append(len(p["thoughts"]))
if p["tool_calls"]:
parallel_widths[len(p["tool_calls"])] += 1
for c in p["tool_calls"]:
tool_calls[c.get("name", "
n_calls += len(p["tool_calls"])
elif t["from"] == "tool":
r = parse_tool(t["value"])
blob = json.dumps(r).lower()
if "error" in blob or ‘"exit_code": 1’ in blob or "traceback" in blob:
n_err += 1
calls_per_traj.append(n_calls)
errors_per_traj.append(n_err)
print(f"\nVerificadas {len(sub)} trajetórias")
print(f"Média de turnos/trajetória : {np.mean(turns_per_traj):.1f}")
print(f"Média de chamadas de ferramenta/trajetória : {np.mean(calls_per_traj):.1f}")
print(f"% com >=1 erro : {100np.mean([e>0 for e in errors_per_traj]):.1f}%")
print(f"% turnos paralelos : {100sum(v for k,v in parallel_widths.items() if k>1)/max(1,sum(parallel_widths.values())):.1f}%")
print("Top 10 ferramentas :", tool_calls.most_common(10))
fig, axes = plt.subplots(2, 2, figsize=(13, 9))
top = tool_calls.mostcommon(15)
axes[0,0].barh([t for t, in top][::-1], [c for _,c in top][::-1], color="teal")
axes[0,0].set_title("Top 15 ferramentas por volume de chamadas")
axes[0,0].set_xlabel("chamadas")
ks = sorted(parallel_widths)
axes[0,1].bar([str(k) for k in ks], [parallel_widths[k] for k in ks], color="coral")
axes[0,1].set_title("Chamadas de ferramenta por turno do assistente (largura paralela)")
axes[0,1].set_xlabel("# chamadas de ferramenta em um turno"); axes[0,1].set_ylabel("contagem")
axes[0,1].set_yscale("log")
axes[1,0].hist(turns_per_traj, bins=40, color="steelblue")
axes[1,0].set_title("Comprimento da conversa"); axes[1,0].set_xlabel("turnos")
cats, vals = zip(*cat_counts.most_common())
axes[1,1].pie(vals, labels=cats, autopct="%1.0f%%", startangle=90)
axes[1,1].set_title("Distribuição de categorias")
plt.tight_layout(); plt.show()
Olha que legal! A gente consegue ver quais ferramentas são mais usadas, se os agentes preferem fazer várias chamadas de ferramenta de uma vez e até qual a distribuição das tarefas que eles resolvem. Muita informação útil pra entender melhor como eles trabalham!
Vendo o Filme Completo: Rastreando Conversas e Preparando para o Treinamento
Analisar os números é show de bola, mas ver a conversa inteira, passo a passo, é outra história! Criamos uma ferramenta pra "renderizar" as conversas completas num formato fácil de ler. É como ter um debugger para as interações do agente, permitindo uma inspeção mais profunda.
Além disso, a gente extrai os "esquemas das ferramentas", que são como os manuais de instrução de cada ferramenta que o agente pode usar. E pra deixar tudo pronto pro futuro, convertemos o dataset para o formato de mensagens estilo OpenAI. Isso é super importante porque padroniza os dados e os torna compatíveis com a maioria das plataformas de treinamento de modelos de IA. É a base pra gente entender tanto a estrutura das ferramentas quanto como as conversas podem ser padronizadas.
python
def render_trace(ex, max_chars=350):
print(f"\n{‘=’72}\nTAREFA [{ex[‘category’]} / {ex[‘subcategory’]}]: {ex[‘task’]}\n{‘=’72}")
for t in ex["conversations"]:
role = t["from"]
if role == "system":
continue
if role == "human":
print(f"\n[USUÁRIO]\n{textwrap.shorten(t[‘value’], 600)}")
elif role == "gpt":
p = parse_assistant(t["value"])
for th in p["thoughts"]:
print(f"\n[PENSAMENTO]\n{textwrap.shorten(th, max_chars)}")
for c in p["tool_calls"]:
args = json.dumps(c.get("arguments", {}))[:200]
print(f"[CHAMADA] {c.get(‘name’)}({args})")
if p["final"]:
print(f"\n[RESPOSTA]\n{textwrap.shorten(p[‘final’], max_chars)}")
elif role == "tool":
print(f"[RESPOSTA DA FERRAMENTA] {textwrap.shorten(t[‘value’], 220)}")
print("="*72)
idx = int(np.argmin(np.abs(np.array(turns_per_traj) – 10)))
render_trace(sub[idx])
def get_tool_schemas(ex):
try: return json.loads(ex["tools"])
except: return []
schemas = get_tool_schemas(sample)
print(f"\nO Exemplo 0 tem {len(schemas)} ferramentas disponíveis")
for s in schemas[:3]:
fn = s.get("function", {})
print(" -", fn.get("name"), "—", (fn.get("description") or "")[:80])
ROLE_MAP = {"system": "system", "human": "user", "gpt": "assistant", "tool": "tool"}
def to_openai_messages(conv):
return [{"role": ROLE_MAP[t["from"]], "content": t["value"]} for t in conv]
example_msgs = to_openai_messages(sample["conversations"])
print("\nPrimeiras 2 mensagens do OpenAI:")
for m in example_msgs[:2]:
print(" ", m["role"], "→", m["content"][:120].replace("\n", " "), "…")
Com essas ferramentas, a gente não só entende o "porquê" das coisas, mas também deixa o caminho livre para o próximo passo: ensinar a IA a ser ainda mais esperta!
Otimizando o Aprendizado: Tokenização, Máscaras e Replay de Ações
Chegamos a uma etapa crucial para quem quer treinar modelos de linguagem: a preparação dos dados! Primeiro, a gente "tokeniza" as conversas. Isso significa transformar o texto em uma sequência de números, que é o que os modelos de IA realmente entendem.
Depois, aplicamos o que chamamos de "mascaramento de rótulos". É uma técnica inteligente que garante que apenas as respostas do assistente de IA contribuam para o treinamento. Assim, o modelo aprende o que ele deve responder, sem ser influenciado por nossas perguntas ou pelas respostas das ferramentas que ele mesmo chamou.
Também analisamos a distribuição de comprimento dos "pensamentos", chamadas de ferramentas e respostas finais. Isso nos dá uma ideia de quão "complexo" é o trabalho do agente em cada etapa. E, pra gente ter uma visão ainda mais clara, implementamos um "replayer de rastros". É como assistir a um filme em câmera lenta de como o agente pensou, chamou ferramentas e respondeu – super didático! E, claro, tudo isso nos prepara para, se quisermos, rodar um pequeno ciclo de fine-tuning para aprimorar um modelo.
python
from transformers import AutoTokenizer
TOK_ID = "Qwen/Qwen2.5-0.5B-Instruct"
tok = AutoTokenizer.from_pretrained(TOK_ID)
def build_masked(conv, tokenizer, max_len=2048):
msgs = to_openai_messages(conv)
for m in msgs:
if m["role"] == "tool":
m["role"] = "user"
m["content"] = "[TOOL OUTPUT]\n" + m["content"]
input_ids, labels = [], []
for m in msgs:
text = tokenizer.apply_chat_template([m], tokenize=False, add_generation_prompt=False)
ids = tokenizer.encode(text, add_special_tokens=False)
input_ids.extend(ids)
labels.extend(ids if m["role"] == "assistant" else [-100] * len(ids))
return input_ids[:max_len], labels[:max_len]
ids, lbls = build_masked(sample["conversations"], tok)
trainable = sum(1 for x in lbls if x != -100)
print(f"\nExemplo tokenizado: {len(ids)} tokens, {trainable} treináveis ({100*trainable/len(ids):.1f}%)")
think_lens, call_lens, ans_lens = [], [], []
for ex in sub.select(range(min(500, len(sub)))):
for t in ex["conversations"]:
if t["from"] != "gpt": continue
p = parse_assistant(t["value"])
for th in p["thoughts"]: think_lens.append(len(th))
for c in p["tool_calls"]: call_lens.append(len(json.dumps(c)))
if p["final"]: ans_lens.append(len(p["final"]))
plt.figure(figsize=(10,4))
plt.hist([think_lens, call_lens, ans_lens], bins=40, log=True,
label=["
plt.legend(); plt.xlabel("caracteres"); plt.title("Distribuições de comprimento (log y)")
plt.tight_layout(); plt.show()
class TraceReplayer:
def init(self, ex):
self.ex = ex
self.steps = []
pending = None
for t in ex["conversations"]:
if t["from"] == "gpt":
if pending: self.steps.append(pending)
pending = {"think": parse_assistant(t["value"]), "responses": []}
elif t["from"] == "tool" and pending:
pending["responses"].append(parse_tool(t["value"]))
if pending: self.steps.append(pending)
def len(self): return len(self.steps)
def play(self, i):
s = self.steps[i]
print(f"\n── Passo {i+1}/{len(self)} ──")
for th in s["think"]["thoughts"]:
print(f"💭 {textwrap.shorten(th, 280)}")
for c in s["think"]["tool_calls"]:
print(f"⚙️ {c.get(‘name’)}({json.dumps(c.get(‘arguments’, {}))[:140]})")
for r in s["responses"]:
print(f"📥 {textwrap.shorten(json.dumps(r), 200)}")
if s["think"]["final"]:
print(f"💬 {textwrap.shorten(s[‘think’][‘final’], 200)}")
rp = TraceReplayer(sample)
for i in range(min(3, len(rp))):
rp.play(i)
Bloco de treinamento opcional (descomente e configure para rodar)
TRAIN = False
if TRAIN:
import torch
from transformers import AutoModelForCausalLM
from trl import SFTTrainer, SFTConfig
train_subset = ds.select(range(200))
def to_text(batch):
msgs = to_openai_messages(batch["conversations"])
for m in msgs:
if m["role"] == "tool":
m["role"] = "user"; m["content"] = "[TOOL]\n" + m["content"]
batch["text"] = tok.apply_chat_template(msgs, tokenize=False, add_generation_prompt=False)
return batch
train_subset = train_subset.map(to_text)
model = AutoModelForCausalLM.from_pretrained(
TOK_ID,
torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
device_map="auto" if torch.cuda.is_available() else None,
)
cfg = SFTConfig(
output_dir="hermes-sft-demo",
per_device_train_batch_size=1,
gradient_accumulation_steps=4,
max_steps=20,
learning_rate=2e-5,
logging_steps=2,
max_seq_length=1024,
dataset_text_field="text",
report_to="none",
fp16=torch.cuda.is_available(),
)
SFTTrainer(model=model, args=cfg, train_dataset=train_subset, processing_class=tok).train()
print("Demonstração de fine-tuning finalizada.")
print("\n✅ Tutorial completo! Agora você tem parsers, análises, gráficos, um replayer, exemplos SFT tokenizados + com rótulos mascarados e um hook de treinamento opcional.")
Ufa! Que jornada, hein? Desenvolvemos um fluxo de trabalho completo para analisar e trabalhar com os rastros de raciocínio dos agentes de IA. Conseguimos quebrar conversas complexas em componentes significativos, entender como os agentes raciocinam passo a passo e medir como eles interagem com as ferramentas para resolver problemas. As visualizações e análises nos deram insights valiosos sobre padrões e comportamentos comuns no dataset. Além disso, convertemos os dados para um formato ideal para treinar modelos de linguagem, incluindo a tokenização e o mascaramento de rótulos. Esse processo todo é uma base super sólida para estudar, avaliar e melhorar sistemas de IA que usam ferramentas de forma prática e escalável. É a base para a IA do futuro!
Minha Visão
Galera, é fascinante como a gente está conseguindo desmistificar o funcionamento interno da IA. Ver esses "rastros de raciocínio" não é só uma curiosidade técnica; é fundamental para construirmos sistemas de IA mais confiáveis, éticos e, principalmente, mais eficazes. Com essa transparência, podemos otimizar o aprendizado dos modelos e criar agentes que realmente entendam e executem tarefas complexas de forma autônoma, quase como um "colega" digital. É o caminho para uma IA que não só nos ajuda, mas que a gente realmente entende como funciona.
E você, o que acha desse "olhar por dentro" na mente dos agentes de IA? Acredita que entender esses detalhes é o segredo para criar a próxima geração de assistentes superinteligentes? Deixa seu comentário!
Referência: Matéria Original
Posts relacionados:

THINKING PROCESS:
Analyze the original title: "I just played Crimson Desert on PS5 Pro — here’s how this massive RPG looks and runs on console"
- Core subject: Crimson Desert
- Platform: PS5 Pro (and "console" in general)
- Main aspects discussed: Looks (graphics), runs (performance)
- Implied tone: Personal experience, informative review/first impression.
- Keywords: Crimson Desert, PS5 Pro, RPG, looks, runs, console.
Understand the target platform: Google Discover.
- Characteristics: Visual-driven, snackable content, engaging, curiosity-inducing, often uses questions, bold claims, or intriguing statements. Personalization is key.
- Goal: Make people click now.
Understand the requirements:
- Language: Portuguese (BR, implied).
- Length: Short.
- Tone: Curious.
- Output: ONLY the final title text.
Brainstorm Portuguese equivalents for key concepts:
- "I just played" -> Joguei, Acabei de jogar, Minha experiência com
- "Crimson Desert" -> Crimson Desert (keep original)
- "PS5 Pro" -> PS5 Pro (keep original)
- "massive RPG" -> RPG massivo, RPG gigante, mundo aberto gigante
- "looks and runs" -> Gráficos e desempenho, Como roda, Como está, O que esperar
- "on console" -> No console, No PS5 Pro
Focus on "short and curious" for Google Discover:
Curiosity triggers:
- Questions (e.g., "Impressionado?", "Como será?", "Vale a pena?")
- Intriguing statements (e.g., "Você não vai acreditar!", "Chocante!", "Surpreendente!")
- Direct address ("Você precisa ver")
- Highlighting the impressiveness or challenge of the game/platform.
- Using strong adjectives (massivo, gigante, surpreendente, incrível).
- Shortness: Avoid long clauses, get straight to the point.
Drafting – Iteration 1 (Mental or scratchpad):
- "Joguei Crimson Desert no PS5 Pro: veja como roda!" (Too plain)
- "Crimson Desert no PS5 Pro: gráficos e desempenho surpreendem?" (Better, but maybe more active)
- "Como Crimson Desert roda no PS5 Pro? É gigante!" (A bit clunky)
- "O RPG massivo Crimson Desert no PS5 Pro: veja como ele se saiu!" (A bit long)
Focus on the "massive RPG" aspect combined with "PS5 Pro":
- The challenge for an "RPG massivo" to run well on a console is the implicit hook. The PS5 Pro is the specific new tech.
Drafting – Iteration 2 (Closer to final):
- "Crimson Desert no PS5 Pro: o RPG massivo surpreende no console?" (Good, but "surpreende" might be too weak, or implies it might not surprise).
- "Crimson Desert no PS5 Pro: É assim que o RPG massivo roda!" (A bit bland).
- "Joguei Crimson Desert no PS5 Pro: o RPG massivo impressiona!" (Good, but "joguei" might make it sound too personal, not for a broader audience seeking a quick info bite). Discover often uses more declarative or question-based styles.
Refine for "curious" and "short":
What makes people curious about a huge game on a powerful console?
- Does it push the limits?
- Is it smooth?
- Are the graphics mind-blowing?
Combine game, platform, and key performance aspects.
- Option 1 (Question-based): "Crimson Desert no PS5 Pro: O RPG massivo impressiona?" (Good, short, curious. "Impressiona" covers both looks and runs implicitly).
- Option 2 (Statement-based with intrigue): "Crimson Desert no PS5 Pro: O RPG gigante em ação!" (A bit less specific on performance).
- Option 3 (Focus on the how): "Crimson Desert no PS5 Pro: Veja como o RPG massivo se saiu!" (A bit long, "se saiu" is okay but not super punchy).
Final selection strategy:
- Choose the one that best combines "Crimson Desert," "PS5 Pro," "massive RPG," and the core idea of "looks and runs" in a curious and short way.
- The question format is excellent for Discover.
"Impressiona" in Portuguese is a strong verb that covers both visual appeal and performance satisfaction.
- Consider: "Crimson Desert no PS5 Pro: como o RPG massivo roda?" (Good, direct, but maybe a bit too much "como roda" which is expected.)
- Consider: "Crimson Desert no PS5 Pro: gráficos e desempenho no RPG gigante!" (A bit descriptive, less curious).
Consider: "Crimson Desert no PS5 Pro: O RPG massivo te surpreende?" (Very good, direct to the reader).
- Let’s try one more: "Crimson Desert no PS5 Pro: O RPG massivo te convence?" (A bit strong on "convence" – implies skepticism).
"Crimson Desert no PS5 Pro: Vale a pena?" (Too generic).
Let’s go back to focusing on the impact of the game running on the Pro. "Impressiona" is good because it covers both visual fidelity and smooth performance, and it directly asks if the combination delivers.
- "Crimson Desert no PS5 Pro: o RPG massivo impressiona?"
- Short: Yes.
- Curious: Yes, it asks a direct question that makes the reader wonder about the answer.
- Covers key elements: Game title, platform, genre, and the core "how it performs" implied by "impressiona."
- Final check: Is it only the title text? Yes.
The chosen title is concise, uses the game and console names, highlights its genre ("RPG massivo") as a key challenge/feature, and asks a direct question ("impressiona?") to pique curiosity about its performance and visuals.
"Crimson Desert no PS5 Pro: O RPG massivo impressiona?"Crimson Desert no PS5 Pro: O RPG massivo impressiona?
Google: Um mapa para salvar as florestas do Brasil?
Aproveite as melhores ofertas antecipadas da Costco para a Black Friday 2025
Amazono está oferecendo um MacBook Pro recondicionado por R$ 430, que já custou R$ 2.000 – veja o que inclui.