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

Bigger Copilot Bills? GitHub Now Lets You Peek into Per-Model Token Usage

1 Share
GitHub has added input, output and cache token counts to its downloadable AI usage reports, providing more detail about how individual models consume AI Credits.
Read the whole story
alvinashcraft
4 hours ago
reply
Pennsylvania, USA
Share this story
Delete

Tig’s Toolbox for Product Management

1 Share

People ask me which of my posts to read if they want to get better at product management. I never had a good answer, because I did not write them as a curriculum. I wrote them because something was on fire.

This is the inventory.

The job, as I have been saying since 2015:

Builders create the product. The PM’s job is to generate clarity and commitment so builders can create magic, and to protect one coherent customer experience across the rest of the company.

Everything below is a tool for doing that job. If a post does not help you create clarity, make a choice, or install a mechanism, it is not in the toolbox.

1. What the job is

Start here if you think PM means owning the backlog, insulating engineers from customers, or being the smartest person in the room.

2. Work backwards from the customer

Never start with the technology; start with the customer experience…then invent what has to be true.

3. Write it down…then debate it.

If it is not written in complete sentences, it is not thought through. If it is not debated early, execution will suck later because folks will be fundamentally misaligned.

4. Choose. Starve the rest. Ship.

A roadmap is not strategy. A long list of “priorities” is peanut butter. Dates must be treated with sanctity.

5. Install mechanisms, or it is just a slide

Good intentions never survive contact with a calendar. If the behavior has to happen when you are not in the room, it needs an owner, a tool, adoption, and inspection.

If you want the index of the mental models themselves, that is Mental Models and Tools to Achieve Clarity of Thought. This post is the product-management cut.

How to use this

Do not read it top to bottom.

If you cannot explain the PM job without saying “backlog,” start at 1. If you are about to build something, start at 2 and write a PRFAQ. If the room is using the same words and meaning different things, start at 3. If everything is urgent, start at 4. If it only works when you personally hero it, start at 5.

I am putting these under a Product Management category on tig.log so the set is findable as a set. If I missed a tool, tell me.

The post Tig’s Toolbox for Product Management first appeared on tig.log.
Read the whole story
alvinashcraft
4 hours ago
reply
Pennsylvania, USA
Share this story
Delete

SE Radio 733: Max Corbridge on Securing AI Agents

1 Share

Max Corbridge, an ethical hacker and red teamer who is co-founder and CEO of Secure Agentics, speaks with SE Radio host Amey Ambade about how AI agents get attacked and what engineers can actually do to defend them. Drawing on years of offensive security work, Corbridge frames agents as a new and largely undefended attack surface: the industry has handed AI systems autonomy and the ability to act in the real world while carrying forward prompt injection, a flaw the frontier labs themselves describe as effectively unsolvable. He likens the moment to the early, lawless days of the web, when SQL injection was everywhere and adoption ran far ahead of security.

The conversation builds from first principles as Corbridge explains what separates an agent from ordinary software and why three properties make them hard to secure: they are non-deterministic, their language-model core can be coerced, and they are increasingly interconnected through MCP servers, other agents, databases, and email. Turning to the attack surface, Corbridge lays out his "lethal trifecta" (a vulnerable core, dense interconnection, and security tooling that has not caught up) and contrasts the decades of layered defenses protecting an ordinary email inbox with the thin protection around agents that take autonomous actions on critical systems.

The heart of the episode is defense. Corbridge orders practices by leverage: least-privilege access and privilege separation, sandboxing where feasible, imperfect-but-useful guardrails as one layer of defense in depth, and human-in-the-loop for irreversible actions (which he notes is contentious and does not scale). The discussion closes on detecting a compromised or drifting agent, the value of watching an agent's chain-of-thought reasoning alongside its actions, the open-source tooling landscape (including Corbridge's own project, Adrian), and his central advice: build security in proactively, define what good agent behavior looks like up front, and avoid bolting it on after agents have already spread across the business.





Download audio: https://traffic.libsyn.com/secure/seradio/733-max-corbridge-securing-ai-agents.mp3?dest-id=23379
Read the whole story
alvinashcraft
4 hours ago
reply
Pennsylvania, USA
Share this story
Delete

Grok 4.6 Shows How Fast Your AI Options Are Expanding

1 Share
From: AIDailyBrief
Duration: 24:35
Views: 390

Grok 4.6 is fast, capable, dramatically cheaper than the leading models—and another sign that AI users have more genuinely strong options than ever. NLW explores how competition from xAI, Chinese labs, and open-weight models is giving individuals and businesses more freedom to choose the right combination of intelligence, speed, and price. In the headlines: massive funding rounds, booming infrastructure demand, and changes to the White House model-testing framework.

The AI Daily Brief helps you understand the most important news and discussions in AI.
Subscribe to the podcast version of The AI Daily Brief wherever you listen: https://pod.link/1680633614
Get it ad free at http://patreon.com/aidailybrief
Learn more about the show https://aidailybrief.ai/

Read the whole story
alvinashcraft
4 hours ago
reply
Pennsylvania, USA
Share this story
Delete

Shiny.DocumentDb — Hidden Gems

1 Share
NuGet package Shiny.DocumentDb

Every release post covers the headline. Thirteen major versions of those leaves a lot of API that shipped in a bullet list and never got explained — which is a shame, because some of it is the stuff I actually reach for most.

This post is the sweep. Three features get real depth because they change how you’d write the code around them, and the rest are quick hits. Nothing here is new in v13; all of it is in the box today.


1. The store already knows what changed

You have a document in the database and an object in memory that’s been edited by a form, a merge, or an LLM. The question every app asks next is what actually changed? — for an audit line, for a confirmation screen, for an “are you sure, this touches 4 fields” dialog.

The usual answer is to write a comparer. You don’t have to:

var patch = await store.GetDiff<Order>(order.Id, edited);
// JsonPatchDocument<Order>, RFC 6902 — null when no such document exists
foreach (var op in patch!.Operations)
logger.LogInformation("{Op} {Path} => {Value}", op.Op, op.Path, op.Value);

GetDiff reads the stored document, compares it against the candidate, and hands back a JsonPatchDocument<T> — a real RFC 6902 patch, not a string. Note the shape of it: this is a read. You haven’t written anything yet, so it’s the thing to call before the save, which is exactly when the question gets asked.

Writing only what changed

The write side has the same idea from the other direction. A full Update replaces the stored body; an Upsert deep-merges it, RFC 7396:

// only the non-null properties are touched; everything else stays as stored
await store.Upsert(new Order { Id = id, Status = "shipped" });

That’s the default behaviour of Upsert on every provider. Its inverse — replace-on-update rather than merge — is Upsert(patch, patchIfUpdate: false), which the relational providers implement and the rest refuse rather than fake. There’s a matching Update(document, patch: true) when the document must already exist.

For a genuine partial update through a typed object, remember the type has to be able to express “unset”: JsonIgnoreCondition.WhenWritingNull on the properties, or use the JSON collection lane where the body is a JsonObject and absent means absent.

And when it’s one field, skip the object entirely:

await store.SetProperty<Order>(id, o => o.Status, "shipped");
await store.RemoveProperty<Order>(id, o => o.CancelReason);

Both return boolfalse when no such document — and both are a single statement against the JSON column. No read, no round trip, no lost update from the write you didn’t know was concurrent with yours.

Then turn on temporal and it gets silly

Map a type as temporal and every one of those writes leaves a version behind:

options.ConfigureDocument<Order>(cfg => cfg.MapTemporal(o => o.Retention = TimeSpan.FromDays(90)));
var temporal = (ITemporalDocumentStore)store;
await temporal.History<Order>(id); // every version
await temporal.AsOf<Order>(id, lastTuesday); // the document as it was
await temporal.ChangesByActor<Order>("user:42"); // everything one actor touched
await temporal.GetDiffBetween<Order>(id, 3, 7); // a patch between two versions
await temporal.Restore<Order>(id, version: 3); // put it back

That’s an audit trail, a point-in-time read, a per-user change log and an undo button, from one line of configuration. It works on every provider — relational, Cosmos, MongoDB, LiteDB, IndexedDB — through ITemporalDocumentStore, which you probe for rather than assume (store is ITemporalDocumentStore).

The reason I keep pointing at this one: almost everybody hand-rolls a ChangeLog table, and it’s almost always worse than this, because a hand-rolled one records the fields somebody remembered to record.


2. Stop deserializing JSON just to serialize it again

Here’s the shape of an enormous number of API endpoints:

app.MapGet("/orders/{id}", async (string id, IDocumentStore store) =>
{
var order = await store.Get<Order>(id); // JSON -> Order
return Results.Ok(order); // Order -> JSON
});

The database handed you a perfectly good JSON document. You parsed it into an object graph, allocated every string and list in it, and then serialized it straight back into bytes that are — modulo whitespace — what you started with. Order did no work. It was overhead with a type name.

DocumentDb stores JSON. So take JSON:

app.MapGet("/orders/{id}", async (string id, IDocumentStore store) =>
{
var raw = await store.Query<Order>().Where(o => o.Id == id).FirstOrDefaultRawJson();
return raw is null ? Results.NotFound() : Results.Content(raw, "application/json");
});

And for a list, don’t even materialize the list — stream it into the response as it comes off the reader:

ctx.Response.ContentType = "application/json";
await store.Query<Order>()
.Where(o => o.Status == "open")
.OrderByDescending(o => o.CreatedAt)
.WriteJsonArrayTo(ctx.Response.Body, ct);

The point that makes this usable rather than a curiosity: you still build the query with the typed surface. Where, OrderBy, Paginate, global query filters, soft delete, tenancy — all of it applies, because only the terminal changed. You get the compiler checking your predicate and the database handing back bytes.

There’s a node lane too, when you want to touch the JSON before it leaves: ToJsonList, ToJsonAsyncEnumerable, FirstJson / FirstOrDefaultJson, SingleJson / SingleOrDefaultJson, ToJsonCursorPage — all returning JsonObject, all built on the same RawJsonRows primitive the raw lane uses.

The fidelity rules, because they matter

  • On the relational providers and Cosmos DB, these are the persisted bytes, untouched. Zero parses.
  • Everywhere else the provider has to materialize T to finish the query, so the body is re-serialized through the type’s JsonTypeInfo. Same JSON, same API — but the round trip is real, and you should expect no win.
  • Materialized computed properties live outside the body, so they don’t appear. A DocumentBlob shows up as its metadata envelope, not its payload.
  • A type with encrypted properties throws. The stored body is ciphertext, and only the typed terminals decrypt.

That last group is why there’s a SupportsRawJson flag on the query. Test it rather than catching the throw when the JSON lane is an optimization and the typed path is still correct — which is exactly how the built-in OData and AI surfaces pick a lane.

Trim the fields on the way out

Pair it with the string projection when the caller only wants some of the document:

// a REST ?fields= sparse fieldset, resolved at runtime
var rows = await store.Query<Order>()
.Project("id, number, total, customer.name as customer, lower(status) as status")
.ToJsonList();

Dotted paths reach into nested objects and become first-class output keys. Scalar functions from the string grammar (lower, length, substring, year, soundex, …) can be projected too, and require an alias. Relational providers do this in SQL; the document providers do it client-side.


3. Everything soft delete taught me about extensibility

Soft delete shipped in v12 as a one-liner:

options.ConfigureDocument<Customer>(cfg => cfg.AddSoftDelete(x => x.IsDeleted));

Deletes set the flag instead of deleting, and every read hides flagged documents. The part worth writing about is that nothing in any store knows it exists. Here is, essentially, the whole implementation:

options.AddInterceptor(interceptor); // cancel the delete, set the flag instead
options.AddBulkInterceptor(interceptor); // same for ExecuteDelete / Clear
options.Mappings.AddQueryFilter("soft-delete", mapping.NotDeleted);

Three public calls. No provider changes, no if (softDelete) anywhere in the query pipeline, and it works identically on all twenty-odd backends because it never went near one.

The two primitives it’s made of are worth knowing on their own.

ctx.Cancel() — replace a write, don’t just watch it

An interceptor’s BeforeWrite can substitute itself for the write:

public class ArchiveOnDelete : IDocumentInterceptor
{
public async Task BeforeWrite(DocumentWriteContext ctx, CancellationToken ct)
{
if (ctx.Operation != DocumentOperation.Delete || ctx.DocumentType != typeof(Order))
return;
var order = await ctx.Store.Get<Order>(ctx.Id!, cancellationToken: ct);
if (order != null)
{
await ctx.Session
.Add(new ArchivedOrder { Id = order.Id, Body = order, ArchivedAt = DateTimeOffset.UtcNow })
.SaveChanges(ct);
}
ctx.Cancel(); // the store performs no delete, and reports success to the caller
}
public Task AfterWrite(DocumentWriteContext ctx, CancellationToken ct) => Task.CompletedTask;
}

Cancel() means the store does nothing, no AfterWrite runs, and no change notification is published — the caller gets the outcome you name (Cancel(succeeded: false) reports failure). It’s only legal inside BeforeWrite; calling it later throws rather than silently doing nothing. ctx.Session is scoped to the write’s own transaction, so the archive row commits with the operation that caused it, or not at all.

That’s an append-only archive, in about twenty lines, that no provider needed to hear about.

Named query filters — and lifting them one query at a time

A global filter usually gets registered anonymously and then becomes a problem the first time an admin screen needs to see past it. Give it a name:

options.ConfigureDocument<Order>(cfg => cfg.AddQueryFilter("archived", o => !o.IsArchived));
store.Query<Order>().IgnoreQueryFilters("archived"); // just this one
store.Query<Order>().IgnoreQueryFilters(); // all of them

Which is exactly what soft delete’s own IncludeDeleted() does — it’s a one-line extension over IgnoreQueryFilters(SoftDelete.FilterName). Features here are meant to be built this way: extension methods over public hooks, so an optional feature never becomes a member on an options class that every provider has to carry.


Quick hits

ToQueryString() — see what your LINQ actually became, without running it.

var q = store.Query<Order>().Where(o => o.Total > 100).OrderBy(o => o.CreatedAt).ToQueryString();
Console.WriteLine(q.Sql); // the provider's SQL (or MongoDB's rendered BSON)
Console.WriteLine(q.Parameters); // the bound values

Relational providers and Cosmos return SQL; MongoDB returns its filter as JSON; the in-memory evaluators (LiteDB, IndexedDB) throw, because there’s nothing to show.

Cursor paginationSkip/Take gets slower the deeper you go and shifts under concurrent writes. Keyset paging doesn’t:

CursorPage<Order> page = await store.Query<Order>()
.Where(o => o.Status == "open")
.OrderByDescending(o => o.CreatedAt)
.ToCursorPage(cursor, take: 50);
page.Items; // this page
page.NextCursor; // opaque token; null means that was the last page
page.HasMore;

O(log n) per page with an index on the sort key, an Id tiebreaker appended for you, and a shape hash so a cursor can’t be replayed against a differently-filtered query. ToJsonCursorPage is the same thing in the JSON lane. There’s no total count — that’s what Paginate is for.

DocumentFunctions.Soundex — fuzzy name matching that pushes down to the engine:

store.Query<Person>().Where(p => DocumentFunctions.Soundex(p.Name) == DocumentFunctions.Soundex("Smith"));

Native SOUNDEX() on SQL Server and MySQL, fuzzystrmatch on PostgreSQL, a registered UDF where there’s nothing built in.

NotifyOnChange() — a change feed scoped to one query, as an IAsyncEnumerable:

await foreach (var change in store.Query<Order>().Where(o => o.Status == "open").NotifyOnChange(ct))
Console.WriteLine($"{change.ChangeType}: {change.Id}");

IDocumentSeeder — versioned, provider-agnostic seed data with a marker so it runs once:

public class ProductSeeder : IDocumentSeeder
{
public string Name => "products";
public int Version => 3; // bump to re-run
public Task SeedAsync(IDocumentStore store, CancellationToken ct)
=> store.BatchInsert(Products, cancellationToken: ct);
}
services.AddDocumentSeeder<ProductSeeder>(); // runs at startup

JSON Schema validation (Shiny.DocumentDb.JsonSchema) — draft 2020-12, checked against the exact bytes about to hit disk:

options.ConfigureDocument<Order>(cfg => cfg.MapJsonSchemaFromFile("schemas/order.json"));

Schema-free doesn’t have to mean unvalidated, and it’s per type — validate the two documents that matter and leave the rest open.

Computed properties — a value derived from other fields that you can still filter and sort on:

options.ConfigureDocument<OrderLine>(cfg =>
cfg.MapComputedProperty(x => x.LineTotal, x => x.Quantity * x.UnitPrice, indexed: true));

indexed: true asks for a materialized, indexable computed column where the backend has one; without it, it’s an alias expanded into the query.

IDocumentMaintenanceClearAll() wipes every type including temporal, spatial and vector sidecars (tests and dev resets, not tenant-scoped), and SweepOrphanedBlobs<T>() collects blob rows whose owning document went away out of band. Probe for it: store is IDocumentMaintenance.


And the tools nobody’s seen

The two most under-advertised things in the project aren’t API at all. ShinyDocDbMyAdmin has had a terminal front end since v12.5 — the same tool as the web UI, as a dotnet tool, over SSH — and the web one has shipped as a Docker Desktop extension since 13.0.1, which hands it every database container already running on your machine, connected.

Both deserve their own post with screenshots, and they’re getting one. In the meantime: the admin docs.

Read the whole story
alvinashcraft
4 hours ago
reply
Pennsylvania, USA
Share this story
Delete

Microsoft Agent Framework and Foundry

1 Share

In the .NET development world the two most significant frameworks for AI are Microsoft Agent Framework and Microsoft Foundry. Together, they create a powerful ecosystem for building, deploying, and managing AI agents that can automate tasks, respond to user queries, and integrate seamlessly with various services. This post will explore how these two technologies relate to each other, their key features, and their real-world applications.

What is Microsoft Agent Framework?

To quickly review, Microsoft Agent Framework is a development framework designed specifically for creating AI agents. These agents are capable of interacting with various services and data sources, making them versatile tools for developers. The framework provides a rich set of tools and libraries that enable developers to build intelligent applications that can automate tasks, respond to user queries, and integrate with other systems.

Key Features of Microsoft Agent Framework

  1. Development Tools: The framework includes a variety of libraries and APIs that simplify the process of building AI agents. Developers can leverage these tools to create agents that can understand natural language, process data, and perform complex tasks.
  2. Integration Capabilities: The framework is designed to work with various data sources and services, allowing developers to create agents that can pull information from multiple platforms and provide comprehensive responses to user queries.
  3. Flexibility: Developers can use the Microsoft Agent Framework alongside other frameworks, such as the OpenAI Agents SDK, to create a wide range of applications, from simple chatbots to complex AI-driven solutions.

For much more on Microsoft Agent Framework and agentics in general see the blog posts beginning here.

What is Microsoft Foundry?

Microsoft Foundry is a managed platform that provides a comprehensive environment for building, deploying, and scaling AI agents. It offers a suite of tools and services that enable developers to utilize various AI models and frameworks, making it easier to create sophisticated applications.

Key Features of Microsoft Foundry

  1. Managed Environment: Foundry provides a fully managed environment, which means developers can focus on building their applications without worrying about the underlying infrastructure. This allows for faster development cycles and easier scaling.
  2. AI Model Integration: Foundry supports a wide range of AI models and tools, enabling developers to leverage the latest advancements in AI technology. This integration allows for the creation of more intelligent and capable agents.
  3. Governance and Observability: Foundry includes features that help organizations maintain compliance with regulations, making it particularly beneficial for industries that require strict governance, such as finance and healthcare.

The Relationship Between Microsoft Agent Framework and Microsoft Foundry

The relationship between the Microsoft Agent Framework and Microsoft Foundry is one of synergy and integration. Together, they provide a robust environment for developing AI agents that can handle complex tasks and workflows. Here are some key aspects of their relationship:

1. Integration of Services

The Foundry Agent Service acts as a bridge between the Microsoft Agent Framework and various data sources and other agents. This integration enables seamless communication and data exchange, which is crucial for developing multi-agent workflows that can manage complex business processes.

Additionally, the Responses API serves as a single entry point for accessing Foundry models and tools. This allows developers to build agents using the Agent Framework while leveraging the capabilities of Foundry, creating a more cohesive development experience.

2. Multi-Agent Workflows

Both Foundry and Microsoft Agent Framework support the creation of multi-agent workflows. This feature allows developers to orchestrate complex, multi-step processes, enhancing the capabilities of AI applications. By enabling multiple agents to work together, organizations can automate intricate workflows that would be challenging to manage with a single agent.

3. Identity and Security

Security is a paramount concern in the development of AI applications. The Microsoft Agent Framework utilizes Microsoft Entra ID for managing agent identities, ensuring secure authentication and authorization. With Foundry you get this and many other infrastructure features out of the box.

4. Development Flexibility

The combination of the Microsoft Agent Framework and Microsoft Foundry offers developers significant flexibility in how they create agents. They can choose to build agents using the Microsoft Agent Framework and have them hosted in Foundry, or they can create AI applications directly with Foundry. This flexibility is essential for meeting the diverse needs of businesses and organizations.

For more on this, be sure to watch my video interview of Bruno Capuano and Jon Galloway, both of Microsoft, where, among other things, Bruno demonstrates how easy it is to have Foundry host a Microsoft Agent Framework application.

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