Skip to content

Add multi-surface optimization: prompt, skills and tool descriptions - #17

Merged
Teq2412 merged 7 commits into
strands-labs:mainfrom
jordanyuan111-hub:feature/multi-surface-jq
Sep 17, 2026
Merged

Teq2412 merged 7 commits into
strands-labs:mainfrom
jordanyuan111-hub:feature/multi-surface-jq

Conversation

@jordanyuan111-hub

Copy link
Copy Markdown
Contributor

Summary

One reflector reads a sample of traces and edits the agent's system prompt, skill
library and tool descriptions together, placing each finding in the surface that
reaches the agent when it matters. Builds on the skill-library work in #15.

  • ToolDescriptionFormula: full base map plus sparse overrides; only the overrides
    are delivered, so unedited tools keep the runtime's own text. StrandsAdapter
    patches tool specs in place for in-process agents.
  • MultiSurfaceFormula: wraps the prompt, skill-library and tool-description
    formulas as one Formula. update_params routes by key and is all-or-nothing.
  • MultiSurfaceOptimizer: writes each step into a fresh output_folder/step_NNNN/;
    applies whatever is structurally valid on disk, drops the rest with a reason,
    raises when nothing usable was written and no findings.json explains why.
    Objective is TaskSuccessScore = Reward.reward by default; objective_weights
    and objective_definitions add terms read from Reward.metadata["scores"].
  • Runtimes: both examples accept a sparse tool_descriptions payload key, echo
    tool_descriptions_applied, and ship their tool descriptions as YAML. New
    *_multi_surface_optimization.py clients; run_example.sh --multi.

Two fixes as their own commits: the template environment HTML-escaped variables
(" became " in every rendered prompt), and the WebShop Dockerfiles no longer
built after python:3.12-slim moved to Debian 13.

Verification

  • 216 unit tests pass; optimizer tests use a stubbed writer so surface-choice
    assertions are deterministic.
  • Bedrock smoke on 20 real traces: one skill and one tool edit applied, 3 findings.
  • End-to-end on the AppWorld image built from this branch: 20 real traces in, a
    prompt edit and one skill out, delivered to the live container, three real tasks
    echoed skills_applied=1 and scored 1.0.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Jiaqing Yuan and others added 7 commits September 13, 2026 23:09
ToolDescriptionFormula holds a full base map of tool descriptions and a
sparse set of overrides. The overrides are the tunable parameter and the
only thing delivered, so unedited tools keep the runtime's own text and
the edited set stays visible. A name outside the base or a description
over 800 characters is dropped with a warning (raises under strict); an
empty string keeps the current text. from_yaml / render_effective_yaml
read and write the `tool_descriptions: {name: text}` shape.

StrandsAdapter now reports `tool_descriptions` in extract_context from the
agent's tool registry and patches the named tools in place on
update_context: `mcp_tool.description` for MCP tools, the `tool_spec` dict
for @tool functions. apply_to_agent checks that seam when a formula tunes
tool descriptions, so a missing registry fails at attach time with a clear
message rather than inside a hook.

Adds pyyaml as a dependency and documents the new formula and context key.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
MultiSurfaceFormula wraps a SystemPromptFormula, a SkillLibraryFormula
and a ToolDescriptionFormula as one Formula, so an optimizer that edits
several surfaces in one step still fits FormulaOptimizer's single-formula
contract. get_tunable_params merges the members' parameters;
update_params routes by key (system_prompt, decisions_dir/skill_dir,
tool_descriptions) and leaves a member with no key untouched. process
runs the members in order and returns only the keys that changed.

collect_decisions now reads update/ alongside optimize/. It is the word
the multi-surface templates use for a revised skill body under an
existing name; aliasing it keeps one set algebra for both vocabularies.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The sandboxed Jinja environment had autoescape on. These templates render
prompts for an LLM, not HTML, so a system prompt or skill description
passed in as a variable came out with `"` as `&#34;` and `<` as `&lt;`,
and the optimizer reasoned about text that was never in the agent.
Sandboxing is unrelated to escaping and stays on.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…e pass

One reflector agent reads a sample of traces plus a harness-computed census
and objective table, is shown the current prompt, the deployed skills in
full and the current tool descriptions, and writes only the surfaces it
decided to change, plus a findings.json ledger, into a fresh
output_folder/step_NNNN/. The harness then applies what is on disk through
MultiSurfaceFormula.update_params and materializes the resolved skill set.

Step outcome is decided by what is on disk: valid surfaces apply and
invalid ones are dropped with a logged reason; all-invalid raises; no
artifacts with a findings.json is a legitimate no-op; no artifacts and no
ledger raises. Validation is structural only (frontmatter, YAML shape).

Objective: TaskSuccessScore = Reward.reward by default. objective_weights
names additional terms read from Reward.metadata["scores"]; a missing term
raises before the agent runs. Trace files gain an `objective` block and
`response.eval_result`, copied from the rollout's evaluator output.

The reflector runs with shell and editor, a 120-message window with the
task message pinned, and is rebuilt and re-run on transient Bedrock
errors with agent-written paths cleared between attempts. The base class
gains two hooks for this: _conversation_manager and _trace_extra.

Templates ship verbatim under templates/multi_surface/.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Both example runtimes accept a `tool_descriptions` payload key: a sparse
{tool_name: description} of the tools the optimizer edited. The runtime
resets every tool to its docstring and then patches the named ones, so an
override the optimizer later drops does not linger on the long-lived agent.
It echoes `tool_descriptions_applied` next to `skills_applied`, and both
AgentCore rollout engines now keep those echoes in Rollout.metadata, where
the client can tell "the change did not help" from "it never reached the
agent".

Each runtime ships prompts/tool_descriptions.yaml, its tool descriptions as
the agent sees them, generated from the @tool docstrings by
dump_tool_descriptions.py; the optimizer starts from that map.

New clients *_multi_surface_optimization.py drive one MultiSurfaceOptimizer
across iterations, shipping prompt text, the packed skill set and the tool
overrides on every batch. run_example.sh gains --multi.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
python:3.12-slim now tracks Debian 13, which no longer ships
openjdk-17-jdk-headless, so the image build fails at apt-get. Pinning the
tag to -bookworm keeps JDK 17 and the JAVA_HOME paths the Dockerfiles
already set.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Three integration test modules import it at collection time, so the CI
test step (`hatch run test -x --strict-markers`) has failed at collection
since the package was renamed, on main as well as on every PR.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Teq2412
Teq2412 previously approved these changes Sep 16, 2026
@jordanyuan111-hub

Copy link
Copy Markdown
Contributor Author

@Teq2412 Pushed one commit on top of your review: python-dotenv declared as a dev dependency. CI's test step has been failing at collection since strands-agents 1.55 moved to mcp 2.x, which stopped pulling python-dotenv in transitively; main at 05bb1f9 has the same failure. Could you approve the workflow run and re-approve once it is green?

@Teq2412
Teq2412 self-requested a review September 17, 2026 01:46
@Teq2412
Teq2412 merged commit c68e455 into strands-labs:main Sep 17, 2026
2 checks passed
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.

2 participants