Blog/Core Platform/Optimize Production AI Functions with CoCoEvolve
Nov 9, 2026/10 min readCore Platform

Optimize Production AI Functions with CoCoEvolve

Tuning an AI system mostly means running the same loop: run an eval, see what went wrong, update the system and retest. This loop is the same across many different AI systems from optimizing a prompt, building an AI Function inside a SQL pipeline or tuning an agent.

Evolutionary optimizers like GEPA, AlphaEvolve and OpenEvolve automate this loop. They work by picking one candidate version, showing an LLM its failures and asking for a better version in one call.

CoCoEvolve, a new evolutionary optimizer developed by Snowflake, has the same goal but takes a different approach. Rather than using an LLM on its own to identify improvements, it uses Snowflake’s AI coding agent, Snowflake CoCo. Because it uses a coding agent, CoCoEvolve can query the full search history and test hypotheses against the live system before proposing a change. We call this meta-reflection.

Here is what we found:

  • Optimizing AI Functions: CoCoEvolve beat GEPA, AdaEvolve and OpenEvolve on all six AI Function workloads we tested.
  • Optimizing prompts: On HotpotQA, CoCoEvolve reached 0.793 held-out accuracy, compared with 0.683 for GEPA and 0.363 for the unoptimized seed. It also led GEPA on every IFBench split.
  • Optimizing agents: On a held-out split of DABStep questions, CoCoEvolve took a Cortex Agent from 21.6% to 92.8% accuracy on hard questions, compared with 78.4% for Meta-Harness and 30.4% for GEPA under the same split and iteration budget.1

A lightweight, more efficient version of CoCoEvolve also powers AI_FUNCTION_OPTIMIZATION, now in public preview, which you can use to optimize your own Snowflake Cortex AI Functions from SQL.

Why one-shot mutation stalls on production AI systems

Most LLM-guided evolutionary optimizers share the same structure: select a parent from the search frontier, show an LLM that parent's failures and ask for a better version in a single call. This works well when a failure makes both the defect and its repair obvious. On production AI systems, it runs into two structural problems:

  1. Failures are underdetermined: The parent's failure often doesn't reveal the root cause, and the evidence needed to diagnose it may be spread across earlier candidates in the search history. A locally good edit can also quietly regress something another candidate had already solved.
  2. No interaction with the environment: A single LLM call can't run a query, test a hypothesis or revise its edit based on intermediate results.

The search ends up trading one solved task for another, retrying strategies that already failed or plateauing.

How does CoCoEvolve work?

CoCoEvolve treats the thing being optimized as a "program": code, a prompt, a configuration, an agent specification or any other text-representable artifact. Each iteration follows a familiar evolutionary loop. It selects a parent from an archive of evaluated candidates, mutates it into a child, evaluates the child and decides whether to keep it.

The difference is in the mutation step. We define meta-reflection as reasoning beyond one parent's failures by selectively retrieving evidence from the accumulated search history and actively probing the target environment. CoCoEvolve implements it with three components:

  1. Strategy ledger: Each iteration, the mutator receives a compact ledger of recent strategies and their score changes. It uses the ledger to decide whether to continue the current strategy, adopt a new one or revert to undirected mutation, without a hand-tuned stagnation trigger.
  2. On-demand history queries: The mutator can query the archive as evidence is needed. Which program first solved a task? Did a later mutation lose that capability? Which tasks tend to regress together? It can retrieve the exact diff that last restored a lost capability, or check a candidate against known co-regressions before committing. Results enter the context only when requested, so the full archive doesn’t have to fit in the prompt.
  3. Live environment interaction and self-testing: The mutator is a coding agent working against the real system. For an AI Function, that means running the draft SQL on sample rows, reading compiler errors and fixing them before the harness spends a full evaluation.

What meta-reflection looks like in practice

Lockpicking with Interference is a synthetic AI Function task we built to stress this. Each input is a six-digit string, and the label is how many positions stay valid after 12 hidden interference rules are applied. The seed function knows the valid digits for each position but only one of the 12 rules.

The hard part is that the score is underdetermined. The evaluator returns only the final count, not which positions were invalidated, so many different rule sets agree on most of the 50 examples. A patch can raise accuracy without finding the rule that's actually responsible.

A parent-local optimizer fits the rows in front of it. AdaEvolve reached 45/50 at iteration 34 and stayed there for the remaining 16 iterations, even after two stagnation-triggered attempts to change strategy. Only two of its 12 learned rules had the correct targets, and none of them could invalidate the first position.

CoCoEvolve's mutator compared failures across lineages instead of re-attacking the same five residual errors. It recovered all 12 rules, compiled them into a deterministic SQL CASE expression and reached 50/50 at iteration 6.

How CoCoEvolve picks what to mutate

For objectives scored over many rows or test cases, CoCoEvolve keeps a small active roster of parents chosen for complementary coverage: each one solves rows the others don't. A curator inspects that roster, the strategy ledger and the training failures, then hands the mutator one parent, an optional second program to merge from and up to ten representative rows. The mutator can verify its SQL on those rows before the child goes to full validation.

Optimizing AI Functions with AI_FUNCTION_OPTIMIZATION

Snowflake Cortex AI Functions embed LLM inference into SQL for classification, extraction and generation. Because they run inside larger pipelines over large data sets, a small change to the prompt, the model or the surrounding SQL affects every row, and checking it by hand means rerunning the whole evaluation.

What we found in research

In our experiments, each AI Function task started from a deliberately minimal SQL body with a single AI_COMPLETE call. CoCoEvolve could rewrite that body into a multistage pipeline (refining instructions, adding common table expressions or deterministic SQL postprocessing, or composing multiple model calls), as long as the function signature and output stayed the same.

We tested six workloads: a synthetic hidden-rule diagnostic (Lockpicking with Interference), emotion classification on Reddit comments (go_emotions), legal issue classification (lex_glue), financial reasoning over tables and dialogue (finqa, convfinqa) and normalization of noisy social media text (multilexnorm). All methods used the same seed, the same claude-sonnet-5 backbone and the same budget, and were scored on held-out data (except the synthetic Lockpicking diagnostic, which is scored on its full 50-example set).

 

 

Figure 1. A comparison across six workloads.

Figure 1. A comparison of CoCoEvolve, GEPA, AdaEvolve and OpenEvolve across six different workloads.

 

CoCoEvolve led on all six workloads. Its largest gains came on Lockpicking (+10 points) and multilexnorm (+7.7 points), the two tasks that depend most on uncovering hidden rules.

Two mechanisms account for most of the gap:

  1. History queries escape plateaus: On Lockpicking, the baselines kept fitting surrogate rules that scored well on the visible rows. CoCoEvolve used history queries across lineages to recover the real rule system, as described above.
  2. Self-validation prevents wasted cycles: On go_emotions, GEPA repeatedly generated SQL that placed LATERAL FLATTEN inside a UDF. Each compiler error collapsed to a zero score with no diagnostic, and broken variants consumed up to 71% of its budget. CoCoEvolve runs drafts on a small sample and repairs syntax errors within the same mutation, so outer-loop evaluation is spent only on programs that execute.

Across tasks, the evolved programs were still ordinary SQL, but much more explicit than their seeds. Generic instructions turned into decision procedures: ordered rules for classification, sign and reference conventions for numerical reasoning and token-preservation rules for normalization.

Using AI_FUNCTION_OPTIMIZATION in Snowflake

AI_FUNCTION_OPTIMIZATION, in AI Function Studio, brings a lightweight version of CoCoEvolve to your own AI Functions. You provide the seed function to be optimized, a labeled data set, a scoring metric and the models the optimizer may use. Snowflake generates candidate implementations, evaluates each one against the dataset and records its quality and cost. Your original function isn't modified.

Optimization runs in two statements: create an experiment from a YAML specification, then execute it. The following example from the documentation optimizes a redaction function on models such as claude-haiku-4-5 and claude-sonnet-4-6:

 

CREATE OR REPLACE EXPERIMENT my_db.my_schema.redact_opt_exp
  TYPE = 'AI_FUNCTION_OPTIMIZATION'
  FROM SPECIFICATION $$
function:
  function_name: "my_db.my_schema.redact(VARCHAR)"
metrics:
  - name: redaction_match
dataset:
  name: my_db.my_schema.redaction_ds
  version: v1
  column_mapping:
    argument_mapping:
      text: text_col
    ground_truth: expected_output
optimization:
  models:
    - claude-haiku-4-5
    - claude-sonnet-4-6
    - mistral-7b
    - openai-gpt-5-nano
  reflection_model: claude-sonnet-4-5
  strategy: quality_first
  budget: ultra-light
$$;

EXECUTE EXPERIMENT my_db.my_schema.redact_opt_exp;

 

The specification controls four things:

  1. Candidate models: Each model in models is optimized in parallel, and the best implementations across all of them form a single quality/cost frontier.
  2. Reflection model: The reflection_model drives the optimizer itself, so use the most capable supported model available.
  3. Strategy: quality_first (the default) maximizes the metric, balanced trades quality against cost and cost_first minimizes cost while holding quality.
  4. Budget: This ranges from ultra-light (about 4–6 proposal iterations per model) to heavy (about 27–40).

The experiment runs asynchronously on serverless compute and produces a tree of runs: a SEED run that evaluates your function as-is, plus runs for each candidate. Each run records its validation score, its cost relative to the seed and whether it sits on the cost-quality frontier. Frontier runs also record a held-out test_score.

 

Figure 2. Cost-Quality frontier chart shown in Snowflake UI for an optimization run.

Figure 2. Cost-quality frontier chart shown in Snowflake UI for an optimization run.

 

When you've picked a winner, you promote it to a new AI Function:

 

SHOW RUNS IN EXPERIMENT my_db.my_schema.redact_opt_exp;
SHOW RUN METRICS IN EXPERIMENT my_db.my_schema.redact_opt_exp RUN ITER_7;

CREATE OR REPLACE AI FUNCTION my_db.my_schema.redact_tuned(text VARCHAR)
  RETURNS VARCHAR
  FROM EXPERIMENT my_db.my_schema.redact_opt_exp RUN ITER_7;

 

Because results form a quality/cost frontier, the best choice isn't always the top score. With balanced or cost_first, a cheaper model that matches the seed's quality may be the right pick, and you only see it because several models competed in the same run.

To get the best results, we recommend the following specifications:

  • Use a labeled data set of about 50–200 rows; the maximum is 1,000.
  • Start with ultra-light to confirm the pipeline works end to end before spending on medium or heavy.
  • Keep to five or fewer candidate models.
  • Compare candidates on test_score rather than val_score, which can overfit to the validation split.

The public preview tunes the prompt and model configuration around exactly one AI_COMPLETE call. The multistage pipeline rewrites described in our research results aren't part of the preview.

Prompt optimization

AI_FUNCTION_OPTIMIZATION tunes the prompt, model and SQL definition around a single AI_COMPLETE call. To further validate its performance on the prompt alone, we tested CoCoEvolve’s ability to optimize the prompt alone while holding the model and SQL function fixed, using two prompt-only benchmarks from the GEPA paper: HotpotQA and IFBench. In these tests, CoCoEvolve finished 11 points ahead of GEPA on HotpotQA and two points ahead of GEPA on IFBench.

 

Figure 3. Held-out test accuracy. IFBench scores are the mean of three evaluations of each final prompt.

Figure 3. Held-out test accuracy. IFBench scores are the mean of three evaluations of each final prompt.

Try it yourself

Optimize your own AI Functions using this approach with AI_FUNCTION_OPTIMIZATION, in Snowsight by choosing AI -> AI Functions -> Optimization or by following this QuickStart.


1 DABStep's 450 questions were divided into a 300-question optimization set and a 150-question held-out test set, using reference answers derived from public leaderboard submissions. The same split was applied to all methods compared. ↩

Share this post

Subscribe to our blog newsletter

Get the best, coolest and latest delivered to your inbox each week

Where Data Does More