azure-ai-agents-persistent-dotnet
microsoft/skills
Azure AI Agents SDK for .NET을 사용하여 스레드, 메시지, 실행 및 도구를 통해 영구적인 AI 에이전트를 생성하고 관리하세요.
...모든 것을 확장하십시오Azure.AI.Agents.Persistent (.NET)
스레드, 메시지, 실행 및 도구를 사용하여 영구적인 AI 에이전트를 생성하고 관리하기 위한 저수준 SDK입니다.
설치
dotnet add package Azure.AI.Agents.Persistent --prerelease
dotnet add package Azure.Identity
현재 버전: 안정 버전 v1.1.0, 미리 보기 버전 v1.2.0-beta.8
환경 변수
PROJECT_ENDPOINT=https://.services.ai.azure.com/api/projects/ # 필수: Azure AI 프로젝트 엔드포인트
MODEL_DEPLOYMENT_NAME=gpt-4o-mini # 필수: 모델 배포 이름
AZURE_BING_CONNECTION_ID= # 필수: Bing 연결 리소스 ID
AZURE_AI_SEARCH_CONNECTION_ID= # 필수: Azure AI Search 연결 리소스 ID
AZURE_TOKEN_CREDENTIALS=prod # 프로덕션 환경에서 DefaultAzureCredential을 사용하는 경우에만 필수
인증
using Azure.AI.Agents.Persistent;
using Azure.Identity;
var projectEndpoint = Environment.GetEnvironmentVariable("PROJECT_ENDPOINT");
// 로컬 개발 환경: DefaultAzureCredential. 프로덕션: AZURE_TOKEN_CREDENTIALS=prod 또는 AZURE_TOKEN_CREDENTIALS=로 설정
var credential = new DefaultAzureCredential(
DefaultAzureCredential.DefaultEnvironmentVariableName
);
// 또는 프로덕션 환경에서 특정 자격 증명을 직접 사용할 수 있습니다:
// https://learn.microsoft.com/dotnet/api/overview/azure/identity-readme?view=azure-dotnet#credential-classes 참조
// var credential = new ManagedIdentityCredential();
PersistentAgentsClient client = new(projectEndpoint, credential);
클라이언트 계층 구조
PersistentAgentsClient
├── 관리 → 에이전트 CRUD 작업
├── 스레드 → 스레드 관리
├── 메시지 → 메시지 작업
├── 실행 → 실행 및 스트리밍
├── 파일 → 파일 업로드/다운로드
└── 벡터 스토어 → 벡터 스토어 관리
핵심 워크플로
1. 에이전트 생성
var modelDeploymentName = Environment.GetEnvironmentVariable("MODEL_DEPLOYMENT_NAME");
PersistentAgent agent = await client.Administration.CreateAgentAsync(
model: modelDeploymentName,
name: "Math Tutor",
instructions: "당신은 개인 수학 튜터입니다. 수학 문제에 답할 수 있는 코드를 작성하고 실행하세요.",
tools: [new CodeInterpreterToolDefinition()]
);
2. 스레드 및 메시지 생성
// 스레드 생성
PersistentAgentThread thread = await client.Threads.CreateThreadAsync();
// 메시지 생성
await client.Messages.CreateMessageAsync(
thread.Id,
MessageRole.User,
"`3x + 11 = 14`라는 방정식을 풀어야 합니다. 도와주실 수 있나요?"
);
3. 에이전트 실행 (폴링)
// 실행 생성
ThreadRun run = await client.Runs.CreateRunAsync(
thread.Id,
agent.Id,
additionalInstructions: "사용자를 Jane Doe라고 부르세요."
);
// 완료 여부 확인
do
{
await Task.Delay(TimeSpan.FromMilliseconds(500));
run = await client.Runs.GetRunAsync(thread.Id, run.Id);
}
while (run.Status == RunStatus.Queued || run.Status == RunStatus.InProgress);
// 메시지 가져오기
await foreach (PersistentThreadMessage message in client.Messages.GetMessagesAsync(
threadId: thread.Id,
order: ListSortOrder.Ascending))
{
Console.Write($"{message.Role}: ");
foreach (MessageContent content in message.ContentItems)
{
if (content is MessageTextContent textContent)
Console.WriteLine(textContent.Text);
}
}
4. 응답 스트리밍
AsyncCollectionResult stream = client.Runs.CreateRunStreamingAsync(
thread.Id,
agent.Id
);
await foreach (StreamingUpdate update in stream)
{
if (update.UpdateKind == StreamingUpdateReason.RunCreated)
{
Console.WriteLine("--- 실행 시작! ---");
}
else if (update is MessageContentUpdate contentUpdate)
{
Console.Write(contentUpdate.Text);
}
else if (update.UpdateKind == StreamingUpdateReason.RunCompleted)
{
Console.WriteLine("\n--- 실행 완료! ---");
}
}
5. 함수 호출
// 함수 도구 정의
FunctionToolDefinition weatherTool = new(
name: "getCurrentWeather",
description: "지정된 위치의 현재 날씨를 가져옵니다.",
parameters: BinaryData.FromObjectAsJson(new
{
Type = "object",
Properties = new
{
Location = new { Type = "string", Description = "도시 및 주, 예: San Francisco, CA" },
Unit = new { Type = "string", Enum = new[] { "c", "f" } }
},
Required = new[] { "location" }
}, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase })
);
// 함수를 사용하여 에이전트 생성
PersistentAgent agent = await client.Administration.CreateAgentAsync(
model: modelDeploymentName,
name: "Weather Bot",
instructions: "You are a weather bot.",
tools: [weatherTool]
);
// 폴링 중 함수 호출 처리
do
{
await Task.Delay(500);
run = await client.Runs.GetRunAsync(thread.Id, run.Id);
if (run.Status == RunStatus.RequiresAction
&& run.RequiredAction is SubmitToolOutputsAction submitAction)
{
List outputs = [];
foreach (RequiredToolCall toolCall in submitAction.ToolCalls)
{
if (toolCall is RequiredFunctionToolCall funcCall)
{
// 함수 실행 및 결과 가져오기
string result = ExecuteFunction(funcCall.Name, funcCall.Arguments);
outputs.Add(new ToolOutput(toolCall, result));
}
}
run = await client.Runs.SubmitToolOutputsToRunAsync(run, outputs, toolApprovals: null);
}
}
while (run.Status == RunStatus.Queued || run.Status == RunStatus.InProgress);
6. 벡터 스토어를 이용한 파일 검색
// 파일 업로드
PersistentAgentFileInfo file = await client.Files.UploadFileAsync(
filePath: "document.txt",
purpose: PersistentAgentFilePurpose.Agents
);
// 벡터 스토어 생성
PersistentAgentsVectorStore vectorStore = await client.VectorStores.CreateVectorStoreAsync(
fileIds: [file.Id],
name: "my_vector_store"
);
// 파일 검색 리소스 생성
FileSearchToolResource fileSearchResource = new();
fileSearchResource.VectorStoreIds.Add(vectorStore.Id);
// 파일 검색 기능을 갖춘 에이전트 생성
PersistentAgent agent = await client.Administration.CreateAgentAsync(
model: modelDeploymentName,
name: "Document Assistant",
instructions: "사용자가 문서에서 정보를 찾을 수 있도록 도와주세요.",
tools: [new FileSearchToolDefinition()],
toolResources: new ToolResources { FileSearch = fileSearchResource }
);
7. Bing Grounding
var bingConnectionId = Environment.GetEnvironmentVariable("AZURE_BING_CONNECTION_ID");
BingGroundingToolDefinition bingTool = new(
new BingGroundingSearchToolParameters(
[new BingGroundingSearchConfiguration(bingConnectionId)]
)
);
PersistentAgent agent = await client.Administration.CreateAgentAsync(
model: modelDeploymentName,
name: "Search Agent",
instructions: "Bing을 사용하여 시사 문제에 대한 질문에 답하세요.",
tools: [bingTool]
);
8. Azure AI Search
AzureAISearchToolResource searchResource = new(
connectionId: searchConnectionId,
indexName: "my_index",
topK: 5,
filter: "category eq 'documentation'",
queryType: AzureAISearchQueryType.Simple
);
PersistentAgent agent = await client.Administration.CreateAgentAsync(
model: modelDeploymentName,
name: "Search Agent",
instructions: "질문에 답하기 위해 문서 인덱스를 검색하세요.",
tools: [new AzureAISearchToolDefinition()],
toolResources: new ToolResources { AzureAISearch = searchResource }
);
9. 정리
await client.Threads.DeleteThreadAsync(thread.Id);
await client.Administration.DeleteAgentAsync(agent.Id);
await client.VectorStores.DeleteVectorStoreAsync(vectorStore.Id);
await client.Files.DeleteFileAsync(file.Id);
사용 가능한 도구
| 도구 | 클래스 | 용도 |
|---|---|---|
| 코드 인터프리터 | CodeInterpreterToolDefinition |
파이썬 코드 실행, 시각화 생성 |
| 파일 검색 | FileSearchToolDefinition |
벡터 스토어를 통해 업로드된 파일 검색 |
| 함수 호출 | FunctionToolDefinition |
사용자 정의 함수 호출 |
| Bing 그라운딩 | BingGroundingToolDefinition |
Bing을 통한 웹 검색 |
| Azure AI 검색 | AzureAISearchToolDefinition |
Azure AI Search 인덱스 검색 |
| OpenAPI | OpenApiToolDefinition |
OpenAPI 사양을 통해 외부 API 호출 |
| Azure Functions | AzureFunctionToolDefinition |
Azure Functions 호출 |
| MCP | MCPToolDefinition |
모델 컨텍스트 프로토콜 도구 |
| SharePoint | SharepointToolDefinition |
SharePoint 콘텐츠에 액세스 |
| Microsoft Fabric | MicrosoftFabricToolDefinition |
Fabric 데이터에 액세스 |
스트리밍 업데이트 유형
| 업데이트 유형 | 설명 |
|---|---|
StreamingUpdateReason.RunCreated |
실행 시작됨 |
StreamingUpdateReason.RunInProgress |
실행 처리 중 |
StreamingUpdateReason.RunCompleted |
실행 완료 |
StreamingUpdateReason.RunFailed |
실행 중 오류 발생 |
MessageContentUpdate |
텍스트 콘텐츠 청크 |
실행 단계 업데이트 |
단계 상태 변경 |
키 유형 참조
| 유형 | 목적 |
|---|---|
PersistentAgentsClient |
주요 진입점 |
PersistentAgent |
모델, 지침, 도구를 갖춘 에이전트 |
PersistentAgentThread |
대화 스레드 |
PersistentThreadMessage |
스레드 내의 메시지 |
스레드 실행 |
스레드에 대한 에이전트 실행 |
실행 상태 |
대기 중, 진행 중, 조치 필요, 완료, 실패 |
ToolResources |
통합된 도구 리소스 |
툴 출력 |
함수 호출 응답 |
모범 사례
- 클라이언트는 항상 해제해야 합니다 —
using문을 사용하거나 명시적으로 해제하십시오 - 적절한 지연 시간을 두고 폴링하십시오 — 상태 확인 간격은 500ms를 권장합니다
- 리소스 정리 — 작업 완료 시 스레드와 에이전트를 삭제하십시오
- 모든 실행 상태 처리 —
RequiresAction,Failed,Cancelled상태 확인 - 실시간 사용자 경험을 위해 스트리밍을 사용하십시오 — 폴링보다 더 나은 사용자 경험 제공
- 객체가 아닌 ID를 저장하십시오 — ID를 통해 에이전트/스레드를 참조하십시오
- 비동기 메서드 사용 — 모든 작업은 비동기 방식으로 수행되어야 함
오류 처리
using Azure;
try
{
var agent = await client.Administration.CreateAgentAsync(...);
}
catch (RequestFailedException ex) when (ex.Status == 404)
{
Console.WriteLine("리소스를 찾을 수 없음");
}
catch (RequestFailedException ex)
{
Console.WriteLine($"오류: {ex.Status} - {ex.ErrorCode}: {ex.Message}");
}
관련 SDK
| SDK | 용도 | 설치 |
|---|---|---|
Azure.AI.Agents.Persistent |
저수준 에이전트(이 SDK) | dotnet add package Azure.AI.Agents.Persistent |
Azure.AI.Projects |
고수준 프로젝트 클라이언트 | dotnet add package Azure.AI.Projects |
참고 링크
| 리소스 | URL |
|---|---|
| NuGet 패키지 | https://www.nuget.org/packages/Azure.AI.Agents.Persistent |
| API 참조 | https://learn.microsoft.com/dotnet/api/azure.ai.agents.persistent |
| GitHub 소스 | https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/ai/Azure.AI.Agents.Persistent |
| 예제 | https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/ai/Azure.AI.Agents.Persistent/samples |
---
name: azure-ai-agents-persistent-dotnet
description: Create and manage persistent AI agents with threads, messages, runs, and tools using the Azure AI Agents SDK for .NET.
license: MIT
---
# Azure.AI.Agents.Persistent (.NET)
Low-level SDK for creating and managing persistent AI agents with threads, messages, runs, and tools.
## Installation
```bash
dotnet add package Azure.AI.Agents.Persistent --prerelease
dotnet add package Azure.Identity
```
**Current Versions**: Stable v1.1.0, Preview v1.2.0-beta.8
## Environment Variables
```bash
PROJECT_ENDPOINT=https://<resource>.services.ai.azure.com/api/projects/<project> # Required: Azure AI project endpoint
MODEL_DEPLOYMENT_NAME=gpt-4o-mini # Required: model deployment name
AZURE_BING_CONNECTION_ID=<bing-connection-resource-id> # Required: Bing connection resource ID
AZURE_AI_SEARCH_CONNECTION_ID=<search-connection-resource-id> # Required: Azure AI Search connection resource ID
AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production
```
## Authentication
```csharp
using Azure.AI.Agents.Persistent;
using Azure.Identity;
var projectEndpoint = Environment.GetEnvironmentVariable("PROJECT_ENDPOINT");
// Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential>
var credential = new DefaultAzureCredential(
DefaultAzureCredential.DefaultEnvironmentVariableName
);
// Or use a specific credential directly in production:
// See https://learn.microsoft.com/dotnet/api/overview/azure/identity-readme?view=azure-dotnet#credential-classes
// var credential = new ManagedIdentityCredential();
PersistentAgentsClient client = new(projectEndpoint, credential);
```
## Client Hierarchy
```
PersistentAgentsClient
├── Administration → Agent CRUD operations
├── Threads → Thread management
├── Messages → Message operations
├── Runs → Run execution and streaming
├── Files → File upload/download
└── VectorStores → Vector store management
```
## Core Workflow
### 1. Create Agent
```csharp
var modelDeploymentName = Environment.GetEnvironmentVariable("MODEL_DEPLOYMENT_NAME");
PersistentAgent agent = await client.Administration.CreateAgentAsync(
model: modelDeploymentName,
name: "Math Tutor",
instructions: "You are a personal math tutor. Write and run code to answer math questions.",
tools: [new CodeInterpreterToolDefinition()]
);
```
### 2. Create Thread and Message
```csharp
// Create thread
PersistentAgentThread thread = await client.Threads.CreateThreadAsync();
// Create message
await client.Messages.CreateMessageAsync(
thread.Id,
MessageRole.User,
"I need to solve the equation `3x + 11 = 14`. Can you help me?"
);
```
### 3. Run Agent (Polling)
```csharp
// Create run
ThreadRun run = await client.Runs.CreateRunAsync(
thread.Id,
agent.Id,
additionalInstructions: "Please address the user as Jane Doe."
);
// Poll for completion
do
{
await Task.Delay(TimeSpan.FromMilliseconds(500));
run = await client.Runs.GetRunAsync(thread.Id, run.Id);
}
while (run.Status == RunStatus.Queued || run.Status == RunStatus.InProgress);
// Retrieve messages
await foreach (PersistentThreadMessage message in client.Messages.GetMessagesAsync(
threadId: thread.Id,
order: ListSortOrder.Ascending))
{
Console.Write($"{message.Role}: ");
foreach (MessageContent content in message.ContentItems)
{
if (content is MessageTextContent textContent)
Console.WriteLine(textContent.Text);
}
}
```
### 4. Streaming Response
```csharp
AsyncCollectionResult<StreamingUpdate> stream = client.Runs.CreateRunStreamingAsync(
thread.Id,
agent.Id
);
await foreach (StreamingUpdate update in stream)
{
if (update.UpdateKind == StreamingUpdateReason.RunCreated)
{
Console.WriteLine("--- Run started! ---");
}
else if (update is MessageContentUpdate contentUpdate)
{
Console.Write(contentUpdate.Text);
}
else if (update.UpdateKind == StreamingUpdateReason.RunCompleted)
{
Console.WriteLine("\n--- Run completed! ---");
}
}
```
### 5. Function Calling
```csharp
// Define function tool
FunctionToolDefinition weatherTool = new(
name: "getCurrentWeather",
description: "Gets the current weather at a location.",
parameters: BinaryData.FromObjectAsJson(new
{
Type = "object",
Properties = new
{
Location = new { Type = "string", Description = "City and state, e.g. San Francisco, CA" },
Unit = new { Type = "string", Enum = new[] { "c", "f" } }
},
Required = new[] { "location" }
}, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase })
);
// Create agent with function
PersistentAgent agent = await client.Administration.CreateAgentAsync(
model: modelDeploymentName,
name: "Weather Bot",
instructions: "You are a weather bot.",
tools: [weatherTool]
);
// Handle function calls during polling
do
{
await Task.Delay(500);
run = await client.Runs.GetRunAsync(thread.Id, run.Id);
if (run.Status == RunStatus.RequiresAction
&& run.RequiredAction is SubmitToolOutputsAction submitAction)
{
List<ToolOutput> outputs = [];
foreach (RequiredToolCall toolCall in submitAction.ToolCalls)
{
if (toolCall is RequiredFunctionToolCall funcCall)
{
// Execute function and get result
string result = ExecuteFunction(funcCall.Name, funcCall.Arguments);
outputs.Add(new ToolOutput(toolCall, result));
}
}
run = await client.Runs.SubmitToolOutputsToRunAsync(run, outputs, toolApprovals: null);
}
}
while (run.Status == RunStatus.Queued || run.Status == RunStatus.InProgress);
```
### 6. File Search with Vector Store
```csharp
// Upload file
PersistentAgentFileInfo file = await client.Files.UploadFileAsync(
filePath: "document.txt",
purpose: PersistentAgentFilePurpose.Agents
);
// Create vector store
PersistentAgentsVectorStore vectorStore = await client.VectorStores.CreateVectorStoreAsync(
fileIds: [file.Id],
name: "my_vector_store"
);
// Create file search resource
FileSearchToolResource fileSearchResource = new();
fileSearchResource.VectorStoreIds.Add(vectorStore.Id);
// Create agent with file search
PersistentAgent agent = await client.Administration.CreateAgentAsync(
model: modelDeploymentName,
name: "Document Assistant",
instructions: "You help users find information in documents.",
tools: [new FileSearchToolDefinition()],
toolResources: new ToolResources { FileSearch = fileSearchResource }
);
```
### 7. Bing Grounding
```csharp
var bingConnectionId = Environment.GetEnvironmentVariable("AZURE_BING_CONNECTION_ID");
BingGroundingToolDefinition bingTool = new(
new BingGroundingSearchToolParameters(
[new BingGroundingSearchConfiguration(bingConnectionId)]
)
);
PersistentAgent agent = await client.Administration.CreateAgentAsync(
model: modelDeploymentName,
name: "Search Agent",
instructions: "Use Bing to answer questions about current events.",
tools: [bingTool]
);
```
### 8. Azure AI Search
```csharp
AzureAISearchToolResource searchResource = new(
connectionId: searchConnectionId,
indexName: "my_index",
topK: 5,
filter: "category eq 'documentation'",
queryType: AzureAISearchQueryType.Simple
);
PersistentAgent agent = await client.Administration.CreateAgentAsync(
model: modelDeploymentName,
name: "Search Agent",
instructions: "Search the documentation index to answer questions.",
tools: [new AzureAISearchToolDefinition()],
toolResources: new ToolResources { AzureAISearch = searchResource }
);
```
### 9. Cleanup
```csharp
await client.Threads.DeleteThreadAsync(thread.Id);
await client.Administration.DeleteAgentAsync(agent.Id);
await client.VectorStores.DeleteVectorStoreAsync(vectorStore.Id);
await client.Files.DeleteFileAsync(file.Id);
```
## Available Tools
| Tool | Class | Purpose |
|------|-------|---------|
| Code Interpreter | `CodeInterpreterToolDefinition` | Execute Python code, generate visualizations |
| File Search | `FileSearchToolDefinition` | Search uploaded files via vector stores |
| Function Calling | `FunctionToolDefinition` | Call custom functions |
| Bing Grounding | `BingGroundingToolDefinition` | Web search via Bing |
| Azure AI Search | `AzureAISearchToolDefinition` | Search Azure AI Search indexes |
| OpenAPI | `OpenApiToolDefinition` | Call external APIs via OpenAPI spec |
| Azure Functions | `AzureFunctionToolDefinition` | Invoke Azure Functions |
| MCP | `MCPToolDefinition` | Model Context Protocol tools |
| SharePoint | `SharepointToolDefinition` | Access SharePoint content |
| Microsoft Fabric | `MicrosoftFabricToolDefinition` | Access Fabric data |
## Streaming Update Types
| Update Type | Description |
|-------------|-------------|
| `StreamingUpdateReason.RunCreated` | Run started |
| `StreamingUpdateReason.RunInProgress` | Run processing |
| `StreamingUpdateReason.RunCompleted` | Run finished |
| `StreamingUpdateReason.RunFailed` | Run errored |
| `MessageContentUpdate` | Text content chunk |
| `RunStepUpdate` | Step status change |
## Key Types Reference
| Type | Purpose |
|------|---------|
| `PersistentAgentsClient` | Main entry point |
| `PersistentAgent` | Agent with model, instructions, tools |
| `PersistentAgentThread` | Conversation thread |
| `PersistentThreadMessage` | Message in thread |
| `ThreadRun` | Execution of agent against thread |
| `RunStatus` | Queued, InProgress, RequiresAction, Completed, Failed |
| `ToolResources` | Combined tool resources |
| `ToolOutput` | Function call response |
## Best Practices
1. **Always dispose clients** — Use `using` statements or explicit disposal
2. **Poll with appropriate delays** — 500ms recommended between status checks
3. **Clean up resources** — Delete threads and agents when done
4. **Handle all run statuses** — Check for `RequiresAction`, `Failed`, `Cancelled`
5. **Use streaming for real-time UX** — Better user experience than polling
6. **Store IDs not objects** — Reference agents/threads by ID
7. **Use async methods** — All operations should be async
## Error Handling
```csharp
using Azure;
try
{
var agent = await client.Administration.CreateAgentAsync(...);
}
catch (RequestFailedException ex) when (ex.Status == 404)
{
Console.WriteLine("Resource not found");
}
catch (RequestFailedException ex)
{
Console.WriteLine($"Error: {ex.Status} - {ex.ErrorCode}: {ex.Message}");
}
```
## Related SDKs
| SDK | Purpose | Install |
|-----|---------|---------|
| `Azure.AI.Agents.Persistent` | Low-level agents (this SDK) | `dotnet add package Azure.AI.Agents.Persistent` |
| `Azure.AI.Projects` | High-level project client | `dotnet add package Azure.AI.Projects` |
## Reference Links
| Resource | URL |
|----------|-----|
| NuGet Package | https://www.nuget.org/packages/Azure.AI.Agents.Persistent |
| API Reference | https://learn.microsoft.com/dotnet/api/azure.ai.agents.persistent |
| GitHub Source | https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/ai/Azure.AI.Agents.Persistent |
| Samples | https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/ai/Azure.AI.Agents.Persistent/samples |
모든 파일
0개 파일azure-ai-agents-persistent-dotnet 설치
스킬 파일을 다운로드하여 .claude/skills/ 디렉터리에 압축을 풀어주세요.
ZIP 다운로드저장소를 클론하고 스킬 파일을 프로젝트에 복사하세요.
git clone https://github.com/microsoft/skills/tree/main/.github/plugins/azure-sdk-dotnet/skills/azure-ai-agents-persistent-dotnet # Copy SKILL.md to your .claude/skills/ directory
복사





집
