1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
11 small wins to finish your pathNext lesson →
Span kinds: agents, operations and tools
Decorators mark what each part of your code is: @agent on a class, @operation or @task on its methods, @tool on functions it calls. Each call becomes a nested span.
from agentops.sdk.decorators import agent, operation, tool
ORDERS = {"A-1001": "shipped on 12 March"}
@tool(cost=0.002)
def lookup_order(order_id):
return ORDERS.get(order_id, "no such order")
@agent(name="desk")
class Desk:
@operation
def answer(self, ticket):
return f"Order status: {lookup_order('A-1001')}"
@agentops.trace(name="support-ticket")
def handle(ticket):
return Desk().answer(ticket)print(handle("Where is my order A-1001?"))
local_collector.flush()
local_collector.tree()python kinds.pyThe trace holds four spans, nested by which function called which. Creating Desk() started the agent span; answer, an @operation, ran inside it, and lookup_order inside that. The span names are the function or agent name plus the kind. AgentOps records @operation as a task span; the two decorators are the same.
python attributes.pyEach span carries the function's input and output as JSON: task.input, tool.output and so on. The cost given to @tool is recorded as gen_ai.usage.total_cost, not shown here, so a dashboard can add up what tools cost per trace.
| Decorator | Span kind | Use it on |
|---|---|---|
@agentops.trace | session | The function that handles one request |
@agent | agent | A class that does the work |
@operation, @task | task | A step of the agent |
@workflow | workflow | A group of steps |
@tool | tool | A call to something outside, with an optional cost |
@guardrail | guardrail | A check on input or output, lesson 8 |
Try it yourself
- Add a
@workflowmethod that callsanswertwice and print the tree. - Call
lookup_orderoutside any trace and see what the collector gets. - Pass
name="order-lookup"to@tool.
Slow is fine. Stopping is the only problem.