How to Write a New Protocol
Writing a new protocol can be a complex undertaking, but following a structured process can help ensure better success. The basic workflow is:
- Using pencil and paper, draw out a flow chart of all of the steps in your current protocol.
- Annotate this chart with any information you need to consider while drafting the protocol. For example, what is your sample type? What are the details to the layout of your plate? What is the specific instruction as to how this sample has to be treated? For example, "the sample has to be returned to -20C."
- Figure out the container form factor for each step in your flow chart. What's in a 1.5 ml tube? What is in 96-well flat-bottom (assay) plates? What should be done in 384-well PCR plates? Taking a look at the available container types and their details such as dead volume can aid in this process.
- Decompose this flow chart into discrete processes.
- For example, in a cloning protocol you may have a fragment generation step, an assembly step, a transformation step, a screening step, and a final growth and DNA extraction step.
- Mark each step of each process with the relevant Autoprotocol instruction (for example: your PCR step would be called
thermocycle
using Autoprotocol). - Make a list of all of the potential issues where a step might fail to result in successful, accurate results. This should include things like "the liquid handling in step X wasn't accurate", "there was cross-contamination in the samples at step Y" and "reagent Y was bad".
- Turn the list of failure modes into a list of controls you will need to include. Annotate these controls onto the relevant steps in your flow chart.
- Use the form factor and controls information to figure out how many sample wells you have at each step. From this, write down how you want to lay out samples on your containers at each step in your flow chart. How many samples can you process per plate?
- Use the annotated flow chart you've now built to draft the protocol generation code. List all the autoprotocol instructions in the order required for your protocol and start to fill out the parameters for each. You can use the general Autoprotocol JSON specifications or use the Autoprotocol python library. Each of the discrete processes will be a separate script. For example in cloning you will have several scripts to cover source amplification, assembly, transformation and verification. qPCR on the other hand is a single script process.
- Add aliquots to your Strateos inventory using the shipping kit and finalize your draft by referring to those particular aliquots by their
id
s (an alphanumeric string starting with "ct"). - From this version you can generalize your protocol to take various numbers of aliquots or different types of reagents as well as other parameters. For example, to change the length of time you let the DNA anneal, you do not need to write a new qPCR script. You can simply adjust the parameter in the one you have already written.
Designing controls
Every protocol you write should include thorough biological and technical controls. Though Strateos continuously monitors and validates device performance, the remote nature of Strateos's cloud laboratory requires you to think in a more structured way regarding your controls than you might be accustomed to when working by hand.
In particular, automated liquid handling requires special attention. Whenever possible, you should include in-line technical controls using optical dyes to validate pipetting. The OrangeG dye, which is a part of the starter kit, is a convenient option as its absorbance at 475 nm correlates with its volume (this requires a clear flat-bottom plate). Pipetting a range of volumes randomly distributed across the plate should reveal a linear correlation of volume to absorbance with a correlation coefficient of >0.98. With this technical control you can, for example, distinguish between a general pipetting error and a source well running low in sample.
In addition to technical controls, biological controls should be included. Negative as well as positive controls. During your design you will have identified several possible failure modes of the experiment. To assess contamination in an experiment involving bacteria, a subset of wells can be filled with media only to serve as negative control. It is most effective to distribute these wells randomly across the plate. Similarly, you should include a positive control that will help you to determine if all components of the reaction were working as expected. This is commonly done by repeating a reaction that has succeeded multiple times. The difference between the reaction components in the control versus the samples should be minimal, in the best case it is a single factor only.
For example in a PCR reaction you would keep the PCR conditions and primers used stable, while changing the DNA template. Alternatively, you adjust a parameter in the PCR conditions, such as the annealing temperature, while keeping everything else (primers, DNA, etc.) constant. You may run many of these tests in parallel, where each well corresponds to a single change. With this setup you can screen a matrix of parameters in a small number of runs to optimize your conditions.
Example: Design a PCR workflow
This example experiment is a high-throughput qPCR screen of gDNA for the presence or absence of certain genes/alleles. All assays use two primers that have the same annealing temperature. The PCR product length is similar across all assays with a maximum of 250bp.
To date this has been done manually following this protocol:

Manual PCR protocol
To get us started, the general workflow can be represented in this basic flow chart:

PCR basic workflow
Now we can add some basic information from our experimental design. We assume we have 96 samples and have four assays developed already. These can be run all on one 384-well PCR plate:

PCR workflow with experiment specific notes
Now we can start adding the relevant Autoprotocol instructions and also consider any additional steps that need to be included. In this case, all source plates need to be sealed again and return to storage, whereas the tubes containing the SYBR Taq master mix can be discarded.

PCR workflow annotated with autoprotocol instructions as well as adding final destination of plates
Next, we will consider control placement. Since this assay uses a PCR plate, we cannot read it in the plate reader to determine the amount of a standard dye that was added. But we can use positive and negative controls that are randomly distributed in our sample plate. Later, these will give us information about any potential contamination and sample quality. In addition to basic "all-or-none" control we can also add a standard curve using a serially diluted control. Using a serial dilution of known concentration we can later generate a standard curve and its linearity will be a proxy of pipetting accuracy.

Control placement
Finally, we take our instructions from the flow chart and start writing the code. In a first step, we can bring them in the order they should execute, add a reference to the correct plate, and add any other parameters we want to consider later, such as mix_after
.
Available parameters for all instructions can be found in the Autoprotocol specification.

Sort instructions to start writing the protocol
From this basic skeleton of instructions, you can start to write your script using your favorite language. If you chose python, Autoprotocol has a library that will help you get started. The documentation can be found here and the next section gives you a short primer on how to get your system ready to develop and test your own protocols.
Installing all required tools
MacOSX
- Make sure you have the apple command line tools or XCode installed.
- Install homebrew by pasting this into your Terminal prompt:
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
. - Update your path variable to refer to the Homebrew directory first.
- In
~/.profile
addexport PATH=/usr/local/bin:/usr/local/sbin:$PATH
to the bottom of the file.
- In
Linux
- You can install Linuxbrew, a version of homebrew, by following the instructions described here.
Strateos tools
- Install python through homebrew by typing
brew install python
. - Use
pip install autoprotocol
to install the autoprotocol python library. - Use
pip install transcriptic
to install the Transcriptic runner, a command-line tool for interacting with the Strateos API.
Writing your protocol
From the prior outlines, we can now think of our parameters for the script. First, we will need to examine all the containers and aliquots that will come from your inventory and determine their type. The dna_plate
will be a single container, whereas for taq_tubes
we will refer to several aliquots and for primer_pairs
we will take several groups of one or more primers. Similarly, we will want to specify some other parameters that can be different every time we run this protocol, such as the amount of primer to add, master mix to distribute or DNA to transfer.
"inputs": {
"dna_plate":"container",
"taq_tubes":"aliquot+",
"primer_vol_total":"volume",
"mm_vol":"volume",
"dna_vol":"volume",
"primer_pairs":"aliquot++",
"annealing_temp":"temperature"
}
We can now define some reasonable values for these parameters that we will use for trouble shooting the protocol and later on to create a preview of the protocol on the launch screen.
To determine the volumes, let's assume we want to make a 20uL reaction, of which 2uL will be DNA. Hence the master mix will be 18uL per well. We want to make 96 reactions per master mix and to account for well dead-volume and small, but compounding inaccuracies in pipetting we will make a master mix for 110 reactions.
We are using a SYBR master mix that is provided in the Taq tube as a 1x mix already. The vendor suggests we add 0.8uL of primer from a 400uM stock per reaction. So that means we have to add 88uL per each primer to its respective Taq tube.
"parameters": {
"dna_plate":"dna_plate",
"taq_tubes":["taq1/0", "taq2/0", "taq3/0", "taq4/0"],
"primer_vol_total":"88:microliter",
"mm_vol":"18:microliter",
"dna_vol":"2:microliter",
"primer_pairs": [
["primer_plate/0","primer_plate/1"],
["primer_plate/2","primer_plate/3"],
["primer_plate/4","primer_plate/5"],
["primer_plate/6","primer_plate/7"]
],
"annealing_temp":"60:celsius"
}
In order to create a package and validate the protocol at a later time, we have to create the containers that this code is referring to. This does not create real containers, it is just used for code validation only. At the appropriate time, these containers will be substituted for the containers you select from your inventory. It is good practice to make the volumes in these containers match as realistically as possible, but it is not absolutely required.
While the volumes for the dna_plate
and primer_plate
are set arbitrarily, the volume we want to provide in the Taq tubes is 1804uL since (110reactions18uL)-(88uL2primers) is 1804uL.
"refs":{
"primer_plate": {
"type": "96-deep",
"aliquots": {
"0": {"volume": "500:microliter"},
"1": {"volume": "500:microliter"},
"2": {"volume": "500:microliter"},
"3": {"volume": "500:microliter"},
"4": {"volume": "500:microliter"},
"5": {"volume": "500:microliter"},
"6": {"volume": "500:microliter"},
"7": {"volume": "500:microliter"}
},
"store":"cold_20"
},
"taq1": {
"type": "micro-2.0",
"aliquots": {
"0": {"volume": "1804:microliter"}
},
"discard": true
},
"taq2": {
"type": "micro-2.0",
"aliquots": {
"0": {"volume": "1804:microliter"}
},
"discard": true
},
"taq3": {
"type": "micro-2.0",
"aliquots": {
"0": {"volume": "1804:microliter"}
},
"discard": true
},
"taq4": {
"type": "micro-2.0",
"aliquots": {
"0": {"volume": "1804:microliter"}
},
"discard": true
},
"dna_plate": {
"type": "96-pcr",
"aliquots": {
"0": {"volume": "50:microliter"},
"1": {"volume": "50:microliter"},
"2": {"volume": "50:microliter"},
"3": {"volume": "50:microliter"},
"4": {"volume": "50:microliter"},
"5": {"volume": "50:microliter"},
"6": {"volume": "50:microliter"},
"7": {"volume": "50:microliter"}
},
"store":"cold_20"
}
}
We can now start writing the first draft of our protocol, focusing on a single primer set.
from autoprotocol.util import make_dottable_dict
def myqPCR(protocol, params):
params = make_dottable_dict(params)
primer_plate = params.primer_pairs[0][0].container
dest_plate = protocol.ref('dest_plate', cont_type='384-pcr',
storage='cold_4')
# transfer the first primer
protocol.transfer(params.primer_pairs[0][0], params.taq_tubes[0],
params.primer_vol_total)
# transfer the second primer and mix the solution
protocol.transfer(params.primer_pairs[0][1], params.taq_tubes[0],
params.primer_vol_total,
mix_after=True, mix_vol=params.primer_vol_total,
repetitions=5)
# distribute the master mix from the first Taq tube to the first
# quadrant of the PCR plate, mix before aspirating the solution
protocol.distribute(params.taq_tubes[0], dest_plate.quarter(0),
params.mm_vol,
allow_carryover=True, mix_before=True,
mix_vol=params.primer_vol_total, repetitions=4)
# add the DNA from the 96-well plate to the first quadrant and mix
protocol.stamp(params.dna_plate, dest_plate, 0, params.dna_vol,
mix_after=True, mix_vol=params.dna_vol, repetitions=3)
# seal and thermocycle
protocol.seal(dest_plate)
protocol.thermocycle(dest_plate, [{
"cycles": 1, "steps": [{
"temperature": "95:celsius",
"duration": "3:minute"}]
}, {
"cycles": 40, "steps": [{
"temperature": "95:celsius",
"duration": "5:second"}, {
"temperature": params.annealing_temp,
"duration": "30:second", "read": True}]
}],
volume="20:microliter",
dataref="myqPCR_data",
# read all wells for SYBR green dye
dyes={"SYBR": dest_plate.all_wells().indices()},
# standard melting curve parameters
melting_start="65:celsius",
melting_end="95:celsius",
melting_increment="0.5:celsius",
melting_rate="5:second")
# seal source plates before they go back to storage
protocol.seal(primer_plate)
protocol.seal(params.dna_plate)
if __name__ == '__main__':
from autoprotocol.harness import run
run(myqPCR)
Note how we identify the primer container by querying the container type of the first primer aliquot. This is needed for sealing the plate back later. To cover the case where the primers are presented on several 96-deep plates, we can do the following:
primer_plates = []
for primers in params.primer_pairs:
for primer in primers:
# list all containers that are used for primers
primer_plates.append(primer.container)
# find which ones are unique
unique_primer_plates = set(primer_plates)
...
for primer_plate in unique_primer_plates:
protocol.seal(primer_plate)
Similarly, we should now loop through all primer pairs and add them to the correct Taq tube:
for i, primer in enumerate(params.primer_pairs):
protocol.transfer(primer[0], params.taq_tubes[i],
params.primer_vol_total)
protocol.transfer(primer[1], params.taq_tubes[i],
params.primer_vol_total, mix_after=True,
mix_vol=params.primer_vol_total, repetitions=5)
Using the same approach, we can now implement our final protocol. It is good practice to assert that your protocol is used in the intended way. For example, this script can only take 4 different PCR reactions, so using an assert we can add a message that this script does currently not support primer_pairs
with more than 4 entries.
from autoprotocol.util import make_dottable_dict
def myqPCR(protocol, params):
params = make_dottable_dict(params)
dest_plate = protocol.ref('dest_plate', cont_type='384-pcr',
storage='cold_4')
primer_plates = []
for primers in params.primer_pairs:
for primer in primers:
primer_plates.append(primer.container)
unique_primer_plates = set(primer_plates)
assert len(params.taq_tubes) == len(params.primer_pairs), ('You are using more primer pairs then you supply taq tubes.')
assert len(params.taq_tubes) < 5, ('Currently this script only '
'supports up to 4 reactions as '
'defined by primer pairs and Taq '
'tubes')
for i, primer in enumerate(params.primer_pairs):
protocol.transfer(primer[0], params.taq_tubes[i],
params.primer_vol_total)
protocol.transfer(primer[1], params.taq_tubes[i],
params.primer_vol_total, mix_after=True,
mix_vol=params.primer_vol_total, repetitions=5)
for i, tube in enumerate(params.taq_tubes):
protocol.distribute(tube, dest_plate.quadrant(i), params.mm_vol,
allow_carryover=True, mix_before=True,
mix_vol=params.primer_vol_total,
repetitions=4)
for i in xrange(0, 3):
protocol.stamp(params.dna_plate, dest_plate, params.dna_vol, i,
mix_after=True, mix_vol=params.dna_vol,
repetitions=3)
protocol.seal(dest_plate)
protocol.thermocycle(dest_plate, [{
"cycles": 1, "steps": [{
"temperature": "95:celsius",
"duration": "3:minute"}]
}, {
"cycles": 40, "steps": [{
"temperature": "95:celsius",
"duration": "5:second"}, {
"temperature": params.annealing_temp,
"duration": "30:second", "read": True}]
}],
volume="20:microliter",
dataref="myqPCR_data",
dyes={"SYBR": dest_plate.all_wells().indices()},
melting_start="65:celsius",
melting_end="95:celsius",
melting_increment="0.5:celsius",
melting_rate="5:second")
protocol.seal(params.dna_plate)
for primer_plate in unique_primer_plates:
protocol.seal(primer_plate)
if __name__ == '__main__':
from autoprotocol.harness import run
run(myqPCR, 'MyqPCR')
Finally, we add our manifest.json with the information we defined first:
{
"version": "1.0.0",
"format": "python",
"license": "MIT",
"protocols": [
{
"name": "MyqPCR",
"description": "This is my first qPCR script",
"command_string": "python -m myqPCR",
"inputs": {
"dna_plate":"container",
"taq_tubes":"aliquot+",
"primer_vol_total":"volume",
"mm_vol":"volume",
"dna_vol":"volume",
"primer_pairs":"aliquot++",
"annealing_temp":"temperature"
},
"preview": {
"refs":{
"primer_plate": {
"type": "96-deep",
"aliquots": {
"0": {"volume": "500:microliter"},
"1": {"volume": "500:microliter"},
"2": {"volume": "500:microliter"},
"3": {"volume": "500:microliter"},
"4": {"volume": "500:microliter"},
"5": {"volume": "500:microliter"},
"6": {"volume": "500:microliter"},
"7": {"volume": "500:microliter"}
},
"store":"cold_20"
},
"taq1": {
"type": "micro-2.0",
"aliquots": {
"0": {"volume": "1804:microliter"}
},
"discard": true
},
"taq2": {
"type": "micro-2.0",
"aliquots": {
"0": {"volume": "1804:microliter"}
},
"discard": true
},
"taq3": {
"type": "micro-2.0",
"aliquots": {
"0": {"volume": "1804:microliter"}
},
"discard": true
},
"taq4": {
"type": "micro-2.0",
"aliquots": {
"0": {"volume": "1804:microliter"}
},
"discard": true
},
"dna_plate": {
"type": "96-pcr",
"aliquots": {
"0": {"volume": "50:microliter"},
"1": {"volume": "50:microliter"},
"2": {"volume": "50:microliter"},
"3": {"volume": "50:microliter"},
"4": {"volume": "50:microliter"},
"5": {"volume": "50:microliter"},
"6": {"volume": "50:microliter"},
"7": {"volume": "50:microliter"}
},
"store":"cold_20"
}
},
"parameters": {
"dna_plate":"dna_plate",
"taq_tubes":["taq1/0", "taq2/0", "taq3/0", "taq4/0"],
"primer_vol_total":"88:microliter",
"mm_vol":"18:microliter",
"dna_vol":"2:microliter",
"primer_pairs": [
["primer_plate/0","primer_plate/1"],
["primer_plate/2","primer_plate/3"],
["primer_plate/4","primer_plate/5"],
["primer_plate/6","primer_plate/7"]
],
"annealing_temp":"60:celsius"
}
},
"dependencies": []
}
]
}
In the folder containing those two files, you can now use the runner to see the JSON output of your protocol by executing transcriptic preview MyqPCR
.
Congratulations, you have written your first custom protocol!
You are now all set to follow the package upload direction in the package creation quick-start guide to make this protocol available to all members of your organization.
Submitting and testing your protocol
To start writing your own protocol, please refer to the Autoprotocol specifications on autoprotocol.org and the autoprotocol-python library.
To access autoprotocol examples, you can fork the autoprotocol-core library. Another good place to start is to follow the package creation quickstart.
It is good practice to test the simplest case of each protocol first. If a protocol can be broken down into self-contained units, these should be tested separately first. For example for a cloning workflow (fragment generation, assembly, transformation, verification and final extraction) you can test each step separately before combining it in one workflow. Similarly, it is good practice to test the script with a small number of samples first. This reduces your turn-around times and costs.
You can submit a protocol of a package without uploading the entire package with the following command:
transcriptic preview MyProtocol | transcriptic submit --project MyProject
This will use the preview parameters you specified in your manifest.json
to create the run.
Note that for Strateos runner to work you need to run transcriptic login
first.
When all individual parts of your protocol are optimized, you can combine them and run them together. Once you have completed this basic troubleshooting process you are ready to upload your package to your organization for easy use through the UI as illustrated here.
Updated over 2 years ago