mirror of
https://github.com/Piebald-AI/claude-code-system-prompts.git
synced 2026-06-19 11:30:17 +08:00
170 lines
6.1 KiB
Markdown
170 lines
6.1 KiB
Markdown
<!--
|
|
name: 'Data: Tool use reference — C#'
|
|
description: C# tool use reference including defining tools and reconstructing response content for the follow-up assistant message
|
|
ccVersion: 2.1.182
|
|
-->
|
|
# Tool Use — C#
|
|
|
|
For conceptual overview (tool definitions, tool choice, tips), see [shared/tool-use-concepts.md](../../shared/tool-use-concepts.md).
|
|
|
|
## Tool Use
|
|
|
|
### Defining a tool
|
|
|
|
`Tool` (NOT `ToolParam`) with an `InputSchema` record. `InputSchema.Type` is auto-set to `"object"` by the constructor — don't set it. `ToolUnion` has an implicit conversion from `Tool`, triggered by the collection expression `[...]`.
|
|
|
|
```csharp
|
|
using System.Text.Json;
|
|
using Anthropic.Models.Messages;
|
|
|
|
var parameters = new MessageCreateParams
|
|
{
|
|
Model = Model.ClaudeSonnet4_6,
|
|
MaxTokens = 16000,
|
|
Tools = [
|
|
new Tool {
|
|
Name = "get_weather",
|
|
Description = "Get the current weather in a given location",
|
|
InputSchema = new() {
|
|
Properties = new Dictionary<string, JsonElement> {
|
|
["location"] = JsonSerializer.SerializeToElement(
|
|
new { type = "string", description = "City name" }),
|
|
},
|
|
Required = ["location"],
|
|
},
|
|
},
|
|
],
|
|
Messages = [new() { Role = Role.User, Content = "Weather in Paris?" }],
|
|
};
|
|
```
|
|
|
|
Derived from `anthropic-sdk-csharp/src/Anthropic/Models/Messages/Tool.cs` and `ToolUnion.cs:799` (implicit conversion).
|
|
|
|
See [shared tool use concepts](../../shared/tool-use-concepts.md) for the loop pattern.
|
|
### Converting response content to the follow-up assistant message
|
|
|
|
When echoing Claude's response back in the assistant turn, **there is no `.ToParam()` helper** — manually reconstruct each `ContentBlock` variant as its `*Param` counterpart. Do NOT use `new ContentBlockParam(block.Json)`: it compiles and serializes, but `.Value` stays `null` so `TryPick*`/`Validate()` fail (degraded JSON pass-through, not the typed path).
|
|
|
|
```csharp
|
|
using Anthropic.Models.Messages;
|
|
|
|
Message response = await client.Messages.Create(parameters);
|
|
|
|
// No .ToParam() — reconstruct per variant. Implicit conversions from each
|
|
// *Param type to ContentBlockParam mean no explicit wrapper.
|
|
List<ContentBlockParam> assistantContent = [];
|
|
List<ContentBlockParam> toolResults = [];
|
|
foreach (ContentBlock block in response.Content)
|
|
{
|
|
if (block.TryPickText(out TextBlock? text))
|
|
{
|
|
assistantContent.Add(new TextBlockParam { Text = text.Text });
|
|
}
|
|
else if (block.TryPickThinking(out ThinkingBlock? thinking))
|
|
{
|
|
// Signature MUST be preserved — the API rejects tampering
|
|
assistantContent.Add(new ThinkingBlockParam
|
|
{
|
|
Thinking = thinking.Thinking,
|
|
Signature = thinking.Signature,
|
|
});
|
|
}
|
|
else if (block.TryPickRedactedThinking(out RedactedThinkingBlock? redacted))
|
|
{
|
|
assistantContent.Add(new RedactedThinkingBlockParam { Data = redacted.Data });
|
|
}
|
|
else if (block.TryPickToolUse(out ToolUseBlock? toolUse))
|
|
{
|
|
// ToolUseBlock has required Caller; ToolUseBlockParam.Caller is optional — don't copy it
|
|
assistantContent.Add(new ToolUseBlockParam
|
|
{
|
|
ID = toolUse.ID,
|
|
Name = toolUse.Name,
|
|
Input = toolUse.Input,
|
|
});
|
|
// Execute the tool; collect ONE result per tool_use block — the API
|
|
// rejects the follow-up if any tool_use ID lacks a matching tool_result.
|
|
string result = ExecuteYourTool(toolUse.Name, toolUse.Input);
|
|
toolResults.Add(new ToolResultBlockParam
|
|
{
|
|
ToolUseID = toolUse.ID,
|
|
Content = result,
|
|
});
|
|
}
|
|
}
|
|
|
|
// Follow-up: prior messages + assistant echo + user tool_result(s)
|
|
List<MessageParam> followUpMessages =
|
|
[
|
|
.. parameters.Messages,
|
|
new() { Role = Role.Assistant, Content = assistantContent },
|
|
new() { Role = Role.User, Content = toolResults },
|
|
];
|
|
```
|
|
|
|
`ToolResultBlockParam` has no tuple constructor — use the object initializer. `Content` is a string-or-list union; a plain `string` implicitly converts.
|
|
|
|
---
|
|
|
|
## Structured Output
|
|
|
|
```csharp
|
|
OutputConfig = new OutputConfig {
|
|
Format = new JsonOutputFormat {
|
|
Schema = new Dictionary<string, JsonElement> {
|
|
["type"] = JsonSerializer.SerializeToElement("object"),
|
|
["properties"] = JsonSerializer.SerializeToElement(
|
|
new { name = new { type = "string" } }),
|
|
["required"] = JsonSerializer.SerializeToElement(new[] { "name" }),
|
|
},
|
|
},
|
|
},
|
|
```
|
|
|
|
`JsonOutputFormat.Type` is auto-set to `"json_schema"` by the constructor. `Schema` is `required`.
|
|
|
|
---
|
|
|
|
## Anthropic-Defined Tools
|
|
|
|
Web search, bash, text editor, and code execution are Anthropic-defined tools with built-in schemas. Web search and code execution are server-executed; bash and text editor are client-executed (you handle the `tool_use` locally — see `shared/tool-use-concepts.md`). Type names are version-suffixed; constructors auto-set `name`/`type`. **Wrap each in `new ToolUnion(...)` explicitly.**
|
|
|
|
```csharp
|
|
Tools = [
|
|
new ToolUnion(new WebSearchTool20260209()),
|
|
new ToolUnion(new ToolBash20250124()),
|
|
new ToolUnion(new ToolTextEditor20250728()),
|
|
new ToolUnion(new CodeExecutionTool20260120()),
|
|
],
|
|
```
|
|
|
|
Also available: `new ToolUnion(new WebFetchTool20260209())`, `new ToolUnion(new MemoryTool20250818())`. `WebSearchTool20260209` optionals: `AllowedDomains`, `BlockedDomains`, `MaxUses`, `UserLocation`.
|
|
|
|
---
|
|
|
|
## Tool Runner (Beta)
|
|
|
|
The C# SDK provides a `BetaToolRunner` for automatic tool execution loops. Define tools with raw JSON schemas, and the runner handles the API call → tool execution → result feedback loop.
|
|
|
|
```csharp
|
|
using Anthropic.Models.Beta.Messages;
|
|
|
|
// Define tools and create params as shown in the Tool Use section above,
|
|
// but using the beta namespace types (BetaToolUnion, etc.)
|
|
var runner = client.Beta.Messages.ToolRunner(betaParams);
|
|
|
|
await foreach (BetaMessage message in runner)
|
|
{
|
|
foreach (var block in message.Content)
|
|
{
|
|
if (block.TryPickText(out var text))
|
|
{
|
|
Console.WriteLine(text.Text);
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|