Skip to content

Invoke multiple user skills from one message (phase 1b) #567

Description

@andrii-novikov

QuickApps version

latest

What is the problem this feature will solve?

A message can invoke at most one skill. #549 applies the first entry in custom_content.skills and tells the user the rest were ignored ("Only one skill can be invoked per message…").

That limit is not a policy anyone wanted — it falls out of the mechanism. The injector turns the pick into exactly one synthetic read_skill call and result, and SyntheticToolCallInjector emits one pair per injector.

Real requests need more than one skill at a time:

  • "Review this PR with my review checklist and write it up in my report style."
  • "Use my SQL style guide and my chart conventions for this dashboard."

Today the user has to split the request into two messages, or pick one skill and describe the other in words — which lands them back on the model's discretion, the thing invocation exists to remove.

What is the feature you are proposing to solve the problem?

Let one message invoke several skills, and apply all of them to that message.

Expected behaviour

  • A message can invoke more than one skill, and every invoked skill is applied to it.
  • The order the skills reach the model is deterministic and follows the order the user picked them.
  • Identical entries count once.
  • One skill failing to load does not fail the message or the other skills: the user is told which one failed, and the agent answers with the rest, as in Invoke user skills from a chat message (phase 1) #549.
  • The conversation-wide cap (SKILL_INVOCATION_MAX_SKILLS) still holds and is still spent newest-first, so the skills invoked on the message being answered are never the ones dropped.
  • A message invoking one skill, or none, behaves exactly as today.
  • Still a preview feature.

To decide first — a design doc under docs/designs/, before any code:

  1. Do we still need synthetic read_skill pairs? The alternative is an instruction: tell the model that the user's message may name skills and that it must read each one before answering. That is the path the model already uses to pick a skill by description, and it needs no injector, no multi-pair support and no fixed insertion slot. Against it: the model can still skip a skill, which is what Invoke user skills from a chat message (phase 1) #549 set out to prevent. Decide deterministic-but-mechanical versus simple-but-model-dependent, and say why — the answer decides whether item 2 exists at all.
  2. If synthetic: how do several pairs get injected? SyntheticToolCallInjector emits a single call/result pair and places it in a fixed slot after the first user message. N pairs for one message means touching shared orchestration used by every other injector, so it may be worth its own task rather than a line item here.
  3. Two invoked skills that share a manifest name. With several picks per message this stops being exotic. DialSkillResolver's name dedup currently keeps the oldest and drops the rest, so the second skill silently fails to load. Resolved properly by Resolve skill name collisions across predefined, app and user skills #568.

Dependencies

Builds on: #549 (closed). Phase 1a design: docs/designs/skill_invocation.md.

Out of scope: loading the user's whole skill catalogue through Core dependencies (#569); resolving name collisions across predefined, app and user skills (#568); invoking the app's own skills (#550).

What alternatives have you considered?

  • Keep one skill per message and let users split the request across turns: the current state. It breaks a single intent into two, and the second turn's skill is not in context when the first one's work is done.
  • Let the user invoke one skill and describe the others in words: puts the model back in charge of the skills the user explicitly chose.
  • Concatenate the invoked skills into a single synthetic result to keep the one-pair mechanism: avoids touching the shared injector, but fakes one read_skill call as the source of several manifests, so the history no longer reflects what was read — and nothing ties a bundled-file read back to the right skill.

Activity

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

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions