fix(xhs): reject offline agent for analysis and normalize browse task results

Validate agent is online before starting analysis tasks; normalize list_feeds JSON when persisting task reports.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zzh 2026-07-01 12:36:28 +08:00
parent f7be966adc
commit 50c6fd8ac8
3 changed files with 63 additions and 2 deletions

View File

@ -18,6 +18,7 @@ public class XhsAnalysisService : ApplicationService
private IXhsAnalysisSessionRepository AnalysisSessionRepository => LazyServiceProvider.GetRequiredService<IXhsAnalysisSessionRepository>(); private IXhsAnalysisSessionRepository AnalysisSessionRepository => LazyServiceProvider.GetRequiredService<IXhsAnalysisSessionRepository>();
private IXhsTaskRepository TaskRepository => LazyServiceProvider.GetRequiredService<IXhsTaskRepository>(); private IXhsTaskRepository TaskRepository => LazyServiceProvider.GetRequiredService<IXhsTaskRepository>();
private IXhsSkillRepository SkillRepository => LazyServiceProvider.GetRequiredService<IXhsSkillRepository>(); private IXhsSkillRepository SkillRepository => LazyServiceProvider.GetRequiredService<IXhsSkillRepository>();
private IXhsAgentRepository AgentRepository => LazyServiceProvider.GetRequiredService<IXhsAgentRepository>();
private ICustomerRepository CustomerRepository => LazyServiceProvider.GetRequiredService<ICustomerRepository>(); private ICustomerRepository CustomerRepository => LazyServiceProvider.GetRequiredService<ICustomerRepository>();
[HttpPost("/xhs/analysis/start")] [HttpPost("/xhs/analysis/start")]
@ -167,10 +168,11 @@ public class XhsAnalysisService : ApplicationService
}; };
await AnalysisSessionRepository.InsertAsync(session); await AnalysisSessionRepository.InsertAsync(session);
var agentId = await ResolveTaskAgentIdAsync(customerId, input.AgentId);
var task = new XhsTask var task = new XhsTask
{ {
CustomerId = customerId, CustomerId = customerId,
AgentId = input.AgentId, AgentId = agentId,
TaskType = taskType, TaskType = taskType,
ToolName = toolName, ToolName = toolName,
ArgumentsJson = OmsDocHelper.SerializeJson(args) ?? "{}", ArgumentsJson = OmsDocHelper.SerializeJson(args) ?? "{}",
@ -269,6 +271,27 @@ public class XhsAnalysisService : ApplicationService
}; };
} }
private async Task<int?> ResolveTaskAgentIdAsync(int customerId, int? requestedAgentId)
{
if (requestedAgentId is not > 0)
{
return null;
}
var agent = await AgentRepository.FirstOrDefaultAsync(x => x.Id == requestedAgentId.Value);
if (agent == null || agent.CustomerId != customerId)
{
throw new BusinessException("Agent 不存在");
}
if (!XhsHelper.IsAgentOnline(agent))
{
throw new BusinessException("所选 Agent 离线,请先在「启动服务」确认在线后再采集");
}
return agent.Id;
}
private static string BuildSessionName(string analysisType, string? keyword) => private static string BuildSessionName(string analysisType, string? keyword) =>
analysisType switch analysisType switch
{ {

View File

@ -146,7 +146,7 @@ public class XhsTaskService : ApplicationService
if (status == XhsTaskStatusCodes.Succeeded) if (status == XhsTaskStatusCodes.Succeeded)
{ {
session.FeedsJson = OmsDocHelper.SerializeJson(input.Result); session.FeedsJson = OmsDocHelper.SerializeJson(XhsHelper.NormalizeBrowseTaskResult(input.Result));
await AnalysisSessionRepository.UpdateAsync(session); await AnalysisSessionRepository.UpdateAsync(session);
} }
else if (status == XhsTaskStatusCodes.Failed) else if (status == XhsTaskStatusCodes.Failed)

View File

@ -79,4 +79,42 @@ public static class XhsHelper
var json = OmsDocHelper.SerializeJson(feeds) ?? string.Empty; var json = OmsDocHelper.SerializeJson(feeds) ?? string.Empty;
return json.Length <= 12000 ? json : json[..12000]; return json.Length <= 12000 ? json : json[..12000];
} }
/// <summary>
/// Agent 上报的 browse 结果可能是 { summary, raw },raw 内为 CLI JSON 输出。
/// </summary>
public static object? NormalizeBrowseTaskResult(object? result)
{
if (result == null)
{
return null;
}
var json = OmsDocHelper.SerializeJson(result);
var obj = OmsDocHelper.DeserializeJson<Dictionary<string, object>>(json);
if (obj == null)
{
return result;
}
if (obj.ContainsKey("feeds") || obj.ContainsKey("items") || obj.ContainsKey("notes"))
{
return obj;
}
if (obj.TryGetValue("raw", out var raw) && raw != null)
{
var rawText = raw.ToString();
if (!string.IsNullOrWhiteSpace(rawText))
{
var parsed = OmsDocHelper.DeserializeJsonObject(rawText);
if (parsed != null)
{
return parsed;
}
}
}
return result;
}
} }