Skip to main content

rig_ecs/
prompt.rs

1//! The system prompt, put together for each model call from two parts:
2//! the agent's own [`SystemPrompt`](super::agent::SystemPrompt) and the
3//! [`PromptSection`]s, the app's and the agent's own ([`SectionOf`]).
4//! Nothing in it depends on the turn, so it stays the same from call to
5//! call and the provider's prompt cache keeps it. A section whose text
6//! changes costs the whole conversation's cache once per change, so what
7//! changes goes in a message instead (`DeliveryMode::Inform`); a section
8//! that changes rarely sorts before one that changes more often. A request
9//! carries it in parts, rig-core's `SystemPrompt`
10//! ([`ModelRequest::prompt`](super::turn::ModelRequest::prompt)), until
11//! Start sends it as one system message.
12
13use bevy_ecs::prelude::*;
14use bevy_reflect::prelude::*;
15
16/// A part of every agent's system prompt, on an entity of its own, or of one
17/// agent's with a [`SectionOf`] that agent. A plugin spawns one and changes its
18/// `text` when what it describes changes (with `set_if_neq`, so an unchanged
19/// section is not marked changed); an empty `text`, or Bevy's `Disabled` on the
20/// entity, leaves the section out. Sections are sent in `order`, then `tag`,
21/// each between `<tag>` and `</tag>`.
22#[derive(Component, Reflect, Clone, Debug, Default, PartialEq, Eq)]
23#[reflect(Component, Clone, Debug, Default, PartialEq)]
24pub struct PromptSection {
25    /// Where the section goes: lower first. See the `ORDER_*` constants.
26    pub order: i32,
27    /// The tag around the text, such as `skills`.
28    pub tag: String,
29    /// The text.
30    pub text: String,
31}
32
33impl PromptSection {
34    /// The order of a role an agent plays besides its own prompt, such as a
35    /// subagent's.
36    pub const ORDER_ROLE: i32 = 100;
37    /// The order of what a project or the harness adds.
38    pub const ORDER_PROJECT: i32 = 200;
39    /// The order of facts that change most often, which so come last.
40    pub const ORDER_ENVIRONMENT: i32 = 300;
41
42    /// A section of `text` between `<tag>` and `</tag>`, at `order`.
43    pub fn new(order: i32, tag: impl Into<String>, text: impl Into<String>) -> Self {
44        Self {
45            order,
46            tag: tag.into(),
47            text: text.into(),
48        }
49    }
50}
51
52/// On a [`PromptSection`]: it is part of this agent's system prompt only.
53/// Despawning the agent despawns it. A restored agent is a new entity, so
54/// a plugin spawns the sections of its agents again, such as when the
55/// saved component they come from is added.
56#[derive(Component, Reflect, Debug)]
57#[reflect(Component)]
58#[relationship(relationship_target = AgentSections)]
59pub struct SectionOf(pub Entity);
60
61/// The prompt sections of this agent alone.
62#[derive(Component, Reflect, Debug)]
63#[reflect(Component)]
64#[relationship_target(relationship = SectionOf, linked_spawn)]
65pub struct AgentSections(Vec<Entity>);