Read more of this story at Slashdot.
Read more of this story at Slashdot.
In this stream, I'll keep the search going on why my test app for Rocks is giving odd errors based on the number of tasks in-flight.
https://github.com/JasonBock/Rocks/issues/362
#dotnet #csharp
Microsoft’s Agent Framework treats retrieval as a first‑class capability so agents can fetch only what they need (or always fetch), attach source metadata, and call search as a tool during reasoning. The result: more efficient, auditable, and controllable Retrieval‑Augmented Generation (RAG) for production assistants.

Key terms
Core components
How RAG is implemented — simple flow
1) Index your docs into a VectorStore:
Injection mechanics — what actually gets passed to the model
Defaults and practical parameter guidance
BeforeAIInvoke vs OnDemandFunctionCalling — choose by use case
Example: a multi-step agentic sequence (conceptual)
1) User: “How do I roll back build 1.2.3?”
2) Agent (thinking): Not sure. Calls Search tool with query “roll back build 1.2.3 runbook”.
3) Search returns runbook chunks A, B (with source URLs).
4) Agent inspects chunks, calls a “Validate-Runbook” tool to confirm commands are safe.
5) Agent composes final answer quoting steps and adds “[source: Runbook / sectionX | URL]” inline for each step.
Prompt and output formatting — keep answers auditable
Code outline (C#) — on‑demand search example (conceptual)
// 1) Create embedding generator (IEmbeddingGenerator), vector store and TextSearchStore
var embeddingGenerator = /* AzureOpenAI embedding client */;
var vectorStore = new InMemoryVectorStore(new() { EmbeddingGenerator = embeddingGenerator });
using var textSearchStore = new TextSearchStore(vectorStore, collectionName: “Docs”, vectorDimensions: 1536);
// 2) Create TextSearchProvider with on‑demand behavior
var options = new TextSearchProviderOptions { SearchTime = TextSearchProviderOptions.TextSearchBehavior.OnDemandFunctionCalling, TopK = 4 };
var textSearchProvider = new TextSearchProvider(textSearchStore, options);
// 3) Attach to agent thread
var agentThread = new ChatHistoryAgentThread();
agentThread.AIContextProviders.Add(textSearchProvider);
// 4) Invoke the agent — the agent may call the search tool during reasoning
var response = await agent.InvokeAsync(“How do I roll back build 1.2.3?”, agentThread).FirstAsync();
// response includes final answer; the framework supplies tool outputs when the agent invoked the search tool
Production checklist and operational considerations
Where to learn more
This blog post explores the new PRODUCT function in SQL Server 2025, which calculates the product of a set of numeric values — similar to how SUM and AVG work for addition and averaging, but for multiplication.
Prior to SQL Server 2025, SQL Server lacked a built-in way to compute the product of values in a set. You had to use workarounds like looping or user-defined aggregates. With PRODUCT, this is now a simple one-line expression.
PRODUCT supports both aggregate and analytic (windowed) forms and works with both ALL values (default) and DISTINCT values. Nulls are ignored, and the function is compatible with all numeric types except bit.
The first example illustrates how to use the new PRODUCT aggregate function in SQL Server 2025 to calculate the cumulative product of prices for each product across multiple orders. It also shows how to compute the product considering only distinct price values.
CREATE TABLE OrderDetail ( OrderId int, ProductId int, Price decimal(10, 4))INSERT INTO OrderDetail (OrderId, ProductId, Price) VALUES (1, 101, 136.87), (1, 102, 29.57), (1, 103, 396.85), (2, 101, 136.87), (2, 102, 29.57), (3, 101, 136.87), (3, 102, 29.57), (4, 101, 149.22), (4, 102, 29.57)-- Compute product of all prices and distinct prices for each ProductIdSELECT ProductId, ProductOfPrices = PRODUCT(Price), ProductOfDistinctPrices = PRODUCT(DISTINCT Price)FROM OrderDetailGROUP BY ProductId
Result:
| ProductId | ProductOfPrices | ProductOfDistinctPrices |
|---|---|---|
| 101 | 382606053.829162 | 20423.741400 |
| 102 | 764548.953348 | 29.570000 |
| 103 | 396.850000 | 396.850000 |
OVER (PARTITION BY ...)This version computes the product for each row using a windowed aggregate (that is, using OVER rather than GROUP BY). This allows you to retain the detail rows (which were lost in the previous GROUP BY query) while also showing the total product per partition.
SELECT ProductId, OrderId, Price, ProductOfPrices = PRODUCT(Price) OVER (PARTITION BY ProductId)FROM OrderDetailORDER BY ProductId, OrderId
Result:
| ProductId | OrderId | Price | ProductOfPrices |
|---|---|---|---|
| 101 | 1 | 136.8700 | 382606053.829162 |
| 101 | 2 | 136.8700 | 382606053.829162 |
| 101 | 3 | 136.8700 | 382606053.829162 |
| 101 | 4 | 149.2200 | 382606053.829162 |
| 102 | 1 | 29.5700 | 764548.953348 |
| 102 | 2 | 29.5700 | 764548.953348 |
| 102 | 3 | 29.5700 | 764548.953348 |
| 102 | 4 | 29.5700 | 764548.953348 |
| 103 | 1 | 396.8500 | 396.850000 |
The next example uses PRODUCT to compute the compounded return for financial instruments over multiple time periods.
CREATE TABLE Instrument ( InstrumentId varchar(10), Period tinyint, RateOfReturn decimal(10, 4))INSERT INTO Instrument (InstrumentId, Period, RateOfReturn) VALUES ('BOND1', 1, 0.035), ('BOND1', 2, 0.0275), ('BOND1', 3, 0.0325), ('ETF1', 1, 0.08), ('ETF1', 2, -0.045), ('ETF1', 3, 0.06), ('STOCK1', 1, 0.125), ('STOCK1', 2, 0.095), ('STOCK1', 3, 0.113)-- Compute compounded return for each instrumentSELECT InstrumentId, CompoundedReturn = PRODUCT(1 + RateOfReturn) - 1, CompoundedReturnPercentage = FORMAT((PRODUCT(1 + RateOfReturn) - 1) * 100, 'N1') || '%'FROM InstrumentGROUP BY InstrumentId
Result:
| InstrumentId | CompoundedReturn | CompoundedReturnPercentage |
|---|---|---|
| BOND1 | 0.098026 | 9.8% |
| ETF1 | 0.093284 | 9.3% |
| STOCK1 | 0.371077 | 37.1% |
The above query calculates the compounded return for each instrument by taking the product of (1 + RateOfReturn) for all periods and then subtracting 1 to return the CompoundedReturn column. The CompoundedReturnPercentage column shows the same value formatted for display as a percentage with one decimal place.
Using BOND1 as an example, the calculation would be:
| (1 + 0.035) = 1.035 | * | Period 1 return |
| (1 + 0.0275) = 1.0275 | * | Period 2 return |
| (1 + 0.0325) = 1.0325 | = | Period 3 return |
| 1.098026 | – 1 = | Growth factor (includes the original principal $1) |
| 0.098026 | = | Compounded return (i.e., the percentage gain) |
| 9.8% | Isolated profit/loss percentage |
OVER (PARTITION BY ...)Like the first example, this version uses windowing with OVER to calculate the compounded return for each individual row.
SELECT InstrumentId, Period, RateOfReturn, CompoundedReturn = PRODUCT(1 + RateOfReturn) OVER (PARTITION BY InstrumentId) - 1, CompoundedReturnPercentage = FORMAT((PRODUCT(1 + RateOfReturn) OVER (PARTITION BY InstrumentId) - 1) * 100,'N1') || '%'FROM InstrumentORDER BY InstrumentId, Period
Result:
| InstrumentId | Period | RateOfReturn | CompoundedReturn | CompoundedReturnPercentage |
|---|---|---|---|---|
| BOND1 | 1 | 0.0350 | 0.098026 | 9.8% |
| BOND1 | 2 | 0.0275 | 0.098026 | 9.8% |
| BOND1 | 3 | 0.0325 | 0.098026 | 9.8% |
| ETF1 | 1 | 0.0800 | 0.093284 | 9.3% |
| ETF1 | 2 | -0.0450 | 0.093284 | 9.3% |
| ETF1 | 3 | 0.0600 | 0.093284 | 9.3% |
| STOCK1 | 1 | 0.1250 | 0.371077 | 37.1% |
| STOCK1 | 2 | 0.0950 | 0.371077 | 37.1% |
| STOCK1 | 3 | 0.1130 | 0.371077 | 37.1% |
The new PRODUCT function in SQL Server 2025 brings native multiplicative aggregation to T-SQL, eliminating the need for workarounds when calculating the product of a set of numeric values. It supports standard aggregation with GROUP BY, including DISTINCT, as well as analytic calculations using OVER (PARTITION BY ...) to preserve individual detail rows. As we demonstrated with product prices and compounded investment returns, PRODUCT makes calculations that depend on multiplying values across a set simpler and more expressive.
Happy coding!
This weekend, I finished tending my guide on AI that I had planted a seed for in my digital garden.
It includes links to various articles on it’s roles in environmental destruction, labor exploitation, economic failure, degrading critical thinking skills, and fascist empowerment.
I’ve also included my own personal thoughts, and links to various other thought pieces from other people that I’ve found useful or informative.