CodeActAgent (CodeAct)
CodeActAgent est lâĂ©quivalent de LlmAgent qui agit en Ă©crivant et
en exĂ©cutant du code au lieu dâĂ©mettre un appel dâoutil Ă la fois. Ă chaque tour, le modĂšle
produit un script unique ; les outils sont exposés sous forme de fonctions appelables que le script peut
composer ; et le script communique son résultat en renvoyant une valeur marquée.
Il sâagit du modĂšle CodeAct : plutĂŽt que call tool A â observe â call tool B,
le modĂšle Ă©crit b(a(x)) dans un seul script, de sorte que le travail en plusieurs Ă©tapes sâeffectue en un seul
tour. Il est activé par la fonctionnalité codeact de adk-agent.
Quand lâutiliser
- Tùches qui enchaßnent ou combinent plusieurs outils par tour (manipulation de données, opérations par lots, logique de raccordement).
- ModĂšles ayant fait lâobjet dâun post-entraĂźnement pour la gĂ©nĂ©ration de code.
- Flux de travail dans lesquels un vĂ©ritable interprĂ©teur (par ex. Python) est disponible comme substrat dâaction.
Pour les appels dâoutils natifs, prĂ©fĂ©rez LlmAgent. Pour un environnement de
codage avec fichiers et shell en bac Ă sable, consultez lâAgent de codage.
Fonctionnement de la boucle
Ă chaque tour :
- Le modÚle émet un bloc de code délimité (un script).
- Le script sâexĂ©cute sur un [
CodeRuntime] ; les appels dâoutils remontent jusquâĂ lâhĂŽte, qui exĂ©cute lâoutil et reprend le script avec le rĂ©sultat. - Le script renvoie un
ScriptOutputmarquĂ© :observationâ renvoyĂ© au modĂšle ; la boucle continue.errorâ renvoyĂ© sous forme de message ; la boucle continue.final_resultâ renvoyĂ© Ă lâappelant ; la boucle se termine.transfer_to_agentâ transmet le contrĂŽle Ă un autre agent ; la boucle se termine.
Le framework est indĂ©pendant du langage : le trait CodeRuntime constitue lâinterface de lâinterprĂ©teur
pas Ă pas, et il indique lui-mĂȘme son langage et son environnement au modĂšle via une invite
en texte libre. Lâadaptateur de production prĂ©vu encapsule
Monty, un interpréteur Python natif en Rust.
Durabilité : suspendre et reprendre
CodeActAgent est sans Ă©tat entre les invocations â lâĂ©tat durable rĂ©side dans la
session, exactement comme pour LlmAgent. Deux situations suspendent
lâexĂ©cution :
- un outil soumis Ă confirmation, pour lequel aucune dĂ©cision nâa encore Ă©tĂ© prise (HITL), et
- un outil de longue durée dont le résultat arrive hors bande.
Lors de la suspension, la continuation de lâinterprĂ©teur en cours est sĂ©rialisĂ©e dans un
CodeActCheckpoint et Ă©crite dans lâĂ©tat de la session ; le run() suivant la
relit et reprend lâexĂ©cution â la dĂ©cision de confirmation arrive via
RunConfig::tool_confirmation_decisions, et le rĂ©sultat dâune opĂ©ration de longue durĂ©e arrive sous la forme dâun
FunctionResponse dans le message suivant. Les appels dâoutils inline sont encadrĂ©s par des points de contrĂŽle
write-ahead (SAVE-BEFORE) et SAVE-AFTER : une fois le point de contrĂŽle SAVE-AFTER
persistĂ©, la rĂ©cupĂ©ration reprend avec le rĂ©sultat enregistrĂ© et nâexĂ©cute jamais
Ă nouveau lâoutil. Un plantage dans la courte fenĂȘtre suivant lâeffet secondaire dâun outil, mais
avant lâenregistrement de son point de contrĂŽle SAVE-AFTER, entraĂźnera la
rĂ©exĂ©cution de lâoutil lors de la rĂ©cupĂ©ration ; les outils qui ne sont pas idempotents
doivent donc sâen prĂ©munir (la mĂȘme limite « au moins une fois » que
LlmAgent).
Cela nĂ©cessite un environnement dâexĂ©cution capable de prendre un instantanĂ© dâun appel en pause. Un environnement dâexĂ©cution qui ne le peut pas exĂ©cute les outils de longue durĂ©e en ligne et refuse les pauses de confirmation.
CrĂ©ation dâun CodeActAgent
use adk_agent::codeact::CodeActAgent;
use std::sync::Arc;
// `model` implements `adk_core::Llm`; `runtime` implements `CodeRuntime`.
let agent = CodeActAgent::builder()
.name("analyst")
.model(model)
.runtime(runtime)
.instruction("Prefer concise, composable steps.")
.tool(Arc::new(load_csv_tool))
.output_key("report")
.build()?;
model et runtime sont requis ; tout le reste possÚde une valeur par défaut.
Parité avec LlmAgent
Le générateur reflÚte LlmAgentBuilder :
- ModĂšle :
generate_content_configainsi que les raccourcistemperature/top_p/top_k/max_output_tokens. - Instructions :
instruction/instruction_provider,global_instruction/global_instruction_provider, avec injection de modÚle{state.key}; ainsi que les compétences (fonctionnalitéskills). - Historique :
include_contents. - Outils :
tools statiques ettoolsets par invocation ;tool_timeout,default_retry_budget/tool_retry_budget,circuit_breaker_threshold, ainsi que les solutions de replion_tool_error. - Autorisation :
ToolConfirmationPolicy(require_tool_confirmation/require_tool_confirmation_for_all). - Transfert :
sub_agents etdisallow_transfer_to_parent/disallow_transfer_to_peers. - Sortie :
output_key,output_schema/output_typeavec une boucle de correction et de nouvelle tentative (output_max_retries). - Rappels :
before_callback/after_callback,before_model_callback/after_model_callback, etbefore_tool_callback/after_tool_callback/after_tool_callback_full. Les rappels aprĂšs lâappel dâun outil peuvent inspecter les mĂ©tadonnĂ©es dâexĂ©cution structurĂ©es viaCallbackContext::tool_outcome(). - ConditionnĂ© par fonctionnalitĂ© : garde-fous dâentrĂ©e/sortie (
guardrails) et le pipelineEnhancedPlugin(enhanced-plugins).
Chaque appel dâoutil reçoit un nouveau ToolContext qui transporte lâidentifiant dâappel de lâinterprĂ©teur
et dĂ©lĂšgue les artefacts, la mĂ©moire, lâĂ©tat partagĂ©, les portĂ©es utilisateur et les secrets Ă lâinvocation active â ainsi, un outil se comporte de maniĂšre identique sous CodeActAgent ou LlmAgent.
Différences délibérées
- La mise en bac Ă sable de lâexĂ©cution de code relĂšve de la responsabilitĂ© du
CodeRuntime, et ne constitue pas un ajout rapportĂ©. - La distribution des outils est sĂ©quentielle par conception (une continuation unique est capturĂ©e Ă une seule limite dâappel), il nây a donc pas de
tool_execution_strategyparallĂšle. - Il nâexiste pas dâoption de construction
skip_summarizationâ le modĂšle termine lui-mĂȘme la boucle viafinal_resultâ bien quâun outil qui dĂ©finisseskip_summarizationsur ses actions termine tout de mĂȘme lâexĂ©cution.
Exemple
Une dĂ©monstration de bout en bout exĂ©cutable et sans dĂ©pendances â un CodeRuntime autonome ainsi quâun modĂšle dĂ©terministe â se trouve dans
examples/codeact_agent:
cargo run --manifest-path examples/codeact_agent/Cargo.toml
ImplĂ©mentation dâun CodeRuntime
Un CodeRuntime analyse et exécute un script par étapes, en exposant un appel externe à la fois :
pub trait CodeRuntime: Send + Sync {
fn start(&self, script: &str, script_name: &str) -> Result<RunStep, RuntimeError>;
fn resume(&self, snapshot: &[u8], with: ResumeWith) -> Result<RunStep, RuntimeError>;
fn capabilities(&self) -> RuntimeCapabilities { /* default */ }
fn render_tools(&self, tools: &[Arc<dyn Tool>]) -> String { /* default */ }
}
RunStepest un ensemble de variantes de structure âCall { call, stdout },Complete { value, stdout }etRaised { message, stdout }. Construisez-les avec les assistantsRunStep::call/RunStep::complete/RunStep::raisedet associez la sortie capturĂ©e avec.with_stdout(..).RunStep::Callexpose exactement un appel en attente ; reprenez-le avec une valeur ou une erreur, oudump()sa continuation pour le suspendre. LestdoutattachĂ© par un environnement dâexĂ©cution est renvoyĂ© au modĂšle et conservĂ© dans les points de contrĂŽle, de sorte quâil survive Ă la suspension et Ă la reprise.- Un
PendingCalltransmet ses arguments tels que lâinterprĂ©teur les a produits âpositional_args()etkeyword_args()sĂ©parĂ©ment. Ne faites pas correspondre vous-mĂȘme les arguments positionnels aux noms : le pilote les associe aux paramĂštres de lâoutil de maniĂšre centralisĂ©e viaadk_agent::codeact::bind_call_args; un environnement dâexĂ©cution nâa donc pas besoin de schĂ©ma dâoutil Ă la limite de lâappel, etrender_toolspeut ĂȘtre une fonction pure de la tranche dâoutils. - Erreurs du script et erreurs de lâhĂŽte. Tout ce que le modĂšle pourrait corriger en Ă©crivant un
code diffĂ©rent â une erreur de syntaxe ou dâanalyse, une exception non interceptĂ©e, une
annulation due Ă une limite de ressources â est une
RunStep::Raised(une chaĂźne opaque renvoyĂ©e au modĂšle textuellement).RuntimeErrorest rĂ©servĂ© aux vĂ©ritables dĂ©faillances de lâhĂŽte (sĂ©rialisation ou dĂ©sĂ©rialisation dâun instantanĂ©, erreurs internes de lâinterprĂ©teur) et interrompt lâexĂ©cution. RuntimeCapabilities::supports_suspensiondoit ĂȘtretruepour permettre le HITL et le report des opĂ©rations de longue durĂ©e ;promptdĂ©crit le langage et lâenvironnement au modĂšle.
Voir examples/codeact_agent/src/runtime.rs pour une implémentation complÚte et minimale
prenant en charge la suspension et la reprise.
Python avec Monty
Lâadaptateur de production prĂ©vu est
adk-codeact-monty,
un CodeRuntime reposant sur Pydantic Monty. Il
permet au modĂšle dâagir en Ă©crivant du Python, sâexĂ©cute dans le mĂȘme processus,
sans conteneur ni sous-processus, et capture une exécution en pause sous forme
dâoctets â exactement ce dont la suspension/reprise a besoin. LâinterprĂ©teur Monty
est utilisé via le noyau embedded-python de adk-code,
qui regroupe les crates monty en un seul endroit ; rustc 1.95+ est requis.
[dependencies]
adk-agent = { version = "2.1.0", features = ["codeact"] }
adk-codeact-monty = "2.1.0"
Ou via la crate parapluie (réexportée sous le nom de adk_rust::codeact_monty) :
[dependencies]
adk-rust = { version = "2.1.0", features = ["minimal", "codeact-monty"] }
use adk_codeact_monty::MontyRuntime;
// Conservative default resource limits (per-advance time + memory caps) make
// `new()` safe for untrusted, LLM-generated code.
let runtime = Arc::new(MontyRuntime::new());
// Tighten or relax with the builder; `unlimited()` removes the caps for
// trusted scripts only.
let runtime = Arc::new(
MontyRuntime::builder()
.max_duration(std::time::Duration::from_secs(2))
.max_memory(64 * 1024 * 1024)
.build(),
);
AccĂšs au systĂšme dâexploitation
Les effets sur le systĂšme dâexploitation quâun script tente dâeffectuer â lectures/Ă©critures
du systĂšme de fichiers, os.getenv/os.environ et date.today()/datetime.now() â sont traitĂ©s
sur place conformĂ©ment Ă une politique contrĂŽlĂ©e par lâhĂŽte. Ce ne sont pas des
outils et ils ne mettent jamais en pause la boucle de lâagent. Par dĂ©faut, un runtime est
entiÚrement isolé (aucun accÚs au systÚme de fichiers, environnement vide, horloge de
lâhĂŽte activĂ©e). Accordez des accĂšs spĂ©cifiques avec le builder :
use adk_codeact_monty::{MontyRuntime, PathAccess};
let runtime = Arc::new(
MontyRuntime::builder()
// Mount host directories at virtual paths; Monty enforces the boundary
// (canonicalization + symlink-escape detection) so a script can never
// escape a mount. Reads/writes outside every mount raise PermissionError.
.allow_path("/data", "/srv/agent/data", PathAccess::ReadOnly)
.allow_path("/out", "/srv/agent/out", PathAccess::ReadWrite)
// Expose an explicit environment map to os.getenv / os.environ. Empty by
// default â the host process environment is never exposed implicitly.
.environ_var("PROJECT", "acme")
// date.today() / datetime.now() read the host clock (enabled by default).
.system_clock(true)
.build(),
);
LâaccĂšs au rĂ©seau et aux sous-processus ne dispose dâaucune surface dâappel systĂšme Monty et reste indisponible quelle que soit la politique. Les accĂšs accordĂ©s sont dĂ©crits au modĂšle dans le prompt systĂšme, afin quâil sache quels chemins il peut lire ou Ă©crire et quelles variables dâenvironnement existent.
Monty nâimplĂ©mente quâun sous-ensemble de pathlib.Path ; lorsque des chemins sont montĂ©s,
le prompt répertorie les méthodes exactement prises en charge (toute autre méthode
déclenche AttributeError) :
- Lecture/requĂȘte (tout montage) :
exists(),is_file(),is_dir(),is_symlink(),read_text(),read_bytes(),stat(),iterdir(),resolve(),absolute(),open("r"). - Ăcriture (montages en lecture-Ă©criture uniquement) :
write_text(),write_bytes(),append_text(),append_bytes(),mkdir(),unlink(),rmdir(),rename(),open("w")/open("a"). - OpĂ©rations pures sur les chemins (sans E/S) : lâopĂ©rateur
/etjoinpath(),is_absolute(),with_name(),with_stem(),with_suffix(),as_posix(), ainsi que les propriétés.name,.parent,.stem,.suffix,.suffixes,.parts.
Les outils sont invoquĂ©s au moyen dâune unique fonction intĂ©grĂ©e,
call_tool("name", {"arg": value, ...}) â le seul moyen dâappeler un outil ; ils ne sont
jamais accessibles comme fonctions appelables isolĂ©es. Le nom de lâoutil est une chaĂźne littĂ©rale et chaque
argument est une entrée indexée par une chaßne dans un dictionnaire, de sorte que le véritable nom est
transporté dans la continuation sérialisée (et conservé lors de la suspension et de la reprise, sans table de noms
cĂŽtĂ© hĂŽte), un outil ainsi que chaque argument pouvant porter nâimporte quel nom (pas nĂ©cessairement un
identifiant Python valide comme "fetch-cart", un mot-clĂ© Python, ni mĂȘme "call_tool"), et le pilote associe
exactement les entrĂ©es du dictionnaire par leur nom â sans dĂ©duction positionnelle. Chaque outil apparaĂźt dans
lâinvite sous la forme dâune ligne dâutilisation call_tool("name", {...}) avec ses paramĂštres et sa
description. Toute autre forme que celle-ci â un fetch_cart(...) isolĂ©, des
arguments nommĂ©s, un argument qui nâest pas un dictionnaire ou une clĂ© qui nâest pas une chaĂźne â est refusĂ©e
avec une erreur corrective plutĂŽt que distribuĂ©e silencieusement ; le modĂšle nâa donc quâune seule forme dâappel
Ă apprendre.
LâĂ©lĂ©ment exĂ©cutable
examples/codeact_monty_agent
exécute un CodeActAgent avec du Python réel, entiÚrement hors ligne.