跳转到内容

Use Shared State

此内容尚不支持你的语言。

Use this when agents need to share persistent state across thread boundaries, agent types, or delegation trees. Shared state lives in the ProfileStore and is addressed by a typed namespace (ProfileKey) and a key (&str), giving you fine-grained control over who sees what.

  • A working awaken agent runtime (see First Agent)
  • A ProfileStore backend configured on the runtime (e.g. file store or Postgres)

Shared state has two dimensions:

DimensionTypePurpose
NamespaceProfileKeyDefines what is stored — a compile-time binding between a static string key (KEY) and a typed Value. Each key is registered once per plugin via register_profile_key.
Key&str (or StateScope helper)Defines which instance — a runtime string that partitions storage. Different keys isolate or share data between agents and threads.

Together, (ProfileKey::KEY, key: &str) uniquely identifies a shared state entry in the profile store.

Create a struct that implements ProfileKey. The KEY constant is the namespace; the Value type is what gets serialized.

use serde::{Deserialize, Serialize};
use awaken::contract::profile_store::ProfileKey;
#[derive(Clone, Default, Serialize, Deserialize)]
pub struct TeamContext {
pub goal: String,
pub constraints: Vec<String>,
}
pub struct TeamContextKey;
impl ProfileKey for TeamContextKey {
const KEY: &'static str = "team_context";
type Value = TeamContext;
}

Inside your plugin’s register method, call register_profile_key on the registrar.

use serde::{Deserialize, Serialize};
use awaken::contract::profile_store::ProfileKey;
use awaken::{Plugin, PluginDescriptor, PluginRegistrar, StateError};
#[derive(Clone, Default, Serialize, Deserialize)]
pub struct TeamContext {
pub goal: String,
pub constraints: Vec<String>,
}
pub struct TeamContextKey;
impl ProfileKey for TeamContextKey {
const KEY: &'static str = "team_context";
type Value = TeamContext;
}
pub struct TeamPlugin;
impl Plugin for TeamPlugin {
fn descriptor(&self) -> PluginDescriptor {
PluginDescriptor { name: "team" }
}
fn register(&self, r: &mut PluginRegistrar) -> Result<(), StateError> {
r.register_profile_key::<TeamContextKey>()?;
Ok(())
}
}

In any phase hook, obtain ProfileAccess from the context and use read / write with a key string. StateScope is a convenience builder for common key patterns — call .as_str() to get the key.

use serde::{Deserialize, Serialize};
use awaken::contract::shared_state::StateScope;
use awaken::PhaseContext;
use awaken::contract::profile_store::ProfileKey;
#[derive(Clone, Default, Serialize, Deserialize)]
pub struct TeamContext {
pub goal: String,
pub constraints: Vec<String>,
}
pub struct TeamContextKey;
impl ProfileKey for TeamContextKey {
const KEY: &'static str = "team_context";
type Value = TeamContext;
}
async fn execute(ctx: &mut PhaseContext) -> Result<(), Box<dyn std::error::Error>> {
let profile = ctx.profile().expect("ProfileStore not configured");
let identity = &ctx.run_identity;
// Build a scope key from the current agent's parent thread
let scope = match &identity.parent_thread_id {
Some(pid) => StateScope::parent_thread(pid),
None => StateScope::global(),
};
// Read (returns TeamContext::default() if missing)
let mut team: TeamContext = profile.read::<TeamContextKey>(scope.as_str()).await?;
// Mutate and write back
team.goal = "Ship the feature".into();
profile.write::<TeamContextKey>(scope.as_str(), &team).await?;
Ok(())
}

StateScope has several constructors. Pick the one that matches your sharing pattern:

ScenarioScopeExample
All agents across all threadsStateScope::global()Org-wide configuration
All agents spawned from the same parent threadStateScope::parent_thread(id)A delegation tree sharing context
All instances of the same agent typeStateScope::agent_type(name)Planner agents sharing learned heuristics
Single thread onlyStateScope::thread(id)Thread-local scratchpad
Custom partitionStateScope::new("custom-key")Any application-defined grouping

You can also pass any raw &str directly — StateScope is optional convenience.

MechanismLifetimeScopeBest for
StateKeySingle run (in-memory snapshot)One agent threadTransient per-run state (counters, flags, accumulated context)
ProfileKey with agent/system keyPersistent (profile store)Per-agent or systemPer-agent or per-user settings that don’t cross boundaries
ProfileKey with StateScope keyPersistent (profile store)Any StateScope stringCross-agent, cross-thread persistent state

Use ProfileKey with a StateScope key when state must survive across runs and be visible to agents in different threads or of different types.

SymptomCauseFix
profile key not registered: <ns>Key not registered in any pluginCall r.register_profile_key::<YourKey>() in the plugin’s register method
Always reads Value::default()Writing and reading use different key stringsVerify both sides construct the same StateScope or use the same &str key
Data leaks between scopesUsing StateScope::global() when a narrower scope is neededSwitch to parent_thread, agent_type, or thread scope
PathPurpose
crates/awaken-runtime-contract/src/contract/shared_state.rsStateScope type
crates/awaken-runtime-contract/src/contract/profile_store.rsProfileKey trait, ProfileOwner enum
crates/awaken-runtime/src/profile/mod.rsProfileAccess with read, write, delete methods
crates/awaken-runtime/src/plugins/registry.rsPluginRegistrar::register_profile_key registration