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>);