Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
157226 stories
·
33 followers

Learn T-SQL With Erik: Aligning Queries and Indexes Part 4

1 Share

Learn T-SQL With Erik: Aligning Queries and Indexes Part 4


Chapters

Full Transcript

Erik Darling here with Darling Data and today’s video we are going to carry on in our task which is learning how to better align our queries and our indexes. If you need help aligning your queries and your indexes, boy do I have options for you. You can hire me for, aside from watching these videos, you can hire me for consulting, do this stuff all day.

You can also purchase my training. The videos that you’re watching here are just tiny little snippets from the full course material in the Learn T-SQL with Erik course. The link to buy that for a hundred bucks off is down in the video description if you feel like doing that sort of thing and watching more videos of me. It’s crazy. You can also become a supporting member of the channel, ask me office hours questions, and I guess outside of the downstairs links you can also do other things that would make me think of you as a more useful human being.

Such as liking this video, subscribing to this channel, and forcing all of your friends. Hijack their browsers and force them to love me as well. If you need SQL Server performance monitoring, I got you covered.

There’s nothing Erik Darling won’t do for you. Maybe a couple of things. But this thing I’ll do for you. I would do anything for you but I won’t do that.

Anyway, I don’t like that song. Totally free, open source. You can see everything it’s doing. It’s free. It’s right out there on GitHub. It’s a bunch of T-SQL collectors.

They all run on a schedule. They collect important performance information from your SQL Server, put it into pretty charts and graphs, and allow you to talk via your robot companions using MCP servers to do that analysis on your performance data. The MCP stuff is all opt-in.

It is not on by default if you don’t want it broadcasting that it’s there. But it’s just, you know, gives you a different way of… figuring out what’s up with your SQL Server aside from just looking at the pretty charts and graphs and doing your own form of analysis.

So, all that good stuff. If you want to see me live out in the world and you happen to be in the Croatian area, I also got you covered. June 12th and 13th.

I will be at Data Saturday Croatia. I have an advanced T-SQL pre-con. It’ll be the material that you’re seeing here and more. If you come to the class, you get all of the T-SQL stuff. All of the T-SQL videos that I publish as part of the full course.

So you show up, you hang out with me for a day, and then you get like 100 hours of videos to go watch at home. But until then, let’s continue our maddening descent into heat brain leaking hell. I guess that’s what this is.

Maybe it’s just allergies. I get those too. The databases are just allergic as hell to everything. Especially users and developers. Just like me.

Anyway. We’re going to look at some interesting sort of tipping point queries. And this video is going to explore both rewriting queries to get better performance and tweaking an index to get better performance. So you get a twofer on this one.

Don’t say I never did nothing for you aside from all this stuff I already do for you. Anyway. We’re going to start by running this query. And we are going to use drop clean buffers.

Not because this one ends up terribly. Because the next one will end up terribly. So we’re just like this worst case scenario. This has a little go to after it.

So it executes twice. Even if you look in the messages tab, you will see this handy little message here. Beginning execution loop. Batch execution completed two times. Thank you. But the first query, it is a little bit slower. It does take about 1.2 seconds to run. And the second query takes about half that time.

And this is just the effective cache data. Right? And what’s kind of funny is it’s like when you look at these things, it’s like almost hard to spot where they really go astray. Like sure, this takes 60 milliseconds.

This takes 237 milliseconds. Somehow we end up at 922 milliseconds in the nested loops join. So the nested loops join did spend some extra time in there. I’ll talk about why in a minute.

But if you look down here, really the big difference in time. It’s not this, right? That’s about like 12 seconds different. That’s actually 60 milliseconds slower somehow, right? 237 to 295.

But this is at 460 milliseconds. Now part of that is because the nested loops join is responsible for a little bit more work than it lets on if you are just looking at the graphical execution plan. If you right click and you go into the properties, you will see this prefetch attribute assigned to your nested loops join.

This one just happens to be unordered. The same thing would happen if it were ordered. But this is just essentially telling SQL Server to go out and read a bunch of data ahead of time and get some extra stuff that we might need to make this query run and return stuff.

So the nested loops join here doing a little bit more work than in this one. We’ll forgive it though. But this isn’t really like the crappy one.

The crappy one comes. So this is looking through 2013-03-18. This is looking through 2013-03-19. And if we run this one, this is where things get demonstrably worse, right?

Because we have hit a tipping point when SQL Server is no longer willing to give us the query plan that we had before. It is no longer willing to do that key lookup. It just goes ahead and scans the clustered index.

Scanning the clustered index on the POST table for me takes about 8 seconds when I’m reading from disk. When I’m not reading from disk, it takes about 10 seconds. When I’m not reading from disk, it takes about 618 milliseconds.

I know which one I prefer. I also know that I’m pretty sure that I would prefer if SQL Server chose that lookup plan a little bit more reliably. How can we do that?

Great question. If we wanted to influence the optimizer to avoid the clustered index, we might rewrite the query like this, right? So what we’ll say is, again, sort of almost doing the same sort of self-join technique.

But we can just use an answer. We’ll say, just give me the top 1000 rows that would qualify for our original query. And just say where the ID from the outer POST table is in this list of IDs.

And this will influence SQL Server to use that same fast query. Use our nonclustered index instead of the clustered index, right? We’re going to go seek right into that bad boy over here.

Find the rows that we care about. And narrow it down to just the 1000 that we need to satisfy our query. And then go get the columns from the POST table via the self-join here.

And we return all that out. And that’s even a bit faster than either of the ones that we did before at 147 milliseconds. Now, IN and EXISTS often behave as far as the execution plan goes identically.

Often, right? But not in this case. When you have a top 1000 in an IN subquery, you look at this.

Again, the query plan, it looks like this. You see a top operator in it, right? SQL Server is like, oh, I need to limit this to a top 1000. If you do that with EXISTS, though, and I’m just going to get the estimated plan here.

Because if I run this query, things will not go as maybe they look here. The top 1000 is not, there is no top operator present in this. SQL Server will go and find all of the top 1000.

The rows and figure out which ones exist. The top is just ignored inside of EXISTS. SQL Server just throws that away.

It’s not valid to use top in there. So this does not turn out probably as you might expect or as you might have planned on it turning out. This would run for a long time and return a lot of rows.

Because we’re just essentially asking for everything from the POST table where the IDs exist. Even the top 1000 here and all of the rows that this would match. So we could do this, right?

But even this won’t turn out so great. What we’ll do is, no, I’m in the right place. There we go. We’ll say, we’ll put the top 1000 on the outer part of the query where SQL Server can no longer just dispose of it and throw it away and say, you’re not valid.

But if we run this, it’s still a little bit clunky, right? We’re back up to like a second on this. We had this tuned nicely with that in sub query.

If we’re not in a place where SQL Server might use, I should probably stop here for a moment. We get a batch mode adaptive join for this query, right? So good for us, right?

We’re on developer edition. So we’re getting that enterprise edition class for free. That’s cool. But we get a batch mode adaptive join here. SQL Server has chosen batch mode for the query.

And it said, well, I’m going to figure out. The best join strategy based on, at runtime, how many rows come out of one thing or the other. And then I will choose the correct join type based on how many rows leave here.

Great. You may not always get that. If you don’t always get that, you will most likely end up with a hash join here. And the hash join takes, on its own, just about the same amount of time.

Most of the stuff in here does still run in batch mode on rowstore. So you’re still getting just about the same improvement. Just without the join choice at runtime.

The join choice at runtime doesn’t add anything bad here. But it doesn’t add anything good here either. Batch mode makes this thing, like, still okay, but not where it was before. We did a much better job.

We could also force a nested loops join here if we wanted. And we could get down to an okay amount of time. But still 678 milliseconds.

That’s not really what we had before. If you recall. It was several queries ago with our beautiful in clause query with the top 1000 in it. This all ran in 135 milliseconds.

So that’s really more the time to beat. Everything is 600, 800 milliseconds. That’s a regression. It’s not a huge one. But, you know, it’s not really one.

We don’t tune queries to make them regress, do we? We tune queries to make them faster. It’s a crazy concept, I know.

Now, one thing that I want to point out is kind of funny about the array. The original query is… And all of the other ones are ordered by elements. Yeah.

Mouthful of marbles. Are creation date and then score descending. If we just run this query ordered by creation date and score, no longer descending on the score column, our original query still runs really quickly.

Actually, it runs faster than ever. Interesting. Well, we spent a lot of time rewriting this query to sort of have it suit the index that we had available better. But sometimes, every once in a while, you might be able to change an index.

And if we change our index definition, or rather we’re going to create a new index, I guess, to creation date and then score descending, so this fits the query that we were writing, better suits the query that we had originally, then we get the same fast execution as we did when we changed our query.

So, sometimes there are ways to rewrite your query to better suit the indexes that you have. Other times, if you have options and choices, you might choose to change your indexes up a little bit so that they better suit the queries that you have.

All right. I reached the end of the file. Thank you for watching. I hope you enjoyed yourselves. I hope you learned something. And I will see you next week on Tuesday for Office Hours.

All right. Have a great weekend, everybody.

Going Further


If this is the kind of SQL Server stuff you love learning about, you’ll love my training. Blog readers get 25% off the Everything Bundle — over 100 hours of performance tuning content. Need hands-on help? I offer consulting engagements from targeted investigations to ongoing retainers. Want a quick sanity check before committing to a full engagement? Schedule a call — no commitment required.

The post Learn T-SQL With Erik: Aligning Queries and Indexes Part 4 appeared first on Darling Data.

Read the whole story
alvinashcraft
12 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

v1.0.7-preview.0

1 Share

Feature: enableManagedSettings for enterprise policy enforcement (all SDKs)

Applications can now pass enableManagedSettings when creating or resuming a session to opt the runtime into enterprise managed-settings enforcement (bypass-permissions policy) using the session's gitHubToken. This is purely additive and opt-in — omitting it behaves exactly as before. (#1925)

const session = await client.createSession({
  gitHubToken: "...",
  enableManagedSettings: true,
});
var session = await client.CreateSessionAsync(new SessionConfig
{
    GitHubToken = "...",
    EnableManagedSettings = true,
});
  • Python: enable_managed_settings=True kwarg on create_session / resume_session
  • Go: EnableManagedSettings: boolPtr(true) on SessionConfig / ResumeSessionConfig
  • Rust: .with_enable_managed_settings(true) builder on SessionConfig
  • Java: sessionConfig.setEnableManagedSettings(true) on SessionConfig

Fix: deterministic tool schema serialization in Rust SDK ⚠️ Breaking change

The Rust SDK previously used HashMap for Tool.parameters and mcp_servers fields, causing random key ordering in serialized JSON each process startup. This busted the model provider's prompt cache on the system+tools prefix, increasing cost and latency. HashMap is now replaced with IndexMap for these model-visible maps. (#1931)

IndexMap mirrors HashMap's API and is re-exported as github_copilot_sdk::IndexMap — migration is mechanical:

// Before: use std::collections::HashMap;
// After:
use github_copilot_sdk::IndexMap; // same API, deterministic iteration order

Affected public types: Tool.parameters, mcp_servers on SessionConfig / ResumeSessionConfig / CustomAgentConfig, and the tool_parameters / try_tool_parameters return types.

New contributors

  • @agoncal made their first contribution in #1951

Generated by Release Changelog Generator · sonnet46 987.8K

Read the whole story
alvinashcraft
42 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Introducing Muse Spark 1.1

1 Share

Introducing Muse Spark 1.1

Following Muse Spark in April, here's Muse Spark 1.1 - the first Spark model to offer an API. Meta claim significant improvements in agentic tool calling and computer use.

There are a lot more details are in the Muse Spark 1.1 Evaluation Report.

I had a few days of preview access which was long enough to put together llm-meta-ai, a new plugin for LLM providing CLI (and Python library) access to the model. Here's how to try that out:

uv tool install llm
llm install llm-meta-ai
llm keys set meta-ai
# paste API key here
llm -m meta-ai/muse-spark-1.1 "Generate an SVG of a pelican riding a bicycle"

Here's that pelican transcript:

The bicycle is the correct shape. The pelican is a little blocky but still recognizable as a pelican.

Tags: ai, generative-ai, llms, llm, meta, pelican-riding-a-bicycle, llm-release

Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete

Introducing a way to reflect on how you use Claude

1 Share
Introducing a way to reflect on how you use Claude
Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete

Ollama: all aboard open models

1 Share
Serving 8.9 million developers, Ollama has raised $88M from Benchmark, Theory Ventures, 8VC, Y Combinator, and many incredible angel investors.
Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete

Agent Harness: Scaling the claw or harness capabilities

1 Share

Part 3 of Build your own claw and harness with Microsoft Agent Framework.

In Part 2 our personal finance assistant learned to work with your data safely: it reads your portfolio, asks before it trades, and remembers what matters across sessions. It’s useful – but everything it knows is baked into one prompt, it does its work one step at a time, and it can’t reach past the file-access tools to actually reorganize anything.

This part makes the claw more capable along four axes:

  1. Skills – package know-how (valuation, risk-scoring) as discoverable files the agent loads on demand, including centrally-managed Foundry skills.
  2. Shell – shell tools, to tidy and restructure files.
  3. CodeAct – let the agent write and run code to compute answers it can’t just look up.
  4. Background agents – fan work out to sub-agents that run concurrently, then aggregate.

As before, we only supply what makes our agent ours; the harness provides the machinery. Let’s take them in turn.

Teach it on demand: skills

Stuffing every instruction the assistant might ever need into its system prompt doesn’t scale – it bloats the context and dilutes its focus. Skills solve this: each skill is a small SKILL.md file with a name, a one-line description, and instructions (plus optional reference docs and scripts). The agent sees only the names and descriptions up front, and progressively loads a skill’s full content only when a request matches it.

Our claw gets two file-based skills under the skills/ folder. The skills instruct the agent in how to do risk scoring and valuations.

For more information on skills see Give Your Agents Domain Expertise with Agent Skills in Microsoft Agent Framework and What’s New in Agent Skills: Code Skills, Script Execution, and Approval for Python

Usage

The harness turns a skills provider on by default (it discovers SKILL.md files from the working directory). Here we build our own provider so we can point it at this sample’s skills/ folder and run the skills’ scripts.

In .NET, compose a provider with AgentSkillsProviderBuilder and turn the default one off:

var skillsProvider = new AgentSkillsProviderBuilder()
    // File-based skills; SubprocessScriptRunner runs their Python scripts.
    .UseFileSkills([skillsDir], scriptRunner: SubprocessScriptRunner.RunAsync)
    .Build();

AIAgent agent = chatClient.AsHarnessAgent(new HarnessAgentOptions
{
    DisableAgentSkillsProvider = true,         // we supply our own
    AIContextProviders = [skillsProvider],
    // … file access, tools …
});

In Python, build a SkillsProvider from the same folder and pass it in:

from agent_framework import SkillsProvider

skills_provider = SkillsProvider.from_paths(
    skill_paths=[str(skills_dir)],
    script_runner=subprocess_script_runner,  # lets the skills' scripts run
)

agent = create_harness_agent(
    client=client,
    skills_provider=skills_provider,
    # … file access, tools …
)

Now “Value MSFT for me” makes the agent load the valuation skill, read its guide, run its script, and report a fair-value estimate – and “How risky is my portfolio?” pulls in risk-scoring instead. Nothing about valuation or risk was in the system prompt.

Skills you manage centrally: Foundry skills

File-based skills ship with the agent – to change one, you redeploy. Foundry skills flip that around: skills are published and updated centrally in your Foundry project, and the agent picks them up at runtime. The valuation method can evolve without anyone touching the claw.

Because it needs a Foundry endpoint, we make it opt-in – the sample runs fine on the local skills alone.

For how to publish and manage Foundry skills, see the Foundry skills docs.

In .NET, Foundry skills are discovered live from a Foundry Toolbox MCP endpoint; just add an MCP source to the same builder:

// Opt-in: only when a Toolbox endpoint is configured.
if (!string.IsNullOrWhiteSpace(toolboxUrl))
{
    var (mcpClient, _) = await FoundrySkills.ConnectAsync(toolboxUrl, credential);
    skillsBuilder.UseMcpSkills(mcpClient);   // fold them into the same provider
}

In Python, it’s the same story: connect to the Foundry Toolbox MCP endpoint and add an MCPSkillsSource alongside the local FileSkillsSource:

from agent_framework import (
    AggregatingSkillsSource, DeduplicatingSkillsSource,
    FileSkillsSource, MCPSkillsSource, SkillsProvider,
)

sources = [FileSkillsSource(str(skills_dir), script_runner=subprocess_script_runner)]

# Opt-in: only when a Toolbox endpoint is configured.
if toolbox_url:
    session = await _connect_foundry_toolbox(stack, toolbox_url)
    sources.append(MCPSkillsSource(client=session))

source = sources[0] if len(sources) == 1 else AggregatingSkillsSource(sources)
skills_provider = SkillsProvider(DeduplicatingSkillsSource(source))

Either way the agent sees one unified set of skills; it neither knows nor cares which ones came from disk and which were managed in Foundry.

Skills aren’t only for domain how-tos – a skill can just as easily carry general rules about how the agent should behave. Because Foundry skills update centrally, governance rules like this are exactly the kind of thing you’d manage there: tighten the policy once and every running claw picks it up, no redeploy. To try it, publish a skill named financial-agent-rules to your Foundry toolbox:

---
name: financial-agent-rules
description: General rules about how you should behave as a financial agent. Use this skill for all requests.
---

You should politely refuse to answer any questions unrelated to finance, managing portfolios or managing confirmations.

Because its description says “Use this skill for all requests”, the agent loads it on every turn. Once it’s published and Foundry skills are enabled (set FOUNDRY_TOOLBOX_MCP_SERVER_URL), ask the claw something off-topic like “What’s the capital of France?” and it will politely decline and steer you back to finance.

Reach into the file system: shell

File access lets the claw read and write individual files, but reorganizing a messy folder – moving, renaming, batching – is exactly what a shell is for. The user’s trade confirmations pile up as a flat heap of inconsistently-named files:

working/confirmations/
  trade confirmation 1.txt
  conf_AAPL.txt
  copy of trade 3.txt
  SPY sell.txt
  …

The harness can expose a shell as an (approval-gated) run_shell tool. Letting an agent run shell commands is powerful and dangerous, so we hem it in two ways: a confined working directory (every command is re-anchored to the confirmations vault and can’t escape it) and a deny-list policy that pre-filters obviously destructive commands.

In .NET, configure a LocalShellExecutor and pass it as the ShellExecutor:

await using var shell = new LocalShellExecutor(new LocalShellExecutorOptions
{
    WorkingDirectory = vaultDir,
    ConfineWorkingDirectory = true,                 // can't escape the vault
    Policy = new ShellPolicy(denyList:
    [
        @"\brm\s+-rf\b", @"\bsudo\b", @":\(\)\s*\{", @"\bmkfs\b", @">\s*/dev/sd",
    ]),
    Timeout = TimeSpan.FromSeconds(15),
});

AIAgent agent = chatClient.AsHarnessAgent(new HarnessAgentOptions
{
    ShellExecutor = shell,   // exposed as the approval-gated run_shell tool
    // … skills, file access, tools …
});

In Python, a LocalShellTool plays the same role:

from agent_framework_tools.shell import LocalShellTool, ShellPolicy

shell = LocalShellTool(
    workdir=str(vault_dir),
    confine_workdir=True,                            # can't escape the vault
    policy=ShellPolicy(denylist=[
        r"\brm\s+-rf\b", r"\bsudo\b", r":\(\)\s*\{", r"\bmkfs\b", r">\s*/dev/sd",
    ]),
    timeout=15,
)

agent = create_harness_agent(
    client=client,
    shell_executor=shell,   # exposed as the approval-gated run_shell tool
    # … skills, file access, tools …
)

The policy is a UX guardrail, not a security boundary. A deny-list catches obvious mistakes, but it won’t stop a determined or cleverly-worded command. Real isolation comes from the confined working directory and the approval prompt – and for untrusted input, a sandboxed executor like DockerShellExecutor (.NET) or DockerShellTool (Python).

Now “Tidy up my trade confirmations” lets the agent inspect the folder, propose a plan, and (with your approval on each command) move the files into a year/month layout renamed to YYYY-MM-DD_TICKER_BUY|SELL.txt.

Let it compute: CodeAct

Some questions aren’t a lookup – they’re a calculation. “What’s my portfolio worth?” or “what was my return on this position?” are better answered by running a little code than by asking the model to do arithmetic in its head. CodeAct gives the agent a sandboxed interpreter it can write and run code in.

CodeAct runs model-authored code in a sandbox, and we wire it in as a context provider in both languages.

In .NET, CodeAct uses Hyperlight (a micro-VM, so it needs hardware virtualization). The guest module path is resolved automatically from the Hyperlight.HyperlightSandbox.Guest.Python NuGet package:

using HyperlightSandbox.Guest.Python;
using Microsoft.Agents.AI.Hyperlight;

var codeAct = new HyperlightCodeActProvider(
    HyperlightCodeActProviderOptions.CreateForWasm(PythonGuestModule.GetModulePath()));

AIAgent agent = chatClient.AsHarnessAgent(new HarnessAgentOptions
{
    AIContextProviders = [skillsProvider, codeAct],
    // … shell, file access, tools …
});

In Python, Monty is a pure, cross-platform interpreter – no hypervisor required – added as a context provider:

from agent_framework_monty import MontyCodeActProvider

context_providers = [skills_provider, MontyCodeActProvider(approval_mode="never_require")]

agent = create_harness_agent(
    client=client,
    context_providers=context_providers,
    # … shell, file access, tools …
)

With CodeAct on, “Work out the total value of my portfolio” lets the agent read the holdings, write a few lines of Python to multiply shares by price and sum them, run it, and report the result – arithmetic it can show its working for, rather than guess.

For more ideas on how to use CodeAct, read CodeAct in Agent Framework: Faster Agents with Fewer Model Turns

Do many things at once: background agents

Asking “Research MSFT, NVDA and SPY” one ticker at a time is slow, and folding all that web searching into the main agent muddies its context. The harness supports background agents: sub-agents you hand to the claw so it can delegate units of work that run concurrently and report back.

We build a lean, web-search-only research agent – a plain chat-client agent with just the web search tool (no harness machinery needed):

// ResearchAgent.Create(...)
AIAgent research = chatClient.AsAIAgent(
    name: "TickerResearchAgent",
    description: "Searches the web for recent news about a single stock ticker.",
    instructions: "You research a single ticker and return 3-4 factual bullet points.",
    tools: [new HostedWebSearchTool()]);   // the only tool it needs

Then we hand it to the main claw via BackgroundAgents:

AIAgent agent = chatClient.AsHarnessAgent(new HarnessAgentOptions
{
    BackgroundAgents = [research],   // exposes the background_agents_* tools
    // … skills, shell, file access, tools …
});

In Python it’s the background_agents argument:

research_agent = Agent(
    client,
    name="TickerResearchAgent",
    description="Searches the web for recent news about a single stock ticker.",
    instructions="You research a single ticker and return 3-4 factual bullet points.",
    tools=[client.get_web_search_tool()],   # the only tool it needs
)

agent = create_harness_agent(
    client=client,
    background_agents=[research_agent],   # exposes the background_agents_* tools
    # … skills, shell, file access, tools …
)

This gives the main agent a set of background_agents_* tools: it can start a research task per ticker, let them run in parallel, check on them, and collect the results – then summarize all three together. The fan-out is the agent’s decision; the harness handles the plumbing.

Run it

.NET

cd dotnet
dotnet run --project samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step03_ScalingCapabilities

Python

uv run python/samples/02-agents/harness/build_your_own_claw/claw_step03_scaling_capabilities.py

Then try these in order (the sample starts in execute mode – quick lookups don’t need a plan):

  1. Value MSFT for me. – the agent loads the valuation skill, runs its script, and reports a

fair-value estimate.

  1. How risky is my portfolio? – it reads portfolio.csv and loads the risk-scoring skill.
  2. /mode plan, then Tidy up my trade confirmations. – switching to plan mode first makes the agent inspect working/confirmations/ and propose a reorganization plan before touching anything; once you approve it switches to execute and uses the shell to move and rename the files, prompting you to approve each command.
  1. Work out the total value of my portfolio. – it writes and runs Python to compute the answer.
  1. Research MSFT, NVDA and SPY and summarize the latest news. – it fans the tickers out to the background research agent and aggregates the findings.
  1. What's the capital of France? – with the financial-agent-rules Foundry skill published and enabled (see below), the agent loads it, recognizes the question is off-topic, and politely declines, steering you back to finance.

To enable the opt-in Foundry skills: set FOUNDRY_TOOLBOX_MCP_SERVER_URL.

The runnable samples

Use these building blocks in your own agent

As always, none of this is locked inside the harness. Each capability is a plain context provider or an executor/tool you can pick up on its own. Here’s where to find them:

Feature .NET (type — namespace) Python (import)
Skills AgentSkillsProvider / AgentSkillsProviderBuilderMicrosoft.Agents.AI (MCP skills via Microsoft.Agents.AI.Mcp) from agent_framework import SkillsProvider, MCPSkillsSource
Shell LocalShellExecutor / ShellPolicyMicrosoft.Agents.AI.Tools.Shell from agent_framework_tools.shell import LocalShellTool, ShellPolicy
CodeAct HyperlightCodeActProviderMicrosoft.Agents.AI.Hyperlight from agent_framework_monty import MontyCodeActProvider
Background agents BackgroundAgentsProviderMicrosoft.Agents.AI from agent_framework import BackgroundAgentsProvider

In .NET, skills and background agents ship in the Microsoft.Agents.AI package, the shell in Microsoft.Agents.AI.Tools.Shell, and Hyperlight CodeAct in Microsoft.Agents.AI.Hyperlight. In Python, skills and background agents come from agent-framework, the shell from agent-framework-tools, and Monty CodeAct from agent-framework-monty. The providers plug in through an agent’s context providers (and the shell as an executor) – the same wiring the harness does on your behalf.

What’s next

Our claw can now teach itself new skills, restructure files, compute answers it writes itself, and work in parallel. In the final part we make it production-ready: observability with traces and logs, governance and data protection, evaluation, and deployment as a hosted Foundry agent.

📚 The series

Part of Build your own claw with Microsoft Agent Framework:

The post Agent Harness: Scaling the claw or harness capabilities appeared first on Microsoft Agent Framework.

Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories