Create an example demonstrating the Oracle pattern using Race.
The Oracle pattern runs a high-capability model (e.g., Claude Opus, O3) alongside workers to provide architectural guidance. Uses Race to run advisory and work in parallel.
module Omni.Agent.Programs.Oracle where
import Omni.Agent.Op
-- | Run work with oracle advisor
-- Oracle watches progress and can inject advice
withOracle :: Model -> Op s a -> Op s a
withOracle oracleModel work = do
-- Shared advice channel
adviceRef <- liftIO (newIORef Nothing)
-- Race between work completing and oracle advising
-- But oracle should advise periodically, not complete
-- So we need a different pattern...
-- Better: oracle runs in parallel, work checks for advice periodically
undefined
-- | Alternative: Explicit advice checkpoints
withOracleCheckpoints :: Model -> Int -> Op s a -> Op s a
withOracleCheckpoints oracleModel checkpointInterval work = do
-- Wrap work with periodic oracle consultations
-- Every N iterations, pause and ask oracle for advice
undefined
-- | The oracle advisory loop
oracleAdvisor :: Model -> TVar (Maybe Text) -> Op s ()
oracleAdvisor model adviceRef = do
-- Get current state and trace
state <- get
trace <- getTrace
-- Ask oracle for advice
advice <- infer model (oraclePrompt state trace)
-- Post advice
liftIO (writeIORef adviceRef (Just advice))
-- Emit event
emit (EventCustom "oracle_advice" (toJSON advice))
-- Wait and repeat
liftIO (threadDelay 30_000_000) -- 30 seconds
oracleAdvisor model adviceRef
-- | Worker that checks for oracle advice
workWithAdvice :: IORef (Maybe Text) -> Op s a -> Op s a
workWithAdvice adviceRef baseWork = do
-- Check for advice
advice <- liftIO (readIORef adviceRef)
case advice of
Nothing -> baseWork
Just adv -> do
-- Incorporate advice into context
emit (EventCustom "incorporating_advice" (toJSON adv))
-- Clear advice
liftIO (writeIORef adviceRef Nothing)
-- Continue work with advice in mind
-- (The agent will see the advice event in trace and adjust)
baseWork
-- | More practical: oracle consultation at key decision points
consultOracle :: Model -> Text -> Op s Text
consultOracle oracleModel question = do
state <- get
trace <- getTrace
emit (EventCustom "oracle_consultation" (toJSON question))
advice <- infer oracleModel (oracleConsultPrompt question state trace)
emit (EventCustom "oracle_response" (toJSON advice))
pure (responseContent advice)
-- | Coder with oracle for architecture decisions
coderWithOracle :: Model -> Model -> Namespace -> Task -> Op CodeState Result
coderWithOracle workerModel oracleModel ns task = do
-- First, consult oracle for approach
approach <- consultOracle oracleModel
("How should I approach this task? " <> task)
-- Do initial work
checkpoint "after-planning"
initialWork <- doWork workerModel approach
-- Hit a tricky problem? Consult oracle
when (hasError initialWork) do
advice <- consultOracle oracleModel
("I hit this error: " <> getError initialWork <> ". What should I try?")
doWork workerModel advice
-- Continue...
checkpoint "work-complete"
verify ns
-- | Race worker against oracle
-- If oracle spots a problem before worker finishes, cancel and restart
raceWithOracle :: Model -> Model -> Op s a -> Op s a
raceWithOracle workerModel oracleModel work = do
result <- race
[ Left <$> work
, Right <$> oracleWatchdog oracleModel
]
case result of
Left a -> pure a -- work completed
Right warning -> do
-- Oracle spotted a problem
emit (EventCustom "oracle_intervention" (toJSON warning))
-- Could: restart work with warning in context
-- Could: ask user what to do
-- Could: continue anyway
work -- for now, just continue
oracleWatchdog :: Model -> Op s Text
oracleWatchdog model = do
-- Periodically check trace for warning signs
loop where
loop = do
liftIO (threadDelay 10_000_000) -- 10 seconds
trace <- getTrace
state <- get
-- Ask oracle if anything looks wrong
response <- infer model (watchdogPrompt trace state)
if detectsProblems response
then pure (responseContent response)
else loop