summaryrefslogtreecommitdiff
path: root/src/Agent.Core/Prompts/PromptComposer.cs
blob: 2899088d9cb4f3742a8d37e645cef71d8470b477 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
using Agent.Core.Ollama;
using Agent.Core.Sessions;
using Agent.Core.Skills;

namespace Agent.Core.Prompts;

public sealed record PromptContext(
    string FixedSystemPrompt,
    string UserSystemPrompt,
    string Locale,
    IReadOnlyList<AgentSkill> AvailableSkills,
    IReadOnlyList<AgentSkill> SelectedSkills,
    string SandboxCapabilitySummary,
    string TaskGraphSummary,
    string PreviousSummary,
    IReadOnlyList<ExecutionResult> ExecutionResults,
    IReadOnlyList<ConversationMessage> Conversation,
    string Request,
    bool RequiresExternalEffect,
    IReadOnlyList<string>? Images = null);

public static class PromptComposer
{
    public static IReadOnlyList<OllamaMessage> Compose(PromptContext context)
    {
        var messages = new List<OllamaMessage>
        {
            new("system", context.FixedSystemPrompt),
            new("system", context.UserSystemPrompt),
            new("system", $"Respond in locale: {context.Locale}."),
            new("system", ComposeSkillCatalog(context.AvailableSkills)),
            new("system", context.SandboxCapabilitySummary),
            new("system", context.TaskGraphSummary),
            new("system", context.PreviousSummary),
            new("system", ComposeExecutionResults(context.ExecutionResults)),
            new("system", ComposeExecutionRequirement(context.RequiresExternalEffect))
        };
        // Inject selected skill instructions (Progressive Disclosure Stage 3).
        foreach (AgentSkill skill in context.SelectedSkills)
            messages.Add(new("system", skill.Instructions));
        messages.AddRange(context.Conversation.Select(static item => new OllamaMessage(item.Role, item.Content)));
        messages.Add(new OllamaMessage("user", context.Request, context.Images));
        return messages;
    }

    private static string ComposeExecutionRequirement(bool requiresExternalEffect) => requiresExternalEffect
        ? "The current Planner Task requires a verified external effect. Call the appropriate available Internal Tool now. " +
          "A prose description, promise, plan, or fenced JSON example does not perform the operation and cannot complete this Task. " +
          "After a successful read, use its returned content and proceed to the required write or process operation without repeating the read unnecessarily."
        : "The current Planner Task does not require an external effect. Use an Internal Tool only when its verified result is needed; do not claim an unverified result.";

    private static string ComposeExecutionResults(IReadOnlyList<ExecutionResult> results)
    {
        if (results.Count == 0) return "No prior Execution Results are available for the current Session.";
        var lines = new List<string> { "Recent verified Execution Results:" };
        foreach (ExecutionResult result in results)
            lines.Add($"- {result.ToolName} ({(result.Success ? "success" : "failed")}): {result.Output}");
        return string.Join("\n", lines);
    }

    /// <summary>
    /// Stage 1 Discovery: only name + description are included. Full instructions are NOT included.
    /// </summary>
    private static string ComposeSkillCatalog(IReadOnlyList<AgentSkill> skills)
    {
        if (skills.Count == 0) return "No skills available.";
        var lines = new List<string> { "Available skills:" };
        foreach (AgentSkill skill in skills)
            lines.Add($"- {skill.Name}: {skill.Description}");
        lines.Add("If a skill is relevant to the request, indicate which skills to use.");
        return string.Join("\n", lines);
    }
}