Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions AuthorAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace BlogWriter;
/// </summary>
public class AuthorAgent : IAuthorAgent
{
private readonly ChatClientAgent _agent;
private readonly AIAgent _agent;

// Emits a span per draft creation/revision. Activated by the ActivityListener
// registered in Program.cs (or an OpenTelemetry TracerProvider).
Expand All @@ -38,7 +38,10 @@ public AuthorAgent(IChatClient llm, ChatOptions chatOptions, ILogger<AuthorAgent
Temperature = chatOptions.Temperature,
MaxOutputTokens = chatOptions.MaxOutputTokens,
},
});
})
.AsBuilder()
.UseOpenTelemetry(sourceName: "BlogWriter.Agents")
.Build();
_logger.LogInformation("AuthorAgent initialized.");
}

Expand Down
7 changes: 5 additions & 2 deletions BloggerAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class BloggerAgent : IBloggerAgent
{
// Built once and reused. Holds the static Blogger instructions; the volatile
// state is passed per-turn as the user message.
private readonly ChatClientAgent _agent;
private readonly AIAgent _agent;

// Web-style options are sufficient: BloggerDecision carries explicit
// [JsonPropertyName] attributes (next_step / task_description) that drive the
Expand Down Expand Up @@ -47,7 +47,10 @@ public BloggerAgent(IChatClient llm, ChatOptions chatOptions, ILogger<BloggerAge
Temperature = chatOptions.Temperature,
MaxOutputTokens = chatOptions.MaxOutputTokens,
},
});
})
.AsBuilder()
.UseOpenTelemetry(sourceName: "BlogWriter.Agents")
.Build();
_logger.LogInformation("BloggerAgent initialized.");
}

Expand Down
17 changes: 12 additions & 5 deletions Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,13 @@
// calls the model requests — without it, attaching the Tavily tool to the
// Researcher agent would let the model ask for a search but nothing would run it.
// Middleware is applied inner-to-outer, so function invocation wraps the raw
// OpenAI client. (To add distributed tracing later, chain .UseOpenTelemetry()
// here and register the source with a TracerProvider.)
// OpenAI client.
//
// UseOpenTelemetry() emits a GenAI span per model round-trip (model name, token
// usage, tool calls). Its source is named "BlogWriter.ChatClient" so the
// ActivityListener registered below (which listens to every "BlogWriter.*"
// source) captures it alongside the agent/workflow spans — no TracerProvider
// or extra packages required.
//
// TokenCapChatClient is registered *after* function invocation, which makes it
// the innermost wrapper around the raw client — so it observes every individual
Expand All @@ -47,6 +52,7 @@
.AsIChatClient()
.AsBuilder()
.UseFunctionInvocation()
.UseOpenTelemetry(sourceName: "BlogWriter.ChatClient")
.Use(inner => new TokenCapChatClient(inner, maxTotalTokens: 10000))
.Build();

Expand Down Expand Up @@ -90,9 +96,10 @@
var app = new BlogWorkflow(bloggerAgent, researcherAgent, authorAgent, reviewerAgent);

// Distributed tracing: an ActivityListener activates every "BlogWriter.*"
// ActivitySource in the app (agents + workflow) and writes span start/stop to
// the console. Swap this listener for OpenTelemetry's TracerProvider (and chain
// IChatClient.UseOpenTelemetry() above) to export the same spans instead.
// ActivitySource in the app (agents, workflow, and the IChatClient's
// "BlogWriter.ChatClient" GenAI spans) and writes span start/stop to the
// console. Swap this listener for OpenTelemetry's TracerProvider to export the
// same spans to a backend instead.
var appActivitySource = new ActivitySource("BlogWriter.Program");

ActivitySource.AddActivityListener(new ActivityListener
Expand Down
7 changes: 5 additions & 2 deletions ResearcherAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class ResearcherAgent : IResearcherAgent
// The agent is built once and reused for every research turn. It is stateless
// across turns (no AgentSession is retained), which matches the original
// per-call behaviour while gaining tool-calling for free.
private readonly ChatClientAgent _agent;
private readonly AIAgent _agent;

// Emits a span per research turn. Activated by the ActivityListener
// registered in Program.cs (or an OpenTelemetry TracerProvider).
Expand Down Expand Up @@ -48,7 +48,10 @@ public ResearcherAgent(IChatClient llm, ChatOptions chatOptions, AIFunction tavi
// Attaching the tool lets the model call it autonomously.
Tools = [tavilyTool],
},
});
})
.AsBuilder()
.UseOpenTelemetry(sourceName: "BlogWriter.Agents")
.Build();

_logger.LogInformation("ResearcherAgent initialized with Tavily tool: {ToolName}", tavilyTool.Name);
}
Expand Down
7 changes: 5 additions & 2 deletions ReviewerAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace BlogWriter;
/// </summary>
public class ReviewerAgent : IReviewerAgent
{
private readonly ChatClientAgent _agent;
private readonly AIAgent _agent;

// Emits a span per review. Activated by the ActivityListener registered in
// Program.cs (or an OpenTelemetry TracerProvider).
Expand All @@ -39,7 +39,10 @@ public ReviewerAgent(IChatClient llm, ChatOptions chatOptions, ILogger<ReviewerA
Temperature = chatOptions.Temperature,
MaxOutputTokens = chatOptions.MaxOutputTokens,
},
});
})
.AsBuilder()
.UseOpenTelemetry(sourceName: "BlogWriter.Agents")
.Build();
_logger.LogInformation("ReviewerAgent initialized.");
}

Expand Down