Skip to main content
In UR-2.0, the Pipeline achieves data binding through variable names. Each Tool declares its input parameters and output variables during registration.
At runtime, the Pipeline uses these variable names to transfer and share data between different steps.
This mechanism is simple and intuitive, making it easy to build sequential data flows.
However, in multi-round calls or complex control structures, variable name conflicts or data overwriting may occur.
To address this, UR-2.0 provides a parameter renaming mechanism, allowing developers to flexibly rename variables in the Pipeline without modifying any source code.

How Does Data Flow?

Each Tool declares its own input and output variable names during registration, defining the entry and exit points of the data flow. For example:
def __init__(self, mcp_inst):
    mcp_inst.tool(
        self.retriever_search,
        output="q_ls,top_k->ret_psg",
    )

def retriever_search(self, q_ls, top_k) -> ...
    ...
    return {"ret_psg": ...}
This definition means:
  • The Tool receives two input variables: q_ls and top_k
  • The Tool returns one output variable: ret_psg
If you call the same Tool multiple times (e.g., retriever_search) but want to pass different input variables (such as q_ls for the first call and subq_ls for the second),
you need a way to tell the Pipeline that these variables are “aliases” of the same parameter.

Parameter Renaming Mechanism

To resolve variable name conflicts and binding ambiguities, UR-2.0 provides a flexible parameter renaming mechanism.
You can directly use the input: and output: fields in the Pipeline YAML file to explicitly specify parameter-variable mappings—without modifying any Server code.
https://mintcdn.com/ultrarag/T7GffHzZitf6TThi/images/yaml.svg?fit=max&auto=format&n=T7GffHzZitf6TThi&q=85&s=69b41e79144bc908039c2ee3abbb1c3b
- module.tool:
    input:
      function_parameter_name: pipeline_variable_name
    output:
      tool_output_key: pipeline_variable_name
This mechanism follows the principle of explicit name-based binding:
input: maps to the function’s parameter names, while output: maps to the output keys defined during Tool registration.
The simplest practice: keep input and output names consistent between function definitions and Tool registration to avoid confusion and unnecessary remapping.

Example 1: Input Variable Renaming

Suppose the Tool function is defined as follows:
async def retriever_search(
        self,
        query_list: List[str],
        top_k: Optional[int] | None = None,
        query_instruction: str = "",
        use_openai: bool = False,
    ) -> Dict[str, List[List[str]]]:
You can explicitly rename the input variable in the Pipeline:
https://mintcdn.com/ultrarag/T7GffHzZitf6TThi/images/yaml.svg?fit=max&auto=format&n=T7GffHzZitf6TThi&q=85&s=69b41e79144bc908039c2ee3abbb1c3b
- retriever.retriever_search:
    input:
      query_list: sub_q_ls
Here, the Tool originally expects a parameter named query_list, but we map it to the Pipeline variable sub_q_ls, achieving seamless data binding.
Input mapping is based on the function’s parameter names.

Example 2: Output Variable Renaming

Suppose the Tool is registered as follows:
mcp_inst.tool(
    self.retriever_search,
    output="q_ls,top_k,query_instruction,use_openai->ret_psg",
)
You can override the output variable name in the Pipeline:
https://mintcdn.com/ultrarag/T7GffHzZitf6TThi/images/yaml.svg?fit=max&auto=format&n=T7GffHzZitf6TThi&q=85&s=69b41e79144bc908039c2ee3abbb1c3b
- retriever.retriever_search:
    output:
      ret_psg: round1_result
In this case, regardless of what the internal function returns, as long as the registered output key is ret_psg,
the result will be mapped to the variable round1_result, which can be accessed by subsequent steps.
Output mapping is based on the output keys defined during Tool registration.
If a downstream module depends on this output:
@app.prompt(output="q_ls,ret_psg,template->prompt_ls")
def qa_rag_boxed(
    q_ls: List[str], ret_psg: List[str | Any], template: str | Path
) -> list[PromptMessage]:
You can explicitly redirect the input in the Pipeline:
https://mintcdn.com/ultrarag/T7GffHzZitf6TThi/images/yaml.svg?fit=max&auto=format&n=T7GffHzZitf6TThi&q=85&s=69b41e79144bc908039c2ee3abbb1c3b
- prompt.qa_rag_boxed:
    input:
      ret_psg: round1_result
Now, the qa_rag_boxed Tool will read ret_psg from the variable round1_result, successfully achieving data transfer between steps.

Example 3: Renaming Both Input and Output

https://mintcdn.com/ultrarag/T7GffHzZitf6TThi/images/yaml.svg?fit=max&auto=format&n=T7GffHzZitf6TThi&q=85&s=69b41e79144bc908039c2ee3abbb1c3b
- retriever.retriever_search:
    input:
      q_ls: round1_query
    output:
      ret_psg: round1_result
This approach is especially common in loop structures—each iteration can use unique input and output variable names, avoiding naming conflicts.
By using parameter renaming effectively, you can keep your RAG workflow clean and manageable even in complex scenarios involving multi-round iterations and dynamic branching—all without modifying any source code.