NAME
Chorus::Expert - Orchestrator for one or more Chorus::Engine agents working on a shared task.
VERSION
2.01
DESCRIPTION
Chorus::Expert does three things:
Registers one or more Chorus::Engine agents.
Provides every agent with a shared Chorus::Frame called BOARD, used for inter-agent communication and to carry the input to the pipeline.
Runs a
do/untilloop over the agents until one of them signalsSOLVEDorFAILED.
SYNOPSIS
use Chorus::Expert;
use Chorus::Engine;
my $agent1 = Chorus::Engine->new(_IDENT => 'Enrich');
$agent1->addrule( ... );
my $agent2 = Chorus::Engine->new(_IDENT => 'Validate');
$agent2->addrule( ... );
my $xprt = Chorus::Expert->new();
$xprt->register($agent1, $agent2);
my $ok = $xprt->process($input); # 1 = solved, undef = failed
METHODS
new
Creates a new Chorus::Expert instance with an empty agent list and a fresh shared BOARD frame.
my $xprt = Chorus::Expert->new();
Note -- arguments passed to new() are currently ignored. To override _MAX_ITER, assign directly after construction:
my $xprt = Chorus::Expert->new();
$xprt->{_MAX_ITER} = 50_000; # default is 10,000
register
Registers one or more agents. Each agent receives:
BOARD-- the shared frame, accessible as$agent->BOARD.EXPERT-- a back-reference to this expert instance.
$xprt->register($agent1, $agent2, $agent3);
Agents are stored in registration order, which determines the order in which process() calls their loop() method.
The termination agent (the one that calls solved()) should be registered last.
debug
Enables verbose output to STDERR for the main process loop.
$xprt->debug(1); # enable
$xprt->debug(0); # disable
process
Runs the pipeline.
my $ok = $xprt->process(); # no input
my $ok = $xprt->process($something); # $something available as $agent->BOARD->INPUT
The main loop iterates over all registered agents in order, calling loop() on each one, until BOARD-{SOLVED}> or BOARD-{FAILED}> is set. It respects _REPLAY and _REPLAY_ALL signals from the agents.
An agent tagged with _LOCK_UNTIL_STABLE is skipped when any earlier agent in the current iteration has already succeeded (_SUCCES is true). This allows priority-based sequencing without explicit coupling.
If _MAX_ITER full iterations complete without termination, a warning is emitted and process() returns undef.
Returns 1 if SOLVED, undef if FAILED or if _MAX_ITER is exceeded.
_LOCK_UNTIL_STABLE
An optional flag set directly on an agent frame:
$agent->{_LOCK_UNTIL_STABLE} = 'Y';
When _LOCK_UNTIL_STABLE is set on agent N, process() skips that agent in the current iteration if any earlier agent has already succeeded (_SUCCES is true on that agent). This implements priority-based sequencing: earlier agents are given another full pass before the locked agent is allowed to run.
Typical use: a "global cleanup" or "conformity check" agent that should only run once all upstream agents have stabilised for the current cycle.
_REPLAY and _REPLAY_ALL
These flags are set by the corresponding engine methods and are handled transparently by process().
_REPLAY-
Set by
$agent->replay().process()re-runsloop()on the same agent immediately (innerdo/whileloop), without advancing to the next agent. _REPLAY_ALL-
Set by
$agent->replay_all().process()restarts the outer agent loop from the beginning — all agents are iterated again from agent 1.
Both flags are automatically deleted by process() before the re-run, so they fire exactly once per call.
BOARD
Every agent registered with register() receives a reference to a shared Chorus::Frame called BOARD. Access it from inside any rule:
my $board = $agent->BOARD;
Reserved slots
SOLVED-
Set to
'Y'by$agent->solved(). Causesprocess()to return1immediately after the currentloop()call finishes. Deleted byprocess()before returning. FAILED-
Set to
'Y'by$agent->failed(). Causesprocess()to returnundefimmediately. Deleted byprocess()before returning. INPUT-
Set by
process($input)before the main loop starts. Holds the raw input value passed to the pipeline.my $input = $agent->BOARD->INPUT;
Custom slots for inter-agent communication
Any other slot can be freely written and read by agents to exchange state that does not belong to individual domain frames:
# In agent 1's _APPLY:
$agent->BOARD->set('phase', 'enrichment');
# In agent 2's _APPLY:
my $phase = $agent->BOARD->phase; # 'enrichment'
Use BOARD for pipeline-level flags and counters. Domain knowledge (facts about specific objects) belongs on Chorus::Frame instances, not on the BOARD.
AUTHOR
Christophe Ivorra
BUGS
new() ignores its arguments. Parameters passed to Chorus::Expert-new()> (including _MAX_ITER) are silently discarded. Always set _MAX_ITER by direct assignment immediately after construction:
my $xprt = Chorus::Expert->new();
$xprt->{_MAX_ITER} = 50_000; # mandatory for long pipelines
The default is 10,000 iterations. For a pipeline of N frames × M total rules, a safe heuristic is N × M × safety_margin (typically ×10).
Please report other bugs via the CPAN request tracker: http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Chorus
SUPPORT
perldoc Chorus::Expert
RT -- http://rt.cpan.org/NoAuth/Bugs.html?Dist=Chorus-Expert
AnnoCPAN -- http://annocpan.org/dist/Chorus-Expert
CPAN Ratings -- http://cpanratings.perl.org/d/Chorus-Expert
Search CPAN -- http://search.cpan.org/dist/Chorus-Expert/
SEE ALSO
LICENSE AND COPYRIGHT
Copyright 2013 Christophe Ivorra.
This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.
See http://dev.perl.org/licenses/ for more information.