Agent skill transfer
Break It Down, Pass It On Cross-Task Skill Transfer in LLM Agents
Computer Science Department, Stony Brook University
An agent that stores what it just did as a reusable skill gets better with experience only if it breaks the task down first. Whole-task skills mostly make it worse.
01 Abstract
Large language model (LLM) agents can induce skills from completed tasks and reuse them later to grow more capable with experience. In practice, induced skills may transfer unreliably and can even harm the agent that retrieves them. When agent-induced skills transfer reliably across tasks remains an open question. We conduct a comprehensive and controlled study of how the way skills are induced shapes their transfer across tasks. Specifically, we compare task-level with subtask-level skill induction and text with code skill formats, the two axes along which existing methods differ. Task-level skills mostly reduce the agent's performance below its no-memory baseline while subtask-level skills raise it above on average, and text skills transfer better than code skills. To further understand our findings, we examine two complementary properties of the induced skills: specificity, which measures how closely a skill matches real tasks, and abstractness, which measures how evenly its relevance spreads across tasks. Neither property alone predicts task success, but their combined effect does, which we propose as a skill utility score. The score correlates consistently with task success when skills are transferred, and subtask-level and text skills score higher. Computing skill utility only needs the skills and task descriptions, so our score serves as a lightweight diagnostic of a skill memory for practitioners.
01 Motivation
Experience should make an agent better, not worse
An agent that finishes a task can write down what it did and pull it back up later, so it should grow more capable the more it works. In practice the opposite often happens: a retrieved skill drags the agent off course and the memory that was meant to help ends up costing accuracy. What separates the two outcomes is not whether the agent has a memory, but how the skill was carved out of its own experience.
Break the task down before you store it
Skills help often when they are induced at the subtask level, while task-level induction often makes the same memory harmful. Text skills mostly help more than code skills at both induction levels.
A useful skill is specific and abstract at once
A skill is useful when it is both specific to real tasks and abstract enough to remain relevant across many of them. Neither dimension alone predicts task success. We therefore propose a skill utility score that requires both jointly. Computed directly from the induced skills, it predicts task success and matches observed reuse.
02 Pipeline
The two axes along which existing methods differ
An agent solves a task, induces a skill from part of its trajectory, stores it in a skill memory, and retrieves it on a later task. We compare task-level with subtask-level skill induction and text with code skill formats, holding everything else fixed.
On the right, the pipeline runs one stage at a time. Switch the induction level to watch the trajectory split into subtasks, and switch the format to see what the stored skill looks like.
→ drag the diagram sideways
03 Results
Induction level and format decide whether a skill helps
Task-level skills mostly reduce the agent's performance below its no-memory baseline while subtask-level skills raise it above on average, and text skills transfer better than code skills.
04 Why
When do skills transfer
Our proposed metric
Skill utility = specificity × abstractness
- Specificity
- how close a skill is to the tasks it most resembles
- Abstractness
- how evenly its relevance spreads over many tasks
Computing it only needs the skills and task descriptions, and it predicts task success and matches observed reuse.
See the paper for the exact definition of each dimension.
05 Induced skills
The same task, induced two ways
Real skills induced from one AppWorld task, which asks the agent to order all weightlifting benches in the cart. Task-level induction yields one skill for the whole task; subtask-level induction yields one skill per subtask. The contrast is clearest in the code format: the task-level function keeps the source task's product keyword as a default argument, while the subtask-level functions stay generic.
One workflow note spans the whole trajectory
Description: Place an order on Amazon for specific items by filtering cart contents - Procedure: apis.supervisor.show_profile → apis.supervisor.show_account_passwords to get Amazon password → apis.amazon.login(username=profile['email'], password=password) to get access_token → apis.amazon.show_cart(access_token) to retrieve cart_items → filter items by keyword → apis.amazon.clear_cart(access_token) → add each filtered item via apis.amazon.add_product_to_cart → apis.amazon.show_payment_cards(access_token) and filter by expiry → select first valid payment_card_id → apis.amazon.show_addresses(access_token) → select address_id → apis.amazon.place_order(access_token, payment_card_id, address_id) → apis.supervisor.complete_task() - Amazon login does NOT use stored tokens between sessions — must re-login on each task - apis.amazon.show_cart(access_token) returns full cart with 'cart_items' as list of product dicts - Payment and address IDs come from Amazon's apis.amazon.show_payment_cards and apis.amazon.show_addresses, NOT from supervisor app - Card expiry check: filter cards where (expiry_year > current_year) or (same year and expiry_month >= current_month) - Order placement fails with 422 if cart is empty or missing payment_card_id / address_id - To filter cart by category, must clear and re-add only matching items since no bulk remove by filter API exists - apis.amazon.login requires 'username' field, not 'email' — unexpected field name - acces_token from login is a dict with 'access_token' string inside - apis.amazon.show_cart returns full cart with cost breakdown and 'cart_items' list - No API to remove items by filter; must clear and re-add - apis.amazon.clear_cart(access_token) removes all items - apis.amazon.add_product_to_cart requires product_id and quantity - Payment card expiry must be checked using current year/month; expired cards cause 422 on order - apis.amazon.place_order requires access_token, payment_card_id, and address_id — all three
One function reproduces the whole task, keyword and all
Description: Filter cart for weightlifting benches, then place order using valid payment and shipping
def place_weightlifting_benches_order(access_token, product_keywords=['weightlifting bench']):
# Retrieve full cart
cart = apis.amazon.show_cart(access_token=access_token)
# Filter for relevant product types
filtered_items = [item for item in cart['cart_items'] if any(kw in item['product_name'].lower() for kw in product_keywords)]
if not filtered_items:
raise Exception("No weightlifting benches found in cart")
# Clear cart and re-add only filtered items
apis.amazon.clear_cart(access_token=access_token)
for item in filtered_items:
apis.amazon.add_product_to_cart(
product_id=item['product_id'],
quantity=item['quantity'],
access_token=access_token
)
# Get valid payment method (not expired)
from datetime import datetime
now = datetime.now()
cards = apis.amazon.show_payment_cards(access_token=access_token)
valid_card = next(
c for c in cards
if c['expiry_year'] > now.year or (c['expiry_year'] == now.year and c['expiry_month'] >= now.month)
)
# Get shipping address
addresses = apis.amazon.show_addresses(access_token=access_token)
if not addresses:
raise Exception("No shipping address found")
# Place final order
return apis.amazon.place_order(
access_token=access_token,
payment_card_id=valid_card['payment_card_id'],
address_id=addresses[0]['address_id']
)One note per subtask
Description: Login to an app using supervisor-provided credentials - Procedure: apis.supervisor.show_account_passwords() → filter by 'account_name' → apis.<app>.login(username, password) → extract 'access_token' - apis.<app>.login() param is 'username', not 'email' — always verify via show_api_doc - Access token returned in 'access_token' key of response - Store access_token in variable for later API calls - Amazon login uses 'username' (accepts email address), not 'email' - Passwords may contain special characters — use as-is without modification - 'access_token' from login is required for subsequent API calls and may expire - Always confirm login parameter names using apis.api_docs.show_api_doc Description: Filter and retain specific items in a cart by product name - Procedure: apis.amazon.show_cart(access_token) → examine returned structure (key 'cart_items') → iterate over items → conditionally call apis.amazon.delete_product_from_cart(access_token, product_id) for non-matching items - apis.amazon.show_cart(access_token) returns a dict with metadata (total_cost, tax) and 'cart_items' as a list of dicts - 'product_id' is integer, not string — pass as int to delete_product_from_cart - apis.amazon.delete_product_from_cart requires both product_id and access_token - Always re-check cart contents after deletions using show_cart to confirm success - apis.amazon.show_cart(access_token) returns cart with key 'cart_items', not 'items' - Removal is done via apis.amazon.delete_product_from_cart, not 'remove_from_cart' - Always verify cart structure before iterating to avoid KeyError on 'items' - Use product_id as integer, not string, in calls Description: Complete a task by placing an order for items in Amazon cart - Procedure: call apis.supervisor.complete_task() with no arguments - apis.supervisor.complete_task() should be called with no arguments when the task is action-based (e.g. place an order, like songs) - Calling apis.supervisor.complete_task() ends the agent loop and completes the task
One generic function per subtask
Description: Login to Amazon and retrieve the shopping cart contents
def login_and_get_cart(email, password):
login_result = apis.amazon.login(username=email, password=password)
access_token = login_result['access_token']
cart = apis.amazon.show_cart(access_token=access_token)
return cart
Description: Remove items from Amazon cart that do not match a keyword in their product name
def clean_cart_of_non_matching_items(access_token, keyword):
# Retrieve the current cart
full_cart = apis.amazon.show_cart(access_token=access_token)
cart_items = full_cart['cart_items']
# Identify items that do not contain the keyword in their name
non_matching_items = [item for item in cart_items if keyword.lower() not in item['product_name'].lower()]
# Remove each non-matching item
for item in non_matching_items:
apis.amazon.delete_product_from_cart(access_token=access_token, product_id=item['product_id'])
# Return the cleaned list of remaining items
updated_cart = apis.amazon.show_cart(access_token=access_token)
return updated_cart['cart_items']
Description: Place an Amazon order using the first valid (non-expired) payment card and specified address
def place_order_with_valid_payment(access_token, address_id):
from datetime import datetime
cards = apis.amazon.show_payment_cards(access_token=access_token)
now = datetime.now()
current_year, current_month = now.year, now.month
valid_card = None
for card in cards:
exp_year = card['expiry_year']
exp_month = card['expiry_month']
if exp_year > current_year or (exp_year == current_year and exp_month >= current_month):
valid_card = card
break
if valid_card is None:
raise Exception('No valid payment card found')
payment_card_id = valid_card['payment_card_id']
return apis.amazon.place_order(access_token=access_token, payment_card_id=payment_card_id, address_id=address_id)06 Citation
If you find our work useful, please cite our paper
@misc{feng2026breakdownpasson,
title={Break It Down, Pass It On: Cross-Task Skill Transfer in LLM Agents},
author={Yiyang Feng and Biddut Sarker Bijoy and Niranjan Balasubramanian and Jiawei Zhou},
year={2026},
eprint={2608.20274},
archivePrefix={arXiv},
primaryClass={cs.AI},
url={https://arxiv.org/abs/2608.20274}
}