Skip to main content
Weave for Agents is in public preview. Features, APIs, and the Agents view UI may change before general availability.
Try in Colab · GitHub source The Weave SDK allows you to trace agents built with popular SDKs or custom harnesses. This quickstart guides you through how to manually integrate Weave into a custom-built multi-turn agent to emit and capture OpenTelemetry spans. For conceptual understanding on Weave for agents, see Trace your agents. If you are looking to integrate Weave with popular SDKs or harnesses, such as the Claude Agent SDK or Codex, see Trace agent integrations. Weave autopatches into several popular agent-building SDKs and agent harnesses for quick integration.

What you’ll learn

The code in this guide sets up a small research agent that can look things up on Wikipedia. It asks three questions (three turns), lets the AI decide when to search Wikipedia for an answer, and uses Weave to record every step (the conversation, each question, each AI response, and each Wikipedia lookup) so you can see exactly what happened in the Weave Agents view. This guide shows you how to:
  • Initialize Weave for agent tracing with weave.init()
  • Open a session and a turn with start_session / startSession and start_turn / startTurn
  • Wrap LLM calls with start_llm / startLLM and record usage
  • Wrap tool executions with start_tool / startTool and record results
  • View the resulting session, turns, and tool calls in the Agents view

How the Weave SDK works with agents

The Weave SDK includes a generic OTel ingest system for agents, meaning that Weave can capture information from any OTel span in your agent’s code. However, Weave requires special handling of the following spans to render your agent’s traces in the Agents view of the Weave UI.
ConceptPythonTypeScriptOTel span
A conversationweave.start_session(...)weave.startSession(...)(no span — groups turns)
One user / agent exchangeweave.start_turn(...)weave.startTurn(...)invoke_agent
One LLM API callweave.start_llm(...)weave.startLLM(...)chat
One tool executionweave.start_tool(...)weave.startTool(...)execute_tool
In Python, all four functions work as context managers (with weave.start_*(...) as obj:). On exit, they end the span and flush attributes, including on exceptions. In TypeScript, call .end() on each returned object — use try { ... } finally { obj.end(); } to guarantee cleanup on exceptions. Other GenAI semantic-convention attributes, such as gen_ai.usage.* and gen_ai.agent.name, enable additional rendering, but they are optional.

Prerequisites

  • A W&B account and API key
  • An OpenAI API key
  • Python 3.10+ (for the Python examples)
  • Node.js 18+ (for the TypeScript examples — built-in fetch is required)

Install packages

Install the following packages into your developer environment:
pip install weave openai requests
npm install weave openai

Initialize Weave

weave.init() authenticates with W&B and configures the OTel exporter that sends agent spans to the Agents view. If the project does not exist on your team, Weave creates it the first time you write to it.
import getpass
import os

os.environ["WANDB_API_KEY"] = getpass.getpass("Enter your W&B API key: ")
os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter your OpenAI API key: ")

TEAM = input("Enter your W&B team name: ")
PROJECT = input("Enter your W&B project name: ")

import weave
weave.init(f"{TEAM}/{PROJECT}")
// Set WANDB_API_KEY, OPENAI_API_KEY in your environment before running this project
import * as weave from 'weave';

await weave.init(`[YOUR-TEAM]/[YOUR-PROJECT]`);

Define a tool

The following code defines the agent’s Wikipedia search tool and an OpenAI tool schema to determine when and how to use the tool.
import json
import requests

def wikipedia_search(query: str) -> str:
    r = requests.get(
        "https://en.wikipedia.org/w/api.php",
        params={
            "action": "query", "generator": "search", "gsrsearch": query, "gsrlimit": 1,
            "prop": "extracts", "exintro": True, "explaintext": True, "format": "json",
        },
        headers={"User-Agent": "weave-demo"},
    ).json()
    return next(iter(r["query"]["pages"].values()))["extract"]

wikipedia_tool_schema = {
    "type": "function",
    "function": {
        "name": "wikipedia_search",
        "description": "Search Wikipedia for a topic and return its intro paragraph.",
        "parameters": {
            "type": "object",
            "properties": {"query": {"type": "string"}},
            "required": ["query"],
        },
    },
}
async function wikipediaSearch(query: string): Promise<string> {
  const url = new URL('https://en.wikipedia.org/w/api.php');
  url.search = new URLSearchParams({
    action: 'query',
    generator: 'search',
    gsrsearch: query,
    gsrlimit: '1',
    prop: 'extracts',
    exintro: 'true',
    explaintext: 'true',
    format: 'json',
  }).toString();
  const res = await fetch(url, { headers: { 'User-Agent': 'weave-demo' } });
  const data = (await res.json()) as {
    query: { pages: Record<string, { extract: string }> };
  };
  return Object.values(data.query.pages)[0].extract;
}

const wikipediaToolSchema = {
  type: 'function' as const,
  function: {
    name: 'wikipedia_search',
    description: 'Search Wikipedia for a topic and return its intro paragraph.',
    parameters: {
      type: 'object',
      properties: { query: { type: 'string' } },
      required: ['query'],
    },
  },
};

Run a traced multi-turn agent

The example below runs three turns in a single session. Each turn:
  1. Opens a chat span and lets the LLM decide whether to call the tool
  2. If the LLM requested a tool, opens an execute_tool span around the call and feeds the result back to the LLM
  3. Opens a second chat span to produce the final answer
from openai import OpenAI

openai_client = OpenAI()
MODEL = "gpt-4o-mini"

def run_turn(history, user_message):
    history.append({"role": "user", "content": user_message})

    with weave.start_turn(user_message=user_message, model=MODEL):
        # LLM call 1 — the model may decide to use a tool.
        with weave.start_llm(model=MODEL, provider_name="openai") as llm:
            resp = openai_client.chat.completions.create(
                model=MODEL, messages=history, tools=[wikipedia_tool_schema],
            )
            msg = resp.choices[0].message
            llm.output(msg.content or "")
            llm.usage = weave.Usage(
                input_tokens=resp.usage.prompt_tokens,
                output_tokens=resp.usage.completion_tokens,
            )
            history.append(msg.model_dump(exclude_none=True))

        # If no tool was requested, the first LLM response is the answer.
        if not msg.tool_calls:
            return msg.content

        # Execute each requested tool call.
        for tc in msg.tool_calls:
            with weave.start_tool(
                name=tc.function.name,
                arguments=tc.function.arguments,
                tool_call_id=tc.id,
            ) as tool:
                tool.result = wikipedia_search(**json.loads(tc.function.arguments))
                history.append({
                    "role": "tool",
                    "tool_call_id": tc.id,
                    "content": tool.result,
                })

        # LLM call 2 — synthesize the final answer.
        with weave.start_llm(model=MODEL, provider_name="openai") as llm:
            resp = openai_client.chat.completions.create(model=MODEL, messages=history)
            msg = resp.choices[0].message
            llm.output(msg.content)
            llm.usage = weave.Usage(
                input_tokens=resp.usage.prompt_tokens,
                output_tokens=resp.usage.completion_tokens,
            )
            history.append({"role": "assistant", "content": msg.content})
            return msg.content

with weave.start_session(agent_name="research-bot") as session:
    history = []
    for question in [
        "Who founded Anthropic?",
        "What is Claude (the AI assistant)?",
        "Summarize what we discussed in one sentence.",
    ]:
        print(f"USER: {question}")
        print(f"AGENT: {run_turn(history, question)}\n")
import OpenAI from 'openai';

const openaiClient = new OpenAI();
const MODEL = 'gpt-4o-mini';

// history is a list of OpenAI chat messages; typed loosely for brevity.
async function runTurn(history: any[], userMessage: string): Promise<string | null> {
  history.push({ role: 'user', content: userMessage });

  const turn = weave.startTurn({ userMessage, model: MODEL });
  try {
    // LLM call 1 — the model may decide to use a tool.
    const llm1 = weave.startLLM({ model: MODEL, providerName: 'openai' });
    let msg;
    try {
      const resp = await openaiClient.chat.completions.create({
        model: MODEL,
        messages: history,
        tools: [wikipediaToolSchema],
      });
      msg = resp.choices[0].message;
      llm1.output(msg.content ?? '');
      llm1.usage = {
        inputTokens: resp.usage?.prompt_tokens,
        outputTokens: resp.usage?.completion_tokens,
      };
      history.push(msg);
    } finally {
      llm1.end();
    }

    // If no tool was requested, the first LLM response is the answer.
    if (!msg.tool_calls?.length) {
      return msg.content ?? null;
    }

    // Execute each requested tool call.
    for (const tc of msg.tool_calls) {
      const tool = weave.startTool({
        name: tc.function.name,
        args: tc.function.arguments,
        toolCallId: tc.id,
      });
      try {
        const { query } = JSON.parse(tc.function.arguments);
        tool.result = await wikipediaSearch(query);
        history.push({ role: 'tool', tool_call_id: tc.id, content: tool.result });
      } finally {
        tool.end();
      }
    }

    // LLM call 2 — synthesize the final answer.
    const llm2 = weave.startLLM({ model: MODEL, providerName: 'openai' });
    try {
      const resp = await openaiClient.chat.completions.create({
        model: MODEL,
        messages: history,
      });
      const msg2 = resp.choices[0].message;
      llm2.output(msg2.content ?? '');
      llm2.usage = {
        inputTokens: resp.usage?.prompt_tokens,
        outputTokens: resp.usage?.completion_tokens,
      };
      history.push({ role: 'assistant', content: msg2.content });
      return msg2.content ?? null;
    } finally {
      llm2.end();
    }
  } finally {
    turn.end();
  }
}

const session = weave.startSession({ agentName: 'research-bot' });
try {
  const history: any[] = [];
  for (const question of [
    'Who founded Anthropic?',
    'What is Claude (the AI assistant)?',
    'Summarize what we discussed in one sentence.',
  ]) {
    console.log(`USER: ${question}`);
    console.log(`AGENT: ${await runTurn(history, question)}\n`);
  }
} finally {
  session.end();
}

See your agent traces in the Agents view

When weave.init() runs, it prints a link to your project where you can see:
  • A row in the Agents tab for research-bot
  • One session containing three turns
  • Each turn (invoke_agent) with two chat spans and an execute_tool span nested inside
  • Token counts, latency, model, and the full message exchange on each chat
Click into any turn to inspect the inputs, outputs, tool arguments, and tool results.

Next steps