Skip to content

evals: judge function calls, and show the judge the calls the bot made - #5713

Draft
aconchillo wants to merge 3 commits into
mainfrom
aleix/evals-function-call-judging
Draft

evals: judge function calls, and show the judge the calls the bot made#5713
aconchillo wants to merge 3 commits into
mainfrom
aleix/evals-function-call-judging

Conversation

@aconchillo

Copy link
Copy Markdown
Contributor

Summary

  • eval: is accepted on function_call and function_call_stopped expectations alongside calls:. Each call the expectation matches by name (and by any verbatim args:) is put to the judge by name and arguments, over the conversation so far, so a scenario can check what args: cannot match word for word — "the suggestion is for a session about OpenTelemetry tracing, submitted for Jennifer Smith". A rejected call fails the expectation with kind judge_no and the judge's reason; a continue counts as a no, since a call is not a partial reply.
  • The scripted judge sees the bot's function calls, as the simulation judge already does: every function_call the matcher pops goes into the judge's conversation as an assistant message [tool call] name(args), in arrival order with the reply's segments, so a response criterion can check that a confirmation matches what the bot actually submitted.
  • A judged scenario that asserts on calls requests the full function-call report level, so the judge sees the arguments either way.
  • EvalJudge gains add_tool_call() and evaluate_call(); pipecat.evals.judge.format_tool_call() formats a call as one line.

Testing

  • uv run pytest tests/test_evals_*.py -q

https://claude.ai/code/session_01NgWzoMcT5TQ85tnmFT1BdR

`eval:` is accepted on `function_call` and `function_call_stopped`
expectations alongside `calls:`. Each call matched by name (and by any
verbatim `args:`) is put to the judge with a call-specific ask that names
the function and gives its arguments as compact JSON, over the
conversation so far. A "no" fails the expectation with kind `judge_no`
and the judge's reason; a "continue" counts as a "no", since a call is
not a partial reply. The parser accepts `eval:` on these two events
without a warning, and `required_report_level()` asks for `full` whenever
a judged scenario asserts on calls, so the judge sees the arguments.

Every `function_call` the matcher pops from the stream, claimed or
buffered as pending, is added once to the judge's conversation as an
assistant message `[tool call] name(args)`, in arrival order with the
reply's segments, and the judge's instruction says what such a line is.
A reply's `eval:` can therefore check that a confirmation matches what
the bot actually submitted. `EvalJudge` gains `add_tool_call()` and
`evaluate_call()`, and `format_tool_call()` formats a call as one line.

Claude-Session: https://claude.ai/code/session_01NgWzoMcT5TQ85tnmFT1BdR
@codecov

codecov Bot commented Sep 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
src/pipecat/evals/judge.py 87.22% <100.00%> (+0.75%) ⬆️
src/pipecat/evals/matcher.py 92.70% <100.00%> (+4.84%) ⬆️
src/pipecat/evals/results.py 99.09% <ø> (ø)
src/pipecat/evals/script.py 95.47% <100.00%> (+0.06%) ⬆️

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant