BuildAdvisorMessages prepares a nested advisor prompt using the recent chat context plus the explicit advisor question.
( question string, conversationSnapshot []fantasy.Message, )
| 35 | // BuildAdvisorMessages prepares a nested advisor prompt using the recent chat |
| 36 | // context plus the explicit advisor question. |
| 37 | func BuildAdvisorMessages( |
| 38 | question string, |
| 39 | conversationSnapshot []fantasy.Message, |
| 40 | ) []fantasy.Message { |
| 41 | trimmedQuestion := strings.TrimSpace(question) |
| 42 | if trimmedQuestion == "" { |
| 43 | trimmedQuestion = defaultAdvisorQuestion |
| 44 | } |
| 45 | |
| 46 | messages := make([]fantasy.Message, 0, len(conversationSnapshot)+2) |
| 47 | |
| 48 | // Place inherited system messages before AdvisorSystemPrompt so the |
| 49 | // advisor contract is the final system instruction the model sees. |
| 50 | // Later system directives win when they conflict, and the parent's |
| 51 | // prompt may tell the model to address the end user directly or use |
| 52 | // tools. The advisor must override those behaviors, not be overridden |
| 53 | // by them. |
| 54 | // |
| 55 | // Walk system messages newest-to-oldest when consuming the byte |
| 56 | // budget so that truncation preserves the most recent directives. |
| 57 | // The parent may have injected recent safety or user-instruction |
| 58 | // blocks that should win over older foundational prompts, and later |
| 59 | // directives override earlier ones anyway. After selection, restore |
| 60 | // the original order before appending so the advisor still sees the |
| 61 | // parent's intended directive sequence. |
| 62 | inheritedSystem := make([]fantasy.Message, 0) |
| 63 | remainingSystemBudget := advisorSystemJSONByteBudget |
| 64 | for i := len(conversationSnapshot) - 1; i >= 0; i-- { |
| 65 | msg := conversationSnapshot[i] |
| 66 | if msg.Role != fantasy.MessageRoleSystem { |
| 67 | continue |
| 68 | } |
| 69 | messageBytes := messageJSONByteCount(msg) |
| 70 | if messageBytes > remainingSystemBudget { |
| 71 | // Skip oversized inherited system messages rather |
| 72 | // than forwarding them wholesale. A single massive |
| 73 | // parent system prompt could otherwise push the |
| 74 | // advisor prompt past the model's context window, |
| 75 | // returning a provider error instead of advice. |
| 76 | // Continue walking so smaller older directives can |
| 77 | // still contribute; stopping here would drop them |
| 78 | // solely because a newer sibling was oversized. |
| 79 | continue |
| 80 | } |
| 81 | inheritedSystem = append(inheritedSystem, cloneMessage(msg)) |
| 82 | remainingSystemBudget -= messageBytes |
| 83 | } |
| 84 | slices.Reverse(inheritedSystem) |
| 85 | messages = append(messages, inheritedSystem...) |
| 86 | messages = append(messages, textMessage(fantasy.MessageRoleSystem, AdvisorSystemPrompt)) |
| 87 | |
| 88 | recent := make([]fantasy.Message, 0, min(len(conversationSnapshot), advisorRecentMessageLimit)) |
| 89 | remainingBudget := advisorConversationJSONByteBudget |
| 90 | for i := len(conversationSnapshot) - 1; i >= 0; i-- { |
| 91 | msg := conversationSnapshot[i] |
| 92 | if msg.Role == fantasy.MessageRoleSystem { |
| 93 | continue |
| 94 | } |