핵심 엔진을 claude-code 기준으로 정렬하고 스트리밍 재시도 경계를 정리한다
- StreamingToolExecutionCoordinator에서 조기 실행 대상을 file_read/document_read 중심으로 축소하고 folder_map 등 구조 탐색 도구를 prefetch 대상에서 제거함 - 스트리밍 재시도 전에 RetryReset 이벤트를 추가해 중간 응답 미리보기 누적을 끊고 AgentLoopService가 재시도 경계를 명확히 표시하도록 조정함 - AxAgentExecutionEngine의 Cowork/Code 빈 응답 합성을 보수적으로 바꿔 실행 근거가 있을 때만 완료 요약을 만들고 근거가 없으면 로그 확인 안내를 반환하도록 정리함 - Code 루프의 post-tool verification과 completion gate도 직전 수정에서 함께 정리해 일반 수정의 과검증을 줄였음 - README.md, docs/DEVELOPMENT.md에 2026-04-09 21:03 (KST) 기준 변경 이력과 검증 결과를 반영함 검증 결과: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\ 경고 0 / 오류 0
This commit is contained in:
@@ -8,6 +8,36 @@ using AxCopilot.Services.Agent;
|
||||
|
||||
namespace AxCopilot.Services;
|
||||
|
||||
/// <summary>LLM 응답에서 파싱된 컨텐츠 블록.</summary>
|
||||
public class ContentBlock
|
||||
{
|
||||
public string Type { get; init; } = "text"; // "text" | "tool_use"
|
||||
public string Text { get; init; } = ""; // text 타입일 때
|
||||
public string ToolName { get; init; } = ""; // tool_use 타입일 때
|
||||
public string ToolId { get; init; } = ""; // tool_use ID
|
||||
public JsonElement? ToolInput { get; init; } // tool_use 파라미터
|
||||
public string? ResolvedToolName { get; set; }
|
||||
public Task<ToolPrefetchResult?>? PrefetchedExecutionTask { get; set; }
|
||||
}
|
||||
|
||||
public sealed record ToolPrefetchResult(
|
||||
Agent.ToolResult Result,
|
||||
long ElapsedMilliseconds,
|
||||
string? ResolvedToolName = null);
|
||||
|
||||
public enum ToolStreamEventKind
|
||||
{
|
||||
TextDelta,
|
||||
ToolCallReady,
|
||||
RetryReset,
|
||||
Completed
|
||||
}
|
||||
|
||||
public sealed record ToolStreamEvent(
|
||||
ToolStreamEventKind Kind,
|
||||
string Text = "",
|
||||
ContentBlock? ToolCall = null);
|
||||
|
||||
/// <summary>
|
||||
/// LlmService의 Function Calling (tool_use) 확장.
|
||||
/// Claude tool_use, Gemini function_calling 프로토콜을 지원합니다.
|
||||
@@ -15,35 +45,6 @@ namespace AxCopilot.Services;
|
||||
/// </summary>
|
||||
public partial class LlmService
|
||||
{
|
||||
/// <summary>LLM 응답에서 파싱된 컨텐츠 블록.</summary>
|
||||
public class ContentBlock
|
||||
{
|
||||
public string Type { get; init; } = "text"; // "text" | "tool_use"
|
||||
public string Text { get; init; } = ""; // text 타입일 때
|
||||
public string ToolName { get; init; } = ""; // tool_use 타입일 때
|
||||
public string ToolId { get; init; } = ""; // tool_use ID
|
||||
public JsonElement? ToolInput { get; init; } // tool_use 파라미터
|
||||
public string? ResolvedToolName { get; set; }
|
||||
public Task<ToolPrefetchResult?>? PrefetchedExecutionTask { get; set; }
|
||||
}
|
||||
|
||||
public sealed record ToolPrefetchResult(
|
||||
Agent.ToolResult Result,
|
||||
long ElapsedMilliseconds,
|
||||
string? ResolvedToolName = null);
|
||||
|
||||
public enum ToolStreamEventKind
|
||||
{
|
||||
TextDelta,
|
||||
ToolCallReady,
|
||||
Completed
|
||||
}
|
||||
|
||||
public sealed record ToolStreamEvent(
|
||||
ToolStreamEventKind Kind,
|
||||
string Text = "",
|
||||
ContentBlock? ToolCall = null);
|
||||
|
||||
/// <summary>도구 정의를 포함하여 LLM에 요청하고, 텍스트 + tool_use 블록을 파싱하여 반환합니다.</summary>
|
||||
/// <param name="forceToolCall">
|
||||
/// true이면 <c>tool_choice: "required"</c>를 요청에 추가하여 모델이 반드시 도구를 호출하도록 강제합니다.
|
||||
@@ -150,14 +151,14 @@ public partial class LlmService
|
||||
req.Headers.Add("x-api-key", apiKey);
|
||||
req.Headers.Add(SigmoidApiVersionHeader, SigmoidApiVersion);
|
||||
|
||||
using var resp = await _http.SendAsync(req, ct);
|
||||
using var resp = await _http.SendAsync(req, ct).ConfigureAwait(false);
|
||||
if (!resp.IsSuccessStatusCode)
|
||||
{
|
||||
var errBody = await resp.Content.ReadAsStringAsync(ct);
|
||||
var errBody = await resp.Content.ReadAsStringAsync(ct).ConfigureAwait(false);
|
||||
throw new HttpRequestException(ClassifyHttpError(resp, errBody));
|
||||
}
|
||||
|
||||
var respJson = await resp.Content.ReadAsStringAsync(ct);
|
||||
var respJson = await resp.Content.ReadAsStringAsync(ct).ConfigureAwait(false);
|
||||
using var doc = JsonDocument.Parse(respJson);
|
||||
var root = doc.RootElement;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user