Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions BlogWriter.Tests/ResearchStateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,22 @@ public void StartFollowUp_PreservesDraftAndResearchButResetsReviewCycle()
Assert.Equal(0, state.RevisionNumber);
Assert.Empty(state.NextStep);
}

[Fact]
public void BuildResearchQuery_IncludesOriginalTopicAndFollowUpRefinement()
{
var state = new ResearchState
{
MainTask = "How to build a blog app",
ResearchFindings = ["Concepts: context, prompts, and workflow.", "Drafting is done after research."],
CurrentSubTask = "Add a caching section."
};

string query = state.BuildResearchQuery();

Assert.Contains("How to build a blog app", query);
Assert.Contains("Add a caching section.", query);
Assert.Contains("Concepts: context, prompts, and workflow.", query);
Assert.Contains("Drafting is done after research.", query);
}
}
8 changes: 5 additions & 3 deletions Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,22 +194,24 @@ void PrintResults(BlogSession session)
{
ResearchState result = session.State;
Console.WriteLine("\n\n========== RESULTS ==========");
Console.WriteLine($"\nSession: {session.Id}");
Console.WriteLine($"\nTask: {result.MainTask}");
Console.WriteLine($"\n\n===Research Findings===\n ({result.ResearchFindings.Count}):");
foreach (string finding in result.ResearchFindings)
{
Console.WriteLine($"- {finding}");
}

Console.WriteLine($"\n\n===Draft:===\n{result.Draft}");
Console.WriteLine($"\n\n===Review Notes:===\n {result.ReviewNotes}");
Console.WriteLine($"\n\nRevision Number: {result.RevisionNumber}");
Console.WriteLine($"\n\n===Draft:===\n{result.Draft}");

if (result.RevisionLimitReached)
{
Console.WriteLine("Note: Maximum revision limit reached; draft above printed as-is.");
}
Console.WriteLine("=============================");
Console.WriteLine($"\n\nRevision Number: {result.RevisionNumber}");
Console.WriteLine($"\nSession: {session.Id}\n");
Console.WriteLine("=============================");
Comment on lines 211 to +214
}


59 changes: 58 additions & 1 deletion ResearchState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,74 @@ public class ResearchState
/// <summary>Maximum target word count for the draft. Used by the author and reviewer stages.</summary>
public int MaxWords { get; set; } = DefaultMaxWords;
public List<string> ResearchFindings { get; set; } = [];
public List<string> SearchRefinements { get; set; } = [];
public string Draft { get; set; } = "";
public string ReviewNotes { get; set; } = "";
public int RevisionNumber { get; set; }
public string NextStep { get; set; } = "";
public string CurrentSubTask { get; set; } = "";

/// <summary>
/// Builds the full research query for a follow-up refinement. It keeps the
/// original task, includes the new refinement request, and folds in the
/// previously discovered research context so the next search narrows the
/// earlier work instead of replacing it.
/// </summary>
public string BuildResearchQuery()
{
var parts = new List<string>();

if (!string.IsNullOrWhiteSpace(MainTask))
{
parts.Add($"Original task: {MainTask.Trim()}");
}

if (!string.IsNullOrWhiteSpace(CurrentSubTask))
{
parts.Add($"Follow-up refinement: {CurrentSubTask.Trim()}");
}

if (SearchRefinements.Count > 0)
{
string uniqueRefinements = string.Join(" | ", SearchRefinements
.Where(r => !string.IsNullOrWhiteSpace(r))
.Distinct(StringComparer.OrdinalIgnoreCase));
Comment on lines +55 to +57

if (!string.IsNullOrWhiteSpace(uniqueRefinements))
{
parts.Add($"Refinement history: {uniqueRefinements}");
}
}

if (ResearchFindings.Count > 0)
{
string context = string.Join("\n\n", ResearchFindings
.Where(f => !string.IsNullOrWhiteSpace(f))
.Select(f => f.Trim()));

if (!string.IsNullOrWhiteSpace(context))
{
parts.Add($"Prior research context:\n{context}");
}
}

return string.Join("\n\n", parts);
}

/// <summary>Prepares an approved or revision-capped draft for a user-requested follow-up.</summary>
public void StartFollowUp(string followUp)
{
ArgumentException.ThrowIfNullOrWhiteSpace(followUp);
CurrentSubTask = followUp.Trim();

string trimmed = followUp.Trim();
if (!string.IsNullOrWhiteSpace(CurrentSubTask) &&
!string.Equals(CurrentSubTask, trimmed, StringComparison.OrdinalIgnoreCase))
{
SearchRefinements.Add(CurrentSubTask.Trim());
}

SearchRefinements.Add(trimmed);
CurrentSubTask = trimmed;
ReviewNotes = "";
RevisionNumber = 0;
NextStep = "";
Expand Down
14 changes: 9 additions & 5 deletions ResearcherAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,17 @@ public async Task<string> InvokeAsync(string query, CancellationToken cancellati
/// <summary>Research node that gathers information.</summary>
public async Task<ResearchState> ResearchNodeAsync(ResearchState state, CancellationToken cancellationToken = default)
{
string subTask = !string.IsNullOrEmpty(state.CurrentSubTask) ? state.CurrentSubTask : state.MainTask;
_logger.LogInformation("Researching: {SubTask}", subTask);
string query = state.BuildResearchQuery();
string researchLabel = !string.IsNullOrWhiteSpace(state.CurrentSubTask)
? state.CurrentSubTask
: state.MainTask;

_logger.LogInformation("Researching: {SubTask}", researchLabel);

string findings;
try
{
findings = await InvokeAsync(subTask, cancellationToken);
findings = await InvokeAsync(query, cancellationToken);
string preview = findings.Length > 100 ? findings[..100] : findings;
_logger.LogInformation("Found: {Preview}...", preview);
}
Expand All @@ -86,8 +90,8 @@ public async Task<ResearchState> ResearchNodeAsync(ResearchState state, Cancella
}
catch (Exception e)
{
_logger.LogError(e, "Research failed for sub-task '{SubTask}'.", subTask);
findings = $"Research on {subTask} - information gathered";
_logger.LogError(e, "Research failed for query '{Query}'.", query);
findings = $"Research on {researchLabel} - information gathered";
}

state.ResearchFindings.Add(findings);
Expand Down