# Full Pipeline Guide This guide walks through building a complete ACE pipeline from scratch — choosing components, defining an environment, running training, and saving results. ## Components A full pipeline needs four things: 1. **LLM Client** — the language model powering all three roles 2. **Three Roles** — Agent, Reflector, SkillManager 3. **Environment** — evaluates agent outputs 4. **Samples** — training data with questions and ground truth ## Step 1: Create the Roles Each role takes a model string directly. Supports any [LiteLLM model](https://docs.litellm.ai/) or PydanticAI-native identifier: ```python from ace import Agent, Reflector, SkillManager agent = Agent("gpt-4o-mini") reflector = Reflector("gpt-4o-mini") skill_manager = SkillManager("gpt-4o-mini") ``` Optionally use a cheaper model for learning: ```python agent = Agent("gpt-4o") reflector = Reflector("gpt-4o-mini") skill_manager = SkillManager("gpt-4o-mini") ``` ## Step 3: Define an Environment The environment evaluates agent outputs. Extend `TaskEnvironment` and implement `evaluate()`: ```python from ace import TaskEnvironment, EnvironmentResult class MathEnvironment(TaskEnvironment): def evaluate(self, sample, agent_output): correct = str(sample.ground_truth).lower() in str(agent_output.final_answer).lower() return EnvironmentResult( feedback="Correct!" if correct else f"Incorrect. Expected: {sample.ground_truth}", ground_truth=sample.ground_truth, metrics={"accuracy": 1.0 if correct else 0.0}, ) ``` Or use the built-in `SimpleEnvironment` for basic ground-truth matching: ```python from ace import SimpleEnvironment environment = SimpleEnvironment() ``` ## Step 4: Prepare Samples ```python from ace import Sample samples = [ Sample(question="What is 2+2?", context="", ground_truth="4"), Sample(question="Capital of France?", context="", ground_truth="Paris"), Sample(question="Who wrote Hamlet?", context="", ground_truth="Shakespeare"), ] ``` ## Step 5: Build and Run the Pipeline ```python from ace import ACE runner = ACE.from_roles( agent=agent, reflector=reflector, skill_manager=skill_manager, environment=environment, ) results = runner.run(samples, epochs=3) ``` ## Step 6: Save the Skillbook ```python runner.save("trained.json") print(f"Learned {len(runner.skillbook.skills())} strategies") ``` ## Complete Example ```python from ace import ( ACE, Agent, Reflector, SkillManager, Sample, SimpleEnvironment, ) # Roles (each takes a model string directly) agent = Agent("gpt-4o-mini") reflector = Reflector("gpt-4o-mini") skill_manager = SkillManager("gpt-4o-mini") # Pipeline runner = ACE.from_roles( agent=agent, reflector=reflector, skill_manager=skill_manager, environment=SimpleEnvironment(), ) # Training data samples = [ Sample(question="What is 2+2?", context="", ground_truth="4"), Sample(question="Capital of France?", context="", ground_truth="Paris"), ] # Train and save results = runner.run(samples, epochs=3) runner.save("trained.json") ``` ## Checkpoints Save the skillbook automatically during long training runs: ```python runner = ACE.from_roles( agent=agent, reflector=reflector, skill_manager=skill_manager, environment=environment, checkpoint_dir="./checkpoints", checkpoint_interval=10, # Save every 10 samples ) ``` This creates: - `ace_checkpoint_10.json`, `ace_checkpoint_20.json`, etc. - `ace_latest.json` (always the most recent) ## Deduplication Prevent duplicate skills from accumulating (requires `uv add ace-framework[deduplication]`): ```python from ace import DeduplicationConfig, DeduplicationManager dedup = DeduplicationManager(DeduplicationConfig( enabled=True, embedding_model="text-embedding-3-small", similarity_threshold=0.85, )) runner = ACE.from_roles( ..., dedup_manager=dedup, dedup_interval=10, ) ``` ## Custom Prompts The default prompts are v2.1 and work well out of the box. You can pass your own templates via the `prompt_template` parameter: ```python agent = Agent(llm, prompt_template="Your custom agent prompt with {skillbook}, {question}, {context}") reflector = Reflector(llm, prompt_template="Your custom reflector prompt ...") skill_manager = SkillManager(llm, prompt_template="Your custom skill manager prompt ...") ``` See [Prompt Engineering](prompts.md) for template variables and more examples. ## Testing Without API Calls Use `test` as the model to get PydanticAI's built-in test model, or use `unittest.mock` to patch the agent's `run_sync` method: ```python agent = Agent("test") reflector = Reflector("test") skill_manager = SkillManager("test") ``` ## Observability Add Opik tracing to any pipeline via `extra_steps` (requires `uv add ace-framework[observability]`): ```python from ace import ACE, OpikStep, register_opik_litellm_callback runner = ACE.from_roles( agent=agent, reflector=reflector, skill_manager=skill_manager, environment=environment, extra_steps=[OpikStep(project_name="my-experiment")], ) # Optionally add per-LLM-call cost tracking register_opik_litellm_callback(project_name="my-experiment") ``` See [Opik Observability](../integrations/opik.md) for full details. ## Going Deeper: Manual Pipeline Composition The `ACE.from_roles()` runner composes a `Pipeline` internally. You can build the same pipeline yourself for full control over step ordering, branching, and custom steps: ```python from ace import Pipeline, AgentStep, EvaluateStep, learning_tail pipe = Pipeline([ AgentStep(agent, skillbook), EvaluateStep(environment), *learning_tail(reflector, skill_manager, skillbook), ]) ``` See [Composing Pipelines](composing-pipelines.md) for the complete guide. ## What to Read Next - [Composing Pipelines](composing-pipelines.md) — compose custom pipelines from steps - [Async Learning](async-learning.md) — parallel Reflector execution - [Prompt Engineering](prompts.md) — customize prompt templates - [Integration Pattern](integration.md) — wrap existing agents instead - [Opik Observability](../integrations/opik.md) — monitor costs and traces