Reusable Haystack applications depend on a graph that makes each processing boundary explicit. A Pipeline holds that graph so component inputs, outputs, and execution can be assembled once and invoked with different data.
Each component receives a unique name, while connect() joins one named output socket to a compatible input socket. Runtime data enters only through inputs that do not already receive a value from another component.
The local pipeline uses two custom components and no network service or API key. Its final answer contains a normalized form of the supplied query, so changed spacing and capitalization prove that the value crossed the connection instead of coming from a fixed success message.
Related: How to install Haystack with pip
Related: How to create a custom component in Haystack
Related: How to run a pipeline in Haystack
Steps to create a Haystack pipeline:
- Create pipeline_create_demo.py with the Haystack imports and query-normalizing component.
- pipeline_create_demo.py
import sys from haystack import Pipeline, component @component class QueryNormalizer: @component.output_types(clean_query=str) def run(self, query: str): clean_query = " ".join(query.strip().split()).casefold() return {"clean_query": clean_query}
The query parameter becomes an input socket, and the returned clean_query key matches the output declared by @component.output_types.
- Add the AnswerTemplate component below QueryNormalizer.
@component class AnswerTemplate: @component.output_types(answer=str) def run(self, clean_query: str): return {"answer": f"Pipeline received: {clean_query}"}
The clean_query parameter gives the second component an input socket that can accept the first component's string output.
- Append the named pipeline graph below both component classes.
pipeline = Pipeline() pipeline.add_component("normalizer", QueryNormalizer()) pipeline.add_component("responder", AnswerTemplate()) pipeline.connect("normalizer.clean_query", "responder.clean_query")
The connection starts at the normalizer.clean_query output socket and ends at the responder.clean_query input socket.
- Append the runtime query and downstream result output below the connection.
query = " ".join(sys.argv[1:]) or "How do Haystack pipelines connect components?" result = pipeline.run({"normalizer": {"query": query}}) print(f"input={query}") print(f"answer={result['responder']['answer']}")
Pipeline.run() receives the unconnected normalizer.query input. The connected responder.clean_query socket receives its value from QueryNormalizer instead of the runtime dictionary.
- Review the completed pipeline_create_demo.py file before execution.
- pipeline_create_demo.py
import sys from haystack import Pipeline, component @component class QueryNormalizer: @component.output_types(clean_query=str) def run(self, query: str): clean_query = " ".join(query.strip().split()).casefold() return {"clean_query": clean_query} @component class AnswerTemplate: @component.output_types(answer=str) def run(self, clean_query: str): return {"answer": f"Pipeline received: {clean_query}"} pipeline = Pipeline() pipeline.add_component("normalizer", QueryNormalizer()) pipeline.add_component("responder", AnswerTemplate()) pipeline.connect("normalizer.clean_query", "responder.clean_query") query = " ".join(sys.argv[1:]) or "How do Haystack pipelines connect components?" result = pipeline.run({"normalizer": {"query": query}}) print(f"input={query}") print(f"answer={result['responder']['answer']}")
- Verify the completed pipeline with a query containing mixed case and repeated spaces.
$ python pipeline_create_demo.py 'Route this QUERY' input=Route this QUERY answer=Pipeline received: route this query
The lowercase, single-spaced answer proves that the runtime query passed through QueryNormalizer and reached AnswerTemplate over the named connection.
Related: How to install Haystack with pip
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.